Ruby Class Module Mixins

When you first get started with the Ruby programming and you come from a different language, the only tricky piece is often Ruby’s approach to block/closure/anonymous functions. Sure the metaprogramming seems a bit odd, but you don’t have to use it. That’s why a lot of developers think that Ruby is a simple language. Turns out that when you dig a bit further, you realize that Ruby is actually quite a complex language. Ask any developer who worked on a Ruby implementation, they’ll all tell you the same thing: Ruby is full of small little things that makes it complicated.

An example of something that might seem simple is inheritance. Ruby, unlike C++, doesn’t support multiple inheritance. What that means is that a Ruby class can only have 1 parent class (superclass). However multiple inheritance can be achieved via modules used as a mixins. That’s a very common pattern, people put some code in a module and then mix it in/include it in a bunch of classes. The problem I see though, is that people abuse these concepts and don’t respect the difference between a class, a module and a module used a mixin.

The Class

Object Oriented Programming 101:

“In object-oriented programming, a class is a construct that is used to create instances of itself – referred to as class instances, class objects, instance objects or simply objects. A class defines constituent members which enable its instances to have state and behavior.”

So if you create a class and you don’t create instances, you are using the wrong construct. Here is an example of what I often see:

class Settings

  @settings = {}
  def self.all
    @settings
  end

  def self.[](key)
    all[key]
  end

  def self.[]=(key, value)
    all[key] = value
  end

end

Which can be used as such:

Settings[:secret] = 42 * Math::PI * Time.now.to_f
p Settings[:secret]
# => 177243152913.2707

(granted this isn’t a great example since we could have used a subclass of Hash but just bear with me)

Actually, the developer who wrote the code above would probably also use some Ruby magic like method_missing to provide a more laxed API and allow for “nicer” getters such as Settings.secret and Settings['secret']. I have my own thoughts on the topic but it’s an entirely different subject.

Note also that the way class level methods are defined can also be different depending on who wrote the code, you might see the following variations (and other more esoteric ones):

class Settings

  def Settings.all; end

  # or
  class << self
    def all; end
  end

end

The Settings code above works, the code is simple, yet I will argue one thing: it’s an abuse of the class construct. We’re breaking the #1 rule of classes: “create instances of self”.

It’s easy, whenever you don’t create instances of a class, please don’t use a class.

That’s also true for slightly different examples such as:

class API

  def fetch(id)
    HTTP.get('https://matt.aimonetti.net/article/:id', :id => id)
  end

end
resource = API.new.fetch(42)

There is no need whatsoever to create an instance of API, using a class is picking the wrong construct. Also, I don’t care if you use the Singleton module to only allow 1 instance of the class, you still shouldn’t use a class in the above example.

The Module

In Ruby’s object hierarchy, the Class object actually inherits from the Module object.

ruby -v -e "p Class.ancestors"
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.3.0]
[Class, Module, Object, Kernel, BasicObject]

As per Ruby’s source code defintion:

“A Module is a collection of methods and constants.”

I like to think of modules as namespaced methods and constants. Whenever you want code that logically belongs together but that won’t require that you create instances of a ‘model’, then a module is the right construct to use.

As a matter of fact, the two examples above are great cases where a module should have been used.

The confusing bit is that modules can have module level methods but also instance level methods. Here is an example:

module API

  def self.fetch(id)
    HTTP.get('https://matt.aimonetti.net/article/:id', :id => id)
  end

end

This is a module level function, it could also be written like that:

module API

  module_function

  def fetch(id)
    HTTP.get('https://matt.aimonetti.net/article/:id', :id => id)
  end

end

And be used like that:

resource = API.fetch(42)

Until now, it makes sense. The weird thing is that even though, modules unlike classes aren’t meant to create instances, we have the possibility to define module instance methods.

module Settings

  DATA = {repo: 'https://github.com/mattetti'}

  def repository
    DATA[:repo]
  end

  def secret_key
    DATA[:key] ||= 42*Math::PI
  end

end

Great, but we can’t actually use these methods since they are instance methods and we don’t create instances of modules. Well, that isn’t quite true, there is a way to access them and that’s by using a module as a mixin. What that means is that we inject/copy the module code inside a class or an(other) object. Example in code:

a = Object.new
a.extend(Settings)
a.repository
# => "https://github.com/mattetti"

Or we can add the code to a class so the instances of this class can access our module instance methods:

class Foo
  include Settings
end

Foo.new.repository
# => "https://github.com/mattetti"

The mixin modules

There a plenty of resources online about the many ways to use mixins in Ruby to achieve multiple inheritance and do cool stuff. But the point of this article is to try to demonstrate that mixins shouldn’t be abused.

My problem with the above example is that by mixing in the Settings module inside our Foo class, we created an uneeded, confusing extra level of abstraction. Instances of Foo now have access to two methods/objects: repository and secret_key. These methods or the objects they refer to don’t belong to the Foo class, but it seems convenient to not have to type Settings.repository so we mixed things in. Plus, a lot of Ruby developers seem to dislike adding class/module level methods so they feel that this approach ‘feels better’.

Here is the thing, the convenience of typing a few less characters isn’t worth it. Next time you or someone else will look at an instance of the Foo class calling repository, finding where it is defined is going to be a pain. That’s especially true if you have many mixins in your class. Settings will also probably grow and you will end up with a bunch of methods that have nothing to do with your class instances. In this case, I will call the use of a mixin, an abuse of construct. Sure, Ruby allows you to do it, but that doesn’t mean it’s the right thing to do. In Ruby, unlike in Python, there are 101 ways to do a simple thing. It doesn’t mean that the 101 ways are good, it just means that Matz wasn’t sure how people would use his programming language and chose to give us more freedom to messup/doing it our own way.

When to use mixins?

I have my own rule: use mixins whenever you need to share behaviors between different classes.

In the above example, we weren’t sharing behaviors, we were sharing objects, there was no need to actually use a mixin.

That said, rules aren’t rules without exceptions. A good example of this exception would be the Math module from the standard library. This module offers trigonometric and transcendental functions. You might think that this module would be designed to be a mixin so you can get log, cos, exp and friends available in your math related classes. It turns out, all Math’s methods are defined a module functions meaning that they are meant to be called from the Math module directly.

However, Ruby allows you to mixin module functions, but these functions become private. If you do include the module inside your class, your instance methods will be able to call hypot(x,y) directly, but these methods won’t be available from the outside (Foo.new.log(42) would raise an exception).

To conclude with mixins: mixins are great but don’t abuse them or you will endup with so much abstraction that your coworkers will secretely call you Kandinsky. Stick to simple mixins allowing you to share behaviors between at least a couple classes. See DataMapper for a great way to use mixins.

Modules: your secret functional programming weapon

I have to say that I do like functional programming. The idea of having functions not mutating the states of things around them pleases me. It just seems clean, you feed data to a function and you get another piece of data. No states were changed, maybe some temporary variables were allocated to process the data, but the only thing that matters is the input and the output. Easy to grasp, easy to follow, no magical states being changed by some code fairies.

The good news is that Ruby allows us to write code like that. And this is where modules are great. Very much like the Math module we discussed above, there are many cases where you want to have a bunch of functions that process an input and provide an output without keeping any states. A good example of such a module would be a param verification filter. The filter takes an input, takes some rules and verifies that the input matches the rules. Surely, we could create an instance for each verification, this would allow to keep states in our class and do the usual OOP things. But we could also simply use a module with a bunch of module (level) functions that would pass to each other the input they need to not need to keep states. The end result will be faster, nicer on the GC and easy to follow.

Mixing OOP and functional programming isn’t new, ask Scala developers! If done right, by adopting this approach we can simply our code base, make it faster, easier to maintain and not losing the chance to also use OOP paradigms when needed.

Compromise

As shown earlier, modules and classes have pros and cons. Classes are however much more natural to use for Object Oriented Programming. A compromise about the Settings examples was suggested by Evan Phoenix. The solution is elegant and simple. Use a class and an instance. Here is an implementation based on his suggestion:

class AppSettings < Hash

  def custom_method
  end

end

SETTINGS = AppSettings.new

The point here is not about the class implementation but that fact that we use a class and associate an instance of this class to a constant so it can be shared all over the place. Wow, a constant, this is so nasty you might think. Classes are constants too and so are modules, here we just allocate an instance of a class to a constant. Surpisingly simple and efficient.


1775 Words

2012-07-30

comments powered by Disqus