Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Ovid (2709)

Ovid
  (email not shown publicly)
http://publius-ovidius.livejournal.com/
AOL IM: ovidperl (Add Buddy, Send Message)

Stuff with the Perl Foundation. A couple of patches in the Perl core. A few CPAN modules. That about sums it up.

Journal of Ovid (2709)

Thursday July 16, 2009
07:04 AM

Ruby Mixins: An Example

[ #39301 ]

Many people hear about Ruby mixins, but don't actually know what they are since they don't program in Ruby. Plus, I see a number of questions online about how to write a mixin in Ruby. So here's a quick example for Perl programmers to see how it's done (note: I'm hardly a Ruby guru, so be forewarned).

To use mixins in Ruby, first create modules with the behavior you want to share. A module can be thought of as an incomplete class which you cannot instantiate.

module Bomb
    def explode
        puts "Bomb explode"
    end
    def fuse
        puts "Bomb fuse"
    end
end
module Spouse
    def explode
        puts "Spouse explode"
    end
    def fuse
        puts "Spouse fuse"
    end
end

Then in your class, simply include the mixins:

class PracticalJoke
    include Spouse
    include Bomb
end

So what does that do?

joke = PracticalJoke.new()
joke.explode
joke.fuse

This nicely allows classes to simply be responsible for generating instances and shared behavior is handled by the mixins. Unfortunately, that prints:

Bomb explode
Bomb fuse

In other words, we've exchanged multiple inheritance's "first wins" behavior with mixin's "last wins" behavior. Trying to get one method from each appears to need delegation.

Also, I've noticed that it appears that you're not flattening these methods into your class, as you would with roles, but are instead inheriting from the mixins:

irb(main):026:0> PracticalJoke.ancestors
=> [PracticalJoke, Bomb, Spouse, Object, Kernel]

This means that your mixin methods aren't really getting "mixed in" to your class. I'm not sure of the downside of this (aside from having a longer inheritance chain), but the upside is that you can at least override methods cleanly:

class PracticalJoke
    include Spouse
    include Bomb

    def explode
        puts "PracticalJoke explode"
    end
end

joke = PracticalJoke.new()
joke.explode
joke.fuse

And joke.explode will print PracticalJoke.explode. I think this means that if you want a warning at the PracticalJoke level about an accidental override, you're out of luck.

Further comments from Ruby experts welcome.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • Why would you add two mixins with a similar set of methods to one file? Can you think of a practical example ? A frequent pattern of mixins is adding a certain behavior to different classes, I use this patterm a lot in my apps. For example, I have module Geocodeable, and I mix it in into other classes like for instance Person and Organization. Voila. One of the best examples is how Enumerable is mixed in into Array and Hash, adding its cool methods select, reject, inject, partition and so on, and how can
    • It's easy for this to happen, particularly in large systems. Here's what I'm working on right now:

      $ find lib/ -name '*.pm'|grep -v 'Role/'|wc -l
      600
      $ find lib/ -name '*.pm'|grep 'Role/'|wc -l
      42

      And we're still adding roles.

      When you have a large system with multiple roles, it's likely that you'll eventually encounter situations where roles provide similarly named methods. This should generally only happen if these methods are semantically equivalent, but provide different implementations. As just one exam

      • Using a thing like the Ruby mixins for such cases is over the top.

        The programming God tells me one should use simple delegation for such cases like they do in Cocoa (and it looks simple and beautiful).

        • Delegation is great, but when the thing delegating and the thing being delegated to need to communicate with one another, you start winding up with problems. We already have components (roles) that we can assemble and need to work well together and separating them out via delegation means a separate class heirarchy just have to have something to delegate to. Why would I want to write the scaffolding for delegation? Honestly, roles are really, really simple and make this trivial :)

          Plus, delegation is for

  • If you _really_ need to mixin two modules that have the same methods you could make a proxy for the first: http://gist.github.com/148507 [github.com]
  • According to the Pickaxe book (figure 18.4) [skitch.com] (page 247 of the first edition). Mixins modules are actually proxied. You can see in the diagram that it inserts a Proxy class between Guitar class and the Object class. The Proxy class does not have any methods itself, but instead it has a pointer to the method table of the module it is proxying. This gets more and more ugly and complicated the more modules you mixin, however it does explain the odd "last wins" behavior.

    Of course, I am not sure if this is stil

  • This blog article from Chad Fowler may interest you:

    http://www.chadfowler.com/2009/7/8/how-ruby-mixins-work-with-inheritance [chadfowler.com]