---
title: Handling Associations on Null Objects
teaser: How to use ActiveRecord's NullRelation.
tags: web,rails
author: Sage Griffin
published_on: 2014-01-10
---

The [Null Object Pattern][null-object] is a great tool for removing
conditionals in your code base. Rather than checking for nil or predicates about
an object existing, you instead return a "null" implementation that responds to
the same interface. The most common case I've applied this to in my Rails apps
is the concept of a "Guest User". For example:

```ruby
class Guest
  def email
    ""
  end

  def admin
    false
  end
  alias_method :admin?, :admin

  def purchases
    []
  end
end

class ApplicationController < ActionController::Base
  include Clearance::Controller

  def current_user
    super || Guest.new
  end
end
```

This implementation can serve us quite well. Our code base expands, blissfully
unaware of whether it's dealing with a `User` or a `Guest`. As we add logic for
our store front, we end up with methods like this:

```ruby
class StoreListing < ActiveRecord::Base
  def included_in?(purchases)
    purchases.map(&:store_listing_id).include?(:id)
  end
end
```

Things will continue to work on our empty array, as long as we are only calling
methods from the `Enumerable` module. However, we run into problems as soon as
we write some code like this:

```ruby
class CategoryStoreController < ApplicationController
  def index
    redirect_to(subcategory_store_path(subcategory_for_redirect))
  end

  private

  def subcategory_for_redirect
    last_purchase_in_category.subcategory || category.subcategory.first
  end

  def last_purchase_in_category
    @last_purchase_in_category ||= current_user.purchases.last_in_category(category)
  end

  def category
    @category ||= Category.find(params[:id])
  end
end
```

If the user isn't logged in, this will fail with `NoMethodError: undefined
method 'last_in_category' for []:Array`.

## Rails to the Rescue

Luckily, Rails 4.0 introduced a new method on `ActiveRecord::Relation` to help
with exactly this situation! [Relation#none][relation-none] is a method that
will return a new instance of `NullRelation`.

`NullRelation` responds to every method that a normal instance of
`Relation` would. As a bonus, when you call `.none` on one of your model
classes, it will also respond to all of the class methods that a `Relation` for
that class would have held as well! Our updated `Guest` class would look like
this:

```ruby
class Guest
  def purchases
    Purchase.none
  end
end
```

Once we've utilized `Relation#none` in our null objects, the rest of our code
works as expected, and we can continue blissfully unaware of the existence of
`Guest` in the rest of our codebase.

## Backporting to Rails 3

For those of you who haven't upgraded to Rails 4 yet (you probably should...),
you can achieve a similar effect with this simple back-port:

```ruby
class ActiveRecord::Base
  def self.none
    where("1 = 0")
  end
end
```

This isn't quite the same as `NullRelation`. It'll still hit the database and
try to load data, and the call to `none` could be undone with
`.unscope(:where)`. However, for most cases it should act as a suitable polyfill
until you're able to upgrade to Rails 4.

## What's next

If you enjoyed this article, you might also enjoy:

* [Ruby Science][science] for more examples of refactoring patterns in Ruby.
* [Rails Refactoring Example: Introduce Null Object][thoughtbot-null-object] for
  an example of the Null Object Pattern in action.
* [Testing Null Objects][testing-null-objects] for examples of how to test this
  pattern.

[null-object]: http://www.cs.oberlin.edu/~jwalker/nullObjPattern/
[relation-none]: http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-none
[science]: http://rubyscience.com
[thoughtbot-null-object]: https://thoughtbot.com/blog/rails-refactoring-example-introduce-null-object
[testing-null-objects]: https://thoughtbot.com/blog/testing-null-objects
