5 little-known Rails methods

Posted by Eric
on Wednesday, April 23

While the next release of Rails appears to be coming up, there’s still plenty of small, useful features from previous releases that aren’t widely used.

A few of my favorites:
  1. query_attribute
  2. polymorphic_path
  3. debug
  4. rake -T `query` ( Not Rails specific, but still handy! )
  5. extract_options!

1. ActiveRecord’s query_attribute

Query methods are available for each of a record’s attributes, providing for a cleaner way to check for the presence of an attribute.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# == Schema Information
# Schema version: 17
#
# Table name: users
#
#  id                    :integer(11)     not null, primary key 
#  first_name            :string(255)     
#  last_name             :string(255) 

# Original
class User < ActiveRecord::Base
  def named?
    !first_name.blank? && !last_name.blank?
  end
end

# Refactored to use query_attribute
class User < ActiveRecord::Base
  def named?
     first_name? && last_name?
  end
end

2. Indifferent links with polymorphic paths.

Rails has polymorphic edit/new/formatted path routing available out of the box. Providing an array will namespace the path with those array parameters. Available Methods: (edit|new|formatted|)polymorphic_path(record_or_hash_or_array)

1
2
3
4
5
6
7
8
9
10

# Before:
<% if @record.is_a?(User) %>
<%= user_path(@record) %>
<% elsif @record.is_a?(Friend)
<%= friend_path(@record) %>
 ... etc.

# After:
<%= polymorphic_path(@record) %>
Quite a few options are supported, and other Rails methods take advantage of polymorphic routing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Paths can be namespaced:
#=> admin/users/5/edit
edit_polymorphic_path([:admin, @record])

# Polymorphic urls are also internally used by helpers:
# redirects to store_path(@store)
redirect_to @store
  
# builds a form with an action to 'new_admin_stores_path'
#=> <form action="admin/stores/new" ... />
form_for([:admin, Store.new])

# <a href="/stores/5">A store in Minneapolis, MN</a>
link_to @store.name, @store

3. debug

Especially useful when starting out a project, this is quick way to understand what objects are being used in the view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


<%= debug @user %>

# Yields this in the view:
# - !ruby/object:User 
#  attributes: 
#    salt: 7be4287a1b27426fa6e5b6d733c707dd66425e82
#    updated_at: 2008-04-22 19:01:23
#    crypted_password: abb611def895dac923ba8ea59a78451f77473d5e
#    id: "1"
#    first_name: Eric
#    last_name: Chapweske
#    created_at: 2008-04-06 01:45:33
#  attributes_cache: {}

4. rake -T task

This is a handy Rake feature, and not limited to Rails. Can’t remember the exact syntax for a particular rake task? Trim the results generated with `rake -T` with an optional search parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$rake -T

rake annotate_models                 # Add schema information (as comments)...
rake audit:purge                     # Removes Audit records older than 2 m...
rake db:abort_if_pending_migrations  # Raises an error if there are pending...
... etc.
rake tmp:sockets:clear               # Clears all files in tmp/sockets

// Searching by the task's name

$rake -T db:migrate:r

rake db:migrate:redo   # Rollbacks the database one migration and re migrat...
rake db:migrate:reset  # Resets your database using your migrations for the...

5. extract_options!

While not needed very often, Rails comes bundled with a method to extract the options from methods that utilize the splat operator. This method removes the last object from an array if it’s a Hash, otherwise an empty hash is returned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


class Story < ActiveRecord::Base

  # Example: 
  # Story.published_and_tagged_with('deep', 'thoughts', :order => 'created_at desc') 
  # The generated options for this method look like this: 
  #=> { :include => :tags, :order => 'created_at desc' }
  def self.published_and_tagged_with(*tag_names)
    options = tag_names.extract_options!
    options[:include] ||= :tags
    
    ...
  end
end

References

I wasn’t able to find any write ups on the above methods, so reading the source code may be the best path if you’re curious about their exact implementations.

Rose^h^h^huby Colored Glasses

Posted by Dan Grigsby
on Saturday, May 19

Earlier this year, I attended Avi Bryant’s Applied Web Heresies tutorial session at ETech. Starting from scratch, over about four hours, the other students and I each created a mini-Seaside framework in the language of their choice.

The details of the tuturial are worthy of a post, and Nick Sieger and I are working on one. For this post, I’ll just use his thesis from the talk as a starting point. Avi says,

“A lot of the design decisions that are unconsciously adopted by modern frameworks like Rails or Django were made 10 or 15 years ago. They were probably good decisions at the time, but we need to re-evaluate them now that we have new and better tools available to us.”

The “unconsciously adopted” part of his thesis has been bouncing around in my head since then, and showed up in an unexpected place that I want to explore – think out, really – in this post.

To get where I want to go with this post I’ll need to go a bit off-topic for a couple of paragraphs, but I’ll bring it back quickly, so bear with me.

About a month ago, a group of us – including fellow Rail Spikes contributors Luke and Jon – decided to teach ourselves functional programming. With the help of the Pragmatic Programmers’ Beta Book, I decided to tackle Erlang.

Erlang’s primary data structure unit is a “tuple.” Adapting an example from the book, here’s a tuple that represents a single contact in an address book:

1
2
3
4
{person,
  {first_name, "Dan"},
  {last_name, "Grigsby"}
}.

Even if you’ve never seen erlang before, it should be pretty clear what’s going on. And, of course, an address book would normally have more fields, but this simplified example will be good enough.

Like Ruby, Erlang uses square brackets to signify lists. A list of people in an address book would look like this:

1
2
3
4
5
AddressBook = [
  {person, {first_name, "Dan"}, {last_name, "Grigsby"}},
  {person, {first_name, "Kristy"}, {last_name, "Grigsby"}},
  {person, {first_name, "Dan"}, {last_name, "Buettner"}}
].

Now that we’ve covered this little bit of Erlang, it’s time to bring this post back onto topic. Recalling Avi’s unconscious adoption thesis, I want to talk about about databases and ORMs.

As Rails developers, we spend a lot of time working to “round trip” Ruby objects into and out of the database. While ActiveRecord makes this translation considerably simpler than other ORMs, ultimately we still put in work to get what we want:

We want Ruby objects.

As an experiment, let’s think about working exclusively with lists of pure Ruby objects within the context of the CRUD (Create, Read, Update, Destroy) verbs, since that reflects pretty well what we do with ActiveRecord.

To keep this discussion anchored, let’s port our Erlang Person tuple/record to Ruby and work with that:

1
2
3
4
5
6
7
class Person
  attr_accessor :first_name, :last_name
  
  def initialize(first_name = nil, last_name = nil)
    @first_name, @last_name = first_name, last_name
  end
end

We want an Address book, which we’ll represent as a list of Person objects, so let’s add that:

1
2
3
4
address_book = []
address_book << Person.new('Dan', 'Grigsby')
address_book << Person.new('Kristy', 'Grigsby')
address_book << Person.new('Dan', 'Buettner')

Before we go on, we should take a second to think about storage:

Obviously, Ruby objects can be stored in memory; keep them in a long running process and you’d be able to work with them through a series of http requests. Serialize them and you can store them on disk for when your long-running process dies or needs to be shut down.

Alright, back to CRUD verbs:

Let’s knock down the basics quickly:

Create is demonstrated above: use the Person.new method to create an object and then add it to the address_book list. To Read an object, reference it by its index: address_book[0]. Update follows suit: address_book[0].first_name = 'Bob'. To Destroy, use any of the delete/slice/shift/pop array methods.

You probably noticed that I cheated a bit. My Read example is the equivalent of an ActiveRecord find using an ID. If you happen to know the list-offset then you can retrieve your element. The question becomes: how can we selectively choose element like you could do in a WHERE part of a SQL clause or ActiveRecord :conditions argument?

To answer this quetion, I’ll select out a list of people whose first name is Dan. In SQL, this’d be SELECT * FROM PEOPLE WHERE FIRST_NAME = 'Dan'. In ActiveRecord, :conditions => ["asks.state = 'open'"] would do it. With an array of Person objects, in Ruby this would do it:

1
2
3
4
dans = address_book.inject([]) do |results, person|
  results << person if person.first_name == 'Dan'
  results
end

(Update: See Chris Carter’s comment below about using Enumerable’s .select for a better way to do this.)

Similarly, Ruby’s Array method “delete_if” could be used to selectively delete from the list.

Using nothing other than pure Ruby objects and standard, simple Array methods we’ve demonstrated most of the common uses of ActiveRecord. We never had to leave Ruby to do this.

There’s a simplicity and an odd-elegance to this that appeals to me.

If that sounds crazy to you, consider that Mnesia, Erlang’s wickedly fast, fault-tolerant DBMS operates in the mode I’ve described the process here: it stores lists of native Erlang records (in memory, to disk, or both) and you apply list functions to select items you want. Here’s the select statement demonstrated above in SQL, ActiveRecord and Ruby, but this time in (I’m sure very bad) Erlang:


[ {person, {first_name, "Dan"}, {last_name, X}} || {person, {first_name, "Dan"}, {last_name, X}} <- AddressBook ].

Erlang has Mnesia. Someone who wears “rose colored glasses” could be said to have selective amnesia. Maybe the world needs an adaptation of Mnesia to Ruby: Ruby Colored Glasses.

I’m certain that there are more or less complete Ruby variants of Mnesia. The purpose of this article isn’t to suggest that there should be another one – if you’ve used one, use the comments to tell me about it. Rather, this post examines we can learn by throwing out one of the things we’ve all “unconsciously adopted” with Rails. Stay tuned for my forthcoming – and aforementioned – article on the mini-Seaside I built in Avi’s tutorial where we’ll question other assumptions to interesting ends.

Testing ActiveRecord Transactions

Posted by Luke
on Wednesday, March 28

ActiveRecord allows you to start transactions that will be rolled back in the event of an error.

A good example is importing records from a CSV file. If you want the entire import to roll back if any of the rows fail to import, you could write your code like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def import_csv
  csv_file = params[:csv_file]
  
  begin
    Record.transaction do 
      fastercsv = FasterCSV.new( csv_file )
      while row = fastercsv.readline
        foo, bar = row
        Record.create!( :foo => foo, :bar => bar )
      end
    end
    redirect_to success_action_path
  rescue 
    # do something with the error
    flash[:error] = "CSV import failed"
    redirect_to import_path
  end
end

The Record.create! call will throw an ActiveRecord::InvalidRecord error if one of the rows can’t be saved. Then the rescue block catches the error and reports it to the user instead of showing them an ugly 500 error (or, worse, a corrupted import).

However, this doesn’t play nicely with your tests.

You’d like to do something like this:

1
2
3
4
5
def test_import_csv_failure
  assert_no_difference Record :count do 
    post :import_csv, :csv_file => fixture_file_upload('files/invalid.csv')
  end
end

But this won’t work, because running the test starts a transaction, and ActiveRecord doesn’t support nested transactions. There’s been a patch open on this problem for 9 months, but no action has been taken.

I was able to work around the problem by turning off transactional fixtures for the entire test case class.

1
2
3
4
5
6
7
8
9
class MyTest < Test::Unit::TestCase
  self.use_transactional_fixtures = false

  def test_import_csv_failure
    assert_no_difference Record :count do 
      post :import_csv, :csv_file => fixture_file_upload('files/invalid.csv')
    end
  end
end

This makes the test run slower, but now it passes. If you’re feeling adventurous, you can install the ActiveRecord nested transactions plugin.

Lots of people have hit this problem. Jerry Kuch blogged about it in January 2006 and ticket 5457 was filed back in June. But hopefully this post will help someone else figure out the problem.