Making RSpec, Rake, and Bundler play well together

Posted by Rick DeNatale Fri, 25 Jun 2010 11:45:00 GMT

I'm working a project which uses RSpec, Bundler, and Rails 2.3.4.

The team has been struggling with the changes in gem management with Bundler. The "Old School" rails way to configure gems for different rails environments is to conditionally execute gem.configure based on the rails environment. Then use the rake:gems:install task to gem install the gems, one for each environment.

Bundler does things a bit differently. It uses groups to segregate gems by environment, so you name the gems needed for the test environment in a :test group. When you run bundle install, it installs all of the gems for all of the groups (unless you opt out of some groups. It uses the groups at run time, to selectively expose only gems applicable to the current environment.

This cause problems for things like the spec rake task. If you run

rake spec

without explicitly setting the Rails environment.

The way the spec rake task works is that it depends on a rails provided rake task called :environment, which 'boots' the rails environment, then the spec_helper file which each spec whould require, sets ENV['RAILS_ENV'] to 'test' if it's not already set, then it requires needed gem code, like 'spec/rails'.

In the old school approach this works, since those gems are installed and visible. With bundler, it fails since they won't be exposed, since the bundler environment got set up using the development environment, so gems in the :test group aren't available.

In an attempt to fix this, other members on the team were mixing things up, doing things like putting conditional tests of the rails environment in the Gemfile, and then running the bundle command with different overrides of the RAILS_ENV environment variable.

But that way lies madness.

This morning I worked out a solution which involves deferring setting up the bundle environment.

  1. I needed to keep the spec task from running the environment task. The project was already using a variation of override_rake_task, so I added an override in a task within the lib directory:
          override_task :spec do
            Rake::Task["spec:original"].execute
          end
        
  2. The next step was to bootstrap the rails environment in spec_helper. To do this I started it with:
          ENV["RAILS_ENV"] ||= 'test'
    
          RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
          environment_path = File.expand_path(File.join(RAILS_ROOT, 'config', 'environment'))
          require(environment_path)
            require 'spec/rails'
            #... any other needed requires
        

    It's crucial to expand the path which ensures that the same string is used whenever the environment file is required. Before I did that I was having problems with the environment being required a second time, presumably for the second spec. If you have any code which patches anything using alias_method_chain, or a similar technique, loading that code a second time can cause infinite loops which can be mystifying.

So this seems to be working. I'll try to update if I find anything else, but in the meantime I hope some folks find it useful.


Rails rake_freeze_other_gems

Posted by Rick DeNatale Mon, 02 Jul 2007 15:36:00 GMT

I’m working on a team rails project and adding some timezone support. I installed the tzinfo gem and, since I want to make sure that it gets to the production server, I used the freeze_other_gems rake task, which I’d found by doing a rake -T.

I’d never used it before so I googled to find some documentation.
It wasn’t exactly clear, but it turns out that you need to edit the fourth line of the lib/tasks/gems.rake file

libraries = %w(progressbar tzinfo)

The progressbar gem was already there so I added tzinfo.


Then I invoked:


$ rake freeze_other_gems

Only to get the error:


rake aborted! undefined method `version’ for nil:NilClass

This problem had been seen by others and commented on. I guessed that the problem was not with my newly added gem, but the progressbar gem which was already listed, apparently by one of the other developers.


The Quick Fix


The first step was to determine the version of the progressbar gem. I looked at lib where it had been installed, and found that it was version 0.3. I then installed the gem, and reran the freeze_other_gems rake task.

Success


Good News

Posted by Rick DeNatale Fri, 22 Jun 2007 14:29:00 GMT

Recently on gluttonous, Kevin Clark announced that
Powerset is going to launch their front-end on Ruby.
It seems that they were already pre-disposed to a major ruby comitment having built a sizable Ruby talent pool for their internal applications.

Prior to making the final decision to go all out with ruby for their front-end launch, the did some due diligence which included investigating the facts behind the recent furore caused by an interview with one of Twitter’s developers.


So they went to
Twitter’s lead developer, Blaine Cook to get the straight dope.

Quoting Kevin:

The simple fact is that Ruby wasn’t the source of Twitter’s woes. As it often happens with rapidly growing sites, they ran into architectural problems. Some design decisions don’t hurt until they reach a massive scale and at that point you have to rethink your approach. In an email he writes:

For us, it’s really about scaling horizontally – to that end, Rails and Ruby haven’t been stumbling blocks, compared to any other language or framework. The performance boosts associated with a “faster” language would give us a 10-20% improvement, but thanks to architectural changes that Ruby and Rails happily accommodated, Twitter is 10000% faster than it was in January

That last sounds quite true and corresponds to my experiences in the past with Smalltalk. I used to tell IBM customers that almost all performance comes not from the language but from application design, and that using a dynamic language which allowed the application to be developed in what’s now called an agile development process allows major performance gains through refining, refactoring, and retuning the system as both business and performance requirements get uncovered/discovered.

That’s as least as true of Ruby as it was of Smalltalk 15-20 years ago.