Now When Did I Add That? 1

Posted by Bob Showalter Tue, 16 Oct 2007 17:14:00 GMT

A colleague noticed today that on of our Rails models was raising a validation error when attempting to update a previously-entered row. A validates_uniqueness_of constraint was being violated.

He wondered how the data could have been saved in the first place? As it turns out, the validates_uniqueness_of had been added to the model fairly recently. Here’s how we found out exactly when and by whom:

$ svn blame app/models/widget.rb | grep validates_uniqueness_of
   494   showaltb   validates_uniqueness_of :name

The Subversion blame command annotates each line of a file with the revision number in which that line was last changed and the user that committed that revision. I used grep to just find any validates_uniqeness_of lines in the file.

From the output, you can see that the line was added (or changed) in revision 494 by user showaltb (that’s me).

Hmm, was the line added or changed? Let’s ask Subversion:

$ svn diff -c494 app/models/widget.rb | grep validates_uniqueness_of
+  validates_uniqueness_of :name
-c494 tells Subversion to show me the changes that were made by revision 494 (i.e. between 493 and 494). Yep, it was an add (the + indicates an added line).

Now, when did I add that line? We need to know when changeset 494 was committed:

$ svn info -r494 app/models/widget.rb
Path: app/models/widget.rb
...(snip)...
Last Changed Date: 2007-10-08 09:51:03 -0400 (Mon, 08 Oct 2007)

You can pass a revision number to svn info. Cool. OK, so that line was added on October 8.

Now, generally, if you add or change a validation rule, you need to consider what to do about your existing data, because some of it may not be valid any more. One option would be to create a migration that would fix any validation problems.

For example, with my example here, I could write a migration to find any rows with duplicate names and add some kind of “tie-breaker” to the names to avoid the duplication problem.

Loading Multiple Ruby Files and Finding Descendants of a Class

Posted by Bob Showalter Wed, 26 Sep 2007 15:35:00 GMT

I was playing around with writing a little Ruby program to run simulations of the Prisoner’s Dilemma. I wanted to be able to program various strategies by creating multiple classes that descended from a common Player class:

class FirstPlayer < Player
 ...
end

I put my player classes in little ruby files and wrote a “supervisor” program to load all my classes and then play them against each other, round-robin fashion. I run the supervisor like this:

ruby supervisor.rb player1.rb player2.rb
In order to create my supervisor, I had to do two things:
  1. Load the player files specified on the command line, and
  2. Find all the player classes

I noticed that my command line looks a lot like what happens when you run e.g. rake test:units in a Rails project. This rake task loads all your unit tests and then finds all the test cases (i.e. the classes that are descendants of Test::Unit::TestCase).

The job of loading all the test files is handled by rake_test_loader.rb, which is part of the rake gem. The relevant code is trivial:

ARGV.each { |f| load f unless f =~ /^-/  }
ARGV contains all the command line arguments (after supervisor.rb). The unless part rejects arguments that start with a dash, since those would presumably be option flags.

So I can use that code as-is to load my player classes.

In order to find my player classes, I needed to borrow a technique from Test::Unit to find all classes that are descendants from my base Player class.

The relevant code is in test/unit/autorunner.rb in your standard Ruby library directory. The Test::Unit is more complex than what I needed, but I was able to distill it down to this:

players = []
ObjectSpace.each_object(Class) do |klass|
  players << klass if klass < Player
end
ObjectSpace is a handy Ruby gizmo that, among other things, lets you iterate over all the objects in your current process. By specifying Class in the call to each_object, we iterate over all classes. To find those that are descendants of my Player class, we use a handy < operator defined in Module. This operator returns true if the left-hand argument is a descendant of the right-hand argument.

Can It Be Done in One Line?

You may be wondering why I’m using an array here and then appending to the array as I go. Isn’t there some way to use collect()/select() to extract this data in one call?

The problem is that methods like select() are in module Enumerable, but ObjectSpace is not. However, Ruby provides a handy class called Enumerable::Enumerator that can turn any object with a method that yields to a block into an Enumerable object.

To use this for our class collector, you would write:

require 'enumerator'
players = Enumerator::Enumerable.new(ObjectSpace, :each_object, Class).select {|klass| klass < Player})

I’ll let you decide which form is more readable.

Validating Mutually-Exclusive Attributes 5

Posted by Bob Showalter Sun, 16 Sep 2007 19:47:00 GMT

Let’s say you have a model that holds a location, with a “state” and a “country”. Suppose that you want the user to enter either a state or a country, but not both.

Your first instinct might be to do something like this:

validates_presence_of :state, :if => lambda {|row| row.country.blank?}
validates_presence_of :country, :if => lambda {|row| row.state.blank?}

That’s not quite right however. You won’t get an error if both a state and a country are entered. Furthermore, if both attributes are entered, which one should be considered “wrong”?

Here’s where the generic validate method can help. Rather than validating a specific attribute, validate works on the whole row. If you need to add error messages you use the ActiveRecord::Errors#add() and #add_to_base() methods.

The validation for our state/country example looks like this:

validate do |row|
  if row.state.blank? ^ row.country.blank?
    row.errors.add_to_base 'Either a state or a country is required (but not both)'
  end
end

Note how validate takes a block. The row being saved is passed to the block. The caret (^) is Ruby’s “exclusive-or” operator. It returns true only if exactly one of its operands is true, which fits our requirements perfectly.

The add_to_base method adds an error message to the list of errors for the row, but the message is not specific to any single attribute. In this case, the error relates to two attributes, so I chose to use add_to_base.

Don't Use Instance Variables in Partials 2

Posted by Bob Showalter Tue, 24 Jul 2007 18:26:00 GMT

Using instance variables (e.g. @article) is a handy way to pass data from your controller to your views. When your view is rendered, Rails arranges for all the controller instance variables to be available within the view.

However, you should avoid using instance variables to pass data to partials. Partials are almost always used for snippets of HTML that will be reused in multiple places in your application, and using instance variables makes it harder to achieve that reuse.

Here’s a simple example. Let’s say we have a blog article, and we want to display the article body followed by a set of links to actions that can be performed on the article. So we might have a controller like this:

  def show
    @article = Article.find(params[:id])
  end

Here’s the view, show.rhtml:

  <h1><%= h @article.title %></h1>
  <div><%= h @article.body %></div>
  <%= render :partial => 'links' %>

And here’s the partial, _links.rhtml:

<p>
  <%= link_to 'Comments', :controller => '/articles', :action => 'comments', :id => @article.id %>
</p>

(Note how were referencing @article from inside the partial.)

Now let’s say we want to display a list of articles, and we want to reuse the links partial to add links to each article.

Here’s the controller:

  def list
    @all_articles = Article.find(:all)
  end

And here’s the view:

  <h1>All Articles</h1>
  <% @articles.each do |art| %>
    <h2><%= h art.title %><h2>
    <%= render :partial => 'links' %>
  <% end %>

Uh-oh. How does _links.rhtml find the article id, since we don’t have an @article instance variable?

You might be tempted to “fix” it like this:

  <h1>All Articles</h1>
  <% @articles.each do |art| %>
    <h2><%= h art.title %><h2>
    <% @article = art   # needed by the partial %>
    <%= render :partial => 'links' %>
  <% end %>

Don’t do that. Instead, change your partial to avoid using instance variables and use local variables instead.

You have two choices for naming local variables:

  • Use a local variable named the same as your partial (e.g. “links” in this case.
  • Use any local variable names you wish. For example, we could use article as a local variable for the article instead of the @article instance variable. More about this in a moment.

The first form is useful if your partial is used to display all the details for each item of a collection (array). In this case, you can render the entire array without using an explicit loop by using the :collection argument to render:

   <%= render :partial => 'links', :collection => @all_articles %>

Even though there is no loop, the partial is rendered once for each element in @articles. Within the partial, you would reference the current article through the links local variable:

<p>
  <%= link_to 'Comments', :controller => '/articles', :action => 'comments', :id => links.id %>
</p>

If you only want to render the partial for a single element (as in our example, since we already have a loop that is doing more than just rendering the partial), you pass the element using :object instead of :collection:

  <h1>All Articles</h1>
  <% @articles.each do |art| %>
    <h2><%= h art.title %><h2>
    <%= render :partial => 'links', :object => art %>
  <% end %>

To make this work with our orignal show.rhtml view, we pass the article in the same way:

  <h1><%= h @article.title %></h1>
  <div><%= h @article.body %></div>
    <%= render :partial => 'links', :object => @article %>

Now our partial is much more flexible and is decoupled from dependence on any particular instance variables. We can pass an article to it from whatever context is appropriate.

The only thing that’s a bit ugly is using the local variable name links in our partial. Perhaps you would prefer to use a more meaningful name, like article.

If you do this, you need to pass the variable using :locals instead of :object. :locals takes a hash of variable names/values (so it can also be used to pass multiple variables). For example:

  <h1><%= h @article.title %></h1>
  <div><%= h @article.body %></div>
    <%= render :partial => 'links', :locals => { :article => @article } %>

Within your partial, you would now refer to the article object through the local variable article.

This technique can’t be used with the :collection form; it will always pass the collection element using the same name as the partial itself.

Minimize or disable WEBrick logging

Posted by Bob Showalter Mon, 25 Jun 2007 02:22:00 GMT

I wrote a simple application today that used WEBrick servlets to serve up some content, and I wanted to minimize the logging that WEBrick puts out.

There are two kings of logging used by WEBrick:

  • Server logging, which is controlled by the :Server parameter passed to WEBrick::HTTPServer.new. This uses syslog-style log levels.
  • Access logging, which is controlled by the :AccessLog parameter. This logs each request, and is similar to the Apache access log.

The default server log level is INFO, but I wanted to change it to WARN. I also wanted to disable Access logging altogether.

Here’s what I ended up using:
include WEBrick
server = HTTPServer.new(
  :Port => 8000,
  :Logger => Log.new(nil, BasicLog::WARN),
  :AccessLog => []
)
Now the server is silent unless an unexpected problem occurs.

autotest errors after migration?

Posted by Bob Showalter Sun, 24 Jun 2007 00:15:00 GMT

If you generate a new model or run a migration that makes changes to your database, autotest will probably start reporting errors.

For instance, if you generate a new model, edit the migration and then rake db:migrate, you might see something like this:

ActiveRecord::StatementInvalid: Mysql::Error: #42S02Table 'sample_test.widgets' doesn't exist: DELETE FROM widgets

autotest is really smart, which is great. But this one trips it up a bit. The problem is that autotest is running your new widget_test.rb, which is trying to load your new Widget fixtures. However, rake db:migrate only affects your production database, not your test database.

The fix is simple:

  1. Run rake db:test:prepare. This applies the current development schema to your test database.
  2. Press Ctrl-C (once) in your autotest window. That tells autotest to restart itself and run the full test suite.

Write at least a simple functional test for each of your actions

Posted by Bob Showalter Sat, 23 Jun 2007 18:03:00 GMT

If you’re like me, you focus on writing unit tests more than functional tests. Most of the key business logic should be in your models anyway, and it’s easier to test at that level.

Nevertheless, it’s a good idea to at least write a basic functional test for each action to make sure the action at least runs through without any exceptions.

If you run autotest (which is a great tool), or at least run rake:test before any commits, these simple tests can catch boneheaded mistakes like one I made today.

I modified a controller and added an instance varible, which I then referenced in a partial. Everything worked fine and it looked good in the browser, so I committed it.

Unfortunately, I stupidly forgot that the partial was used in another context, and that action promptly broke, because I didn’t create the instance variable there. As it turns out, the proper fix was to move my instance variable out of the partial and into the main template.

A simple functional test on each action would have revealed my mistake right away.

Controller Code Smell in the Wild

Posted by Bob Showalter Thu, 21 Jun 2007 20:37:00 GMT

One of the trends you should be practicing is keeping unnecessary code out of your controllers. Here’s a real-world example of a controller Code Smell that I found in an application I’m doing maintenance on.

Here’s the original controller method:
  def clone_billing_address
    @customer = Customer.find(params[:id])
    l = Location.new
    l.address = @customer.address
    l.addr2 = @customer.addr2
    if @customer.is_commercial? then location_name = 'Main Building' else location_name = 'Home' end
    l.name = location_name
    l.city = @customer.city
    l.state = @customer.state
    l.zip = @customer.zip
    l.save
    @customer.locations << l
    l.customer = @customer
    l.save
    @customer.save
    redirect_to :action => 'show', :id => @customer
  end

The purpose of this action is to take some information from the Customer record and create a Location record, which is then linked to the Customer.

The smell is all the @customer method calls. That’s a hint that the Customer object should know how to clone itself. Think in terms of the “Tell, don’t ask” principle. Instead of asking the Customer object for a bunch of details about itself in order to create a Location, tell the object to create a location.

Here’s the refactored method:
  def clone_billing_address
    @customer = Customer.find(params[:id])
    @customer.create_default_location
    begin
      @customer.save!
    rescue
      flash[:warning] = "Unable to clone billing address: #{$!}"
    end
    redirect_to :action => 'show', :id => @customer
  end

All the logic for creating the location has been moved to the Customer model. Note that the original method didn’t do any error checking, so I added some.

Getting on the bandwagon

Posted by Bob Showalter Thu, 21 Jun 2007 15:13:00 GMT

Hey, all the cool cats are blogging on Rails, so here’s my go at it…