Ruby Mixin Tutorial
In Java you just have classes (both abstract and concrete) and interfaces. The Ruby language provides classes, modules, and a mix of both. In this post I want to dive into mixins in Ruby.
In the Ruby language a mixin is a class that is mixed with a module. In other words the implementation of the class and module are joined, intertwined, combined, etc. A mixin is a different mechanism to the extend construct used to add concrete implementation to a class. With a mixin you can extend from a module instead of a class. Before we get started with the mixin examples let me first explain what a module is.
I think of a module as a degenerate abstract class. A module can’t be instantiated and no class can directly extend it but a module can fully implement methods. A class can leverage the implementation of a module by including the module’s methods. A module can define methods that can be shared in different and separate classes either at the class or instance level.
Let me define a module, albeit a trivial one, that would convert a numeric integer value to English.
# Convert a integer value to English. module Stringify # Requires an instance variable @value def stringify if @value == 1 "One" elsif @value == 2 "Two" elsif @value == 3 "Three" end end end
Note that the Stringify module makes use of a @value instance variable. The class that will be mixed with this module needs to define and set a @value instance variable since the Stringify module uses it but does not define it. In addition to instance variables a module could invoke methods defined not in the module itself but in the class that it will be mixed with.
Now let me construct a self contained module that is not dependent on the implementation of any class that it can be mixed with.
# A Math module akin to Java Math class. module Math # Could be called as a class, static, method def add(val_one, val_two) BigInteger.new(val_one + val_two) end end
The methods in the Math module are intended to be invoked like class methods, also known as static methods. The add method in the Math module accepts two integer values and returns an instance of BigInteger. Let me now define the mixin BigInteger class.
# Base Number class class Number def intValue @value end end # BigInteger extends Number class BigInteger < Number # Add instance methods from Stringify include Stringify # Add class methods from Math extend Math # Add a constructor with one parameter def initialize(value) @value = value end end
I loosely modeled the BigInteger and Number classes after the Java versions. The BigInteger class defines one constructor and directly inherits one method from the Number base class. To mix in the methods implemented in the Stringify and Math modules with the BigInteger class you will note the usage of the include and extend methods, respectively.
# Create a new object bigint1 = BigInteger.new(10) # Call a method inherited from the base class puts bigint1.intValue # --> 10
The extend method will mix a module’s methods at the class level. The method defined in the Math module can be used as a class/static method.
# Call class method extended from Math bigint2 = BigInteger.add(-2, 4) puts bigint2.intValue # --> 2
The include method will mix a module’s methods at the instance level, meaning that the methods will become instance methods. The method defined in the Stringify module can be used as an instance method.
# Call a method included from Stringify puts bigint2.stringify # --> 'Two'
There is another use of the extend method. You can enhance an object instance by mixing it with a module at run time! This is a powerful feature. Let me create a module that will be used to extend an object, changing its responsibilities at runtime.
# Format a numeric value as a currency module CurrencyFormatter def format "$#{@value}" end end
To mix an object instance with a module you can do the following:
# Add the module methods to # this object instance, only! bigint2.extend CurrencyFormatter puts bigint2.format # --> '$2'
Calling the extend method on an an instance will only extend that one object, objects of the same class will not be extended with the new functionality.
puts bigint1.format # will generate an error
Modules that will be mixed with a class via the include or extend method could define something like a contructor or initializer method to the module. The module initializer method will be invoked at the time the module is mixed with a class. When a class extends a module the module’s self.extended method will be invoked:
module Math def self.extended(base) # Initialize module. end end
The self prefix indicates that the method is a static module level method. The base parameter in the static extended method will be either an instance object or class object of the class that extended the module depending whether you extend a object or class, respectively.
When a class includes a module the module’s self.included method will be invoked.
module Stringify def self.included(base) # Initialize module. end end
The base parameter will be a class object for the class that includes the module.
It is important to note that inside the included and extended initializer methods you can include and extend other modules, here is an example of that:
module Stringify def self.included(base) base.extend SomeOtherModule end end
February 19th, 2007 at 6:21 pm
Thank you for the concise tutorial. It helped me to understand a Rails Plugin I recently installed.
March 3rd, 2007 at 4:04 pm
Nice. Acouple of times you use ‘extern’ where ‘extend’ is meant.
Could be confusing.
Cheers
March 7th, 2007 at 6:04 pm
re misusing ‘extern’ where ‘extend’ is meant – Han is correct. It is confusing.
otherwise, thanks!
March 20th, 2007 at 1:03 pm
Just trying to bump this up in your conciousness…
You should do one more proofread of this…
1. As above, using extern where you mean extend is very confusing.
2. The sentence that goes like this:
When a class includes a module the module’s self.extended method will be invoked.
should be
When a class includes a module the module’s self.included method will be invoked.
3. Sentences that don’t quite make sense:
define initializer methods that will be called when at the time they are
Otherwise thanks…
this also is a good companion for this article:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/classes.html
March 21st, 2007 at 1:20 am
@Han, Anna, Richard – Thanks for you input and making the post just a bit better. I corrected the ‘extern’ typo and fixed other minor errors. Thanks a bunch!
May 12th, 2007 at 12:18 pm
Thanks! This and the companion Rails Plugin Tutorial are both very useful and concise (less superfluous stuff = less confusing)
May 29th, 2007 at 9:32 pm
Wonderful tutorial. Concise and intelligent. Thanks for taking the time to write this for the Ruby beginners out there like myself.
July 7th, 2007 at 11:24 am
[…] C# programmer trying to learn more about ruby. Upon further research I found another post that made it quite clear for […]
July 20th, 2007 at 3:29 am
[…] Juixe TechKnow » Ruby Mixin Tutorial (tags: ruby) […]
August 16th, 2007 at 9:15 pm
[…] the module ActiveSupport::CoreExtensions::Array::Conversions. It defines a to_xml method that is a mixin for the Array […]
September 12th, 2007 at 11:25 pm
Hi ,
I particularly liked your tutorial and I should say this is the only decent unambiguous tutorial on mixins and the first time I started to understand what a mixin is….
I have a doubt :
What is mixin ? I mean is it a concept like inheritance or do we call the module or the host class(class that includes the method ) as a mixin?
September 25th, 2007 at 11:07 am
[…] Ruby permite herança simples, isto é, uma classe pode herdar os atributos e métodos de apenas uma única outra classe. Algumas linguagens, como Python e Eiffel, permitem herança múltipla. Ruby suporta também o conceito de Mixins. […]
October 24th, 2007 at 11:03 pm
[…] These methods allow you to “add functionality” to a class when needed – sort of like Ruby’s mixins but you don’t declare these extensions on the class – you simply have to have a […]
January 10th, 2008 at 4:29 am
[…] Mixin would be great, maybe Traits […]
February 5th, 2008 at 2:56 am
[…] It was an excellent blog post that documented this seeming inconsistency, a blog post you can read here. I’d really like to know if there’s a reason for this, or if it was just an […]
March 22nd, 2008 at 8:50 am
[…] is a rather hackish way to accomplish something resembling ruby’s mixins, I doubt it we will ever be able to do this as elegantly as in ruby. This is the closest I can come […]
May 27th, 2008 at 11:25 pm
[…] for a while. One thing that I’ve missed from Ruby is the strong category support (called Mixins in Ruby). I’ve been reading that the 1.6 version of Groovy is much stronger in this area, and […]
October 19th, 2008 at 11:59 am
[…] de que el plugin A hacia uso del modelo M pero a su vez este modelo hacia uso de un metodo “mixeado” en ActiveRecord::Base por el plugin […]
November 24th, 2008 at 8:52 pm
[…] AsListItem came about when I was starting to play around with Ruby and it’s approach to mixin classes (like “acts_as_taggable”). It seemed clever at the time, but now it just seems […]
January 24th, 2009 at 6:39 pm
[…] which clears up the duplicate code. Another option that you can explore to reduce duplication is mixins. That just about wraps up this post, next time I will work on testing the identity model. Share […]
March 7th, 2009 at 9:10 pm
What about name collisions, when I have similar method names in the different modules I mix with my class?
April 23rd, 2009 at 6:55 am
[…] we create a module that we will use as a mixin to extend a ‘vehicles’ behaviour to include […]
May 23rd, 2010 at 10:16 am
[…] you have trouble understanding the code, you may want to read about blocks and methods, mixins (more on mixins), enumerators. Or asking in the […]
June 10th, 2010 at 10:45 am
Thanks.This article helped in understanding modules in ruby.
August 17th, 2010 at 3:01 pm
Great overview – thanks!
October 18th, 2010 at 11:49 pm
Nicely arranged incremental walkthrough over the powerful concept of ‘mixins’. A bow to your initiative. Could immensly help initiators / newbies of the Ruby world.
Thank you, Keep the contribution coming!
October 31st, 2010 at 6:16 pm
[…] The magic behind this works, because in Ruby (any many other dynamic languages) you can extend an existing class with functionality (instance methods) at runtime. A class that includes functionalities (rather than inherits) in a subclass is called a Mixin. The theory behind Mixins is beyond the scope of this post, but if you want to know more: how Mixins can be used in Ruby is nicely explained in this tutorial: Ruby Mixin Tutorial. […]
January 3rd, 2011 at 3:15 pm
[…] in Ruby are open and can be extended, including classes such as String and NilClass. It supports mixins (think interfaces with implementation) as a workaround for multiple inheritance. Provided APIs are […]
January 28th, 2011 at 12:27 am
Great overview! Thanks a lot!
February 2nd, 2011 at 8:50 pm
Nice and simple explanation.
March 6th, 2011 at 2:34 am
[…] while avoiding modifications of the original plugin/gem code. You can read more about Mixins here: http://juixe.com/techknow/index….1:33amView All 0 CommentsCannot add comment at this time. Add […]
April 27th, 2011 at 7:28 am
Thank you very much for your article. I found it very well written and helpful.
July 19th, 2011 at 11:13 pm
Thanks for the wonderful tutorial about mixins .
August 11th, 2011 at 4:49 pm
[…] you to dynamically add methods to a class from outside the class itself (conceptually similar to Ruby’s Mixins) — you define the function and its input type, and when you type the dot operator, your new […]
January 9th, 2012 at 6:44 am
Great stuff, very helpful.
Couple of minor typos: should be “separate classes” and “its responsibilities”
June 7th, 2012 at 9:57 am
[…] create objects in a running system. In contrast to Java, Ruby’s building blocks are classes, modules, and mixins. In this post we will take a tour of classes in […]
June 29th, 2012 at 7:37 pm
[…] with a associated price. Ruby just seems to have a few more options to enhanced a class, such as mixins and as we have now seen here. It is possible to do byte code manipulation on a Java class to change […]