Archive for the ‘rails’ Category

Howto Rails Generators with SeaCucumber

Monday, August 20th, 2007

Over the weekend I spent some time working on some generators for SeaCucumber the javascript testing framework I am working on with Mike Ward. There is quite a bit of redundant HTML code that serves as plumbing to the framework and a generator would simplify that.

I found the most useful generator documentation is on the RoR wiki: Understanding Generators.

The generator code itself was very simple although it will be interesting to try for a more complex generator.

class SeacucumberGenerator < Rails::Generator::NamedBase
  def manifest
    record do |m|
      m.directory File.join('test/javascripts')
      m.directory File.join('public/javascripts')      

      m.file 'unittest.js', File.join("public", "javascripts", "unittest.js")
      m.file 'test.css', File.join("test", "javascripts", "test.css")

      m.template('javascript_template.js',
          File.join("public", "javascripts", "#{file_name}.js"))
      m.template('seacucumber_template.html',
          File.join("test", "javascripts", "#{file_name}_test.html"))
    end
  end
end

This generator does three basic things.

  • m.directory - Creates directories if necessary.
  • m.file - Copies static files without modification to the target directory.
  • m.template - Executes the two templates using ERB and writes them to the target location.

I used a NamedBase which take an argument. For example I might run this:

script/generate seacucumber MyFunction

Rails::Generator::NamedBase exposes a number of accessible attributes which are created from the argument. The most useful thing this class does is provide a bunch of attributes you can use from the manifest or template. It takes a trip to the source code to find out what they all are. To save time I thought I’d list them along with their values which follow standard rails inflection rules:

  • name MyFunction
  • class_name MyFunction
  • singular_name my_function
  • plural_name my_functions
  • table_name my_functions
  • class_path (blank)
  • file_path my_function
  • class_nesting (blank)
  • class_nesting_depth 0

Next I need to add a bit more sophistication to the templates I am generating to make them sensible for when someone is starting to develop a JavaScript function and needs to generate the Sea Cucumber plumbing.

BDD Model Validation using RSpec and extensions to Hash

Wednesday, August 8th, 2007

I avoid writing fixtures for testing. They are cumbersome to write and management (particularly after the fact) is a hassle. When testing a model’s validations I want to work with a generally static model that only changes one or two attributes which illustrate the test condition I am working on. To this end I followed Luke Redpath’s blog post on BDD and models in particular that uses a more behavior driven approach to testing a model and it’s attributes.

As such in my rails projects now I like to use this Hash extension when defining the validation rules for my model.

module HashExtension
  def except(*keys)
    self.reject { |k,v|
      keys.include? k.to_sym
    }
  end
end

And then I can define a model’s allowed behavior using RSpec. For example:

describe "a test model" do
  def valid_model_attr
     { :name => "name", :age => 21}.extend(HashExtension)
  end 

   it "should be valid" do
      Model.new(valid_model_attr).should be_valid
   end

   it "should be invalid without name" do
       model  = Model.new(valid_model_attr.except(:name))
       mode.should_not be_valid
       model.errors.on(:name).should_not be_nil
   end

  it "should be invalid if age is under 21" do
       model  = Model.new(valid_model_attr.except(:age))
       model.age = 20
       mode.should_not be_valid
       model.errors.on(:age).should_not be_nil
  end
end

I can add as many tests as I want without defining several test specific fixtures which need to be updated when the validation rules for a model change. Using an attribute hash and except I can update one hash.

This technique gets a little more complicated when testing a model with a has_many / belongs_to relationship but I recently had success with that on this site I developed (www.clowndown.net). In that case I defined a test hash a such:

  before(:each) do
    @team  = Team.new
  end

  def valid_team
    {
      :name => "team name",
      :contact_number => "call me here!",
      :people => [valid_person, valid_person],
      :knowledge_of_chicago => "pretty good",
      :why_will_win => "might get lucky",
      :relationship => "great"
    }.extend(HashExtension)
  end

  def valid_person
    person = Person.new
    person.attributes = {
      :email => "email@email.com",
      :name => "My Great Name",
      :shirt_size => "L",
      :hometown => "Chicago",
      :team => @team,
      :occupation => "occupation"
    }
    person
  end

And I was able to write specs like this that used an object relation that focused on testing of the behavior of the has_many / belongs_to relation.

  it "is invalid with 1 person" do
    @team.attributes = valid_team.except(:people)
    @team.people = [valid_person]
    @team.should_not be_valid
    @team.errors.on(:people).should_not be_nil
  end

Enumerable::group_by extension

Friday, August 3rd, 2007

I was working recently on a simple RoR website and needed an easy way to display columns of data on the front page. The easiest possible query was a simple find(:all) but after that I wanted to divide the items returned into groups that would be rendered separately. I wasn’t aware of any grouping available in enumerable so I did a bit of a search and ran across this discussion thread. I didn’t read the entire thread (and it’s mention of Set::classify which I was not aware of) but liked the group_by extension it proposed.

module EnumerableExtension
   def group_by(store=Hash.new)
     self.each do |elem|
       group = yield elem
       (store[group] ||= []) << elem
     end
     store
   end
end
Enumerable.send(:include, EnumerableExtension)

controller

@collections = Collection.find(:all).group_by do |collection|
        collection.name.to_sym
end
@left = collections[:left]
@right = collection[:right]

view

<%= render :partial => “left_well”, :locals => {:items => @left} %>
<%= render :partial => “right_well”, :locals => {:items => @right} %>

If I had to do this again I probably would have used Set::classify but I still like the group_by available in Enumerable and I like not being forced to create a set simply to group objects.

SeaCucumber - JavaScript Testing Tool based on Script.aculo.us

Monday, July 30th, 2007

Michael Ward and I are working on extracting the Script.aculo.us Test.Unit.Runner into a rails gem / plugin. We used this testing framework at our last client and we found it very useful. We would like an easier way to use the testing framework (in and our of Rails). We have called the project Sea Cucumber and are hosting it on RubyForge.

RailsRecipe: Preview a Site on a Different Day

Monday, July 30th, 2007

Recently we had a requirement to build a preview mode for a Ruby on Rails application. An article might be scheduled to appear on the front page in the future and the editorial staff needed a way to see how the site (front page, calendars, etc.) would look then. Our application works largely off Date.today to determine what content to display. We extended the Date, DateTime, and Time objects to transparently provide a user chosen date for the application as opposed to the system clock.

Ingredient One: PreviewController
This preview mode was activated through a preview controller. The preview action accepts a date parameter and placed that onto the user’s session. Following that the action redirected to the url being previewed.

def preview
  session[:preview] = {}
  if params[:date]
    begin
      date = Date.parse(params[:date])
      date_time = DateTime.parse(params[:date])
      session[:preview][:date] = date
      session[:preview][:datetime] = date_time
      session[:preview][:time] = Time.mktime(date.year, date.month, date.day)
    rescue
    end
  end
  redirect_to(params[:url] || "/")
end

Ingredient Two: Around Filter on ApplicationController
An Around Filter was added to ApplicationController that looked for a session[:date] attribute on all page requests. If the session object was present the filter placed the user’s preview date onto Thread.current.

around_filter :preview_date
def preview_date
    begin
    Thread.current[:preview] = session[:preview]
      yield
    ensure
    Thread.current[:preview] = nil
    end
  end

Ingredient Three: Overriding System Date
The final ingredient was extending Date.today, DateTime.now, and Time.now with an alias_method_chain to use the Thread.current objects when present instead of using the CPU clock.

DateExtension

module DateExtension
  def self.extended(object)
    class << object
      alias_method_chain :today, :preview
    end
  end

  def today_with_preview
    (Thread.current[:preview] and Thread.current[:preview][:date]) ?
        Thread.current[:preview][:date] : today_without_preview
  end
end

Similar extensions were done for DateTime and Time. I would have liked to have only extended Time.now but that was not sufficient when testing Date and DateTime.

This solution transparently made our application behave in a user preview mode. As far as the application was concerned the date was the preview date with little change beyond the above code which plugs in transparently to the rest of the application. Our ActiveRecord queries all keyed off Date.today and our code was not polluted by introducing an ApplicationDate class that we might have been forced to use in Java or .NET.