Custom ActiveRecord attribute serializers

Here’s an under-documented but extremely useful feature of ActiveRecord.

If you’re familiar with the serialize macro you’ll know that by default ActiveRecord will serialize the given attribute to YAML, and there’s no apparent way around it, but there actually is a way hidden in serialize’s implementation.

If you provide a second argument to serialize with the class you want the serialized attribute to belong to then ActiveRecord will check whether the given class responds to .dump and .load and use those methods to serialize/deserialize the object instead of the default YAML encoder. For this to work both methods need to be defined, defining just one won’t do.

So you could have, for instance:

class Recipe < ActiveRecord::Base
  serialize :ingredients, IngredientsList
end
 
class IngredientsList < Array
  def self.dump(ingredients)
    ingredients ? ingredients.join("\n") : nil
  end
 
  def self.load(ingredients)
    ingredients ? new(ingredients.split("\n")) : nil
  end
end

This is a very simplistic example (and probably not a very useful improvement over YAML serialization), but this method can become really useful when you want to use custom classes to define your serialized attribute’s logic, especially when those classes aren’t easily mappable to YAML.

Removing REST actions from an ActiveAdmin resource

If you’ve been using ActiveAdmin to manage your administration interfaces in Rails you might have come across the need to get rid of some of the default REST actions. Maybe your model is simple enough that it doesn’t need a “show” view, or perhaps you only want admins to be able to update user-generated content but not create it.

ActiveAdmin’s documentation doesn’t cover how to remove default REST actions, and so you might be tempted to simply replace the action links in your views to “hide” those actions, but that’s not a very pretty solution. Fortunately the API has a nice way of achieving this, you simply have to specify which actions you want using actions in the body of your admin definition, e.g.:

ActiveAdmin.register Moderation do 
  # Remove default REST actions except index, show and update
  actions :index, :show, :update
end

This actions method is actually a method of InheritedResources, a gem ActiveAdmin is built upon, and so it supports this other syntax:

ActiveAdmin.register Moderation do 
  # Remove create and show actions
  actions :all, :except => [:create, :show]
end

I hope you found this useful.

Testing rendering of a partial and the case of the undefined `ref’

So today I was trying to test a controller which renders a partial in response to an XHR request:

context "when issuing an AJAX request" do
  before { request.stub(:xhr?).and_return(true) }
 
  it("renders just the comment") do
    post :create
    should render_template(:partial => 'comments/_comment')
  end
 
  # ...

But I kept getting the following cryptic error:

NoMethodError:
       undefined method `ref' for nil:NilClass

After scratching my head for several minutes I finally stumbled upon this blog post by Alejandro Riera Mainar, who had a similar problem, although not testing. His solution inspired me though, all I had to do was to specify the format in my mocked request:

  it("renders just the comment") do
    post :create, :format => 'html'
    # ...

And magically the test passed.

Setting up the Facebook JavaScript SDK channel file the easy way with Rails

If you’ve used the Facebook JavaScript SDK you probably know about the “channel file” that Facebook recommends setting up in your server.

The docs stress the importance of setting proper cache headers in the server’s response, which means we can’t just drop the file as a static file under public/. Luckily for us we can take advantage of Rails 3′s router, which allows routing paths to Rack applications. The contents of the channel file are very small, just a <script> include tag, so we can simply inline a Rack app that returns those contents with the right headers, and we’ll be done.

Facebook provides this example in PHP:

<?php
$cache_expire = 60*60*24*365;
header("Pragma: public");
header("Cache-Control: max-age=".$cache_expire);
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$cache_expire) . ' GMT');
?>
<script src="//connect.facebook.net/en_US/all.js"></script>

So let’s translate that into a Rack app using a Proc and putting it in config/routes.rb:

# config/routes.rb
get '/channel.html' => proc {
  [
    200,
    {
      'Pragma'        => 'public',
      'Cache-Control' => "max-age=#{1.year.to_i}",
      'Expires'       => 1.year.from_now.to_s(:rfc822),
      'Content-Type'  => 'text/html'
    },
    ['<script type="text/javascript" src="//connect.facebook.net/en_US/all.js"></script>']
  ]
}

If you think that code looks too ugly to be in your routes file you can always move it over to a module and put it in your lib/ folder, just make sure to define self.call as the entry point for the Rack app.

To test that it works we can run the Rails server and make a request using curl:

[pilaf@bmo ~]$ curl -i 0.0.0.0:3000/channel.html
HTTP/1.1 200 OK
Pragma: public
Cache-Control: max-age=31557600
Expires: Thu, 24 Jan 2013 00:30:15 +0000
Content-Type: text/html
X-UA-Compatible: IE=Edge
ETag: "5ee12707f54a4af13db5808826dd8040"
X-Runtime: 0.001898
Content-Length: 59
Connection: keep-alive
Server: thin 1.3.1 codename Triple Espresso

<script src="//connect.facebook.net/en_US/all.js"></script>

There, all headers are showing up, as well as the contents of the file.
Hope you found that useful.

Update: s/from.now/from_now/ (thanks to Ben, Martin and Peter for noticing it)

Did you know #1: Underscores in Ruby numbers

Did you know that you can add underscores between digits in Ruby and the interpreter will gently parse them as numbers? This is nice to represent thousands!

The only limitation is that you can’t start a number with an underscore.

  # Valid Number
  1_000 # => 1000
 
  # Not valid
  _1_000 # => NameError: undefined local variable or method `_1000' for main:Object

Enjoy!