Skip to content
April 21, 2011

Counting ActiveRecord Associations

Q: When is an Array not an Array?

A: When it’s an ActiveRecord association proxy

When you have something like this:

class User < ActiveRecord::Base
  has_many :addresses
end

the addresses property of a User instance feels a lot like an Array. You can iterate through it, you can count it, you can add to it, etc., but, it’s not actually an Array object: it’s an instance of ActiveRecord::Associations::AssociationProxy.

Most of the time, an AssociationProxy will do just what you expect an Array would do, but every now and then you get sandbagged by some unexpected behavior. For example, in Array, the size method is an alias for length; not so with an AssociationProxy.

I recently got bit by this subtlety until this excellent post by Xavier Shay set me straight. (For the impatient: bottom line is to use size most of the time. And don’t forget that count makes a round trip to the database every time).

So remember, kids: nice as those associations are, don’t let the smooth taste fool you.

January 19, 2010

One-Liner To See Your Last Git Commit

I just wrote a short Bash function that would call “git push” then print out my most recent commit (Why? Long story: mostly workflow related).

It took a bit of googling to get that second part to work, so I thought I would post it here:

git --no-pager log -1 --no-merges --author=darin --pretty=oneline

The --no-pager option turns off the automatic piping to less (I’m glad I found that – that might be useful in a number of contexts) and --no-merges means that I’m only seeing commits of actual code that I wrote, and not the merges.

You can also change the --pretty value to something more verbose if you need it. The options are described in the git-log docs (scroll down to “Pretty Formats”). You can use any of the pre-canned options (oneline, short, medium, full, fuller, email, raw) or roll your own with a format string.

Who knew that SCM could actually be fun?

December 30, 2009

Testing Templates In ActionMailer Functional Tests

I’ve been working on an application that takes user-submitted emails as input. After a bit of processing, it sends a reply back to the user, using one of several different templates depending what actually happened.

In my functional tests, I wanted to be able to confirm that the correct template was used in each of the different scenarios, but it turns out that there’s no equivalent to assert_template within the context of ActionMailer.

I tried googling for a solution, but most of the examples I saw just used a regex to confirm the actual copy in the body of the email. This seemed a little fragile to me, so I hacked my own solution: I added a custom X- header into the email that contained the template name, then checked for that header in the test. My mailer ended up looking roughly like this:

def action_succeeded(user)
  from        'support@myapp.com'
  recipients  user.email
  headers     'X-Mail-Template' => 'action_succeeded' if RAILS_ENV == 'test'
  subject     'Your TPS Report Has Been Received!'
  body        :user => user
end

Note that this header is only added in the test environment. That’s a nicety – there’s probably no harm in having that go out in the real emails, but checking the RAILS_ENV is easy enough to do.

To keep the tests readable, I added this method to my test_helper:

def assert_mail_template(template, msg=ActionMailer::Base.deliveries.first)
  assert_equal template, msg.header_string('X-Mailer-Template')
end

And my test method looks like this:

def test_action_succeeded
  do_some_stuff()
  assert_equal 1, ActionMailer::Base.deliveries.size
  assert_mail_template 'action_succeeded'
end

It would be pretty easy to bake something like this directly into the Rails framework. Perhaps I will submit a patch in all of my copious spare time <snark/>.

May 30, 2009

Ruby’s URI.parse and Invalid Characters

The URI.parse method is damned handy and, to its credit, follows the RFC 1738 spec quite religiously. Unfortunately, web developers are not always as careful, and modern browsers are very forgiving. Net result: there are many URLs floating around in the wild that cause URI.parse to choke.

I ran into this recently while implementing an ETL process that parses web logs. For the most part, invalid characters were showing up in the query string, and not anywhere else, so I just broke it off and dealt with it separately. The result is not as clean as one would like, but it does work.

require 'uri'
require 'cgi'

def parse_uri(uri_string)
    return nil unless uri_string && uri_string.size > 0
    (base_uri, query) = uri_string.split('?', 2)
    uri = URI.parse(base_uri)
    return [uri, query]
end

# curly braces in query strings, seem to come up a lot
bad_uri = 'http://www.dummy.com/mangle?nasty=12345678{abcdef}'

uri = URI.parse(bad_uri)
# => URI::InvalidURIError: bad URI(is not URI?): http://www.dummy.com/mangle?nasty=12345678{abcdef}

(uri, query) = parse_uri(bad_uri)
#=> [#<URI::HTTP:0x38ca16 URL:http://www.dummy.com/mangle>, "nasty=12345678{abcdef}"]

CGI.parse(query)
# => {"nasty"=>["12345678{abcdef}"]}
April 25, 2009

Ruby Client for the Google Analytics API

I heart the Ruby community. Two days after Google announced their data export API a Ruby gem appeared on github.

The gem is called Gattica and is a breeze to use:

gs = Gattica.new('johndoe@google.com','password',123456)
results = gs.get({ :start_date => '2008-01-01',
                   :end_date => '2008-02-01',
                   :dimensions => 'browser',
                   :metrics => 'pageviews',
                   :sort => '-pageviews'})

Many thanks to Rob Cameron for making this available so soon!

April 24, 2009

TypeError with REXML pretty print

I got this error today when trying to pretty print an XML document using REXML:

/opt/local/lib/ruby/1.8/rexml/formatters/pretty.rb:133:in `[]': 
no implicit conversion from nil to integer (TypeError)

It appears to be an old bug in the 1.8 library – I’m not sure if it’s been fixed in 1.9 or not.

If you run into this, it’s an easy fix – just open up the pretty.rb file indicated in the error message and add a line to the wrap() method at the bottom of the file:

  def wrap(string, width)
    # Recursivly wrap string at width.
    return string if string.length <= width
    place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
    return string if place.nil? # <---------- ADD THIS LINE
    return string[0,place] + "\n" + wrap(string[place+1..-1], width)
  end
April 24, 2009

Converting a hash to a query string in Ruby

This might be part of a standard library somewhere, but I couldn’t find it after some quick Googling (at which point it became quicker to write the damn thing myself).

  def hash_to_querystring(hash)
    hash.keys.inject('') do |query_string, key|
      query_string << '&' unless key == hash.keys.first
      query_string << "#{URI.encode(key.to_s)}=#{URI.encode(hash[key])}"
    end
  end
Follow

Get every new post delivered to your Inbox.