before(:all) doesn’t do what you’d expect

Like many before me, last week I was bitten by RSpec’s interesting implementation of before(:all). Although the documentation (http://rspec.info/documentation/) clearly states that before(:all) ‘is run once and only once, before all of the examples and before any before(:each) blocks’, it in fact runs once for every context in the scope. This might be useful, but in every situation I’ve seen it isn’t. The documentation goes on to say

Warning: The use of before(:all) and after(:all) is generally discouraged because it introduces dependencies between the Examples. Still, it might prove useful for very expensive operations if you know what you are doing.

which implies that it really does only get invoked once – you wouldn’t want an expensive operation to be executed for every context.

RSpec’s author has explained that the behaviour is actually as he intended it and proposed to change the documentation, but so far that hasn’t happened.

So, why did I need before(:all) to operate as advertised? I have written a test that collects some metadata and then iterates over it to generate common Examples for every attribute in the metadata. This is great because as the metadata is expanded, the test suite expands automatically ensuring consistent implementation of all of my API. The metadata is nested, so it makes sense to take advantage of RSpec’s contexts to nest the Examples so that the generated test results are self-documenting. Unfortunately, the setup for this test involves one expensive operation: login to the API to get the metadata and prepare to probe every attribute. Logging in takes several seconds. It should be faster, but that’s a different story – my test expands to nearly 5000 examples so even if logging in only took half a second we would still waste over 40 minutes. As it is, with RSpec running my expensive operation for every context, the test was taking over 4 hours!

A blog post helped me on my way, but it focussed on resetting the database between tests and didn’t work with Rails RSpec. I modified it to work with Rails RSpec and refactored it to make it useable in any test. Using it is simple; add the following to your spec_helper.rb file:

# HORRIBLE HACK to work around RSpec's broken before(:all) which does not get run
# once as per the documentation - it runs once at the level defined and once per
# sub-context!
# Extend ActiveSupport::TestCase with a 'before_once' that takes a block just like
# before(:all), but only gets called once at the top-level.
# Inspired by http://sickill.net/blog/2009/11/23/quick-and-dirty-hack-for-rspec-before-all.html
# but recast so that it can be used in any test.
class ActiveSupport::TestCase
  # Like before(:all), but only runs once at the top context and not for every
  # sub-context. It keeps track of the classes that are registered and does not
  # invoke the block in subclasses.
  def self.before_once
    _before_once_class_parents = []
    self.instance_variable_set(:"@_before_once_class_parents", _before_once_class_parents)
    before(:all) do
      unless _before_once_class_parents.select{|g| self.class.to_s =~ /^#{g}/}.any?
        _before_once_class_parents << self.class.to_s
        yield
      end
    end
  end
end

It adds a before_once method to ActiveSupport::TestCase that you can use just like before(:all), but it guarantees that it will only run once per scope.

So now, using before_once, I can ensure that my test only logs into the API once and not once per attribute. The net effect is a test that runs in about 90 seconds rather than taking half a day and climbing!

Keeping custom Rails model validations DRY

[tweetmeme]
There are a few blog posts on writing model validators – this is another one which follows the DRY (don’t repeat yourself) philosophy.

Suppose you want to check that an attribute value is present in a table of tags in our database. Now, this isn’t a particularly good use case as you could use validates_inclusion_of, but lets assume that we want to DRY that up a bit.

Let’s write the validation to make the following work:

validates_inclusion_in_tags :primary_tag, :secondary_tag

The validation method would look a little bit like this:

def validates_inclusion_in_tags(*attr_names)
  validates_each attr_names do |record, attr_name, value|
    acceptable_values = Tag.find.all.map{ |obj| obj.name }
    record.errors.add(attr_name, "does not match any available values") unless acceptable_values.include?(value)
  end
end

What’s going on here? Firstly we attach our custom validation to the model using validates_each. This runs the block for each of the attribute names (symbols) in the attr_names array (note that we have used the splat operator in our argument list to flatten the arguments into an array so we can pass a list of attribute to validate). The validation block is passed three arguments: the record being validated, the attribute name (as a symbol) and the current value of the attribute that is being validated. We then execute a find on our Tag model to get an array of AR records which we then map to extract the name of each object into an array. Finally we associate an error to the attribute that we are validating if its value is not in the list of acceptable values we have created.

To make the validation method a little more configurable and a little more generic we can allow the validation method to be called with a hash of options. Maybe we want to define a different error message, supply a different model to validate against, or supply a different attribute to be selected from our model. This can be easily achieved like so:

def validates_inclusion_in_model(*attr_names)
  # Set up any default configuration options and merge on any passed to the validation
  configuration = {
    :message => "does not match any available values",
    :model => Tag,
    :model_attribute => :name,
  }.merge(attr_names.extract_options!)

  validates_each attr_names do |record, attr_name, value|
    acceptable_values = configuration[:model].find.all.map{ |obj| obj.send configuration[:model_attribute] }
    record.errors.add(attr_name, configuration[:message]) unless acceptable_values.include?(value)
  end
end

You’ll note that the default configuration options are merged with the configuration options that were passed into the validation method. This (very common) technique makes use of the extract_options! method which returns either the last item of an array if it is a Hash or an empty Hash.

OK, that’s all good – what’s the best way to get this method available to our models. The best ways are as follows: Either create a validators.rb file in config/initializers and open up the ActiveRecord::Base class like so:

ActiveRecord::Base.class_eval do
  # define your validation methods here
end

Or, the way I prefer is to to create a validations.rb file in your lib directory and use the following code:

module Validations
  # Extend the caller with the ClassMethods module
  def self.included(base) # :nodoc:
    base.extend ClassMethods
  end

  module ClassMethods
    # define your validation methods here
  end
end

Now you can pick and chose which validations are available to your models rather than extend every model that inherits from AR Base by using:

class Person < ActiveRecord::Base
  include Validations
end

Of course, if all of your AR models inherit from a shared base class which in turn inherits from AR Base, then you could also include the validations model at your shared base model level to give all your models access to your new validations.

Note on performance

The validation method here would load all the Tag records into AR objects in memory just to throw them away again. Also, this problem is exacerbated if you were to compare two attributes against the list as the objects are loaded for each call into validates_each.

Reading Geek Night

[tweetmeme]
An open community event for techy-types is getting underway in Reading.  These events are for people to get together in an informal setting, share knowledge and have a couple of beers!

The first of these monthly events is happening next Tuesday on the 10th November.  Add http://readinggeeknight.com/ to your bookmarks and check it out regularly for updates.  You can also follow @rdggeek on twitter or the #rdggeek hashtag.

Talks lined up for the first event are:

Who do you think you are? Ben NunneyThe internet is everywhere, and all of us in the tech community are connected in some way, shape or form. But how powerful is your digital identity, and what does it actu- ally matter to you? The talk will be a brief look at what your digital identity is, was, and can be – who are you, in the eyes of the internet? Does your boss really care what you had for lunch?

A practical introduction to Ruby on Rails Chris TingleyThis live code demonstration will take you from your first line of Ruby code, through some fundamental features of Rails with pit stops in MVC, CRUD, REST, automated testing and meta-programming, crossing the finish line with a functional web app! Hold on to your hats, all this WILL happen in 15 minutes.

What Is Windows Azure? Dom GreenWhat is Windows Azure? How will cloud computing change how we develop applications, manage our IT infrastructure, or even set up an online business? This whirl-wind talk will introduce Windows Azure, Microsoftʼs cloud computing platform.

Coding for kids Jim Anning – Coding is not routinely taught in UK schools until 6th form level. How can we encourage a new generation of kids to code? A quick introduction to MIT’s Scratch programming environment.

Installing modporter for Rails on Ubuntu Hardy Heron

[tweetmeme]
It’s not an uncommon scenario for Apache to not play nicely with Rails when dealing with multipart forms and large uploaded files. There are a few solutions out there, but the best we have seen is modporter from the guys at ActionRails.

So, you’ll need a few things to beef up Apache; if you haven’t got them already:

sudo apt-get install libapache2-mod-apreq2
sudo apt-get install libapreq2-dev
sudo apt-get install apache2-threaded-dev

Now fetch the latest ModPorter from github from here. Once you have downloaded and unpacked this, edit the Rakefile and change the apxs variable to apxs2, then type:

cd modporter-directory
rake

And, you’ll probably want the modporter plugin for Rails to give you a nice consistent interface for uploaded files rather than differentiate String IOs and UploadedFile objects.

If you have git:

script/plugin install git://github.com/actionrails/modporter-plugin.git

Otherwise, download the tar from here, unpack and stick in your vendor/plugins directory.

Now configure Apache to use load some additional modules, edit your apache.conf file to include (your path to modules may be different):

LoadModule apreq_module modules/mod_apreq2.so
LoadModule porter_module modules/mod_porter.so

Comparison <=> operator on string attributes of ActiveRecord objects

[tweetmeme]
Here’s a little gotcha for you which caught me out by surprise, obvious when you think about it, but irritating if you miss it: the ruby String comparison operator does case-sensitive comparisons.

# Good:
['C', 'b', 'A', 'D'].sort #=> ['A', 'b', 'C', 'D']

# Bad (what <=> gives you out the box):
['C', 'b', 'A', 'D'].sort #=> ['A', 'C', 'D', 'b']

For a nice efficient implementation of a AR model comparitor use the String#casecmp method for a case-insensitive comparison:

class MyObject
  def <=>(other)
    self.my_attribute.casecmp(other.my_attribute)
  end
end

Overriding fundamental ActiveRecord methods (like destroy) whilst preserving callbacks

[tweetmeme]
OK, so you’ve decided that it’s a good idea for you to override one of the fundamental methods on one of your ActiveRecord models. But in doing so, you have discovered that all of a sudden your callbacks have stopped working.

The reason for this is when your class is loaded, ActiveRecord::Callbacks have already hooked in the callback methods around the relevant methods using alias_method_chain meaning that the once named destroy method is now called destroy_without_callbacks.

So, to actually override the destroy method, use the following code:

def destroy_without_callbacks
  unless new_record?
    # do your code here
  end
  freeze
end

Note that the implementations will differ for other methods e.g. create, so you should check the ActiveRecord base class to see what should be in there.

Profiling your Ruby on Rails application

[tweetmeme]
At some point you want to check that there aren’t any really slow bits of your application and even if there aren’t, you might like to know where to spend effort in optimising.  Luckily for you the script/performance/request script coupled with ruby-prof gem produces very useful profiling reports.

Getting script/performance/request to work with the standard gem (version 0.6.0) is troublesome impossible, however that nice man Jeremy Kemper from 37signals has published a version (0.6.1) that does work! Hurrah!

You can just install jeremy-ruby-prof from the git gem repo, however this installs the gem with the wrong name if you want to use it with script/performance/request. It can be done by downloading the gem, building the gem and then installing it from the local gem. E.g. (on a Ubuntu box):

wget http://github.com/jeremy/ruby-prof/tarball/89e2a4bc3f5881519a2fe1e5c5c05f7e1e0acf6e
tar -xf jeremy-ruby-prof-89e2a4bc3f5881519a2fe1e5c5c05f7e1e0acf6e
cd jeremy-ruby-prof-89e2a4bc3f5881519a2fe1e5c5c05f7e1e0acf6e
rake gem
sudo gem install pkg/ruby-prof-0.6.1.gem

Ta da! Installed with the right name and now you will be able to create yourself a benchmarking environment and profiling script like the tutorial at Railscase – Request Profiling.

By default, two outputs are generated in your tmp/ directory. An HTML call graph (see Reading Call Graphs) and a flat profile (txt) file.