One of the fundamental principles in Ruby is DRY: Don’t Repeat Yourself; which makes sense. Why waste time and effort re-writing code when you can simply reuse code you’ve already written? It also means if you ever need to change your code, you only have to change it in one place.
When writing an application you’ll often find there are methods that are the same in two or more of your models. For example, you might have a method on both your Comment and User models that strips spaces from the name attribute before saving the record to the database.
class Comment < ActiveRecord::Base
before_save :strip_spaces_from_name
def strip_spaces_from_name
name = self.name.strip
end
end
class User < ActiveRecord::Base
before_save :strip_spaces_from_name
def strip_spaces_from_name
name = self.name.stip
end
end
These methods are exactly the same! We’ve repeated ourselves! How dumb is that? It would make much more sense to store this identical code in a module and then include the module in both the Comment and User classes.
The /lib directory is a great place to store extra modules that can be included in any of your models, controllers etc. In this example, we want to create a new module with an appropriate name like SpaceStripper. Create a file called space_stripper.rb and add the following code to that file:
module SpaceStripper
before_save :strip_spaces_from_name
def strip_spaces_from_name
name = self.name.strip
end
end
Note – the convention here is to give your file and module the same name. File names are under_scored, module names (constants) should be CamelCased.
Any files in the /lib directory are automatically loaded by Rails so you don’t need to require them in any of your code before calling include. If you wish to add other modules or other classes to a module then usually you’d create a subdirectory in /lib with the same name as your module and add the extra files there. In this case you would have to require these extra files within space_stripper.rb.
To include the methods from a module in any of your other modules or classes, simply use the include:
class User < ActiveRecord::Base
include SpaceStripper
end
class Comment < ActiveRecord::Base
include SpaceStripper
end
By including a module into a class, instances of that class inherit all of the module’s methods. However, before_save
is not an instance method, it’s a class method. By calling before_save
directly in the SpaceStripper
module like in the above example, Rails will raise a NoMethodError
. Module doesn’t have any method called before_save
, that’s a metaclass-method found in ActiveRecord::Base. Instead, we need to change our module a little so that it calls before_save
on the class it’s included into when it’s included. We can do this like so:
module SpaceStripper
def included(base)
base.send :before_save, :strip_spaces_from_name
end
def strip_spaces_from_name
name = self.name.strip
end
end
The included method is a callback thats invoked when a module is included into another class or module. The argument (in this case base) is a local variable that represents the class or module you’re including the module in. When we include SpaceStripper
into a class like User, we send that class before_save with :strip_spaces_from_name
as an argument.
Learn to love modules. By DRYing up your code you’ll write less code, you’ll save yourself time if you ever need to make changes to the code and you’ll earn more coder-karma points.