On Ruby modules and concerns

Modules are useful for highly specialized code that can be injected into other classes for fun and profit or for creating Namespaces. An interesting approach is to use modules as an alternative to classical inheritance (without its the usual trade-offs) : the core idea is to share an abstracted role that can be included in a set of different classes.

One should start with the simplest definition of the word role :

The function assumed or part played by a person or thing in a particular situation.

“... or thing in a Particular situation” is key here so how we encode particulars in an object oriented way ? We create classes: blueprints that we can later instantiate and use at will. Next : a function assumed by a thing i.e. a well defined function that is used by objects in particular situations; more exactly different objects start playing a common role and when this happens it becomes clear that the common behaviour can be extracted into a module.

A concern is a nice abstraction that simplifies sharing behaviour using modules. It can be found in ActiveSupport::Concern and it just removes some boiler-plate code e.g. :

def self.included(base)
  base.extend ClassMethods
  base.class_eval do
    # ...
  end

  module ClassMethods
    # ..
  end
end

tl;dr : included gets called when we do something like include MyModule into a class, the base argument is the object in which we include the module i.e. a Class instance (remember that classes in Ruby are instances of Class - interestingly the class of Class is again Class, so it's all turtles all the way down).

The abstracted code looks like this :

module M
  extend ActiveSupport::Concern
  included do
    # ...
  end

  module ClassMethods
    # this gets included automatically 
  end
end

Bit cleaner but the classic way is not bad either as long as you know what's actually going on. We all love Rails's “magic” yet one could say that using it without having any clue on what's going on behind the scenes is an exercise in futility.

Going back to our Concerns : what I do like is how it easily resolves dependencies -> one can ponder further upon the source -> some interesting coding going on in there.

Some simple uses for Concerns ? One can use them to apply the Extract Method refactoring i.e. “code fragments that can be grouped together”. In this 37signals article we have a simple example of a before filter that does some cookie mingling. The only issue here is that taking before filters out of the controller might result in some hard to debug code, one could argue that seeing all the filters per controller requires less mental switching than parsing include Ajax -> “oh wait this sets a before filter”.

Some much better usage one can find in this highly referenced article by DHH : “Put chubby models on a diet with concerns” e.g. a Taggable, Visible and Dropboxed are clusters of functionality that can be injected in other classes so that they can share a common role.

Next stop up the ladder is the DCI paradigm , from a bird eye view, this is a more formalized way of using/sharing roles that also includes a scaffold framework of thinking for organizing code. I'll get into the nitty-gritty details in a new post.

Interesting stuff

Tagged under: