Gareth Adams

Better sanity checking in Rails

When you’re building a web application, Rails does a load of the heavy lifting for you. Many of the core philosophies of Rails are aimed around only implementing functionality in the place it makes sense.

This turns out to be a great idea for readability, and with practice – and thoughtful naming – it isn’t too difficult to keep your code clean enough that you can see at a glance what it does. You can get to the point where your controller code just gives a high-level overview of what the code does, and leaves all the details to the models and other modules.

Lots of the tools in a Rails developer’s toolbox are commonly known, but there are a couple of useful ones that are newer and haven’t been picked up on as much.

As a simple example of how Rails tries to make you code in the right place, here’s a snippet of the kind of code you shouldn’t be writing:

class UsersController < ApplicationController
  def update
    # ...
    @user.attributes = params[:user]
    errors = {}
    errors[:name] = "Name must be given" if user.name.to_s == ""
    errors[:birth_year] = "Birth year must be given" if user.birth_year.to_i == 0
    if errors.empty?
      @user.save
      redirect_to users_url
    end
  end
end

Instead, Rails lets you simplify the code like this:

==

class User < ActiveRecord::Base
    validates_presence_of :name, :message => "must be given"
    validates_presence_of :birth_year, :message => "must be given"
  end
  class UsersController < ApplicationController
    def update


  1. @user.attributes = params[:user]
    if @user.save
    redirect_to users_url
    end
    end
    end

==

This has a few benefits:

  • Code is only written in the place it fits best, rather than the controller needing to know which fields are required, that information is stored in the model. This ties in well with the DRY principle that Rails promotes.
  • It improves the integrity of your code, as you have a much tighter guarantee that a user can never be saved without a name, while the first example only applied the protection to one action in one controller.
  • This approach makes the controller code more readable, instead of scanning through a load of validation code, you can read the action code (almost word for word) as “if the user object saves, redirect to the users list page.”

This level of separation is great news, because it means the business logic in your controllers can be much more descriptive and understandable, and not prescriptive and procedural.

Rails follows its own rules for the most part too. Rather than forcing you to write the code that checks to see whether the name attribute is set, instead it hides this away in a validates_presence_of method – which is implemented away from your specific model, deep in the Rails code.

The example above should be second nature to a Rails developer, validation is usually one of the first “cool” things you hear about when learning Rails. However there are plenty of other, lesser known ways that the readability of your code can be improved by offloading common checks and calculations to prewritten Rails methods.

Do try and pay attention

Here’s a line I’ve written many, many times in various forms throughout multiple Rails apps:

if post.published_by && post.published_by.admin?
  1. Do something
    end

This makes sense to Rubyists – if the post hasn’t been published yet, you can’t call any methods on its publisher. Trying to do that would give you probably the most common kind of error you’ll see in a Rails app:

NoMethodError: undefined method `admin?’ for nil:NilClass

But checking for the presence of something before you access it is repetitive, and if you’re chaining more than a couple of method calls together it’s more difficult to read. Rails gives you an alternative:

if post.published_by.try(:admin?)
  1. Do something
    end

The try method is very similar to the send method, with one difference – if you call try on a nil object, it returns another nil rather than raising a NoMethodError. You can even pass arguments and blocks to try:

post.published_at.try(:to_s, :db) @comments.try(:map) { |c| c.author }

This isn’t a perfect solution as it’s a bit awkward to read back, but I see it as an improvement on the repetition I had before.

Clear and present danger

Another common idiom, especially when dealing with input from HTML forms, is to check whether the user entered anything in a particular field, either a field in the request parameters or a model attribute being returned from your database. However as Rails generally saves an empty form field as the empty string (""), the standard ways of checking for the presence of an object just don’t work in these cases:

if user.name # is true when user.name == "", which is usually unwanted unless user.name.nil? # Also fails when an empty string is entered.

Lots of Rails developers seem to know about Object#blank? — the supercharged version of Object#nil? that allows you to write code like

unless user.name.blank?

However fewer people seem to have discovered the complementary method Object#present? which is the exact opposite of .blank?

if user.name.present? # Doesn’t this seem much better?

I’m sure there are lots of these useful utility methods, and I just haven’t heard about them. Leave your favourites in the comments.

Add a comment

Note: comments are moderated before publication.

Most Popular

Cooliris and the 3D wall.

Paul Sturgess

Cooliris (formerly known as PicLens) is described by it’s developers as a “lightening fast ‘3D wall’ that lets you browse thousands of images, videos and more with ease.” Cooliris is installed as a browser add-on for Firefox, Safari or Internet Expl…

Kyan Jukebox Festive 50 2008

Gavin Shinfield

Alright pop pickers? Not ’arf! Well, it’s that time of year again, best of 2008 lists abound so I thought I’d put together some of the top tracks to be rocking the Kyan jukebox from this year’s releases and now that Saint John no longer graces our airwave…

Get on the 'social media' bandwagon

Matt Hamm

‘Social media’ is the new buzz term. Everybody’s doing it, and why? Because it can generate masses amount of traffic to your website, which can easily turn into revenue. It’s really what ‘web 2.0’ is all about.

Kyan.com design process

Lee Whitelock

It’s great when you get a project you can really sink your teeth into. We pride ourselves on the effort we put into all our projects, of course, but when it’s for your own agency you can really ‘go to town’ and try new things. Our current website was out…