« April 2006 | Main | June 2006 »

May 28, 2006

Alias a Static Method in Ruby

Listen to this articleListen to this article

As much as I love Ruby on Rails, one of the things that dissapoints me is the rather large number of static methods. Maybe it's my Java background but class methods just irk me: They're difficult to override, mock, stub-out, and they're inherintly thread-unfriendly. But that aside, the fact remains that they exist and, now and again, I need to mess with them. Case in point: My Foreign Key Migrations plugin.

The plugin was working fantastically—thanks to the efforts of Ted Davis for giving it a work out—until it came time to running functional tests. Unfortunately, but unsurprisingly, the schema dumper used to copy the database structure from development to test creates tables in alphabetical order. Now I can't actually think of a much better alternative but the problem is that this means the foreign key clauses were being generated against tables that possibly didn't exist at the time of execution. (For example, Order sorts before Product however the orders table has a foreign key to the products table.)

So anyway, the longer-term solution is to batch up the foreign-key declarations until the end of the script and then execute them but in the meantime, I just wanted to disable their generation altogether. In either case, I needed to interfere with the execution of ActiveRecord::Schema.define a static (boo, hiss) method. Now, for the fun bit.

In Ruby, the way to safely mix-in methods (rather than use subclassing) is to use some method chaining. For this we use alias_method. So, for example, if we wanted to override to_s to always place quotes around the value (nopt very useful but it will suffice for now) we could write a module like this:

module QuoteToS
  def self.included(base)
    base.class_eval do
      alias_method :to_s_without_quotes, :to_s unless method_defined?(:to_s_without_quotes)
      alias_method :to_s, :to_s_with_quotes
    end
  end

  def to_s_with_quotes
    "'#{to_s_without_quotes}'"
  end
end

This essentially says that when the module is included (ie mixed-in) to a class then: add a method named to_s_with_quotes; create an alias of the existing to_s named to_s_without_quotes; make an alias of the new to_s_with_quotes named to_s; and finally, whenever to_s. is executed, call the old to_s method (now named to_s_without_quotes) and surround the results with, you guess it, quotes.

To use this you would either manually include the module in a class or, more along the lines of aspects, force the inclusion with some code like this:

MyClass.send(:include, QuoteToS)

(As a side note, the use of unless method_defined?(:to_s_without_quotes) is to work-around a bug in Ruby 1.8.4 that causes an infinite recursion when using alias_method. I never detected it under Mac OS X but apparently it affects windows machines with monotonous regularity. D'oh!)

So that's all very well and good but what happens when you need to do the same thing with static methods? The answer is, use class << self and extend. In my case, overriding the behaviour of ActiveRecord::Schema.define looks something like this:

module ForeignKeyMigrations::Schema
  def self.included(base)
    base.extend(ClassMethods)
    base.class_eval do
      class << self
        alias_method :define_without_fk, :define unless method_defined?(:define_without_fk)
        alias_method :define, :define_with_fk
      end
    end
  end
  
  module ClassMethods
    def define_with_fk(info={}, &block)
      ...
      define_without_fk(info, &block)
    end
  end
end

Here, the use of base.extend causes all the methods defined within the module ClassMethods—an aribitrary name used by convention in most if not all the rails code I've ever seen—to be added as static methods on the class. Then, surrounding the alias_method calls within a class << self causes them to be executed in a static context.

Again, to have this code mixed-in to the existing ActiveRecord::Schema class looks like this:

ActiveRecord::Schema.send(:include, ForeignKeyMigrations::Schema)

Phew!

P.S. all the plugins are now available via SVN.

Update: See the comments on how to simplify the code thanks to Ryan Tomayko.

May 17, 2006

Publishing My Rails Plugins

Listen to this articleListen to this article

I've created a page where I've started to publish my rails plugins. I don't yet have a publicly accessible SVN server but I'm in the process of setting one up so, until I get that completed, I've packaged the plugins as downloadable .tar.gz files that you can extract into your vendor/plugins directory. So far I've published the foreign-key migrations and cascading stylesheets plugins with more to come. But as today is Norway's national day (apparently celebrating the signing of the constitution on the 17th May, 1814) and I'm in Olso, I'm off to do some drinking..ahem I mean..celebrating.

May 16, 2006

Auto-Generate Foreign-Key Constraints in Rails

Listen to this articleListen to this article

I literally just lobbed into my hotel room in Oslo after 30+ hours in transit. (I left home @ 12:30pm on Monday 15th and arrived here @ 21:30 on Tuesday 16th Melbourne time.) It's my first time in Scandinavia and so far I'm loving it. The people are friendly (and *ahem* rather attractive I must say) and I'm hanging out to try some scandinavian beer!

So anyways, after resisting the temptation to turn on my laptop, I finally caved and whipped up a little bit of code to auto-generate foreign-key relationships for migration scripts:

module ActiveRecord
  module ConnectionAdapters
    class ColumnDefinition
      alias nofk_to_sql :to_sql
      def to_sql
        name.to_s =~ /(.*)_id$/ ? "#{nofk_to_sql} REFERENCES #{Migrator.proper_table_name($1)} (id)" : nofk_to_sql
      end
      alias to_s :to_sql
    end
  end
end

The code assumes that if you have a column named customer_id in say an orders table, then you want a foreign key to the id column in the customers table. That doesn't handle situations where you have multiple foreign keys to the same table but...meh...I don't have models that sophisticated yet so bite me :)

Just looking back on it now, it was so trivial to implement that there is more syntactic noise than actual code. Hmmmm.

Update: the plugin is now available in downloadable form and supports a :references option for multiple columns or columns that aren't named for the table they reference.

May 08, 2006

Using a Single Development Database For all Rails Applications

Listen to this articleListen to this article

I used to have a separate database and/or user for each application I was developing. This worked for a while but once I had three or so projects underway, I grew tired of remembering which database and which login to use when running psql, etc. In addition, if anyone else wanted to do development, they too needed to either configure another rails environment (eg. james_development) or they had to create another database/user just as I had.

Then it occurred to me that psql (at least under Mac OSX) defaults to connecting to a database named for the currently logged in user. So, I reasoned, if all the developers had a default database, then if I could configure rails to connect to that I might be rid of my myriad databases. The problem was: how?

At first I tried not specifying a database in config/database.yml at all. D'oh! No luck. Then I wondered if I could add an ERB-ish macro for the current user (I may even have read that it was possible but I can't for the life of me recall where):

development:
  adapter:  postgresql
  database: <%= ENV["USER"] %>
  username: <%= ENV["USER"] %>

I'm not sure at what level this is being handled (rails, yaml, ruby, somewhere in between) but it worked! Now when the application loads in development, the name of the currently logged in user is substituted in for the name of the database.

The only other thing is to always remember to specify a table-name prefix in config/environments/development.rb and all the applications will happily co-exist in the same, default, database:

  config.active_record.table_name_prefix = "cjp_"

I've been playing with having all my applications in the one development database for a while now and it seems to be working out ok. On the down-side, it does mean I need to remember the table-name prefix when using psql, in some ways moving the problem rather than removing it, however it doesn't seem to get in the way especially because psql has table completion for table-names, column-names, indexes, you name it. (That's right: select * from cjp_[tab] where [tab] = '...';). The other downside of course is if I ever want to blow away the entire database I can't but then again, that's what rake migrate VERSION=0 is for.

I'm not sure if I'll continue in the long-term with this but it sure makes adding a new developer to the team dead-simple and makes my local machine configuration a lot simpler in the meantime. Besides, I needed an excuse to try using table-name prefixes so I could test my migration extensions. Guess what? They didn't handle it! They do now :)

May 04, 2006

Transactional DDL

Listen to this articleListen to this article

Have you ever been half way through a rails migration script only to have it bomb out with some error or another? You then correct the error and try to re-run it but because some of the statements have already been executed they fail when executed a second time.

Some databases (PostgreSQL for example) allow you to wrap DDL (Data Definition Language, create table, etc.) as well as DML (Data Manipluation Language, insert, etc.) in a transaction! This means that if any part of the script fails, the whole lot is rolled back. Imagine this: you drop a table, script rolls back and the table magically re-appears as if nothing had happened.

In rails migration scripts it's a simple matter of wrapping the entire up and/or down method with a Model.transaction do ... end. (Yet another time I really think the idea of making transactions a part of the model is particularly ridiculous. Transactions are cross-cutting concerns just like logging. Just ask the Aspect weenies, they'll tell ya.)

Here's a simple example:

  def self.up
    SystemProperty.transaction do
      create_table :system_property do |t|
        t.column :name,         :text,      :null => false
        t.column :value,        :text,      :null => false
        t.column :created_at,   :datetime,  :null => false
        t.column :updated_at,   :datetime,  :null => false
        t.column :lock_version, :integer,   :null => false, :default => 0
      end
    
      add_index :system_property, [:name], :unique => true
    end
  end

Now, I realise that create_table also supports the :force => true option so this is possibly not the best example but at least you can see how to go about making your migration scripts fully transactional, DDL and all.

May 03, 2006

Hej Sweden (and Norway)

Listen to this articleListen to this article

I'll be in Oslo from the 16th until the 19th of May then in Arvika from the 19th until the 22nd and finally Stokholm from the 1st of June until the 7th of June, visiting parts unknown in between so if anyone wants to meet up for a beer and Surstromming (NOT!), that'd be cool. I'll be doing some work-related stuff as well as attending a few Aikido seminars. Any recommendations? Places to visit? Things to see? Hotels, Motels, Hostels to stay at (or avoid)?

Bulletproof Web Design

Listen to this articleListen to this article

You know that feeling when you grok a programming language so that you spend far less time thinking about the how and thus are free to concentrate more on the what? That's how I feel about Java and it's how I'm beginning to feel about Ruby and Rails. The same can't (or I should say couldn't) however be said for (X)HTML+CSS. Until now.

Having scoured the net, read every web site I could find and even sat in bookstores for hours reading Eric Meyer, I still didn't feel comfortable doing web page layout and design. I just didn't get it. I spent so much time trying to work out how I would create a web page that I lost all my creative drive. I new there just had to be a simpler way. Surely (X)HTML+CSS gives me syntactic markup + layout?!

Finally, in a last ditch effort to put myself—and my friends on IM who I wouldn't stop bitching to—out of misery, I spent a few hours in Borders (you know, the library with coffee) and stumbled upon Bulletproof Web Design by Dan Cederholm.

It's a pretty easy read and the examples are very easy to follow. The layout of the book makes a big difference too. I love books that I can just open at any page and get a "nugget" then read on if I feel inclined to do so or even skip back a bit to learn a bit more. This is in stark contrast to the excellent but—for me at least—excruciatingly painful Eric Meyer books which are too densely packed and concentrate too much on the what rather than the how.

I'm still no expert (I've really just started) but if you too are in need of some very simple yet practical help in getting you started thinking with the right mindset, I can highly recommend it. If however you're after an HTML+CSS reference manual or an O'Reilly hacks style of book for that matter, then you might like to try something else.

May 01, 2006

More Managing Multiple Rails Environments

Listen to this articleListen to this article

Yesterday I discussed how we made sure our application was talking to the correct database. Today I'm going to show you how we provide some visual feedback to the user indicating in which environment they're operating.

As mentioned yesterday, we have four different environments, two of which—UAT and production—are available to the end users and as such it's imperitive that we provide some visible means for discrimination (lest they mistake one for the other and accidentally add test data into production). Besides obvious gating procedures such as different access rights, etc. one of the ways we can help them out is by providing some obvious visual feedback to indicate the current environment. In this scenario, we decided to change the background colour from gold in production to red in UAT.

One approach might have been to hard-code some style information into the head element of the main application.rhtml template. Something along the lines of:

<% if RAILS_ENV == "uat" -%>
<style type="text/css">
  body {
    background-color: red;
  }
</style>
<% end -%>

The problem with this approach is that, well quite frankly, it smells. For a start, we've hard-coded the environment and placed style information directly into each web page—something I'm always reluctant to do if I can avoid it. So what other options do we have?

I'm glad you asked. It turns out that we already had a mechanism in place for including various stylesheets depending on which page is being rendered. For a start, we always include application.css on every page. It contains all the global, system-wide styling information. In addition, we also include a stylesheet for each page—if it exists—based on the path to the current action. So for example, if the current action is list in the CustomerController then we check to see if the stylesheet customer/list.css exists and if so, include it in the rendered output. This allows us to easily add specific styling for individual pages if necessary.

The code for the macro that handles this behaviour looks something like:

def stylesheet_link_tag(*sources)
  if sources.include? :defaults
    sources = sources.dup
    sources.delete :defaults
    
    sources << 'application' if File.exists?("#{RAILS_ROOT}/public/stylesheets/application.css") 

    page = "#{@controller.controller_name}/#{@controller.action_name}"
    sources << page if File.exists?("#{RAILS_ROOT}/public/stylesheets/#{page}.css")
  end
  
  super *sources
end

So if you call the stylesheet_link_tag macro with :defaults as one of the arguments, magic happens.

With this in mind, we decided to enhance the default stylesheets list to include #{RAILS_ENV}.css if it exists (with a little DRY re-factoring in the process):

def stylesheet_link_tag(*sources)
  if sources.include? :defaults
    sources = sources.dup
    sources.delete :defaults
    
    ['application', RAILS_ENV, "#{@controller.controller_name}/#{@controller.action_name}"].each do |source|
      sources << source if File.exists?("#{RAILS_ROOT}/public/stylesheets/#{source}.css")
    end
  end
  
  super *sources
end

Now we can easily create a uat.css that looks something like:

body {
  background: #EB5E66;
}

And voila! When users log in to production they see the default (gold) background and in UAT they're immediately hit with a red—technically it's 'cherry'—one. No mistaking which environment they're in now.

Again, you don't have to use a background colour nor even use stylesheets for that matter. The main point is that getting configuration management (of which we consider this to be a part) takes some thought but isn't that hard to get right, especially in Rails.

Managing Multiple Rails Environments

Listen to this articleListen to this article

I've written briefly on build watermarking and environmentally friendly builds before: Some useful techniques to aid support staff in identifying which particular build of your application is causing your end-users—or testers as is often the case—grief.

In the first of these two articles, I mentioned a project we did many years ago where we stored the build number in the database. When the application connected, it checked to that it was running against the correct database version. This was prompted by one of our biggest clients at the time who had several hundred users all running a client-server application (yes it was a loooong time ago) and we needed to ensure they couldn't accidentally run the wrong version and potentially corrupt the database.

A similar problem has now cropped up with a rails application we're developing. This time, rather than a specific build, we're more interested in ensuring that the application is running against the correct database instance. In this scenario, we have four databases: development; test; uat; and production. Not so much a problem for the first two, but it'd be pretty catastrophic if somehow the UAT application were started against the production database!

Our solution is pretty simple and inline with the previous discussion: put something into the database to identifiy the appropriate environment then check for it in the rails application. The implementation (naturally being rails and all) was also quite simple. We already had a generalised SystemProperty class—essentially just a set of key-value pairs supporting hash-like indexing—o we merely created a new instance, :environment, which is then checked via before_filter in the ApplicationController like so:

class ApplicationController < ActionController::Base
  before_filter :check_rails_env
  ...
  def check_rails_env
    raise "Incorrect database: '#{SystemProperty[:environment]}' expected: '#{RAILS_ENV}'" \
      unless SystemProperty[:environment].value == RAILS_ENV
  end
end

Once the code is in place, all you need do is insert a record into the system_properties table with a key of 'environment' and a value of say 'production' (for, you guessed it the production database) and you're good to go. If the application ever runs against the wrong database, you'll be greeted with something along the lines of:

Incorrect database: 'production' expected: 'uat'

A slightly riskier alternative to creating the environment record manually is to perform the task in a database migration script:

class CreateEnvironmentSystemProperty < ActiveRecord::Migration
  def self.up
    SystemProperty.create! :name => "environment", :value => RAILS_ENV
  end
  
  def self.down
    SystemProperty[:environment].destroy
  end
end

I can't say I particularly recommend this approach though unless you are REALLY, REALLY certain of your environment settings—precisely what you probably aren't if you're considering the whole idea in the first place—but, nevertheless, maybe, like me, you just want to ensure that no one can screw up in the future, say while you're on holidays and you've left it up to the new guy ;-)

The one place where this approach falls down is with caching. Because we've used a before filter, any actions that are cached will be served up directly by the web server, without going through the rails controller. Practically though, I doubt this will be much of an issue, especially in our particular case where what we're really worried about is back-office staff either entering data into the UAT database when they thought it was production—a bit of a waste of time but not that bad—or worse, testers entering data into a live system.

Ofcourse this isn't the only problem faced by developers tasked with managing multiple rails environments and I hope to address some more in future posts.