Valium extracts Model attributes without instantiating ActiveRecord objects

August Jaenicke ·

I was recently on a project that captures and logs data as ActiveRecord models.  Each datum had 10 or so numeric attributes.  One story required pulling out all the values for a particular attribute in a time range (i.e. all the temperatures for the last week).  This could involve 1000’s of rows from the database.  I cringed at the thought of instantiating that many ActiveRecord objects.  I was not, however, eager to start executing SQL in the project just for efficiency; it usually violates DRY, makes testing harder and the code more fragile.

Enter Valium, a neat gem that make it easy to use your existing ActiveRecord code – but only pull out the attributes you want.  No ActiveRecord instantiation.  Ernie Miller, the creator, has a great post on the hows and why of using this gem.  After testing it out, I was very pleased.  It was exactly the solution I was looking for.  All you do is add .values_of :field_1, :field_2, … :field_n at the end of your Relation.

Note:  I updated this post to use 0.4.0’s new syntax.

Old code changed from

fields = Dummy.since(date).select(:some_field).map(&:some_field)
fields_and_dates = Dummy.since(date).select('some_field, sample_date') \
     .collect{|d| [d.some_field, d.sample_date] }

To

fields = Dummy.since(data).value_of :some_field
fields_and_dates = Dummy.since(date).values_of :some_field, :sample_date

Not only does this look cleaner (especially the case where you are extracting multiple fields), but you are not instantiating a bunch of ActiveRecord objects.  You can reuse your existing scopes and other Relations.  No SQL required. For a quick performance test, I created a Dummy model with 20 string fields.  I seeded the database with 20,000 of these models, where each field was just the current timestamp as a string.  I then timed how long it took to extract 2 of the 20 fields using each method:

# Method 1, creating AR objects for each row
Dummy.select('field_1, field_2').collect{ |d| [d.field_1, d.field_2]}

# Method 2, using Valium to extract data w/out AR objects
Dummy.values_of :field_1, :field_2

The second (Valium) version was 4x faster. When I pulled out more fields, the performance gap narrowed. For 5, 10 and 20 fields, Valium was still about 3x faster. This is just an informal test based on execution time, and doesn’t consider the object churn and memory involved in creating ActiveRecord objects for the first (non-Valium) method. Ernie posted some benchmarks (and his script) comparing using select/map to Valium.