include
and extend
used in Ruby code and wondered what was going on? If so, then you’ve come to the right place. Mixins can offer a lot of flexibility to your code and by the end of this article you’ll know how to do it using Ruby.
Key Takeaways
- Mixins in Ruby are sets of code that can be added to one or more classes to add additional capabilities without using inheritance. A single class can have many mixins, offering a lot of flexibility to your code.
- Modules in Ruby can either be included or extended, depending on whether the methods will be added as instance methods or class methods. The ‘include’ keyword mixes in the module’s methods as instance methods in the target class, while ‘extend’ mixes in the module’s methods as class methods in the target class.
- A module can be mixed into a single instance at runtime, and you can also override methods in a Mixin. If a class includes a module and defines a method with the same name as a method in the module, the class’s method will override the module’s method.
What’s a Mixin?
A mixin can basically be thought of as a set of code that can be added to one or more classes to add additional capabilities without using inheritance. In Ruby, a mixin is code wrapped up in a module that a class can include or extend (more on those terms later). In fact, a single class can have many mixins.What about Inheritance?
One area I like to use mixins is for logging. In my Rails applications I like each model to have access to a logger without using a global constant of some sort. Initially I thought about using some sort of base model class (which could inherit from ActiveRecord::Base) that would provide access to a logger. However, I didn’t really like the idea of all of my models having to extend a special class just to get a logger. Yes, they would already extend ActiveRecord::Base, so what’s the difference, right? Well, what if I wanted to add access to a logger in other parts of my application which didn’t inherit from ActiveRecord::Base (or a special subclasss)? Ruby only supports single inheritance, so mixins seemed to be the best solution.Include vs Extend
Modules can either be included or extended depending on whether the methods will be added as instance methods or class methods, respectively.module Logging
def logger
@logger ||= Logger.new(opts)
end
end
class Person
include Logging
def relocate
logger.debug "Relocating person..."
# do relocation stuff
end
end
In the example above a Logging
module is defined which provides an instance to a Logger
object via the logger
method. The Person
class includes the Logging
module which will add its methods as instance methods to the Person
class. It’s worth noting that everything defined in the module is added to the class including methods (public, protected, private), attributes (e.g. attr_accessor :name
) and constants.
If instead I wanted to provide a logger via a class method I’d use the extend
method as shown below.
module Logging
def logger
@@logger ||= Logger.new(opts)
end
end
class Person
extend Logging
def relocate
Person.logger.debug "Relocating person..."
# could also access it with this
self.class.logger.debug "Relocating person..."
end
end
There are three differences shown between the last two code snippets. In the latter you can see that the “logger” method creates a class variable, @@logger
, instead of an instance variable. I did this because I expected the Logger to be extended onto other classes instead of being included. Of course, I also used the extend
method instead of include
. Finally, access to the logger
method is done through the class instead of the instance, such as Person.logger.debug
.
It’s also possible to mixin a module to a single instance at runtime as shown in the following example.
module Logger
def logger
@logger ||= Logger.new(opts)
end
end
class Person; end
p = Person.new
# p.logger -- this would throw a NoMethodError
p.extend Logger
p.logger.debug "just a test"
Included vs Extended
In some circumstances you may need to know when your module has been included or extended. One use case might be to help enforce the types of classes that can include your module. For example, I had a module that would check to see if a table existed for my models, in a Rails application. However, I wanted to make sure that my module was only included in classes that inherited fromActiveRecord::Base
because the module invoked the ActiveRecord::Base#table_exists?
method.
module VerifyTableExistance
def self.included(base)
raise "#{self.name} can only be included into classes that inherit from ActiveRecord::Base, #{base.name} does not." unless (base < ActiveRecord::Base)
raise "#{base.name} does not have a table yet" unless base.table_exists?
end
end
class Person < ActiveRecord::Base
include VerifyTableExistance
end
The VerifyTableExistance
module implements the included
class method which gets invoked whenever the module is included into another module or class. This allows me to verify that the class including the module is a kind of ActiveRecord::Base
and, in this example, verify a table exists for the model.
Another common pattern is to define a module that will mixin instance and class methods.
module AcmeModel
def self.included(base)
base.extend(ClassMethods)
end
def brand
"acme"
end
module ClassMethods
def all
# get all of the AcmeModel instances
[]
end
end
end
class Widget
include AcmeModel
end
w = Widget.new
w.brand # "acme" is the output,
Widget.all # invoke the class method that was added
In this example the AcmeModel
provides an instance method named brand
. More importantly it overrides the included
class method, provided by the Module class. The included
method is a callback that gets invoked whenever the module is included by antoher class or module. At that point we extend the class that included the AcmeModel
, the Widget
class in this example, with the ClassMethods
module.
Lastly, you should also know that your module can override the extended
class method which acts as callback when the module is extended.
Conclusion
Hopefully you’ve gotten a quick overview of what mixins are and how to use them in Ruby. If not, or if you feel like I left something out, please leave comments. This is my first article and I hope to have many more. Also, if there’s a particular area of Ruby you’d like to see discussed then leave a comment.Frequently Asked Questions about Ruby Mixins
What is the main purpose of using Mixins in Ruby?
Mixins in Ruby are a way to include methods from one module into another, providing a powerful alternative to multiple inheritances. This allows you to use a module to collect and bundle methods, which can then be mixed into any number of classes. This is particularly useful when you have functionality that doesn’t belong to any single class or when you want to share functionality across multiple classes.
How do you create a Mixin in Ruby?
Creating a Mixin in Ruby is a straightforward process. First, you define a module with the methods you want to include. Then, you use the ‘include’ keyword in the class where you want to mix in the module. This will make all the methods from the module available in the class as instance methods.
Can you override methods in a Mixin?
Yes, you can override methods in a Mixin. If a class includes a module and defines a method with the same name as a method in the module, the class’s method will override the module’s method. This allows for a high degree of flexibility and customization.
What is the difference between ‘include’ and ‘extend’ in Ruby Mixins?
The ‘include’ and ‘extend’ keywords in Ruby serve different purposes. ‘Include’ mixes in the specified module’s methods as instance methods in the target class, while ‘extend’ mixes in the specified module’s methods as class methods in the target class.
How do Mixins relate to the principle of DRY in Ruby?
Mixins are a perfect embodiment of the DRY (Don’t Repeat Yourself) principle in Ruby. By allowing you to define a method once and then include it in multiple classes, Mixins help to eliminate code duplication and make your code more maintainable and efficient.
Can a class include multiple Mixins in Ruby?
Yes, a class can include multiple Mixins in Ruby. This is one of the key advantages of using Mixins as it allows a class to inherit behaviors from multiple sources.
What is the order of method lookup in Ruby when using Mixins?
When a method is called in Ruby, the interpreter first looks in the class of the object. If it doesn’t find the method there, it then looks in the modules included by the class, in the reverse order they were included. If it still doesn’t find the method, it moves up to the superclass and repeats the process.
Can Mixins access instance variables of the class they’re included in?
Yes, Mixins can access and modify the instance variables of the class they’re included in. This allows Mixins to interact with the state of the object, providing a high degree of flexibility.
How do Mixins compare to inheritance in Ruby?
While inheritance allows a class to inherit behavior from a single superclass, Mixins allow a class to inherit behavior from multiple modules. This makes Mixins a powerful tool for code reuse and organization, especially when a piece of functionality doesn’t fit neatly into a single inheritance hierarchy.
Can Mixins be used in conjunction with inheritance in Ruby?
Yes, Mixins can be used in conjunction with inheritance in Ruby. A class can inherit from a superclass and include one or more modules. This allows for a high degree of flexibility and code reuse.
Craig Wickesser is a software engineer who likes to use Ruby, JRuby, Rails, JavaScript, Python, Java and MongoDB. He is also a moderator for RailsCasts, a former writer for InfoQ and a former editor for GroovyMag. You can check Craig's stuff out on his blog.