Managing Gem dependencies with Rails >= 2.0.3

This is how I manage gem dependencies for Rails applications that use Rails >= 2.0.3.

Specify the dependencies in config.rb.

Rails::Initializer.run do |config|
  # ...
  config.gem 'doodle'
  config.gem 'aws-s3', :lib => 'aws/s3'
  config.gem 'smqueue', :version => '0.1.0'
  # ...
end

Since I don't want to make my deployment depend on all the gem sources being available I tend to pull the gems into the source tree and check them in.

sudo rake gems:install
rake gems:unpack
svn add vendor/gems/*

When it's time to deploy the application remember to build the gems that have native extensions.

rake gems:build

If you've got some sort of build system in place that produces application packages then this should be done as part of building that package. If you're using Capistrano then it should be done in an after deploy:update_code callback.

ActiveRecord callback names should be expressive

ActiveRecord provides a bunch of useful callbacks which will be run at various stages during the lifecycle of an ActiveRecord object. There are lots of ways to define these callbacks, and the easiest way is something like this.

class Widget < ActiveRecord::Base
  def after_save
    # What did this code do again?
  end
end

Seems harmless enough, right? Sure, as long as you're writing a throwaway prototype. Try adding another after_save callback, or implementing the callback in a subclass. Try coming back to the callback after 6 months and trying to work out what it's meant to be doing. That way insanity lies.

Name your callbacks expressively and you'll reap the immediate benefits of more readable code and easier implementation. You'll also have a decent indication - the method name - of what the callback was meant to do when you come back to the code in 6 months.

class Widget < ActiveRecord::Base
  after_save :add_widget_to_bill_of_materials
  def add_widget_to_bill_of_materials
    # No need to guess what this method does,
    # it's right there in the name!
  end
end

Handling error feedback from Ajax requests to Rails applications

Ajax is frequently used to provide a richer user experience. Why then are important error messages rarely handled properly in Ajax enabled applications? Handling errors gracefully and in a way that helps the visitor to solve them adds a really high quality feel to your application. We've got all the necessary machinery, all it takes is a little care and attention.

class FoosController < ApplicationController
  def update
    @foo = Foo.find(params[:id])
    respond_to do |format|
      if @foo.save
        format.html do
          flash[:info] = "Your foo has been created."
          redirect_to @foo
        end
        format.js { head :ok }
      else
        format.html do
          flash.now[:warning] = "I could not update the foo."
          render :action => :edit
        end
        format.json do
          head :unprocessable_entity, :json => @foo.errors.to_json
        end
      end
    end
  end
end

Using the above code we get a lovely fallback HTML based behaviour and when we work with Ajax we get to use JSON behaviours. When JSON is requested and something goes wrong we get back an array of arrays that takes the following form.

[
  [ "attribute1", "error1", "error2" ],
  [ "attribute2", "error3"]
]

Just think of the awesome things you could do with such rich feedback...

new Ajax.Request('/foo.json', {
  method: 'PUT',
  parameters: {
    authenticity_token: window._token,
    "foo[subject]": $F('foo_subject'),
    "foo[body]"   : $F('foo_body')
  },
  onSuccess: function(transport) {
    // This is Web 2.0: celebrate with a yellow highlight.
  },
  onFailure: function(transport) {
    var errors = transport.responseJSON;
    errors.each(function(error) {
      var attribute = error.shift();
      var messages = error.join(", ");
      var errorMessage = attribute + " " + messages;
      var inputNode = $("foo_" + attribute);
      if(inputNode) {
        // Show that something is wrong with this field.
        inputNode.addClassName("error");
        // Do something better than an alert box. Alert boxes suck.
        alert(errorMessage);
      }
    });
  }
});

Ajax and the Rails request authenticity token

Rails 1.2.6 introduced CSRF protection in the form of an authenticity token which is a reasonably long string used to make sure that any PUT / POST / DELETE request you've made to an application was really generated by you (or at least your browser) doing something in the application and that you weren't tricked into submitting it by some nefarious third party.

Rails automatically adds this token to any forms generated by it's helpers, but when building rich Ajax applications it can be useful to be able to generate the Javascript by hand.

Fire this snippet into your layout just above including all the other Javascript files to get access to the authenticity token in Javascript and let you submit requests using Ajax.

<%= javascript_tag "window._token = '#{form_authenticity_token}';" %>

Now you can build Ajax requests that are allowed to do stuff to the application.

new Ajax.Request('/foo.json', {
  method: 'PUT',
  parameters: {
    authenticity_token: window._token,
    text: $F('foo_text')
  }
  /* callbacks omitted for brevity */
})

Getting started with Story Driven Development for Rails with Cucumber

I've been hearing about Story Driven Development (SDD) for a while but I haven't tried it out because I was under the impression that there was a huge amount to learn and setup before I could get going. I'm not sure if that used to be true, but I started using Cucumber yesterday and it was really easy.

Install and configure

You'll need to install a bunch of RubyGems.

sudo gem install nokogiri term-ansicolor treetop diff-lcs hpricot cucumber

Install Cucumber into your Rails app.

ruby script/generate cucumber

Install WebRAT. Unfortuantely this doesn't seem to be available as a RubyGem. If you're using Git then install this as a submodule instead. We're not so I clone the repository then svn add it.

git clone git://github.com/brynary/webrat.git vendor/plugin/webrat

Writing your first story

Stories have three components: a business value, the role of a person that uses the feature and some description of the feature.

In order to [do something with business value]
As [role]
Should [describe the feature]

An example might be the ability to order a pizza from the online ordering system of a pizza delivery company.

Feature: Order Pizza
  In order to get some hot, tasty pizza
  A hungry pizza lover
  Should be able to order pizza

Next we need to define some scenarios. Scenarios are things that can happen during the story. Most pizza places aren't open 24 hours a day so two simple scenarios are (1) the pizza shop is closed, and (2) the pizza shop is open.

  Scenario: The pizza shop is closed
    Given the pizza shop is closed
    And I am on the home page
    And I click "Feed Me!"
    Then I should see "Sorry, the shop is closed"

  Scenario: The pizza shop is open
    Given the pizza shop is open
    And I am on the home page
    And I click "Feed Me!"
    Then I should see "Your pizza will be with you soon"

The above description should go in a file called something like features/order_pizza.feature where it is lovely and version controlled and safe.

So, we now have a story that describes how a feature should behave. How does that get turned into acceptance tests? Well, you could pass these descriptions off to your testing team, or you could turn them into part of your test suite.

Automated tests: better than cake

You might notice that when installing Cucumber you got the directory features/steps. That's where you tell your test suite how to understand your stories. There are already two files here: common_webrat.rb which gives your test suite a few funky things like the ability to click links and env.rb which does pretty much the same stuff as spec/spec_helper.rb except for Cucumber. You can ignore env.rb, but common_webrat.rb will provide a few examples of how to start writing story steps.

Create a new file, order_pizza_steps.rb. This is where you define the steps involved in ordering pizza. It's pretty much just regular expressions which match each line of a scenario to some Ruby code.


Given /the pizza shop is open/ do
  PizzaShop.open = true
end

Given /the pizza shop is closed/ do
  PizzaShop.open = false
end

And /I am on the home page/ do
  visits "/"
end

That's all we need to do. The common WebRAT steps provide the necessary mapping for clicking buttons and checking for feedback.

Running your stories

This is pretty simple: run rake features. You should get some rather pretty coloured output, and if anything has gone wrong Cucumber is pretty good at suggesting ways to fix it.

Found this article useful?

If you enjoyed this article I'd appreciate recommendations at Working with Rails.

Content_for is the new GOTO

I don't like content_for. Your view code jumps around up and down files and makes it hard to work out what's going on. It smells a lot like GOTO. When was the last time you saw someone recommend you use a GOTO?

content_for :javascript and content_for :css

Use of content_for can be easily avoided, at least for including CSS and Javascript files. Include the controller name and action name in the body tag in your layout and properly qualify your CSS declarations.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title><%= page_title %></title>
  <meta http-equiv="Content-Language" content="English" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" type="text/css" href="/stylesheets/simple.css" media="screen" />
</head>
<body id="<%= "#{controller.controller_name.tableize.singularize}_#{controller.action_name}" %> class="<%= "#{controller.controller_name.tableize.singularize} #{controller.action_name}" %>">
  <%= yield %>
</body>
</html>

Say you were looking at the Posts views in your app, you can now style these using something like this.

.post.index .article .title { 
  font-size: 1.25em;
}

.post.show .article .title { 
  font-size: 0.9em;
}

Or, in case you need to support browsers that don't let you specify two classes as a selector for a single element, you can write it like this.

#post_index .article .title { 
  font-size: 1.25em;
}

#post_show .article .title { 
  font-size: 0.9em;
}

Since all your Javascript is unobtrusive anyway (right?), it should be pretty easy to qualify the selectors used there with the same CSS selectors shown above.

As an added bonus, by specifying your Javascript / CSS like the above you can package it all in one Javascript or CSS file on deployment to your production environment and save yourself a bunch of HTTP requests.

Scaling: Using MogileFS for storing uploaded images

As you might have guessed from several of my previous posts, the team I've been working in has recently been scaling an application. I've learned a bunch of things along the way, several of which I've got half-written articles about and which I'll totally finish one day, honest.

One of the most awesome technologies I've started using is MogileFS, a distributed BLOB store. In our application we use this to store user-generated assets such as uploaded images and syndication feeds. I'll not go into the pros and cons of the technology here (I might do that another time), rather I'd like to share some code that we've found rather useful when handling image uploads and adding them to MogileFS: the MogileFilesystemBackend for AttachmentFu.

It's necessary to use a shared filestore for uploaded images when the application cluster you're using for uploads needs to scale beyond one physical box as otherwise the uploaded images land on several disks and there's no telling if they'll be available to a particular request to your application (that depends on which application server serves the request).

Getting stuck in

I've done some rather ugly preparation for this work and monkey-patched Kernel to provide an attr_accessor called filestore which is just an instance of MogileFS::MogileFS from the rather excellent MogileFS client by the clever people over at Seattle RB. The patch, which I'm sure will make experienced Rubyists cry, looks like this.

module Kernel
  # Oh noes, I'm screwing with Kernel.
  # 
  mattr_accessor :filestore
end

During the Rails initializer execution the filestore is setup using configuration values pulled from a YAML file in RAILS_ROOT/config/.

Kernel.filestore = MogileFS::MogileFS.new(
  :domain => "APPNAME-#{RAILS_ENV}",
  :hosts => array_of_hosts_from_yaml_file
)

(What I actually do is quite a bit different from this but that's because I've done evil things to the MogileFS client library which I'll probably share in the future. For now, believe the magic).

Now that the setup is complete, how do we get AttachmentFu to work with the filestore? We use the MogileFilesystemBackend of course!

class Image << ActiveRecord::Base
  has_attachment :content_type => :image,
    :storage => :mogile_filesystem,
    :max_size => 5.megabytes,
    :thumbnails => {
      :canonical => '1024x'
    },
    :processor => "MiniMagick"

  validates_as_attachment
end

The power behind the man

Of course, without the actual backend code not much is going to happen. The implementation was pretty heavily influenced by the existing Amazon S3 backend, mostly because the idea behind S3 and MogileFS is very similar.

module MogileFilesystemBackend
  def full_filename(thumbnail = nil)
    "#{class_prefix}:#{filestore_tag(thumbnail)}"
  end

  def filestore_tag(thumbnail = nil)
    "#{parent_id || id}:#{thumbnail || :original}"
  end

  def current_content
    temp_path ? File.read(temp_path) : temp_data
  end
  
  def public_filename(thumbnail = nil)
    [
      editorial_object_type.demodularize.tableize,
      editorial_object_id,
      "#{class_prefix}.#{file_extension}#{thumbnail && "?size=#{thumbnail}"}"
    ].join("/")
  end

  def file_extension
    Mime::Type.lookup(content_type).to_sym
  end

  def filestore_paths(thumbnail = nil)
    filestore.get_paths(full_filename(thumbnail))
  end

  def file_data(thumbnail = nil)
    filestore.get_file_data(full_filename(thumbnail))
  end

  protected
  def current_content_location
    temp_path ? :temp_path : :temp_data
  end

  def destroy_file
    filestore.delete full_filename
  end

  def rename_file
    filestore.rename @old_filename, full_filename
  end

  def save_to_storage
    logger.info "Storing #{self.class.name}\##{id} as #{full_filename(thumbnail)} (class: #{replication_policy}) from #{current_content_location == :temp_path ? temp_path : :memory}"
    filestore.store_content full_filename(thumbnail), replication_policy, current_content
  end

  def class_prefix
    self.class.name.demodularize.underscore.downcase
  end
  alias_method :replication_policy, :class_prefix
end

Technoweenie::AttachmentFu::Backends::MogileFilesystemBackend = ::MogileFilesystemBackend

Serving the public

So now you can get images into MogileFS, but in order to be useful we also need to serve them to the visitors of our application. That'll require a little work in the controller to make it read from the ever-present filestore instead of the database (if you're storing files in the database I will HURT you) or the local filesystem.

class ImageController < ApplicationController
  before_filter :load_image

  def show
  respond_to do |format|
    format.html
    format.any(:png, :jpg, :gif) do
      send_data @image.file_data(params[:size]),
        :type => @image.content_type,
        :disposition => 'inline'
    end
  end
  
  protected
  def load_image
    @image = Image.find(params[:id])
  end
end

There we have it. Images can now be requested through the ImageController and served to your adoring fans.

Found this article useful?

If you enjoyed this article I'd appreciate recommendations at Working with Rails.

Verify database connections in long-running idle Rails processes

I've interfaced one of my xmpp4r bots with the Xeriom Networks control panel. I had intended to write a post about it to show how easy it is, but I've been beaten to it. I can, however, offer one piece of advice that will stop the bot dying after a few hours of idling: periodically verify your database connections.

RAILS_DEFAULT_LOGGER.debug "Launching database connection verifier"
Thread.new do
  loop do
    sleep 1800 # Half an hour
    RAILS_DEFAULT_LOGGER.debug "Verifying database connections"
    ActiveRecord::Base.verify_active_connections!
  end
end

Adding the above code to a script will stop database connections getting dropped (or, at least, will reconnect them if it happens).

To see it in action, add support@xeriom.net to your XMPP roster and have a chat. It's not hugely intelligent, but it does support a few useful commands.

Update: the support bot is no longer running. It was fun, but not hugely useful in this context.

Love me!

If you've found this article useful I'd appreciate beer and recommendations at Working With Rails.

Getting started with Rails 2.0

Rails has changed quite a lot since Agile Web Development with Ruby on Rails (2nd Ed) was released. A number of new best practice techniques are now used in favour of those described in the book.

To demonstrate these techniques it is necessary to have a new Rails application to build upon. In this article I'll cover the basics needed to setup and run a Rails 2 project on your desktop or laptop. Future articles can then build on this base.

In this article

Installing Rails 2.0

Rails 2 now uses SQLite as it's development and test database so you needn't worry about setting up MySQL on the development machine. This makes installing Rails and starting a new project a lot easier: we only have to get Ruby and the Rails code onto our machines. To simplify this even further use MacPorts. If you don't have that installed, go install it now. Remember to install XcodeTools - you can get it from your OS X install media if you didn't install it the first time around.

To install Ruby with MacPorts open Terminal.app (it's in Applications / Utilities) and run sudo port install rb-rubygems. Technically this command will install RubyGems, but since Ruby is a dependency that'll get installed too. Once RubyGems has installed (it might take a while) use that to install Rails by running sudo gem install -y rails.

That's pretty much it. You now have a working Rails install on your development machine.

Starting your Rails 2.0 project

First, to keep your home directory tidy, create an area to keep all your projects under. I call mine sandbox. In the terminal run the command mkdir ~/sandbox to create the sandbox.

Under the sandbox directory we want to create our project. To do this use the rails command, letting it know the name of your project - in this case I call my project QuickBite.

cd ~/sandbox/
rails QuickBite

You'll see a bit out output scroll past as Rails builds a basic project for you. Let's run that Rails app and see what we get.

cd QuickBite
ruby ./script/server

Open a web browser and type http://localhost:3000/ into the address bar.

The default Rails index page.

It's not much, but our project now has a decent starting point. Lets save our progress in case anything bad happens.

The importance of version control

Version control does just what the name suggests: it allows you to control various version of your project. It's useful because it allows you to see when a change was made, what files the change affected, who made it, and it provides you the option of undoing changes that you don't like.

Git!

The version control system I use is Git. It does lots of cool things, but most of them won't be used until later articles. For just now we're only interested in being able to add changes to our projects so we can undo them if something bad happens.

You are of course free to use whichever system you're comfortable with, but the commands given in this article will only cover Git use.

Installing

Installing Git is simple if you use MacPorts. Simply run sudo port install git-core from the Terminal. Grab a cup of coffee, this can take a while.

Configuring Git

Since Git has only just been installed it doesn't know who we are. Let's tell it.

git config --global user.name "Your Full Name"
git config --global user.email "you@domain.com"

This is useful when working in teams as it lets you check who made a change to the code but it's a good idea to get it set up correctly from the get-go. By using --global we tell git that we want to use these values for all projects, not just this one - you only have to set your name and email address once.

Adding your Rails project to Git

Version control is great, but sometimes we don't want to version certain kinds of file. In a file called .gitignore in the top-level directory of your project add the following directives to ignore development and test databases, generated documentation, logs and temporary files that may be generated while running or developing a Rails project.

.DS_Store
db/*.sqlite3
doc/api
doc/app
log/*.log
tmp/**/*

Git is different from most version control systems in that it tracks content rather than files. We've told Git to ignore all content in tmp/ and log/ but we do want those directories to exist - Rails complains if they don't - so you'll need to create two .gitignore files, one in tmp/ and one in log/.

touch tmp/.gitignore
touch log/.gitignore

Now that Git in only paying attention to the files we're interested in we can add the project.

# Create the repository
git init
# Add the project to the next commit
git add .
# Commit the changes
git commit -m "Setup a new Rails application."

Normally we won't add . as it adds all uncommitted files under the current directory to the next commit. It's preferable to pick the files we want to commit (called staging in Git-speak) by using git add <filename> then using git commit -m "Commit Message" to commit just the staged files.

Once that's committed we can start building our application.

Working with the database: Models

In order to make a useful application we need to model the problem domain that the application operates in. Most - though not all - models interact with a database. Models can be found in app/models/, although there won't be any there yet.

The application I've created is called QuickBite. It's going to be an application for exchanging sandwich recipes. Sandwiches usually have a name - BLT, New York Deli, that sort of thing. We can tell Rails about Sandwiches by generating a model.

ruby ./script/generate model Sandwich name:string

If you look at the output of this command you'll notice that it's created a file in app/models/sandwich.rb. That's our sandwich model. The generator has also create some files called migrations. Migrations are commands that tell Rails how to manipulate your database - adding, deleting and renaming tables, columns and indexes. By running the migrations we ask Rails to tell the database what we know about sandwiches.

# Run any pending migrations
rake db:migrate

Now that Rails knows about sandwiches we should commit our changes to the source code repository in case something bad happens. The generator created some tests for us. I'm going to be a bad man and ignore those for just now - I'll cover testing at a later date.

# Look to see what's been changed
git status

# Stage the models, migrations, schema and tests.
git add app/models/ db/migrate/ db/schema.rb test/

# Commit the changes with a message
git commit -m "Added a model to represent sandwiches."

Creating dynamic pages: Views

Rails knows about sandwiches, but that doesn't help us get information into the database or show the people visiting our application what we've added to the database. We want to create a page that allows us to enter sandwich details and to let people see those details.

Pages are made up of several views - in our case we're just going to use one per page to keep things simple. Views live in subfolders of app/views/. If you take a look you'll see that there's already one subfolder there: layouts. Ignore it for now - we'll play with layouts soon, but they're not needed to get a basic application up and running.

Create a file called app/views/sandwiches/new.html.erb containing the following simple form which will allow us to create sandwich records.

<form action="/sandwiches/create">
  Name: <input type="text" name="sandwich[name]" />
  <input type="submit" value="Save" />
</form>

Now that we have a way of creating records we should allow people to see them. Create a file called app/views/sandwiches/show.html.erb containing the following code.

<p>This sandwich is called <%=h @sandwich.name %>.</p>

There are three important things to notice about the view we've just created.

  1. ERb output blocks (<%= ... %>) are used where something - such as the sandwich name - should appear in the output HTML.
  2. The helper method h is used to make sure that the output is properly sanitised HTML ie it doesn't include any nasty HTML tags that could mess up our page.
  3. The variable we're using starts with an @ symbol. It's not important to understand why at this stage, but it's important to take note of.

Let's try to create a sandwich. Fire up a browser and visit http://localhost:3000/sandwiches/new.

Rails complains that there's no route matching /sandwiches/new action

Oh no, an error! Rails doesn't know what we mean by asking for /sandwiches/new. We should tell Rails what to do when we ask for our new sandwich form, but first we should commit our views in case we screw anything up.

# Check what's changed
git status
# Stage the views for the next commit
git add app/views/sandwiches
# Commit the changes
git commit -m "Added create and show views for sandwiches."

Hooking up the view and the model: Controllers

Controllers tell Rails what to do when the view wants to talk to the model - for example when a view sends data as the result of someone hitting the submit button of a form, or when a browser requests a view. Controllers live in app/controllers/. There will already be one there, the application controller, which we will use as a base for our other controllers.

Create a new file called app/controllers/sandwiches_controller.rb

class SandwichesController < ApplicationController
  def new
  end

  def create
    @sandwich = Sandwich.create(params[:sandwich])
    redirect_to :action => 'show', :id => @sandwich
  end
  
  def show
    # Notice that this variable starts with an @ to match the view.
    @sandwich = Sandwich.find(params[:id])
  end
end

Now that we have told Rails what to do when we submit data, fire up a browser and visit the new sandwich form again. This time when you should see your form, and when you hit save you will be shown the sandwich record you've just created as a webpage.

A sandwich record is shown in the browser window

Success! Our application allows us to create sandwiches and share them with visitors. One last time (for this article), save your progress.

# Look to see what's been changed
git status
# Stage the sandwiches controller
git add app/controllers/sandwiches_controller.rb
# Commit the changes with a message
git commit -m "Added a controller for sandwiches."

What next?

You've seen how to install Rails 2 and create a basic project that allows you to create sandwich records and show them to people.

In the next article we'll flesh out some missing functionality by providing a sandwich index so visitors can find sandwiches and a way of editing and deleting sandwiches, and we'll start using layouts and partials to DRY up views and make them look good. Stay tuned!

If you've found this article useful I'd appreciate recommendations at Working With Rails.

You may also be interested in attending my Rails tutorial at Scotland on Rails Charity Day in Edinburgh, Scotland on April 3rd 2008.

I'm talking at Scotland on Rails

For three days (03 April to 05 April 2008) Scotland on Rails will host a ton of cool speakers covering a massive array of interesting subjects.

I'm talking at the charity tutorial day introducing Rails for those that are not already familiar with it. I'll be covering some best-practice techniques in the talk so even if you already know Rails, come join in, spread the love.

There's still time to register though places are limited so be quick!

Don't rewrite UserDir requests

I have a site that's a Rails application which I also want to be able to use as my home on the web -- a place I keep the stuff that I want to share. Since I use Apache, the simplest way to do this is to use the UserDir module. Unfortunately, the way that I setup my applications catches all requests using mod_rewrite and, after some processing to make sure there's not a cached file to use, passes the request to an application server.

For my future reference, here's the necessary mod_rewrite voodoo to avoid rewriting UserDir requests.

# Don't rewrite UserDir requests
RewriteRule ^/~.*$ - [L]

All hail the mighty mod_rewrite cheat-sheet.

Showing multiple message types with the flash

Traditionally the Rails flash is used to store only one message, usually in flash[:message] but I like to be able to differentiate between warnings and information in my view rather than displaying them both in the same place in the same style.

The flash is a HashWithIndifferentAccess so we can use any key we want to store messages and pull them back out later, in the view.

In your controller you simply use whichever key is most appropriate.

if @widget.save
  flash[:info] = "Your widget has been saved."
  flash[:notice] = "There are now #{Widget.count} widgets."
  redirect_to @widget and return
else
  flash.now[:warning] = "I couldn't save your widget."
  render :action => "edit"
end

In the view you can now iterate over the flash and pick out the key and the message.

<%= flash.sort.collect do |level, message|
  content_tag(:p, message, :class => "flash #{level}", :id => "flash_#{level}")
end.join %>

Say the @widget got saved earlier, you'd end up with some easily understood and styled markup.

<p class="flash info" id="flash_info">Your widget has been saved.</p>
<p class="flash notice" id="flash_notice">There are now 29 widgets.</p>

Of course, in the interest of readability the flash loop in the view should be extracted to a helper, but that's left as an exercise for the reader.

About the boy

A picture of Craig in grayscale

Craig Webster is a software engineer living in London. He usually works with Ruby although sometimes he sneaks in some Erlang or JavaScript. He's into rock climbing, snowboarding, skating, photography and fencing. Yes, this does mean he has a sword.

Near here you'll find Craig's homepage, contact details, PGP key and keysigning policy, and talks.

Licence

The entire content of this blog is public domain. Use it however you fancy. You don't even need to attribute it to me, although it would be nice if you did. Just don't sue me and we'll all be happy.

I Work With Rails

Recommend Me

My Travels

I go places. Do you go places too? Let's meet up!.