zargony.com

#![desc = "Struggling through software development and server administration"]

Five tips for developing Rails applications

During the development of some web applications in the past, I found myself using various techniques again and again on different projects. Here's a collection of five things that I discovered over the time and that I found most useful.

  • #1 -- Pimp your rails console with colors, history and tab completion
  • #2 -- Using source annotations
  • #3 -- Using enum attributes with ActiveRecord
  • #4 -- Protect email addresses in views from being gathered by spammers
  • #5 -- Avoid sending messages to nil

#1 -- Pimp your rails console

The rails console is an incredible helper during development. You can work with models, manually test things or just make changes to your data. Even in production mode, it comes in handy to use the console to manually change data on a live system if absolutely required.

However, the standard console feels like a dumb terminal; it has almost no input assistance like we know from e.g. the bash shell. But luckily, there's Wirble, a gem that pimps your irb console.

Simply install the wirble gem

# sudo gem install wirble

and put the following into ~/.irbrc

require 'rubygems'
require 'wirble'
Wirble.init
Wirble.colorize

and your console feels much more comfortable. You get * Syntax coloring of output. It's much easier now to distinguish the different values if you get a larger object as a result * Input history. All commands entered can be easily browsed and used again with cursor keys and PgUp/PgDn, just like in a shell * Command completion. Feels like a shell, but it's a ruby console. Auto-complete partially entered method names with Tab or see a list of possible methods with 2*Tab.

Additionally I usually redirect the Rails debug log to the console while developing, so that I can see what database queries are done when using methods on models. Simply drop the following into ~/.irbrc and the Rails logging will be printed to the console:

require 'logger'
if ENV.include?('RAILS_ENV') && !Object.const_defined?('RAILS_DEFAULT_LOGGER')
  Object.const_set('RAILS_DEFAULT_LOGGER', Logger.new(STDOUT))
end

#2 -- Source annotations

It's good practice to annotate your code with a comment at places where you know that further work must be done. Usually these annotations are placed as a comment into the source. Over time some keywords have been well-established for this purpose. Namely there are:

  • TODO -- placed where something is incomplete and more work needs to be done
  • FIXME -- placed where it's known that something is broken and needs to be improved
  • OPTIMIZE -- placed where optimization needs to be done

Usually, if you use an editor with syntax coloring, comments containing these keywords will be extra highlighted in some way to get your attention.

Additionally, Rails has some rake tasks that search for comments with the above keywords and print lists of all occurrences, including the filename, line number and description (remaining text after keyword) of such an annotation.

  • rake notes:todo -- list all TODO comments
  • rake notes:fixme -- list all FIXME comments
  • rake notes:optimize -- list all OPTIMIZE comments
  • rake notes -- list all of the above comments

#3 -- ActiveRecord enums

ActiveRecord does not support attribute of type ENUM or SET natively. An easy way to come around this limitation is to use an integer or a string instead. I found it most useful to use a string columns, restrict the values by using validations and additionally symbolize the values so that they look more ruby-style.

Validation is pretty easy and values can be symbolized by defining own getter and setter methods. Lets take a user that can be active or inactive as an example:

class User < ActiveRecord::Base
  validates_inclusion_of :status, :in => [:active, :inactive]
  def status
    read_attribute(:status).to_sym
  end
  def status= (value)
    write_attribute(:status, value.to_s)
  end
end

To make this easier for multiple attributes and work with nil values, I published a plugin that provides a symbolize class method. Using the activerecord_symbolize plugin, the above becomes as easy as symbolize :status. More background info on how it works can be found in an older post in my blog.

The activerecord_symbolize github repository doesn't seem to work at the moment, since there's a problem with my github account. I already notified the github team and hope they'll address it soon.

#4 -- Protect public email addresses

If you put your email address on a public web page, you can usually be sure to get tons of spam from there on, because address harvesters will sooner or later visit your page and recognize the email address.

To prevent spam harvesters from gathering your email addresses, you can try to scramble them so that a harvester won't recognize them but humans can still read them. One way of doing so is included in Rails' view helper mail_to.

mail_to can be used with various options. :replace_at and :replace_dot will obfuscate the email address by replacing the @ character and dots by the given string.

So using :replace_at=>'(at)' and :replace_dot=>'(dot)' would output:

me(at)example(dot)com

Note that only the displayed address is obfuscated, so the plain address is still present in the href attribute of the link. Therefore this method is useless in my opinion, since it only annoys visitors with obfuscated addresses and does not hinder harvesters from gathering the link.

The :encode option can be used to encode the email address using :hex or :javascript. Using :hex will replace all letters of an email address with their hex value, so .com becomes .%63%6f%6d

Using :javascript is probably the best mail_to can do, since it completely replaces the link with a JavaScript snippet that does not look like a link or an email address anymore. So the generated page source doesn't contain any email link, but if displayed in a browser, the JavaScript executes and shows the email address.

However, I suppose that an email address encoded this way can still be harvested easily, since it can be made visible without actually running the JavaScript. So I recently wrote a view helper that creates a more complicated JavaScript snippet (one that cannot easily be reversed without actually executing it).

#5 -- Avoid sending messages to nil

A method usually returns an object if the operation succeeded -- or nil if not. E.g. ActiveRecord finders return nil, if no matching record was found. So the returned value has to be checked for nil before doing further actions on it, or you risk calling a method on a nil object (everybody probably knows the NoMethodError: You have a nil object when you didn't expect it!)

So at many places, code looks like this:

user = User.find_by_username('foo')
user.dosomething unless user.nil?

There's a smart solution of making this look much nicer. On ozmm, Chris wrote how he defines a method Object#try, that calls a method if it exists and returns nil otherwise. Here's the snippet, slightly modified so that it supports multiple parameters:

class Object
  def try (method, *args)
    send(method, *args) if respond_to?(method)
  end
end

With this, the method try can be called on every object (including nil values, since nil is also an Object). try calls the method named in the first parameter, if it exists and returns it return value or nil if the method doesn't exist. This way, the above case can be handled much easier and more readable:

User.find_by_username('foo').try(:dosomething)

You can even cascade it. Instead of writing:

user = User.find_by_username('foo')
address = user.address unless user.nil?
address.dosomething unless address.nil?

you can now do it this way:

User.find_by_username('foo').try(:address).try(:dosomething)@