Archive for the ‘Education’ Category

Rescue your ActionView helpers

Friday, July 27th, 2007

Exceptions help us check for specific places where our code might throw a specific error, but they're also extremely handy when used in the general sense, in combination with the rescue keyword. In views, or any place you regularly throw around ActiveRecord associations, there is a lot of error checking to be done that might not always get caught. Rescuing a general exception in these cases can be very helpful. My problem was view code…

For a long while, I was plagued with view code that might fail if the associations tied to a particular object on which I was working were nil. Take this code, for example:

<!– people/index.rhtml –>
<%= h @person.name %><br />
<%= h @person.company.name %><br />
<%= h @person.company.ceo.phone %><br />

In a perfect world, all of these associations will exist, and the code will spit out exactly what it needs to. Unfortunately, sometimes, through data validation errors or in cases where the data just doesn't come over (say, when working with external systems), you're going to get exceptions. If the @person object exists, but the @person.company association does not, the second and third lines will fail. If the @person.company object exists but the @person.company.ceo association does not, the third line will fail. How do we check against this? Here's one way:

<!– people/index.rhtml –>
<%= h @person.name %><br />
<%= h @person.company.name if @person.company %><br />
<%= h @person.company.ceo.phone if @person.company && @person.company.ceo %><br />

As you might imagine, this code can get unmanageable very quickly. Here's a better way, through the use of helpers and the rescue keyword:

# people_helper.rb
module PeopleHelper
  def show_company_name(person)
    return h(person.company.name)
  rescue
    return ''
  end

  def show_ceo_phone(person)
    return h(person.company.ceo.phone)
  rescue
    return ''
  end
end
<!– people/index.rhtml –>
<%= @person.name %><br />
<%= show_company_name @person %><br />
<%= show_ceo_phone @person %><br />

Now, if either the company or the ceo associations do not exist, Ruby will throw an exception. This exception is rescued by our helper methods and an empty string will be returned. Our view is cleaner and more maintainable, and we can rest easy knowing that if for some reason we have a nil object somewhere in our expression, we'll safely catch it.

Easier Association Proxies

Wednesday, May 2nd, 2007

A few months ago, I wrote about association extensions and how to create more dynamic finders from your associations. While this method is nice, there are other ways to offer the same/similar functionality while inheriting a bit more functionality.

(more…)

Overriding ActiveRecord's attribute accessors

Tuesday, April 17th, 2007

I sometimes create database columns with the intent of having comma-separated values as their contents; a common occurrence is for email recipients. Having a comma-separated list of recipients in my interface, however, isn't really the way I'd like to display the column's contents. When I'm working with multiple values in my views, I'd much rather have an array. By overriding ActiveRecord's default accessor for the column, we can convert the string into something more flexible on the interface side.

(more…)

Inline ERB templating — for free

Wednesday, February 28th, 2007

I needed to support an email templating system in the project I'm working on, but creating my own seemed like a pain, especially with so many templating systems out there. I looked at Liquid (the templating system used by Mephisto) and eRuby, but ultimately, the answer was right under my nose the entire time…

(more…)

Extending render with a collection

Thursday, February 22nd, 2007

Partials offer a lot in the way of cutting out common code, especially when you couple them with a collection of objects. Take this code, for example:

<% for item in @items -%>
  <%= render :partial => "item", :locals => { :item => item } %>
<% end -%>

While pretty nice, Rails has an easier way to do it, with the :collection option:

<!– items/list.rhtml –>
<%= render :partial => "item", :collection => @items %>

<!– items/_item.rhtml –>
<div class="item">
  <%= h item.name %>
  <%= h item.description %>
</div>

Now that looks pretty sweet. Rails automatically takes the name of your partial and adds a local variable by the same name, setting its value to the current iteration through our collection. But… we've got a visual problem with our page. We have the margin-bottom CSS attribute on all div objects in the class item set to 1em. For the last item in the collection, we'd like to make the margin 0, to get rid of extra space. Rails gives you one local variable for free when you render a collection of partials, called {partial_name}_counter. In this case, the name of the counter would be item_counter and it would give you the index in the array for the current item being rendered. Using this variable, you could, in theory, do something like this in your partial:

<!– items/_item.rhtml –>
<div class="item<%= " last" if item_counter + 1 == @items.size %>">
  <%= h item.name %>
  <%= h item.description %>
</div>

That's nice, but it's also pretty ugly. Also, what if you're rendering that partial in another view and you have not declared the @items variable? It's time to add our own auto-generated local variable to the render method. We're basically going to copy and paste Rails' default ActionView::Base.render method, but with one addition. You can put this code in your environment.rb file, if you'd like:

module ActionView
  class Base
    def render(options = {}, old_local_assigns = {}, &block)
      if options.is_a?(String)
        render_file(options, true, old_local_assigns)
      elsif options == :update
        update_page(&block)
      elsif options.is_a?(Hash)
        options[:locals] ||= {}
        options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]

        if options[:file]
          render_file(options[:file], options[:use_full_path], options[:locals])
        elsif options[:partial] && options[:collection]
          # This line is what we're concerned with. Note the addition of the 'last_item' local variable.
          render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals].merge(:last_item => options[:collection].last))
        elsif options[:partial]
          render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
        elsif options[:inline]
          render_template(options[:type] || :rhtml, options[:inline], nil, options[:locals] || {})
        end
      end
    end
  end
end

Now we get a local variable, for free, called last_item that we ca n use in our view to test for the last item in the collection:

<!– items/_item.rhtml –>
<div class="item<%= " last" if item == last_item %>">
  <%= h item.name %>
  <%= h item.description %>
</div>

Obviously we can name the variable whatever we'd like, in case you happen to have a conflicting local variable called last_item. You could prepend the name of the partial to the variable name just to be safe, but I don't mind it, so I'll leave it as it is.

Implicit exception handling

Thursday, February 15th, 2007

Ruby does a fantastic job of making code look good, and today's tip is no exception, pun intended. Take this code, for example:

def delete_items(ids)
  begin
    # This could fail if any of the IDs were not found
    items = find(ids)
    items.each do |i|
      i.parent.log("Deleted #{item.name}")
      i.destroy
    end
  rescue
    return false
  end

  return true
end

While a simple example, it gets the point across. But the begin/rescue control loop has a few tricks up its sleeve, namely else and ensure. Adding an else into exception handling code will do just what you would expect: get run in case no exception was thrown. The other term, ensure denotes a block of code that will be run regardless of whether an exception was thrown or not. What we want to focus on here is the else clause, because Ruby has an implicit begin at the beginning of a method, which cleans up our code a tiny bit–but enough to make it worth doing, in my opinion:

def delete_items(ids)
  # This could fail if any of the IDs were not found
  items = find(ids)
  items.each do |i|
    i.parent.log("Deleted #{item.name}")
    i.destroy
  end
rescue
  return false
else
  return true
end

Looking good, Billy Ray.

Make Array#collect return a hash

Sunday, February 11th, 2007

The Array#collect method is great for taking a hash or an array of objects and returning a new array based on the block you provide it, but sometimes you'd like to return a hash instead of an array. My primary use case for this is in testing Rails form posts with checkboxes. For instance, here is some HTML source of an imaginary page:

<form action="/people/update_item_status/1" method="post">
  <input type="checkbox" name="items[1]" /> Car
  <input type="checkbox" name="items[3]" /> Laptop
  <input type="checkbox" name="items[7]" /> Television
  <input type="submit" value="Update Status" />
</form>

When submitted to Rails with "Car" and "Television" checked, the above form should come over like this:

Processing PeopleController#update_item_status 
  (for 127.0.0.1 at 2007-02-11 17:00:41) [POST]
  Session ID: asdf
  Parameters: {"controller"=>"people", "action"=>"update_item_status", 
    "id"=>"1",  "items" => {"1" => "1", "7" => "1"}}

When testing, you might want to build the items hash based on any number of items you specify. Using Array#collect, you'll get back an array, which won't work when building the params hash. What you need is something like the following:

module Enumerable
  # Usage: items.build_hash { |item| [ item.id, 1 ] } => { 1 => 1, 7 => 1, … }
  def build_hash
    returning(result = {}) do
      self.each do |pair|
        key, value = yield(pair)
        result[key] = value
      end
    end
  end
end

It's probably worth noting that the returning method is something included in Rails, so if you're not using this method inside a Rails project, you can either change the code to declare result = {} and then return result at the end of the method, or add the following code somewhere in your project:

class Object
  def returning(value)
    yield(value)
    value
  end
end

Keep eager loaded associations in association extensions

Thursday, February 8th, 2007

Association extensions are amazingly powerful. If you're not familiar with them, here's a small example of their power:

class Person < ActiveRecord::Base
  has_many :items do
    def expensive(expensive_price = 50.00)
      @expensive ||= find(:all, :conditions => [ "price >= ?", expensive_price ])
    end
  end
end

Then, you might do something like this from your view:

<h1>Adam's Expensive Items</h1>
<% for item in @person.items.expensive %>
  <span class="item-name"><%= h item.name %></span>
  <span class="item-price"><%= number_to_currency item.price %></span>
<% end %>

What Rails is actually doing when the find method is called in the above example is running the find within the scope of its parent association, Person. It effectively gives you the following SQL call:

SELECT *
  FROM people, items
 WHERE people.id        = items.person_id
   AND items.person_id  = 1
   AND items.price     >= 50.00

Now, let's assume you've got the following code in your controller that makes use of eager loading:

class PersonController < ApplicationController
  def show
    @person = Person.find(params[:id], :include => :items)
  end
end

You've included the items association, but in your view, you're still calling the find method in your assocation extension, which makes your eager loaded association less effective. Fortunately, Rails handles association extensions in a smart way that makes sure it plays nicely with eager loading, if you want it to. Here's how:

class Person < ActiveRecord::Base
  has_many :items do
    def expensive(expensive_price = 50.00)
      @expensive ||= self.select { |item| item.price >= expensive_price }
    end
  end
end

The above code makes use of the eager loaded association in self, if available, with the select method invoked, which is basically the same as a find(:all …) call. If the association has not been eager loaded, Rails will load issue the SQL to load it, then will run the select method against the returned collection.

One thing to note about this approach, however, is that the call to self.select will still fetch all the results of the association (that is, every item for the particular person), whereas going the route of using the call to find will only pull those records which match the conditions. If you have hundreds or thousands of items for each person, the latter method might not scale to your needs and sacrificing one more SQL call, as demonstrated in the former, would probably be the better option.

Create code dynamically with eval and here documents

Thursday, February 8th, 2007

Here documents, sometimes called "heredocs," can be a powerful way to spruce up a call to eval without declaring a method on a single line. Take, for instance, this code, which might reside in your Rails project's routes.rb file:

map.with_options(:controller => "items") do |obj|
  obj.items("/items", :action => "list")
  obj.item("/items/:id", :action => "show", :id => /\d+/)
end

map.with_options(:controller => "people") do |obj|
  obj.people("/people", :action => "list")
  obj.person("/people/:id", :action => "show", :id => /\d+/)
end

The above code uses the Object#with_options method, which makes it easy to extract out common parameters in method calls. Without the call to with_options, it would have been necessary to provide the ":controller => "items"" parameter in each of the route calls.

Anyway, back to the point. After creating the above code on one controller, you might want to do it for a second, and third, etc. That quickly becomes unmanageable. Enter eval and here documents:

named_routes