Hoopla!

now with extra whiz-bang!

Hoopla!

Howto: Make CSS Files Act Like Rails Partials

August 20, 2006 · 10 comments

Gone are the days when you could just throw a few CSS definitions in something called ‘style.css’ and call it a good day’s work. With the arrival of semantic html where none of the styling is done inline it has become important to practice good coding habits while creating CSS styles.

It was embarrasingly recently that I had the habit of putting all my CSS into one file. I didn’t realize that it’s the same bad habit of making programs with only one (usually massive) file of source code. Learning to partition the code properly is a natural development step and it’s just as important for CSS as Ruby, Java, C, or any other.

How I do CSS: —-

What I’ve found is I have a few default CSS files named after the following pattern:

  • application.css: usually will overwrite bad default values (like margins on forms)
  • structure.css: the layout of the blocks that make up my layout (body, #wrap, #head, #main, #foot, etc.)
  • fonts.css: font-family, font-size, etc. for all the popular styles
  • forms.css: whether using label/input pairs or definition lists I need lots of styling for forms
  • colors.css: super important to keep all color stuff together so you can both keep the colors in harmony and replace this file with some alternate color theme

These worked for a while but then I realized that I had little pieces of code that were needed that didn’t belong in any of these. I need to specify that a certain logo floats to the right or a users profile just happens to need a different background color.

The answer to this comes in using CSS files kinda like Rails uses partials. Some encapsulized code that has usefulness in a certain portion of your app. I did this at first by making stylesheets named after each of my controllers. This worked for about three seconds until I realized that I needed some conditional way to include them.

Conditionally including CSS files —- Including stylesheets conditionally turned out to be a fairly easy task. I’ll outline the solution here using extractions from my code:

1
2
3
4
5
6
class ApplicationController < ActionController::Base

  attr_accessor :stylesheets
  before_filter {|c| c.stylesheets ||= []; c.stylesheets << c.class.name.sub('Controller','').downcase }

end

This adds an instance var @stylesheets that starts as an array. It is given, by default the name of the currently active controller. note: this prints the derived class’s name, not ‘application’.

This means that you can now add stylesheets to the soon-to-be-rendered view anywhere in your code you want.

1
2
3
4
5
6
class UsersController < ApplicationController
  # if you have a stylesheet that governs just one action
  def show
    stylesheets << 'users_show'
  end
end

And it’s surprisingly simple to output the required code to the browser. Just add one line to your app/views/layouts/application.rhtml file:

1
2
3
4
5
6
7
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'structure'', :media => 'all' %>
<%= stylesheet_link_tag 'fonts'', :media => 'all' %>
<%= stylesheet_link_tag 'forms', :media => 'all' %>
<%= stylesheet_link_tag 'colors', :media => 'all' %>
<%= @stylesheets.collect { |file| stylesheet_link_tag file }.join("\n") if @stylesheets %>
<%= javascript_include_tag :defaults %>

This is so simple that there’s no real point in turning it into a plugin but if anybody wants it that way just let me know.

Update: The version I ended up using in my app did some testing for the existence of the file before it included it as a stylesheet. I decided it was more important to avoid the 404 errors (especially the way they cruft up the logfile) than it was to have the app disk-optimized. So here’s my new application_controller:

1
2
3
4
5
6
7
8
9
10
class ApplicationController < ActionController::Base

  attr_accessor :stylesheets
  before_filter do |c|
    c.stylesheets ||= []
    klass = c.class.name.sub('Controller','').downcase
    c.stylesheets << klass if File.exists?("#{RAILS_ROOT}/public/stylesheets/#{klass}.css")
  end

end

→ 10 comments Tags:

RESTless design - do I need a session controller/model?

August 19, 2006 · 11 comments

Today the underground team of rorBB (an as-yet-unannounced Rails BB) saw that the application was almost completely RESTful – except in it’s login/logout actions on the users controller. It was Ben who pointed out that it was the single thing destroying our otherwise pretty URLs and we decided to do something about it.

I admit that I’ve been slow to get on the REST bandwagon. Why is that you proverbially ask? To tell the truth it’s largely because I CAN’T FIND A DEFINITION ANYWHERE. I went to Wikipedia, oracle of all wisdom, only to be told how it differs from RPC. I have a long, rich history of coding in PHP. That means I dont know shit about real programming and I had to then go and lookup RPC.

So forgive me if I act like this is the biggest, most amazing discovery ever. I’m just really happy that I discovered it before everybody had moved on to the Next Big Thing©.

So, regarding our controllers: the code we had was fairly generic. On the users controller there were the standard `new`, `create`, `update`, `edit`, etc. actions but then we had two others:

UsersController with RESTless actions:

  def login
    if request.post?
      user = User.authenticate(params[:name], params[:password])
      unless user.nil?
        session[:user_id] = user.id
        redirect_to users_url
      else
        flash[:error] = 'Username or password is incorrect'
      end
    end
  end

  def logout
    reset_session
    redirect_to :action => 'login'
  end

In other apps I’ve written this was pretty standard. Sometimes they’d be in a ‘users’ controller, sometimes in an ‘auth’ controller that contained just these two actions.

It wasn’t until I saw a lot of high-quality REST programming that I started to see there was a better way of doing things than just throwing actions in controllers that might fit.

So we talked about the possibility of overhauling our code to allow for a sessions controller that followed the basic verbs of REST. I figured it would take a while but might eventually pay off.

Boy was I surprised to find that even an idiot like me could put it together quickly. Here’s the final code from the sessions controller:

class SessionsController < ApplicationController

  def create
    user = User.authenticate(params[:name], params[:password])
    unless user.nil?
      session[:user_id] = user.id
      redirect_to users_url
    else
      flash[:error] = 'Username or password is incorrect'
      redirect_to new_session_url
    end
  end

  def destroy
    reset_session
    flash[:notice] = "You are now logged out" 
    redirect_to new_session_url
  end

end

If you ignore a couple of slight improvements in the second piece of code you might notice that I ONLY RENAMED THINGS. Can you believe it? Our whole app fit back into REST with some very minor changes.

So, what’s the benefit of the sessions controller? Well, besides getting a free API (though you may never need to build a session over API) the tests got simpler, the routes.rb file got WAY, WAY simpler (back to just one line of code for all our controllers), and our team got just a little bit happier.

And who doesn’t like to be happy?

Update: By request, here’s the original and the modified routes.rb file:

1
2
3
4
5
6
7
8
9
10
11
12
13
ActionController::Routing::Routes.draw do |map|

  map.connect '', :controller => 'forums'

  map.resources *%w[forums topics posts users]

  map.resources :users, :collection => { :login => :get }
  map.resources :users, :collection => { :login => :post }
  map.resources :users, :collection => { :logout => :post }

  map.connect ':controller/:action/:id'

end
1
2
3
4
5
6
7
8
9
ActionController::Routing::Routes.draw do |map|

  map.home '', :controller => 'forums'

  map.resources *%w[forums topics posts users sessions]

  map.connect ':controller/:action/:id'

end

You might notice that even in the first file the routes are using the new map.resources command (normally used for this REST stuff).

→ 11 comments Tags:

Rails Integration Testing - How to learn

July 30, 2006 · 0 comments

I decided to try filling up my ./test/integration folder today because it was empty and I felt I must have been missing out. I can’t stand the idea that all the other Rails devs are enjoying some feature I haven’t found out about yet.

Integration tests seem pretty handy because functional tests are pretty basic – effectively unit tests for controllers rather than tests of real interactivity. So I figured I’d google around a bit to discover this integration testing thing and this is what I found:

(which currently doesn’t have a web server so I had to grab the page from Google’s cache).

That’s it. Those three meager pages are all the info I could find on Rails integration testing. In fact, I have reason to believe that the Rails Recipes book just re-used the code from Jamis Buck’s post – so even they don’t have any more for us.

Well, that’s not acceptable. I’m trying to follow a particular user’s experience through my app and I want to get this working without having to resort to SeleniumOnRails or some other browser-emulator.

My code so far

I’ve got a particular user type `Athlete` that logs in and checks their profile. They update a couple pieces of information and we check that the info was saved. Should be easy. (note: to understand this code check out Jamis’s article – you might need to look in the Google Cache for it)

class AthleteSessionTest < ActionController::IntegrationTest

fixtures  :users,
          :athlete_contacts,
          :coach_contacts,
          :junior_colleges,
          :combines
def test_randy_visits
  randy = new_session_as('Cliffton Player')
  randy.goes_to_profile_edit
  randy.updates_profile_with :first => 'Randy', :last => 'BadAsserson'
  randy.
end
private
module AthleteSessionDSL
def goes_to(place)
  get place
end
def goes_to_login
  get '/auth/login'
  assert_response :success
  assert_template 'auth/login'
end
def goes_to_profile_edit
  get '/athletes/profile_edit'
  assert_response :success
  assert_template 'athletes/profile_edit'
end
def logs_in_as(athlete)
  @current_user = users(athlete)
  post 'auth/login', :user => {:email => @current_user.email, :password => @current_user.password }
  assert_response :redirect
  follow_redirect!
  assert_template 'athletes/profile'
end
def updates_profile_with(contact)
  post 'athletes/update_contact', :contact => contact,
                                  :athlete_id => @current_user.id
  assert_flash_equal 'This profile has been updated with new information', :success
  assert_response :redirect
  follow_redirect!
  assert_template 'athletes/profile'
  contact.each { |k,v| assert_equal v, @current_user.profile.attributes[k.to_s] }
  end
end
end
def new_session
  open_session do |sess|
    sess.extend(AthleteSessionDSL)
    yield sess if block_given?
  end
end
def new_session_as(person)
  new_session do |sess|
    sess.goes_to_login
    sess.logs_in_as(person)
    yield sess if block_given?
  end
end
end

The Problem

So this is fine. So far it’s working great. The problem is that I’ve set it up so that everything my user does is part of my AthleteSessionDSL module in it’s own method. Everything `randy` does has to be a method within the `AthleteSessionDSL` module.

I wanted to try some non-modular navigation to give Randy a bit more of a free spirit. I thought I’d see if I could take some of the functionality out of the module and do it directly:

  def test_randy_visits
    randy = new_session_as('Cliffton Player')
    randy.goes_to_profile_edit
    randy.updates_profile_with :first => 'Randy', :last => 'BadAsserson'
    # trying some direct session navigation outside of the randy session object
    get 'athletes/profile'
    assert_response :success
    assert_template 'athletes/profile'
  end

What happened? Well, since `randy` is really just an instance of a Rails session with some handy methods attached to it I immediately left the session when I stopped using `randy`. My request to get the “athletes/profile” page resulted in a redirect to my login page because that method is authentication protected.

Well, what’s the solution? Do I have to make a million methods for everything I want Randy to do?

The Solution

It turns out to be quite simple. The methods we have been using like `assert_response` and `get` (and all the rest) are, in fact, methods of the session. The `open_session` call in the `new_session` module creates a session object with all the standard methods on it.

So not only can we group functionality into methods and roll those into included modules – we can also call things directly:

  def test_randy_visits
    randy = new_session_as('Cliffton Player')
    randy.goes_to_profile_edit
    randy.updates_profile_with :first => 'Randy', :last => 'BadAsserson'
    randy.get '/athletes/profile'
    randy.assert_response :success
    randy.assert_template 'athletes/profile'
  end

→ 0 comments Tags:

Automatically checking for php parse errors

July 27, 2006 · 0 comments

I’ve been running into a problem on one of my multi-developer sites where we’ve been unable to break the habit of occasionally allowing parse errors to show up on one of our php sites. We mean well, we try to avoid it, but sometimes a quick fix is necessary and we don’t always have time to do testing (it should be noted that we don’t have a testing harness of any kind for this site).

Just last weekend I was horrified to find that I’d left a parse error in a small, seldom-visited part of our site for several days. No users encountered it but it prevented some automated tasks from working as they’re supposed to.

It’s become clear that we need more than just to try harder – we need technology to watch our backs. Do accomplish this I’ve developed a bash script that takes directories as arguments and has php parse the contents of every php script in that folder’s structure.

The Code: -
#!/bin/bash

check_dir() {
  for file in $*/*.php
   do
    if [ -d $file ]; then
      echo "going into $file" 
      check_dir $file
    else
      if [ "`php -l $file`" = "No syntax errors detected in $file" ]; then
        echo "valid in $file" 
      else
        echo "$file invalid" 
      fi
    fi
  done
}

for dir in $*
 do
  check_dir $dir
done

It’s very simple – but it’s all that’s required to get started. From here I’m going to replace the echo’ed output with tasks. Specifically, a php file will be run when files are found to be invalid and both an email and an SMS will be sent to the developer’s phones.

We’re setting this script to run every 5 minutes, hopefully no weekends will pass from now on without being error- (at least parse error-) free.

→ 0 comments Tags:

An Embarrassing History of Code

June 29, 2006 · 0 comments

I’m expanding an application I built for a client a couple of years ago. I’m currently a Rails developer with a whole bunch of Best Practices in my toolbox but at the beginning of 2005 I’d never seen the inside of a well-designed large data web app.

Looking at some of this code I cringe and catch myself thinking “I hope nobody else ever sees this crap.” It gets the job done, but it’s just so damn brittle. Here are some things I did very, very wrong:

1. Posted form variables aren’t enclosed in a parent var. I have things like `$_REQUEST[‘education_level’]` and `$_REQUEST[‘address’]` instead of `$_REQUEST‘user’`, etc.

2. Application-layer lists of data. I’ve got stuff that should, by all rights, be in its own db table in some php array.

3. Everything is overly complex and connected. When I could have just written html files (‘views’ in MVC talk) I opted for functions like tr() and td() and make_table_row() that take wild and varying arguments.

Did I learn anything from looking at this code? Yeah. The only thing that I earn by taking the shortcut while coding is the chance to make a stupid blog post about it. I’d give my left arm to know then what I know now. Well, actually no. I’d give, like, $200. Not an arm – then I couldn’t code.

→ 0 comments Tags:

Programmer Juice (or: How exersize improves my code)

June 20, 2006 · 2 comments

I’ve spent the last year (ever since I started coding in Rails) realizing over and over that I’m way behind in learning the ‘best practices’ that have come to define quality in my industry. MVC architecture, DRY principles, Agile Development, Test-Driven Development, working without distractions (closing my Gmail tab!), version-controlling everything, automating deployment, life-hacking my way to productivity.

It’s been a long trudge uphill but I’m now feeling comfortable with how my work habits reflect the investment I’ve made in them. I’m sleeping earlier, working faster, and feeling better about the code I write.

And yet, there was something missing. My code has improved a lot but little was done to improve my attention span, my ability to stay alert even when I’m not in “flow”), and to avoid the spectre of depression that has been the biggest detractor from my professional life. At first I thought the solution to all these would be some mental excersize or emotional element but I was wrong: it was purely physical.

Introducing: My Programmer Juice:

I’ve started a routing of daily excersize. I know, it’s revolutionary isn’t it? I’ve often done daily excersize before but it’s not until now that I’m realizing that excersize is fundamentally necessary for me to write the highest quality code.

My biggest roadblocks in development come when I don’t have a clear list of tasks in front of me. I’m facing some big ugly monster of a legacy script that I’ve inherited and I don’t know where to stop. The difference between my good days and my bad hinge on my ability to persevere and find some entry point into accomplishing the mammoth task at hand. And that all hinges on whether I’ve got the endorphins running and giving me the edge.

I’ve found I don’t have to do that much in the way of excersize, I just need to exhaust myself somehow. I typically run every other day (to spare my knees) and do little workout stuff on my livingroom floor on alternate days. It’s become my most rewarding habit because it only takes 20 minutes a day and it improves all the work I do on both work and hobby projects.

So that’s my best practice. I’m sure lots of people do it but I never ran across it in all the books I read on “how to code awesomely” – so here it is.

→ 2 comments Tags:

States with abbreviations

June 12, 2006 · 2 comments

Sometimes it’s hard to come by this list when you need it. Hope this saves somebody some time. Here are the 50 states with their two-letter abbreviations in a Ruby-formatted array.
1
2
3
4
5
6
7
8
9
10
11
[ ["ALABAMA","AL"],
  ["ALASKA","AK"],
  ["AMERICAN SAMOA","AS"],
  ["ARIZONA ","AZ"],
  ["ARKANSAS","AR"],
  ["CALIFORNIA ","CA"],
  ["COLORADO ","CO"],
  ["CONNECTICUT","CT"],
  ["DELAWARE","DE"],
  ["DISTRICT OF COLUMBIA","DC"],
  ["FEDERATED STATES OF MICRONESIA","FM"],
  ["FLORIDA","FL"],
  ["GEORGIA","GA"],
  ["GUAM","GU"],
  ["HAWAII","HI"],
  ["IDAHO","ID"],
  ["ILLINOIS","IL"],
  ["INDIANA","IN"],
  ["IOWA","IA"],
  ["KANSAS","KS"],
  ["KENTUCKY","KY"],
  ["LOUISIANA","LA"],
  ["MAINE","ME"],
  ["MARSHALL ISLANDS","MH"],
  ["MARYLAND","MD"],
  ["MASSACHUSETTS","MA"],
  ["MICHIGAN","MI"],
  ["MINNESOTA","MN"],
  ["MISSISSIPPI","MS"],
  ["MISSOURI","MO"],
  ["MONTANA","MT"],
  ["NEBRASKA","NE"],
  ["NEVADA","NV"],
  ["NEW HAMPSHIRE","NH"],
  ["NEW JERSEY","NJ"],
  ["NEW MEXICO","NM"],
  ["NEW YORK","NY"],
  ["NORTH CAROLINA","NC"],
  ["NORTH DAKOTA","ND"],
  ["NORTHERN MARIANA ISLANDS","MP"],
  ["OHIO","OH"],
  ["OKLAHOMA","OK"],
  ["OREGON","OR"],
  ["PALAU","PW"],
  ["PENNSYLVANIA","PA"],
  ["PUERTO RICO","PR"],
  ["RHODE ISLAND","RI"],
  ["SOUTH CAROLINA","SC"],
  ["SOUTH DAKOTA","SD"],
  ["TENNESSEE","TN"],
  ["TEXAS","TX"],
  ["UTAH","UT"],
  ["VERMONT","VT"],
  ["VIRGIN ISLANDS","VI"],
  ["VIRGINIA ","VA"],
  ["WASHINGTON","WA"],
  ["WEST VIRGINIA","WV"],
  ["WISCONSIN","WI"],
  ["WYOMING","WY"] ]

→ 2 comments Tags:

Krugle - code searching

June 09, 2006 · 0 comments

Google lets you see your search history and a day-by-day total of how many searches you do. I’m pretty much always over 40 searches/day thanks to looking for code and trying to solve various development problems.

Enter Krugle. I forget how I got signed up for their beta but I just got the invite and checked it out. At first I thought “search engine for developers. cool.” That quickly turned into “holy shit. this changes everything.” They’ve clearly caught on to the fact that developers have different search needs than the regular consumer.

First off, Krugle lets you choose what language you want to search. THIS IS HUGE. When I’m working with Ruby I don’t care that Java has an implementation of “GoWild.new” – it can’t possibly help. The default is to search with a broad scope, but narrowing down by language is a sure way to get to a solution faster.

Secondly, they’ve parsed all the code they’re searching through and have divided information up based on whether it appeared in a comment, in a class definition, a function call or some other part of the code.

But the thing that really knocked me senseless was their wise use of AJAX. Its not clunky and it allows the whole search process to run that much faster.

Add all that together with a beautiful design, a good community, and a dedication to help developers find answers – and you’ve got a winner.

You can see a demo of their operation on their website

Following is a screenshot of a search I did on Krugle:

→ 0 comments Tags:

Ten Commandments in Hexidecimal Code

May 28, 2006 · 0 comments

Far be it for anyone, human or computer, to be ignorant of the will of God. Translation of the bible has been necessary for every language and it has always been encouraged by scholars and leity alike. It is understood that to communicate deeply with someone and let them know you understand them you must speak their ‘heart language’ – their native tongue or the language that they are most emotionally attached to.

Therefore, I hereby post the Ten Commandments in hexidecimal code. Let no ASCII-rendering digital text processor claim it was ignorant to the restriction against “coveting your neighbor’s ass.”

(King James Version with full punctuation and verse markings converted to ASCII hex)

4920616D20746865204C4F524420796F
757220476F642C2077686F2062726F75
67687420796F75206F7574206F662045
677970742C206F7574206F6620746865
206C616E64206F6620736C6176657279
2E2020596F75207368616C6C20686176
65206E6F206F7468657220676F647320
6265666F7265206D652E2020596F7520
7368616C6C206E6F74206D616B652066
6F7220796F757273656C6620616E2069
646F6C20696E2074686520666F726D20
6F6620616E797468696E6720696E2068
656176656E2061626F7665206F72206F
6E207468652065617274682062656C6F
772E2020596F75207368616C6C206E6F
7420626F7720646F776E20746F207468
656D206F7220776F7273686970207468
656D3B20666F7220492C20746865204C
4F524420796F757220476F642C20616D
2061206A65616C6F757320476F642C20
70756E697368696E6720746865206368
696C6472656E20666F72207468652073
696E206F662074686520666174686572
7320746F207468652074686972642061
6E6420666F757274682067656E657261
74696F6E206F662074686F7365207768
6F2068617465206D652C206275742073
686F77696E67206C6F766520746F2061
2074686F7573616E642067656E657261
74696F6E73206F662074686F73652077
686F206C6F7665206D6520616E64206B
656570206D7920636F6D6D616E646D65
6E74732E2020596F75207368616C6C20
6E6F74206D697375736520746865206E
616D65206F6620746865204C4F524420
796F757220476F642C20666F72207468
65204C4F52442077696C6C206E6F7420
686F6C6420616E796F6E65206775696C
746C6573732077686F206D6973757365
7320686973206E616D652E202072656D
656D6265722074686520736162626174
6820646179206279206B656570696E67
20697420686F6C792E20205369782064
61797320796F75207368616C6C206C61
626F7220616E6420646F20616C6C2079
6F757220776F726B2C20627574207468
6520736576656E746820646179206973
2061207361626261746820746F207468
65204C4F524420796F757220476F642E
20204F6E20697420796F75207368616C
6C206E6F7420646F20616E7920776F72
6B2C206E65697468657220796F752C20
6E6F7220796F757220736F6E206F7220
64617567687465722C206E6F7220796F
7572206D616E73657276616E74206F72
206D61696473657276616E742C206E6F
7220796F757220616E696D616C732C20
6E6F722074686520616C69656E207769
7468696E20796F75722067617465732E
2020466F7220696E2073697820646179
7320746865204C4F5244206D61646520
7468652068656176656E7320616E6420
7468652065617274682C207468652073
65612C20616E6420616C6C2074686174
20697320696E207468656D2C20627574
20686520726573746564206F6E207468
6520736576656E7468206461792E2020
5468657265666F726520746865204C4F
524420626C6573736564207468652073
6162626174682064617920616E64206D
61646520697420686F6C792E2020486F
6E6F7220796F75722066617468657220
616E64206D6F726865722C20736F2074
68617420796F75206D6179206C697665
206C6F6E6720696E20746865206C616E
6420746865204C4F524420796F757220
476F6420697320676976696E6720796F
752E2020596F75207368616C6C206E6F
74206D75726465722E2020596F752073
68616C6C206E6F7420636F6D6D697420
6164756C746572792E2020596F752073
68616C6C206E6F7420737465616C2E20
20596F75207368616C6C206E6F74206D
75726465722E2020596F75207368616C
6C206E6F7420676976652066616C7365
2074657374696D6F6E7920616761696E
737420796F7572206E65696768626F72
2E2020596F75207368616C6C206E6F74
20636F76657420796F7572206E656967
68626F72277320686F7573652E202059
6F75207368616C6C206E6F7420636F76
657420796F7572206E65696768626F72
277320776966652C206F722068697320
6D616E73657276616E74206F72206D61
696473657276616E742C20686973206F
78206F7220646F6E6B65792C206F7220
616E797468696E672074686174206265
6C6F6E677320746F20796F7572206E65
696768626F722E

→ 0 comments Tags: