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)

The unintuitive flushing of ActiveRecord’s Dirty

In Rails 3, ActiveRecord makes use of the very useful ActiveModel::Dirty mixin which keeps around the state of each attribute before they changed (this functionality existed in the Rails 2 days as well, though not as a reusable mixin). This enables you to ask, for instance:

person.name # => "Chihiro"
person.name = "Sen"
person.name_changed? # => true
person.name_was # => "Chihiro"

This is a very useful trick that should be in the tool belt of every Rails developer. It has a little gotcha though: One of the main situations where I’ve had to use it was inside after_save callbacks, but the dirty information is flushed after saving, so we can’t use it there, right? Well, not really, the Rails developers were smart enough to not flush until after the after_save callbacks.

Here’s an example:

class Pirate < ActiveRecord::Base
  after_save :there_can_only_be_one_captain
 
private
 
  def there_can_only_be_one_captain
    # If this pirate was recently promoted to captain
    # then make sure no other pirate is captain too
    #
    # This works because the dirty information
    # hasn't been flushed at this point yet
    #
    if is_captain? && is_captain_changed?
      self.class.update_all({ :is_captain => false }, ['id != ?', id])
    end
  end
end

If for any reason you need to see the old state of the attributes at a later stage you can still use #previous_changes.

Using the Compass CSS framework with WordPress

Coming from the Rails world, I’m pretty much spoiled when it comes to writing CSS: with tools like Sass/SCSS and the godsent Compass framework, it’s been ages since I last had to write a line of CSS directly.

With the new look of our website out, it was time to give our blog a facelift as well. We had been using WordPress before, which worked really well, so instead of looking for any other solutions I decided to just create a new theme for it, but it soon became apparent that writing the CSS by hand was not going to cut it. Luckily for me, Sass and Compass provide some very nice tools to use them outside of Ruby-based projects. All you need to install them (assuming you have Ruby installed) is to run:

$ gem install compass

I based my theme off of the default WordPress theme, Twenty Eleven. As any other WP theme, this one has a main stylesheet named style.css. The first thing to do then was convert this file to Sass format, which was a piece of cake using sass-convert. I decided to put my source Sass files inside a subfolder named sass:

$ cd my-wordpress-theme
$ mkdir sass
$ sass-convert style.css sass/style.sass

Or, if you prefer SCSS, just replace sass/style.sass with sass/style.scss.

The next step was to test the new Sass-based stylesheet. The first thing to do was to remove the original CSS file, although it’s a better idea to keep it around, so I just renamed it:

$ mv style.css style.css.old

In order to test our new Sass-based stylesheet I used the sass tool:

$ sass sass/style.sass style.css

At this point I loaded my theme in WordPress and everything was looking dandy. The next thing I wanted to do was to be able to use the Compass goodness in my stylesheets, so I created a Compass config file using the compass config command:

$ compass config --sass-dir sass --css-dir "" --javascripts-dir js --relative-assets

By default this creates a config file in config/compass.rb. The parameters I used are:

  • --sass-dir sass — Specifies that I’ll be storing my .sass files in the sass subfolder.
  • --css-dir "" — This tells Compass to compile CSSs into the project root.
  • --javascripts-dir js — Tells Compass to look for JavaScript files in the js subfolder.
  • --relative-assets — This enables relative asset paths, so if I use helpers like image-url in my stylesheets (which you should) Compass will compile those paths relative to the CSS, which is what WordPress expects.

With that done I could now start using compass watch to get my stylesheets compiled automatically on each save, which is a must during development:

$ compass watch .
# >>> Compass is watching for changes. Press Ctrl-C to Stop.

With that running all I had to do is edit the sass/style.sass file, and all changes were automatically applied to the CSS. Just remember to run it each time you start working on your stylesheets.

Now all that was left was to actually include Compass in my stylesheet to be able to use its mixins and helpers, so I edited sass/style.sass and added at the top, right after the WordPress meta-data:

@import compass

And that’s it. Now you can enjoy all the benefits of using Sass & Compass in your WordPress theme. Please leave a comment if you found this useful!