The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
How Ruby Mixins Work With Inheritance

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Chad Fowler

Posts: 408
Nickname: chadfowler
Registered: Apr, 2003

A programmer, musician, and language addict.
How Ruby Mixins Work With Inheritance Posted: Jul 8, 2009 9:53 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Chad Fowler.
Original Post: How Ruby Mixins Work With Inheritance
Feed Title: ChadFowler.com
Feed URL: http://feeds2.feedburner.com/Chadfowlercom
Feed Description: Best practices, worst practices, and some generally obvious stuff about programming.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Chad Fowler
Latest Posts From ChadFowler.com

Advertisement

Yesterday I asked on Twitter whether anyone was successfully using is_paranoid in a Rails application, because I had confused myself into thinking it couldn’t possibly work.

The problem I was having wasn’t is_paranoid’s fault, but it turns out it actually can’t do what I wanted it to do in its native state. The explanation of why is something I thought a number of Rubyists might benefit from hearing, so here it is.

Briefly, an explanation of is_paranoid: if you declare an ActiveRecord model to be paranoid, whenever you attempt to delete that model, is_paranoid will instead set a flag which indicates that the record is deleted. is_paranoid uses default_scope to filter out soft-deleted records in your queries. So you can act as if records are deleted without actually removing the rows. If is_paranoid is new to you but sounds familiar, you might be thinking of acts_as_paranoid, which is Rick Olson’s original implementation of this idea.

What I wanted to do for the specific application I’m working on is to declare that every model should inherit the is_paranoid behavior. Easy enough, I thought, given the way these things typically work in Rails’ inheritable accessor setup:

But when I tried to destroy an instance of (for example) Person, the regular ActiveRecord destroy code was invoked and the records were being actually destroyed. Bummer.

So I cracked open the code to is_paranoid and found this perfectly reasonable idiom:

At this point, after pretending I was an idiot for a few minutes, I realized that this code was indeed incapable of doing what I wanted it to do.

Some of you already know why. For the rest of you, let’s talk about how Ruby’s mixins fit into its inheritance mechanism.

Maybe it’s just me, but when I think of something getting “mixed in” to something else, I imagine the two things becoming intertwined. So, the natural assumption when mixing a module into a Ruby class would be that the methods of the module get intertwined with the Ruby class. And for the HelloWorld of mixins, that indeed appears to be the case:

But if you start mixing modules that implement methods the class also implements in, things don’t go quite as smoothly:

Instead of “Overridden do_something” as some might expect, this code prints “Doing something in Thing”.

Why?

Because when we mix a module into a Ruby class, we’re not actually intermingling the methods of the module and the class. Instead we’re inserting the module into the class’s inheritance hierarchy. A good way to see how this works is by using the “ancestors” method:

When a method is called on an instance of SubThing, you can see that it is looked up first in SubThing’s class, then Thing’s class, then in IneffectiveOverride, and so on.

(I used yuml to generate this. Cool site!)

To further demonstrate that mixins don’t really get “mixed in”, notice what happens when you try to include a module at multiple points in the inheritance hierarchy:

If a module is already present at a higher point in the hierarchy, it won’t be mixed in again.

So is_paranoid was apparently built without the goal of being able to mix it into ActiveRecord::Base. Sounds reasonable to me.

Read: How Ruby Mixins Work With Inheritance

Topic: My Apprenticeship - Wednesday, July 7, 2004 Previous Topic   Next Topic Topic: Renum 1.1: pretty instance-specific method definition

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use