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.