I recently started looking into Mutant and related gems for an upcoming presentation about abstract syntax trees I am working on at the moment.
While browsing the source code of Mutant and of the gems that Mutant uses I realized that those codebases are among the cleanest code bases I have ever seen in Ruby land with an amazing overall architecture and I’d love to share what I’ve seen and learned reading their code.
I intend to make this a series with the first part of this series not covering Mutant itself but the gems it uses.
Let’s get started with Mutants gem dependencies.
Concord
Concord is a nice little library that helps you turning this:
1 2 3 4 5 6 7 8 9 | |
into this:
1 2 3 | |
What’s interesting here is how we we include it:
1
| |
Ok, something is including a module. Wait, it’s not a module. We’re calling new on it. Wat? How does this work?
Here’s what happens when you try to include something that is not “module'sque”:
1 2 3 4 5 6 7 | |
Ok, makes sense. Foo’s class is Class, not Module.
Let’s contrast this with a proper module:
1 2 3 4 5 6 | |
Now let’s try to emulate the code I showed you from Mutant:
1 2 3 4 5 6 7 8 9 | |
Now it gets even more confusing - include Foo doesn’t work, include Foo.new does but the class of Foo is still Class not Module. What’s happening here?
Let’s see how Rubinius actually handles this in module.rb (the behaviour below is identical to MRI Ruby):
1 2 3 4 5 6 | |
There it is. If the argument is not a module or if it’s a class we raise.
Now we understand why include Foo doesn’t work - because it’s class is Class - but include Foo.new does not raise - because its class is Foo, not Class.
This elegant trick of having a class inherit from Module allows us to use a class in a kind-of module context and to use a module in a kind-of class context. To put it differently, this allows a module to have an encapsulated context (its instance variables), that does not leak into the ivars of the class that includes that module.
Interesting side note: Concord has a limit of 3 arguments. Intentionally hardcoded. It’ll not accept
1
| |
The idea is that because Concord creates a positional arguments interface constructor, the limit of 3 guarantees you do not overuse Concord.
Procto
Procto turns your ruby object into a method object. The code example from the README speaks for itself so I’ll just paste it here:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The code to make this work is surprisingly small and very elegant.
Let’s start with looking at this part
1
| |
from the example above without paying attention to what happens when it is included:
1 2 3 4 5 6 7 8 9 | |
We define a singleton method called call that takes a name and returns an instance of Procto. (Using the same “include a kind-of class as module trick” Concord used above).
How does the constructor look like?
1 2 3 | |
Here things start to get interesting. We use a lambda (via the stabby lambda syntax) that takes an arbitrary number of arguments. And then it calls new with those. What new does it call? The block is executed at runtime and in the scope of….the object that Procto is included into, so the new here is not Procto#new but rather the object it has been included into.
In other words, the lambda will create a new object of whatever it is included into and call whatever name we pass into it.
That’s basically the
1
| |
part.
So what happens when this is included?
1 2 3 4 5 6 7 8 | |
We’re using the included callback here (note that it is not self.included here due to using the kind-of-class-module set up).
In this callback, we’re using instance_exec on the host class, which would be Printer from the example above. This effectively puts us at class scope.
Furthermore we pass our @block instance variable to BasicObject#instance_exec which instance_exec then passes as block-local variable block to the, uhm, block (sorry, that’s a lot of “block” in one sentence, I know).
Note that this is not a typo: We need to pass @block explicitly to instance_exec because instance variables will not be visible in the block that instance_exec takes (for details see here, here or here).
Now by using Object.define_singleton_method we define the singleton method call (or, in layman’s terms: class method) so we can finally do
1
| |
Equalizer
Equalizer is a module to define equality, equivalence and inspection methods.
An example from the README:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Without the include from above Ruby would evaluate this as false, not true.
When can this be useful?
Think about value objects:
A Value Object is an object that describes some characteristic or attribute but carries no concept of identity. As there is no identity, two Value Objects are equal when all their attributes are equal. An example of a Value Object would be Money.
Value objects are heavily used in methodologies like Domain Driven Design or when using composed_of in Rails (for a great introduction to this whole topic, check out this article series from Victor Savkin).
How does equalizer do this?
Let’s look again at how it is included:
1
| |
Pattern looks familiar, doesn’t it?
Ok, let’s check out the initializer:
1 2 3 4 5 | |
Nothing really to see here so let’s check out define_methods:
1 2 3 4 5 | |
Let’s focus on define_cmp_method and ignore the other two methods for now:
1 2 3 4 5 6 7 8 9 | |
This whole chunk of code looks a little more complex than it actually is:
We define a method called cmp? that takes something that will denote the actual comparison and then other, so the thing we are comparing it to.
We then check all keys (read: attributes) and if they satisfy this monster:
1
| |
What’s happening here?
__send__(key)will basically just return the attribute key points to- on this value we now call the comparator
- and pass in the value of the same attribute for the object we’re comparing to
- so if we assume == as comparator for example this whole line boils down to
attr_on_object == attr_on_other_object
Not so complicated anymore, is it?
With this out of the way, how is it used?
1 2 3 4 | |
So first we check if the objects have the same class.
Then comes the interesting part: We call cmp? and pass __method__ to it which is a special identifier that Ruby sets up for when you enter a method and that gives you the actual method name back.
So we could have also written:
1
| |
Why on earth would you then just not write it that way, you might ask?
There are multiple reasons for this. First and foremost, it communicates intent clearer. Whoever reads this just knows that you are referring to the same thing. Second, it makes it easier to refactor - in case you would change == to something else you could leave the rest of this method untouched.
With this quick digression, let’s come back to this one:
1 2 3 4 | |
Taking into account what we talked about above it’s now obvious what it does: It takes the other object we’re comparing our current object to and calls == for comparison of all attributes both objects have in common.
Wrapping it up
So what did all 3 gems have in common?
The top level class was inheriting from Module so if you take only one thing away from this post it should be this neat little trick that kind of blurs the distinction between classes and modules in Ruby even further and that can help to increase the elegance of your code significantly in my mind.
That’s it for part of this series. In the upcoming part 2 I will be looking at the IceNine gem.