Good notes posted to Ruby on Rails
RSS feed
Re: Helper method taking a block
The same using the ActionView::Helpers::TagHelper#content_tag and ActionView::Helpers::CaptureHelper#capture methods:
def render_tree(collection, &block) concat( content_tag(:ul, collection.collect { |item| content_tag(:li, capture(item, &block)) }.join("\n") ), block.binding ) end
The benefit is that it’s easier to improve with html attributes (just add a hash of options to the content_tag call) and it makes just one call to concat (which probably makes it faster).

Insertion/Deletion callbacks
All ActiveRecord associations except for has_many :through support callbacks for pre- and post-insertion/deletion via the following, self-documenting parameters:
Adding to an Association
:before_add
:after_add
Removing from an Association
:before_remove
:after_remove
The flexibility that these callbacks offer is quite handy, but I’ll demonstrate with a silly example: logging each insertion and deletion.
class Ship < ActiveRecord::Base
has_many :pirates, :after_add => :say_hello, :before_remove => :say_goodbye private def say_hello(pirate) STDOUT.write("hello #{pirate.name} ") end def say_goodbye(pirate) STDOUT.write("goodbye #{pirate.name} ") end end
Now, we’ll see confirmation when we add/remove Pirates in the console from our ship (and yes, this must be the ghetto):
>> jolly_roger = PirateShip.new
=> #<Ship id: nil, created_at: nil, updated_at: nil> >> jolly_roger.pirates << Pirate.create(:name => 'Black Bart') hello Black Bart => [#<Pirate id: 1, name: "Black Bart", created_at: "2008-07-29 14:41:13", updated_at: "2008-08-11 11:51:25"> >> jolly_roger.pirates.first.delete goodbye Black Bart => []

Helper method taking a block
Following the similar egzample by autonomous, here’s a simpler version when you just need to write a flexible helper method that takes a block.
For example, suppose you have a method that renders a tree:
def render_tree(ary, &block) concat("<ul>", block.binding) for elem in ary concat("<li>", block.binding) yield elem concat("</li>", block.binding) end concat("</ul>", block.binding) end
You can use it in your view, eg:
<% render_tree(@objects) do |elem| -%> <%= elem.title -%> <%= link_to 'delete', elem -%> <% end -%>
that would return for egzample:
<ul> <li> Test title <a href="delete">/elems/1</a> </li> </ul>
Testing concat
To test such helper methods, use the following pattern (a utility method added to your Rspec/unit test suite:
def render_for(root, options = {}) _erbout = '' render_tree(root, options) do |node| _erbout.concat(node.title) end _erbout end
and test like this (RSpec example):
it "should return abc" do render_for(object).should == 'abc' end

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:
admin_post_url(@post)

Overriding the default div class="fieldWithErrors"
By default fields that are invalid are wrapped in:
<div class="fieldWithErrors"> <input type="text" name="blah"> </div>
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}" }

Overriding the default div class="fieldWithErrors"
By default fields that are invalid are wrapped in:
<div class="fieldWithErrors"> <input type="text" name="blah"> </div>
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}" }

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'])

Validations
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.


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:
_user.html.erb:
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 %>

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.
Example
require 'uri' class Feed < ActiveRecord::Base validates_format_of :uri, :with => URI.regexp end

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')

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? "admin" else "application" end ... end

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" action.call logger.info "code block after action" end def index logger.info "ACTION" end private # filters should not be available for external URL def action1 logger.info "ACTION1 before yield" yield "ACTION1" logger.info "ACTION1 after yield" end def action2 logger.info "ACTION2 before yield" yield "ACTION2" logger.info "ACTION2 after yield" end end
Results (in log file):
ACTION1 before yield ACTION2 before yield code block before action ACTION Rendering home/index code block after action ACTION2 after yield ACTION1 after yield

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| %>

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' )

Detailed messages for a nested model
Detailed messages for a nested model
<%@address = @order.address%> <%=error_messages_for :address%>

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:
$.ajaxSetup({
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 end end

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
Example
before_filter [:authorize, :set_locale], :except => :login

Value parameter
You can add a value to your hidden field by using the :value parameter.
Example
hidden_field(:object, :field, :value => params[:requestval])

Using gmail SMTP server to send mail
First you would need to sign up with Google Apps, which is a very painless process:
http://www.google.com/a/cpanel/domain/new
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" }

Different Method for Subdomains
@james
You can also access the subdomain via the subdomains array.
request.subdomains.first

Writing and reading a cookie in the same request.
Note that when setting the value of a cookie, the new value won’t be accessible until the next page request. In this way, the cookies hash cannot be used in the same manner as the session hash.
For example:
# given that cookies[:temporary_data] was set to 'foo' in the previous request cookies[:temporary_data] = 'bar' p cookies[:temporary_data] # => 'foo'

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)

Friendlier error message example
The default error messages can be a bit stale and off putting. Try somethings like this:
error_messages_for( :user, :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 %>

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] options.delete(:value) end select_tag(name,options_for_select(select_options,selected),options) end
select_options_tag(‘name’,[[‘oh’,‘no’]],:value=>‘no’)

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> </div> </div>
haml equivalent
#profile .left.column #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

link_to with block (Edge Rails)
New ability in Edge Rails to use link_to called with a block, e.g. The following is a view example:
<% link_to some_path do %> <strong>Awesome link</strong> -- it's pretty awesome <% end %>
This change was made in: http://github.com/rails/rails/commit/8190bce8bc7249b7b9f3680195336eb3ca9508ee
Patch in yourself, or likewise you can use the following snippet (which is the new link_to method with modifications [there are also Array extensions on edge to provide .second, .third etc which aren’t present]).
url_helper_extensions.rb
module ActionView module Helpers module UrlHelper def link_to(*args, &block) if block_given? options = args.first || {} html_options = args[1] concat(link_to(capture(&block), options, html_options)) else name = args.first options = args[1] || {} html_options = args[2] url = case options when String options when :back @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' else self.url_for(options) end if html_options html_options = html_options.stringify_keys href = html_options['href'] convert_options_to_javascript!(html_options, url) tag_options = tag_options(html_options) else tag_options = nil end href_attr = "href=\"#{url}\"" unless href "<a #{href_attr}#{tag_options}>#{name || url}</a>" end end end end end

render_collection
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 end end
In views:
<h2>Comments</h2> <%= render_collection :comments, @photo.comments %>

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 # ... end # will look for daily_notification.erb def weekly_notification # ... end # will look for weekly_notification.erb
your case
Just give necessary value to @template instance variable.
def setup # ... @template = 'notification' end def daily_notification # ... end # will look for notification.erb def weekly_notification # ... end # will look for notification.erb