Bundler and different platforms for development and production

posted: December 29th, 2010

With the current version of Bundler (1.0.7) you have to watch out for platform specific gems when using different platforms for development and production. Bundler generates the lockfile (Gemfile.lock) with the specific version for the current platform which is ignored when using “bundle install” with the –deployment flag.

Workarounds

You could either not check in Gemfile.lock (not recommended) or not use the –deployment flag on deployment (which is used by default by bundlers’s capistrano recipe).

  # override the bundler flags in a capistrano deployment
  # config/deploy.rb
  require "bundler/capistrano"
  set :bundle_flags,       "--quiet"

Alternatively you could update the lockfile on all used platforms by doing “bundle install” on each of them and commit the updated version (only works if the bundle is not already frozen which you can check with “bundle config”). Bundler keeps adding the additional gems to the lockfile and doesn’t overwrite the gems of another platform.

Callbacks outside of the database transaction

posted: September 1st, 2010

Callbacks are a fantastic way to trigger behaviour at a specific time in the lifecycle of an ActiveRecord object. If for example you want to do something before every save you would use the before_save callback. All of these callbacks run inside of a transaction which ensures data integrity. If something goes wrong, everything is rolled back.

The problem

But there are some cases where you want to run the callback after the transaction is committed. One example are expensive operations like image thumbnailing which would block the transaction too long. Another example is asynchronous processing in a queue. In an application which is using resque as the queueing system I just had the problem that every once in a while a job failed because the ActiveRecord object could not be found. The reason for this was the transaction: the resque job was queued in an after_create callback and in some rare cases the resque worker started the job faster than the database could finish the transaction! This could also happen when updating a search index or in any other task which depends on the updated state of the database.

The solution

In Rails 3 we can use the after_commit callbacks, which are triggered right after the commit of the transaction.

  # triggers after the commit of every save
  after_commit :method_name
  # triggers after the commit of a create
  after_commit :method_name, :on => :create
  # triggers after the commit of an update
  after_commit :method_name, :on => :update
  # triggers after the commit of a destroy
  after_commit :method_name, :on => :destroy

When using Rails 2

As the application I am working on is still on Rails 2.3 I am now using the after_commit gem which provides this functionality with a slightly different syntax also for earlier versions of Rails (for all versions between 1.2 and 2.3).

  # triggers after the commit of every save
  after_commit :method_name
  # triggers after the commit of a create
  after_commit_on_create :method_name
  # triggers after the commit of an update
  after_commit_on_update :method_name
  # triggers after the commit of a destroy
  after_commit_on_destroy :method_name

Callbacks for rollback

Both the after_commit gem and Rails 3 are also providing additional callbacks which trigger on a rollback of the transaction. These callbacks could be very useful for cleaning up things outside of the database.

  after_rollback :method_name

Mount Rack apps in Rails 3

posted: May 23rd, 2010

The routing in Rails 3 (powered by the rack-mount gem) offers a lot of improvements in functionality and syntax over the Rails 2 style. You can read up about them in the following posts:

One of the biggest improvements is the possibility to associate arbitrary rack apps with a specific route. There are different methods to configure the use of a Rack app in the router. One of them is the “match” method, which allows to map a path to a rack endpoint. The most common use of “match” is to specify a Rails controller and action (which can act like a rack app in Rails 3):

match "/blog" => "articles#index"

But instead of an action we also can route to any other rack app:

# using a proc as an example for the most simple rack app
match "/blog" => proc { |env| [ 200, {}, [ "my posts" ] ] }
# or a class representing a rack app
match "/blog" => MySinatraBlogApp
# with a glob to catch deeper nested routes
match "/blog(/*other_params)" => MySinatraBlogApp

You still have to do special work for the parts of the path following the “/blog”-prefix (for example specifying a glob so that the router knows to send requests looking like “/blog/archives” to the rack app). But the biggest disadvantage of this approach is that the Rack app has to know this path and use it in its own path recognition. In a request with the path “/blog/archives” the Sinatra app would look like this:

class MySinatraBlogApp < Sinatra::Base
  get "/blog/archives" do
    "my old posts"
  end
end

Anchors

Both problems can be solved by not “anchoring” the route. In the background a path given to “match” is converted to a regular expression by rack-mount. For “/blog” this expression would be /\A\/blog\Z/. The \A and \Z ensure that the path matches exactly and has no additional parts before or after the given route. If we don’t anchor the path the expression would be /\A\/blog/ allowing arbitrary paths after the “/blog”-prefix. Exactly what we want when mounting a rack app! Even better is that the PATH_INFO given to the rack app doesn’t include the prefix anymore which means that the app doesn’t have to know about the mount path.

match "/blog" => MySinatraBlogApp, :anchor => false
# now the sinatra app for /blog/archives can look like this
class MySinatraBlogApp < Sinatra::Base
  get "/archives" do
    "my old posts"
  end
end

Mount

There also is a routing method doing this configuration for us which is unsurprisingly named “mount”:

mount MySinatraBlogApp, :at => "/blog"
# or simply
mount MySinatraBlogApp => "/blog"

The definition is the other way round than when using match and the anchor option is set to false automatically (mount uses match behind the scenes).

Using the prefix in the rack app

How does the rack app know about the prefix (for example necessary to generate correct urls)? It can use the “SCRIPT_NAME” environment variable which is set to the prefix automatically. There is one problem left when using Sinatra as the rack-app: for the root-path (/blog) the PATH_INFO in the environment is set to “” instead of just “/”. This way Sinatra will not recognize the request if its action is defined with a “/”. Instead you would have to use an empty string at the moment!