Good notes posted to Ruby on Rails

RSS feed
August 12, 2008 - (>= v2.1.0)
5 thanks

polymorphic_url and namespaces and nested resources

You can use polymorphic_url with namespaces and nested resources using array as parameter:

polymorphic_url([:admin, @post])

will return:

August 12, 2008
12 thanks

Overriding the default div class="fieldWithErrors"

By default fields that are invalid are wrapped in:

<div class="fieldWithErrors">
  <input type="text" name="blah">

To override and wrap in spans instead of divs place the following in your environment.rb:

ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "<span class=\"fieldWithErrors\">#{html_tag}</span>" }

or to not use wrapping at all:

ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "#{html_tag}" }
August 12, 2008
9 thanks

Overriding the default div class="fieldWithErrors"

By default fields that are invalid are wrapped in:

<div class="fieldWithErrors">
  <input type="text" name="blah">

To override and wrap in spans instead of divs place the following in your environment.rb:

ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "<span class=\"fieldWithErrors\">#{html_tag}</span>" }

or to not use wrapping at all:

ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "#{html_tag}" }
August 11, 2008
10 thanks

RE: Using validates_format_of to validate URIs

Further to Olly’s note below, you can also specify the protocol to further limit the valid uri’s, else things like ‘ftp ://someurl.com’ (there’s only a space in there to get it to display on here) would be valid.

validates_format_of :uri, :with => URI.regexp(['http'])
August 11, 2008
8 thanks


update_attribute will not perform validations checks when the Validation module is included.

If you want to perform validations when updating, use update_attributes instead.

August 10, 2008
4 thanks

converts a Time object to a string in rfc822 / rfc2822 format

>> time = p.updated_at

> Fri Jun 20 14:05:30 +0100 2008

>> time.class

> Time

>> time.httpdate

> “Fri, 20 Jun 2008 13:05:30 GMT”


August 10, 2008 - (>= v2.0.0)
10 thanks

New way of calling partials on collections

You can directly call a partial on a collection of objects like this:

<%= render :partial => @users %>

This will call the partial _user.html.erb and populate a local variable ‘user’ within the partial. Then you can display the user partial in this way:


Name: <%= user.name %> <br />
Email: <%= user.email %> <br />

The above render statement is equivalent to this code:

<% for user in @users %>
  <%= render :partial => 'user', :locals => { :user => user } %>
<% end %>
August 8, 2008
18 thanks

Using validates_format_of to validate URIs

You can use the validates_format_of with the regular expression in the URI library ensure valid URIs.


require 'uri'

class Feed < ActiveRecord::Base
  validates_format_of :uri, :with => URI.regexp
August 8, 2008
7 thanks

Avoiding duplicate results when you do a join

When you use the :joins option you can get multiple instances of the same item. For example, say you want every User who owns one or more Lists. With the code below, if a user owns 5 lists, the User will show up five times in the results:

users = User.find(:all, 
  :conditions => ['users.id = lists.user_id'], 
  :joins => [:lists], 
  :order => 'users.username')

You can cause each found user to appear only once by using the :select option with “DISTINCT”:

users = User.find(:all, 
  :conditions => ['users.id = lists.user_id'], 
  :joins => [:lists], 
  :select => 'DISTINCT users.*'
  :order => 'users.username')
August 7, 2008
12 thanks

Function to Determine Layout

Sometimes its nice to have different layouts choosen automagicly:

class ApplicationController < ActionController::Base
 layout :determine_layout
 def determine_layout
   if is_admin?
August 7, 2008 - (>= v2.1.0)
6 thanks

around_filter - working example

More detailed, working example of usage:

class HomeController < ApplicationController   
  around_filter :action1, :action2

  around_filter do |controller, action|
    logger.info "code block before action"
    logger.info "code block after action"

  def index
    logger.info "ACTION"

  private # filters should not be available for external URL

  def action1            
    logger.info "ACTION1 before yield"
    yield "ACTION1" 
    logger.info "ACTION1 after yield"

  def action2            
    logger.info "ACTION2 before yield"
    yield "ACTION2" 
    logger.info "ACTION2 after yield"

Results (in log file):

ACTION1 before yield
ACTION2 before yield
code block before action
Rendering home/index
code block after action
ACTION2 after yield
ACTION1 after yield 
August 5, 2008
17 thanks

Multipart form

Don’t forget to add :multipart => true if you have file upload in your form.

<% form_for "user", :html => { :multipart => true } do |f| %>
August 4, 2008
7 thanks

Example: find by associated table

Say you have tables “authors” and “books” and they have a one-to-many association.

You want authors who have written books with “cooking” in the title…

cookbook_authors = Author.find(:all, 
  :conditions => ['books.title LIKE ?', '%cooking%'], 
  :joins => [:books], 
  :order => 'authors.last_name' )

For many-to-many associations, it’s a similar pattern. Say you have tables “people” and “organizations” with a many-to-many association through the join table “organization_memberships”.

Ski Club members whose first name starts with “a”…

ski_club_members = Person.find(:all, 
  :conditions => ['first_name LIKE ? AND organizations.name = ?', 
    'a%', 'Ski Club'], 
  :joins => [:organizations], 
  :order => 'people.last_name' )
August 4, 2008
3 thanks

Detailed messages for a nested model

Detailed messages for a nested model

<%@address = @order.address%>
<%=error_messages_for :address%>
August 3, 2008 - (v2.0.0 - v2.1.0)
6 thanks

Cross browser issues

We use jQuery as our Javascript library of choice, but have to use a work around for full cross-browser support.

In jQuery you need to set the AJAX request headers as:


beforeSend: function(xhr) {xhr.setRequestHeader(“Accept”, “text/javascript”);}


But we found that IE and Safari sends headers like: HTTP_ACCEPT=>“text/html, /, text/javascript”, with the javascript header last so this mucks up the respond_to block as it will always enter the first block (usually format.html) and never reach your format.js block.

We have a before filter called on required actions that forces the request format to be javascript if it is an xml_http_request?

def fix_xml_http_request
  if request.xml_http_request?
    request.format = :js
July 31, 2008
8 thanks

Multiple filter methods with :only, :except

Notice that this methods accepts *filters param, so you can pass array of methods with :only or :except too


before_filter [:authorize, :set_locale], :except => :login

July 30, 2008
19 thanks

Value parameter

You can add a value to your hidden field by using the :value parameter.

hidden_field(:object, :field, :value => params[:requestval])
July 30, 2008
7 thanks

Using gmail SMTP server to send mail

First you would need to sign up with Google Apps, which is a very painless process:


Next you need to install a plugin that will allow ActionMailer to make a secure connection to google:

script/plugin install git://github.com/caritos/action_mailer_tls.git

We need this due to transport layer security used by google.

Lastly all you need to do is place this in your environment.rb file and modify it to your settings:

ActionMailer::Base.smtp_settings = {
 :address => "smtp.gmail.com",
 :port => 587,
 :domain => "your.domain_at_google.com",
 :authentication => :plain,
 :user_name => "google_username",
 :password => "password"
July 30, 2008
6 thanks

Different Method for Subdomains


You can also access the subdomain via the subdomains array.

July 29, 2008 - (v2.1.0)
16 thanks

Scoped using - more simple way

Regarding to the example from james, there is a more simple way to do this:

user.messages.update_all(:read => true)
July 28, 2008
15 thanks

Friendlier error message example

The default error messages can be a bit stale and off putting. Try somethings like this:

  :header_message => "Oops - We couldn't save your user!", 
  :message => "The following fields were a bit of a problem:", 
  :header_tag => :h1

You can also use error_messages_for as follows

<%  form_for User.new do |f| %>
  <%=  f.error_messages :header_message => "..." %>
<%  end  %>
July 25, 2008 - (v1.0.0 - v2.1.0)
4 thanks

select_options_tag - no more worries...

no more explicit options_for_select calls..

def select_options_tag(name='',select_options={},options={})
  #set selected from value
  selected = ''
  unless options[:value].blank?
    selected = options[:value]


July 25, 2008 - (v1.0.0 - v2.1.0)
4 thanks

haml, an alternative to ERb

Want something nicer looking (and currently, faster!) than using ERb for your views? Have a look at haml (and it’s companion, sass, for stylesheets). It will make you feel all fuzzy on the inside, I promise :P.

ERb example

<div id="profile">
  <div class="left column">
    <div id="date"><%= print_date %></div>
    <div id="address"><%= current_user.address %></div>

haml equivalent

    #date= print_date
    #address= current_user.address

Shifting to haml from ERb feels strange at first, but after about 20 minutes it starts to feel nice. A little longer and you’ll really start to notice your productivity (and of course, happiness) increase! :). I’ve starting shifting all new projects developed at our work office over to using haml (and sass), it’s been fantastic!

At first I came across a few things that I couldn’t do in haml, though every time a quick read of the overview doc page would show me a simple syntax for overcoming that issue! :) (which out of interest, is located here: http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html)

Give the tutorial a shot if you’re interested: http://haml.hamptoncatlin.com/tutorial

July 24, 2008
3 thanks


You can wrap render in helpers. For example, render_collection. In app/helpers/application.rb:

module ApplicationHelper
  def render_collection(name, collection)
    render :partial => "shared/#{name}", :collection => collection

In views:

<%= render_collection :comments, @photo.comments %>
July 24, 2008
8 thanks

render template file different from your action (method) name

In some cases you have to avoid rails magic that uses template names named as your ActionMailer method.

rails magic

def daily_notification
  # ...
# will look for daily_notification.erb

def weekly_notification
  # ...
# will look for weekly_notification.erb

your case

Just give necessary value to @template instance variable.

def setup
  # ...
  @template = 'notification'

def daily_notification
  # ...
# will look for notification.erb

def weekly_notification
  # ...
# will look for notification.erb
July 24, 2008 - (v2.1.0)
20 thanks

automatically generate scopes for model states

or better known as “throw on some more tasty meta-programming” :). Given an example of a model which has a state (String) which must from a set of defined values, e.g. pending, approved, denied.

class User < ActiveRecord::Base
  STATES = [ 'pending', 'approved', 'denied' ]

  validates_inclusion_of :state, :in => STATES

  # Define a named scope for each state in STATES
  STATES.each { |s| named_scope s, :conditions => { :state => s } }

This automatically defines a named_scope for each of the model states without having to define a named_scope manually for each state (nice and DRY).

July 23, 2008
3 thanks

options_for_select further example (using a collection and with a default value)

In this example, we are editing a collection of region records, each with its own select list of countries. (Region belongs_to :country.) If the region doesn’t have a country associated, then we want a default message of “unassigned”. Of course, if the region does have a country associated then we want that country displayed:

<% name = "region[" + region.id.to_s + "][country_id]" %>
<% id = "region_" + region.id.to_s %>

<%= select_tag(id, options_for_select([["unassigned" , "0" ]] +
                     Country.to_dropdown, region.country_id),

{:name => name} ) %> This give us:

<select id="region_3" name="region[3][country_id]">
  <option value="0">unassigned</option>
  <option selected="selected" value="12">England</option>

NB: we’re using the handy acts_as_dropdown plugin (http://delynnberry.com/projects/acts-as-dropdown/) but we could just as easily prepare the select list with map / collect as above.

July 23, 2008 - (v1.2.0 - v2.1.0)
10 thanks

:only, :except and passing in multiple parameters

To specify that the filter should be applied to or excluded from given controller actions, use the :only and :except parameters. To pass in multiple controller actions use an array:

before_filter :authorize, :except => [:index, :show]
before_filter :authorize, :only => :delete