Get fresh articles in your inbox

Concerned about Code Reuse?

Read Time: 3 min

April
19th,
2012

Right out of the gate, Ruby gives us some powerful ways to re-use instance and class methods without relying on inheritance. Modules in Ruby can be used to mixin methods to classes fairly easily. For example, we can add new instance methods using include.

module DogFort
def call_dog
puts "this is dog!"
end
end
class Dog
include DogFort
end

Now we’re able to call any methods defined in our DogFort Module as if they were simply slipped into (included) into our Dog class.

dog_instance = Dog.new
dog_instance.call_dog
# => "this is dog!"

Using Modules a fairly easy way to re-use methods, if you want you can extend a Module to add methods to a class directly.

Now if we were to call Dog.new.board_the_doors we would get an error, since we’ve added it as a class method instead.

Dog.board_the_doors
# => "no catz allowed"
Dog.class
# => Class

Sweet! Though what if you wanted to add an instance method and a class method to a class. We could have two Modules, one to be included and one to be extended, wouldn’t be to hard but it would be nice if we only had to use one include statement, especially if the two Modules are related. So is it possible to add instance and class methods with only one include statement? Of course…

Enter Concerns

A concern is a Module that adds instance methods (like Dog.new.call_dog) and class methods (like Dog.board_the_doars) to a class. If you’ve poked around the Rails source code you’ll see this everywhere. It’s so common that Active Support added a helper Module to create concerns. To use it require ActiveSupport and then extend ActiveSupport::Concern

Now any methods you put into this Module will be instance methods (methods on a new instance of a class Dog.new) and any methods that you put into a Module named ClassMethods will be added on to the class directly (such as Dog).

Included

That’s not all, Active Support also gives us a special method called included that we can use to call methods during include time. If you add included to your ActiveSupport::Concern any code in there will be called when it is included

module DogCatcher
extend ActiveSupport::Concern
included do
if self.is_a? Dog
puts "gotcha!!"
else
puts "you may go"
end
end
end

So when we include DogCatcher in a class it’s included block will be called immediately.

While this is a contrived example, you can imagine wanting to maybe make a concern for Rails controllers and wanting to add before_filter's to our code. We can do this easily adding the included block.

Is this magic?

Nope, under the hood we’re just using good old fashioned Ruby. If you want to learn more about all the fun things you can do with Modules I recommend checking out one of my favorite Ruby books Metaprogramming Ruby and Dave Thomas also has a fantastic screencast series.

Gotcha

When you’re writing Modules I guarantee that you’ll slip up and accidentally try to create a class method using self or class << self but it won’t work because it’s now a method on the Module.

module DogFort
def self.call_dog
puts "this is dog!"
end
ene

In the example above the context of self is actually the Module object DogFort so when we include it into another class we won’t see the method.