---
title: Nosy models
teaser:
tags: ruby,good code
author: Joe Ferris
published_on: 2009-04-10
---

![''](http://images.thoughtbot.com/ui/2009-4-10-turntables.jpg)

Ruby’s “mixins” provide a simple, middle-ground option to developers that wish
to include reusable functionality in several classes. When using C++-style
multiple inheritence, class hierarchies quickly become muddy, and it becomes
necessary to look in several places to find the “core” behavior of a class.
Mixins allow you to move reusable methods into modules. These methods can
provide an extended interface based on the base functionality of your class
(such as the Enumerable module), or they can be used to include “helper”
methods (such as ActiveSupport’s Memoizable). Using mixins to include reusable
behavior, such as helper methods or interface extensions, can greatly reduce
repetition and bloat in your code.

## Ruby stew

However, mixins can easily be abused. One common use of Ruby modules is to
“break up” a large class that is becoming unmanageable. Let’s say you have a
simple Article model:

```ruby
class ArticleTest < ActiveSupport::TestCase
  should_validate_presence_of :title, :abstract, :body, :author
end

class Article < ActiveRecord::Base
  validates_presence_of :title, :abstract, :body, :author
end
```

So far so good. Now let’s say you decide you want to submit articles to an
external service upon publication, such as the
[crossref](http://www.crossref.org/) service for
[DOIs](http://en.wikipedia.org/wiki/Digital_object_identifier):

```ruby
class ArticleTest < ActiveSupport::TestCase
  should_validate_presence_of :title, :abstract, :body, :author

  should "submit to crossref after being saved" do
    title = 'a title'
    # other constants
    # expectations for net/http
    Factory(:article, :title => title, ...)
  end
end

class Article < ActiveRecord::Base
  after_create :submit_to_crossref

  private

  def submit_to_crossref
    # convert article to xml
    # format a crossref request
    # connect to crossref using net/http
  end
end
```

Very quickly, this `submit_to_crossref` method will get unwieldy, and a good
programmer will refactor it into several small methods. Now you’ve added maybe a
hundred lines to your simple article model, and the tests are probably more
complicated. Once you start handling possible failure cases (such as unexpected
server responses or connection errors), there’s probably more crossref code than
article code.

At this point, the most obvious symptom of your problem is the fact that you
need to look through all of this crossref code when dealing with an article, and
you probably rerun all the crossref tests when changing your article model. One
quick way to deal with this symptom is to quickly move all the methods from your
model into a module and include it:

```ruby
class Article < ActiveRecord::Base
  after_create :submit_to_crossref
  include Crossref
end

module Crossref
  private

  def submit_to_crossref
    # call helpers to format and submit the request
  end

  # helper methods related to crossref
end
```

At that point, you can still test this module through article, but your article
tests are still testing too much functionality, and you’d be in worse trouble if
you decided to add Crossref support to another model. For now, though, let’s
just assume that you only want to submit Article records to Crossref. You’ve
still only treated a symptom of the core problem: dealing with crossref is
really none of your article model’s business. It shouldn’t be tested with your
article code, and it shouldn’t be part of the model’s interface. Moving the code
into a module spreads the behavior out, so that you need to look in several
places to find out what’s going on in Article. However, the concerns are still
completely mixed, and testing the Crossref behavior without having an instance
of Article is impossible, which leads to obscure and fragile tests.

## Adding a class

There’s a basic appeal to the “one domain concept, one model, one class”
approach you find in a lot of Rails applications, but that approach breaks down
in all but the most basic applications. Your models will likely have to deal
with concepts that don’t directly concern them (and aren’t part of the core
domain concept), and you’ve seen that mixins don’t seem to help with these
bloated classes. So, where else can you put this separate behavior? A new class:

```ruby
class Article < ActiveRecord::Base
  after_create :submit_to_crossref

  private

  def submit_to_crossref
    CrossrefRequest.new(:title => title,
                        # other info for the request
                        :author => author).submit
  end
end

class CrossrefRequest
  def initialize(attributes)
    # set attributes
  end

  def submit
    # call helpers to format and submit the request
  end

  private

  # helper methods related to formatting/submission
end
```

Now you have a discrete class that deals only with crossref requests. None of
its helpers end up in your Article model, and you can create focused tests just
for crossref. You no longer need to guess where the `submit_to_crossref` method
comes from – we replaced the call to a mixed in method by instantiating another
class, and it’s much easier to locate a constant. If you’re a fan of mocking,
you can also easily test that Article uses CrossrefRequest appropriately, so
you’ll get immediate feedback if your callback chain gets out of whack:

```ruby
class ArticleTest < ActiveSupport::TestCase
  should_validate_presence_of :title, :abstract, :body, :author

  should "submit to crossref after being saved" do
    title = 'a title'
    request = mock('crossref-request', :submit => true)
    CrossrefRequest.
      expects(:new).
      with(:title => title, ...).
      returns(request)
    Factory(:article, :title => title, ...)
  end
end
```

Whether you decide to mock out CrossrefRequest or take a state-based testing
approach, I recommend also having acceptance tests in place to ensure the
components collectively result in the feature you’re trying to provide. This
class will also be much easier to reuse, and the temptation to couple the two
models will be lower, as methods in your model are not directly available in
CrossrefRequest – everything necessary from the request must be passed in to the
constructor.

## Ruby smoothies

It’s much easier to test and maintain small, discrete objects, so don’t be
afraid to pull functionality out of your models and into classes specific to
their concerns. Next time you end up with a model that hurts your
[editor](https://thoughtbot.com/blog/integrating-vim-into-your-life), take a
look at the method list – I bet a lot of that behavior is none of your model’s
business!
