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
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 can be used with various options.
:replace_dot will obfuscate the email address by replacing the @ character and dots by the given string.
:replace_dot=>'(dot)' would output:
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.
:encode option can be used to encode the email address using
:hex will replace all letters of an email address with their hex value, so
#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:
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: