Agile_disciple_small

Bundler & Formtastic: undefined link_to

We had a bizarre bug pop up recently with while running Bundler 0.9.11, Rails 2.3.5 and Formtastic 0.9.7.

I say bizarre because everything was working fine until we removed Clearance. Then our standard Rails form helpers stopped working.

Here are the steps we took to make it work again:

Remove the monkey patch that seems to be ubiquitous for running Bundler in Rails 2.3.5:


# Delete all this
class Rails::Boot
  def run
    load_initializer
    extend_environment
    Rails::Initializer.run(:set_load_path)
  end
 
  def extend_environment
    Rails::Initializer.class_eval do
      old_load = instance_method(:load_environment)
      define_method(:load_environment) do
        Bundler.require
        old_load.bind(self).call
      end
    end
  end
end

Modify environment.rb to load the Bundler env

require File.join(File.dirname(__FILE__), 'boot')
 
class MyAppInitializer < Rails::Initializer
  def load_gems
    super
    Bundler.require
  end
end

MyAppInitializer.run do |config|
   config.logger = Logger.new(config.log_path)
   config.time_zone = 'UTC'
 end

Ruby Tidbits: Spork your Cucumber

This is a continuation of last week’s post. Not long after Tim Harper released Spork for RSpec, the Cucumber team announced support for Spork in Cucumber. If this sounds like some kind of dining meme, just let me show you how much of a picnic this is to use.

Since the last blog post, Spork 0.5.7 has been released, so I’ll be using that version here. Cucumber is at version 0.3.11.

First, let’s bootstrap Cucumber

  ruby script/generate cucumber

Time to generate some features.

  script/generate feature Spoon color:string size:string

We can make our features pass pretty easily for this example.

  script/generate rspec_scaffold Spoon color:string size:string

Don’t forget to migrate

  rake db:migrate

Now start spork for cucumber

  spork cuc

And run the features

  rake features

Now we’re not going to see much here since this project is so small, but I did time the run on these.

Without Spork

 time rake features

real 0m3.296s
user 0m2.456s
sys 0m0.756s

With Spork:

 time rake features

real 0m1.914s
user 0m1.093s
sys 0m0.298s

I would love to see some real world stats on this on somebody’s cucumber suite. If you want help getting this going on your long running suite, let me know and I’ll lend a hand in exchange for getting some timing results.

Ruby Tidbits: Spork

Last week Tim Harper announced Spork, which is billed as a better RSpec DRb server. What does that mean, and why should I care?

RSpec of course is a Behavior Driven Development framework for Ruby. DRb is a distributed object system for Ruby, and it ships with the standard library.

As a Rails code base gets larger, you can start to see app initialization times increase. In production, this isn’t a big problem, since you aren’t frequently starting your app. However, when you are involved in a rapid development/test cycle this can become a nuisance when you start to find yourself waiting for your app to initialize to run your tests.

Spork aims to tackle this problem by only taking that initialization hit once. It starts up the application environment, and then listens over DRb. Then, RSpec can connect to that DRb server and run specs inside that process, skipping the initialization. Furthermore, before running the specs, Spork will fork itself, which is a relatively inexpensive call on POSIX1 systems. Then, when the specs are done running, this process can just go away, and we don’t have to worry about object pollution when running specs again later.

Let’s see just how easy it is to get going with Spork.

My environment for this tidbit includes:

  • Mac OS X 10.5.7
  • Rails 2.3.2
  • RSpec 1.2.6
  • The Spork I am installing is version 0.5.6

Install Spork from Ruby gems.

  sudo gem install spork

Create (use an existing) Rails project

  rails crappy_spoon

Edit config/environments/test.rb. And add the following lines to tell Rails we are using RSpec.

  config.gem "rspec", :lib => false
  config.gem "rspec-rails", :lib => false

Bootstrap the RSpec environment.

  script/generate rspec

I’m going to use scaffolding just to get some specs to try things out with. (Don’t forget to migrate)

  script/generate rspec_scaffold Fork
  rake db:migrate

I have a little benchmark script that will run a command an number of times and print out the results. Running rake to run these specs 5 times gives:

user system total real
0.000000 0.000000 5.350000 6.364407
0.000000 0.000000 5.300000 5.648598
0.000000 0.000000 5.320000 5.694012
0.000000 0.000000 5.300000 5.610330
0.000000 0.000000 5.350000 5.824451
>total: 0.000000 0.000000 26.620000 29.141798
>avg: 0.000000 0.000000 5.324000 5.828360

So on average on my machine I had to wait 5.8 seconds to run all my specs.

Let’s enable Spork and try it again.

  spork --bootstrap

When that’s done, it prints out a message for us.

Done. Edit /Users/redinger/workspaces/rubyrx/crappy_spoon/spec/spec_helper.rb now with your favorite text editor and follow the instructions.

So, let’s go do that. I moved everything into the prefork section for this example. Here’s my final spec_helper.rb:

  require 'rubygems'
  require 'spork'

  Spork.prefork do
    ENV["RAILS_ENV"] ||= 'test'
    require File.dirname(__FILE__) + "/../config/environment"
    require 'spec/autorun'
    require 'spec/rails'

    Spec::Runner.configure do |config|
      config.use_transactional_fixtures = true
      config.use_instantiated_fixtures  = false
      config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
    end
  end

  Spork.each_run do
  end

Then, edit spec.opts to enable drb:

  spec.opts --drb

Load up spork

 spork 

Rerunning the benchmark:

user system total real
0.000000 0.000000 2.220000 3.100511
0.000000 0.000000 2.210000 3.147859
0.000000 0.010000 2.230000 3.057866
0.000000 0.000000 2.190000 2.919749
0.000000 0.000000 2.190000 2.864102
>total: 0.000000 0.010000 11.040000 15.090087
>avg: 0.000000 0.002000 2.208000 3.018017

And we are now down to an average of 3 seconds. Obviously this is with a bare bones app, so we don’t see the real performance savings that we would with a real world app. But, just for the sake of argument, let’s say someone accidentally added a sleep 5 to the init process (simulating an additional 5 second app initialization time).

Rerunning without Spork running (it’s worth pointing out here if you run rspec without having a Spork process running, the specs will just revert to running the old way, printing out a message notifying you there is no server running.):

user system total real
0.000000 0.000000 6.150000 18.199735
0.000000 0.000000 5.040000 10.131084
0.000000 0.000000 5.040000 10.217521
0.000000 0.000000 5.060000 10.121561
0.000000 0.000000 5.020000 10.150404
>total: 0.000000 0.000000 26.310000 58.820305
>avg: 0.000000 0.000000 5.262000 11.764061

That causes the extra time to be seen for each running of the specs. Under spork, which takes an additional 5 seconds to start up the Spork server:

user system total real
0.000000 0.000000 2.090000 2.722946
0.000000 0.000000 2.080000 2.661724
0.000000 0.000000 2.110000 2.731659
0.000000 0.000000 2.080000 2.665148
0.000000 0.010000 2.100000 2.647866
>total: 0.000000 0.010000 10.460000 13.429343
>avg: 0.000000 0.002000 2.092000 2.685869

No extra time. (Technically, less time, but we’ll count that as margin of error.)

One final thing to point out, if you just run spork -d it will run diagnostic mode, which will list which files are being preloaded, and don’t get reloaded each time the specs are run.

Hopefully if you are using RSpec in your Rails project you can see how using spork can immediately benefit you.

1 Right, I said POSIX, so Windows users will need to pursue a different solution for now.

Default Routes and Rails Engines don't mix

Of course we’ve known for a while to not use default routes in combination with restful routes. However, there is a hidden danger when dealing with Engines. I was doing what should have been a quick experiment with an Engine in Rails 2.3. So, I generated a default app.

  rails cereal
  rails fruits
  cd cereal
  script/generate scaffold cornflake

Then I follow the incantation to get my fruits engine into my cereal app. Part of that process involves copying over the routes.rb file. However, by default, that has these unfortunate routes defined already:

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

Now, I hit my app at /cornflakes/new, hit the ‘Create’ button, and it posts to … my ‘index’ action?? What’s up with that? Well, it took a while, but then I remembered that default route. I’m assuming that the plugin routes get processed first, and that default route is getting installed before my resourceful route. Meaning, nothing in my app’s routes.rb file is going to get recognized. No corn flakes, no lucky charms, nothing.

Removing the offending lines set the world back to normal. Time to eat.

Passenger, nginx & Leopard development

If you’re wondering if you can still develop on your Mac using Passenger along with the nginx config – the answer is of course you can!

First, follow the installation instructions.

Then you’ll need to edit your /etc/hosts file to tell your host about the virtual domain. (This is only necessary I assume until the fantastic Passenger Preference Pane is updated to work with nginx)

  127.0.0.1  localhost my_facebook_killer.local

Finally, update your nginx.conf, which is pretty much the same as the Passenger installation instructions, with one extra line for development.

  server {
    listen 80;
    server_name my_facebook_killer.local;
    root /path/to/your/rails/app/public;
    passenger_enabled on;
    rails_env development;  # This line tells passenger to start in development mode.
  }

Now restart nginx and you should be able to test your app by hitting http://my_facebook_killer.local in your browser