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.
Sorry, comments are closed for this article.
July 8th, 2009 at 03:07 PM
You could just toss a call to “is_paranoid” into #inherited couldn’t you?
July 8th, 2009 at 03:17 PM
Jeremy, yea I ended up getting it working. This was just an explanation of something that I think is easy to misunderstand.
July 8th, 2009 at 03:48 PM
This was an enjoyable read. I hope is_paranoid works out well for you, but let me know if you have any issues. There’s always room for improvement.
July 8th, 2009 at 04:00 PM
Jeff, I like its small size and simplicity. Well done and thanks!
July 8th, 2009 at 07:57 PM
Nice article Chad. This ties in nicely with some of the posts wycats has made over the last few months about using inheritance and mixins, one of which is here for those interested: http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/
thx! Jack
July 8th, 2009 at 08:12 PM
Thanks for the link, Jack!
July 8th, 2009 at 08:58 PM
+1 I learned this the hard way yesterday when trying to override ActiveResource’s save implementation…
July 8th, 2009 at 09:54 PM
Thanks for using http://yUML.me, and mentioning it in your blog. Rails recipes is an inspirational book, both in content and format, you da man :)
July 18th, 2009 at 07:27 PM
Although I have read a good explanation about the hierarchy that gets formed when Modules are mixed into ruby classes in “Programming Ruby 1.9”...this was much better.
July 20th, 2009 at 03:51 PM
great reading and visualizin .. alway get stuck on such ancestory. You cannot read such often enough :-)
July 20th, 2009 at 04:30 PM
Am I the only one who misses a solution? How would you e.g. modify “is_paranoid” to circumvent this “problem”? Sorry, I don’t get it yet.
Thanks, der Flo
July 20th, 2009 at 11:52 PM
der Flo, I didn’t actually provide a solution :) I just admitted that is_paranoid wasn’t intended to work the way I was trying to use it. Jeremy’s idea from the first comment would work.