Yehuda Katz is a member of the Ember.js, Ruby on Rails and jQuery Core Teams; he spends his daytime hours at the startup he founded, Tilde Inc.. Yehuda is co-author of best-selling jQuery in Action and Rails 3 in Action. He spends most of his time hacking on open source—his main projects, along with others, like Thor, Handlebars and Janus—or traveling the world doing evangelism work. He can be found on Twitter as @wycats.
Better Ruby Idioms
November 12th, 2009
Carl and I have been working on the plugins system over the past few days. As part of that process, we read through the Rails Plugin Guide. While reading through the guide, we noticed a number of idioms presented in the guide that are serious overkill for the task at hand.
I don’t blame the author of the guide; the idioms presented are roughly the same that have been used since the early days of Rails. However, looking at them brought back memories of my early days using Rails, when the code made me feel as though Ruby was full of magic incantations and ceremony to accomplish relatively simple things.
Here’s an example:
module Yaffle def self.included(base) base.send :extend, ClassMethods end module ClassMethods # any method placed here will apply to classes, like Hickwall def acts_as_something send :include, InstanceMethods end end module InstanceMethods # any method placed here will apply to instaces, like @hickwall end end |
To begin with, the send is completely unneeded. The acts_as_something method will be run on the Class itself, giving the method access to include, a private method.
This code intended to be used as follows:
class ActiveRecord::Base include Yaffle end class Article < ActiveRecord::Base acts_as_yaffle end |
What the code does is:
- Register a hook so that when the module is included, the ClassMethods are extended onto the class
- In that module, define a method that includes the InstanceMethods
- So that you can say
acts_as_somethingin your code
The crazy thing about all of this is that it’s completely reinventing the module system that Ruby already has. This would be exactly identical:
module Yaffle # any method placed here will apply to classes, like Hickwall def acts_as_something send :include, InstanceMethods end module InstanceMethods # any method placed here will apply to instances, like @hickwall end end |
To be used via:
class ActiveRecord::Base extend Yaffle end class Article < ActiveRecord::Base acts_as_yaffle end |
In a nutshell, there’s no point in overriding include to behave like extend when Ruby provides both!
To take this a bit further, you could do:
module Yaffle # any method placed here will apply to instances, like @hickwall, # because that's how modules work! end |
To be used via:
class Article < ActiveRecord::Base include Yaffle end |
In effect, the initial code (override included hook to extend a method on, which then includes a module) is two layers of abstraction around a simple Ruby include!
Let’s look at a few more examples:
module Yaffle def self.included(base) base.send :extend, ClassMethods end module ClassMethods def acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s end end end ActiveRecord::Base.send :include, Yaffle |
Again, we have the idiom of overriding include to behave like extend (instead of just using extend!).
A better solution:
module Yaffle def acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field self.yaffle_text_field = options[:yaffle_text_field].to_s || "last_squawk" end end ActiveRecord::Base.extend Yaffle |
In this case, it’s appropriate to use an acts_as_yaffle, since you’re providing additional options which could not be encapsulated using the normal Ruby extend.
Another “more advanced” case:
module Yaffle def self.included(base) base.send :extend, ClassMethods end module ClassMethods def acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s send :include, InstanceMethods end end module InstanceMethods def squawk(string) write_attribute(self.class.yaffle_text_field, string.to_squawk) end end end ActiveRecord::Base.send :include, Yaffle |
Again, we have the idiom of overriding include to pretend to be an extend, and a send where it is not needed. Identical functionality:
module Yaffle def acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s include InstanceMethods end module InstanceMethods def squawk(string) write_attribute(self.class.yaffle_text_field, string.to_squawk) end end end ActiveRecord::Base.extend Yaffle |
Of course, it is also possible to do:
module Yaffle def squawk(string) write_attribute(self.class.yaffle_text_field, string.to_squawk) end end class ActiveRecord::Base def self.acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s include Yaffle end end |
Since the module is always included in ActiveRecord::Base, there is no reason that the earlier code, with its additional modules and use of extend, is superior to simply reopening the class and adding the acts_as_yaffle method directly. Now we can put the squawk method directly inside the Yaffle module, where it can be included cleanly.
It may not seem like a huge deal, but it significantly reduces the amount of apparent magic in the plugin pattern, making it more accessible for new users. Additionally, it exposes the new user to include and extend quickly, instead of making them feel as though they were magic incantations requiring the use of send and special modules named ClassMethods in order to get them to work.
To be clear, I’m not saying that these idioms aren’t sometimes needed in special, advanced cases. However, I am saying that in the most common cases, they’re huge overkill that obscures the real functionality and confuses users.

Stephen Sykes, Posted November 12, 2009, 3:30 am
Couldn’t agree more. Well said.
lobo_tuerto, Posted November 12, 2009, 4:12 am
You are right.
For a less convoluted Ruby & Rails world!
Stefan Kroes, Posted November 12, 2009, 4:46 am
I recently wrote an AR tree gem (github.com/stefankroes/ancestry) using exactly this pattern from the guide. Your solution is much more straight forward. Is there any way changing the implementation could break the gem for some users?
Josh, Posted November 12, 2009, 4:56 am
Thanks for writing this. I’ve been following the Rails Guides pattern for a while now… all the while being suspicious that some of the “magic” was unnecessary. This post is really helpful for clearing up how it all works.
Next up… the plugin guide should be revised!
Mislav, Posted November 12, 2009, 5:03 am
Great writeup, but if anyone releases one more acts_as plugin, I’m going to scream
Swanand, Posted November 12, 2009, 5:18 am
Thanks for this post. Turns out I was using this code without really bothering to check for a better idiom (read: without really understanding). All I cared about was “it works”.
ippa, Posted November 12, 2009, 6:53 am
Yes! That pattern was the first thing confusing me when I started on my first rails plugin.
.. Still I just replicated it since I assumed there was some well thought out reason behind it :).
Gilles Mathurin, Posted November 12, 2009, 7:25 am
You are absolutely right. I am currently studying the rails plugin patterns, and was wondering myself why is it so … thx for the simplication.
Dmitry Jemerov, Posted November 12, 2009, 7:44 am
And the life of Rails IDE developers would be so much simpler if we didn’t have to unravel all these unneeded layers of metamagic. :)
Pratik, Posted November 12, 2009, 7:45 am
Please do feel free to fix the guide :)
Ken Collins, Posted November 12, 2009, 8:46 am
In the last case where you opened up the classs. I really do not like that. What about the ability to reflect on how a class is extended? For instance ActiveRecord::Base.included_modules and/or ActiveRecord::Base.extended_by.
http://pastie.org/695381
I love the article and agree. But I think thought should go out to how those familiar with meta programming and reflection. Looking at the examples in that link… you can see what I’m talking about. What are your thoughts? For instance, it may be just pure habbit that I ask the class what was “included” vs “extended”.
Botanicus, Posted November 12, 2009, 9:03 am
Talking about unnecessary sends,
base.send :extend, ClassMethodsis another one. Mainly Object#extend isn’t private method at all, try:Evan Light, Posted November 12, 2009, 9:07 am
Preach on, brother!
Ken Collins, Posted November 12, 2009, 9:08 am
Here is another snippet of code to expand on my point. I just found out that the class method extended_by comes from ActiveSupport (I think) but it’s not vanilla ruby. So maybe this is where the pattern evolved. IE, by using included and letting include do the extend, we are able to reflect on how us/others have extended the class. Which is funny, because the english of it should be allowed. Hence is there a default Ruby way to reflect on extended?
http://pastie.org/695405
All of what your saying might make sense for one’s own lib and code organization. Like for instance, the author of ActiveRecord might just follow your simple pattern at the top and by the time I load up ActiveRecord::Base and asks for it’s included modules, I get a clean slate of [Kernel] which makes the default idiom (which yes does not match english) work. Thoughts?
Erik, Posted November 12, 2009, 9:44 am
Great article!
Regarding the very last example. Where is the best place to save the file in a rails application. Do I need to require the file somewhere?
Karmen Blake, Posted November 12, 2009, 10:05 am
I’ve always wondered that myself. Thanks for formalizing it in a blog post.
Brian Mitchell, Posted November 12, 2009, 10:34 am
Of course… this perpetuates one of the things rails started that I hate most… just a little less than alias_method_chain. acts_as_* as was a horrible API for including functionality. IMO, it should have looked like this:
class Article < ActiveRecord::Base
include Yaffle # Just use the module directly!
end
This is much more Ruby and must less DSL. If you need to pass options you can either call option setters on the class now like `self.yaffle_field = 42` or specialize the module during inclusion:
class Article 42) # There are many ways to do this via clone or new modules
end
Why do this? Because it preserves Ruby’s concept of mixins more carefully. Consider:
article = Article.new.extend Yaffle
This allows it to come into play on the instance as well as class level. You could also use the module specialization to simplify sharing of settings:
MyYaffle = Yaffle(:foo => :bar)
class ….
include MyYaffle
end
Pushing things away from include hurst the overall Rubyness of a lot of things. I’d highly recommend that API’s always strive to allow inclusion and extension even if there are alternative methods.
Adam Hawkins, Posted November 12, 2009, 10:50 am
Agree. I’m waiting for your new guide after reading this. I think a guide written like you just explained would be a great help to people who aren’t too familiar with Ruby or writing Rails plugins. I know when I read the guide for the first time, there was a lot of ‘magic’. Explaining it like this removes some of the magic.
Maksim Horbul, Posted November 12, 2009, 11:22 am
I agree with you, there are some cases when that method seems to overcomplicate the things. But I guess the author of that Guide mentioned the universal way to create plugins. Actually I would rather follow that guide then and have the module’s code in the module itself and then extend AR::Base then writing the code directly in the AR::Base class like you shown at the final snippet.
Leandro Silva, Posted November 12, 2009, 11:26 am
You are very right.
Arya Asemanfar, Posted November 12, 2009, 11:27 am
Completely agree, excellent post!
Matt Southerden, Posted November 12, 2009, 11:46 am
For the last example, I think it’s easier to unit test using the ‘extend’ pattern. The Yaffle could be tested using any object as a container rather than the explicit coupling to ActiveRecord::Base of the final alternative.
Priit Tamboom, Posted November 12, 2009, 12:35 pm
Good points indeed. By the way, more I use ruby more I don’t like the trade-off where I cannot use super in AR models.
Scott Pierce, Posted November 12, 2009, 12:38 pm
Thanks for speaking up about this. My eyes are bleeding less.
Sean Cribbs, Posted November 12, 2009, 12:52 pm
@Ken Collins – extend essentially includes modules into the metaclass, IIRC. If that is the case, you could do:
wycats, Posted November 12, 2009, 1:07 pm
@priit Can you post more on this? Or link me to a post that explains this in detail?
Anil Wadghule, Posted November 12, 2009, 1:12 pm
Bingo! Nice post!
Ryan, Posted November 12, 2009, 1:17 pm
Well I feel alot better that this was all cleared up. Thanks Katz!
Henrik N, Posted November 12, 2009, 1:35 pm
On a somewhat related note, working against ActiveRecord::Base as in
class ActiveRecord::Base
extend Yaffle
end
seems a little wrong to me. I’ve started experimenting with inheriting all app models from an ApplicationModel, analogous to ApplicationController.
Aaron, Posted November 12, 2009, 1:45 pm
Let’s remember that the guide isn’t really at fault here, since it simply reflects the standard (poor) way that plugins have been written for years. The include/extend class/instance method issue is pretty confusing, particularly when it comes to Rails plugins. Most plugins I’ve seen use the techniques critiqued here and it makes plugin development intimidating since it’s easy to assume that there must be a reason if everyone is doing it.
Tj, Posted November 12, 2009, 3:10 pm
Seems pretty obvious to me…
Andy Maleh, Posted November 12, 2009, 6:22 pm
I’ve been unhappy with the acts_as pattern since my first encounter with it and have been wanting to publicly point out how complicated it is compared to simply mixing in a module, but you beat me to it.
Though it may make things more DSL’ish. This is a case of overkill indeed where the acts_as vs include does not affect thinking about things that much.
Great post.
class Article < ActiveRecord::Base
include Yaffle
end
^^^ module include FTW!
nachokb, Posted November 12, 2009, 7:19 pm
I agree with Ken, adding a method by reopenning has the disadvantage of losing some information.
Perhaps that information is not important for a specific plugin (in this case, x.included_modules.include?(Yaffle) could be replaced with x.respond_to?(:acts_as_yaffle) ), there are more complex ones which justify it, and having an uniform mechanism makes them easier to read (it should be grear if it could be made a little easier, though).
– nachokb
Ryan Sobol, Posted November 12, 2009, 7:58 pm
Yehuda,
I thoroughly enjoyed reading your analysis and deconstruction of a common rails idiom. Thank you!
Ryan
Ken Collins, Posted November 13, 2009, 10:15 am
@Sean Cribbs… agreed! I just think that perhaps a path to Ruby should be made (no time here personally) that would expose the simple extended_by or some other way that would yield a common pattern of reflection. Like I was saying, it is just habit for me now to think of using included_modules since I am so used to it.
So ingeneral, I strongly AGREE with this but I love the idea of framing it more around (1) teaching developers of large libraries to not include/extend in such a way that when the library meets my code I can reflect on an empty included/exended array. I’m sure there are pros/cons to this as I adore the type of code organization that say ActiveRecord::Base does and lays out the discrete logic into modules. Perhaps that topic is just academic and food for thought. Number (2) being that opening up a class should be discouraged. I know the article was not championing that, but just wanted to state my opinion. I very seldom have made libraries that have class method calls like “acts_as_foo” that use the reflection of seeing if the module is already included in the parent class before doing some other meta work. I have yet to examine if that was a good idea or not, but wanted to put out there that that reflection via “included_modules” is lost and is where most like myself go out of habbit. Cheers!
paradisaeidae, Posted November 13, 2009, 5:48 pm
Referring to the legacy ‘plugin api’ observed by OP…
This is similar to the sort of nonsense the late eighties was cluttered with in the name of ’4th generation languages’.
Macros producing macros to rid the world of programmers.
Drivel producing krud.
The damage of which sometimes could only be reversed by writing a macro to ripe the crap out of the entire code base.
Further ranting withheld.
Adam Grant, Posted November 13, 2009, 7:25 pm
Holy Cow!
Balls, my friend: clear-thinking BALLS is what you have.
Yeah, I REALLY had a hard time learning how to open classes/extend stuff for the first 2 years of doing Rails work because of this crap. Glad to see someone is as fed up with it as I am!
Hallelujah!
Eloy Duran, Posted November 14, 2009, 9:26 am
Amen
Peter Browne, Posted November 20, 2009, 4:46 pm
Speaking of Rails Plugin Idioms…There should be a better, more consistent approach to including plugins.
class Post
include ActsAsPlugin
end
class Post
acts_as_plugin :configuration => true
end
Compare the 2 approaches: #1 is much less “magical” and also has the advantage of not including the acts_as_plugin method into every ActiveRecord class. But obviously, #2 allows for configuration options.
Wouldn’t it be cool if there was a specific API for including plugins that would have the advantages of both approaches. Something like:
class Post
use ActsAsPlugin, :configuration => true
end
Marek K, Posted November 23, 2009, 5:44 pm
Nice post, it can clear up this include/extend magic indeed.
But what bothers me is why this overkill pattern with additional module layers came up in the first place. I mean – are there some really good reasons to use the classic act_as_overkill way?
Adam Durity, Posted December 11, 2009, 8:50 pm
I must agree whole heartedly. When I first started with Rails, though I had worked with Ruby prior, I didn’t have a grasp on some concepts. I remember slugging through AuthLogic code trying to interpret it. While Ben Johnson did a great job on the library, it is wrought with the kind of patterns that you describe above, making it tough to review.
Hopefully this clears it up for some newer folks.
ste, Posted January 19, 2010, 11:55 am
A bit late, but here’s my take:
http://gist.github.com/281080
If someone is still reading this, can you tell me if it makes sense? :-)
Priit, Posted March 5, 2010, 3:10 pm
@wycats: I think one good overview about AR anti-”super”: http://stackoverflow.com/questions/373731/override-activerecord-attribute-methods and the same thing goes for initialize method as we know. Anyhow, it’s very welcome to see rails 3 become better ruby citizens with optional defaults. Keep up good work about promoting cleaner ruby style.
thoran, Posted May 5, 2010, 3:19 am
@wycats: Rather than “overriding include” I might prefer to call it sidestepping or bypassing extend(). In fact I don’t think I like the term include() at all. Is instance_extend() better? Either that or include is kept and the compliment becomes class_include. Generally, though, the usefulness of having everything modular, such that doing the mixin by loading everything dynamically rather than by simply opening a class, is dependent upon the reversability of mixins in Ruby. If it were possible to drop a module in and rip it back out of a class (or another module) again with equal ease, then sure, don’t go opening classes. However, since Module#uninclude/exclude and Module#unextend/retract (And, assuming instance_extend, then we should have #instance_unextend/instance_retract, or alternately we might have Module#class_uninclude/class_exclude.) don’t exist (I ought to know, since I’m writing it in POR (Plain Old Ruby) right now and hence how I got here for the second time when looking for a pre-existing solution.), and since there doesn’t exist a way to easily remove functionality on a per module basis from the ancestors chain, there is insufficient reason to have everything modular and to load it dynamically at this time. The trick to making Module#exclude or Module#retract work properly is to ensure that by removing one module that the functionality provided by another included/extending module isn’t becoming some portion of useless because of a dependency on the one removed. Short of that, it is possible to do this with a little jiggery-pokery. I’m nearly there by way of introspecting on the module and trashing all the methods on the target class or module… Apart from a dependency issue amongst modules, there remains a problem with it remaining in the ancestors chain though, since a reference to the included module remains doing it this way. The alternative to actually banging on the target class or module, and a feature of Ruby I have wanted for about half a decade now is to be able to create context-specific instances of a class where the desired additional (or reduced functionality), such that the functionality exists either actually within or appears to reside within the class of concern, but then outside the context returns to ‘normal’. Something like the eye-popping lambda-lambda-lambda stuff Raganwald was working on a couple of years ago suffices, but what a pain to have to go to all that trouble, when it isn’t unreasonably included into a language with the sort of dynamism which we expect of Ruby. I was pleased to see that _why also took this approach with Poison. I’m working on something pretty horrible in Ruby to accomplish this as well just now… If it isn’t worse than ghastly I’ll eventually get it onto github. @Peter: The syntax I have at the moment is very much like what you suggest. In your case it would be Post.uses(ActsAsPlugin, :configuration => true) do; stuff; end. In that way Post could optionally act as if it had a plugin. I’m not presently worried about the machine efficiency of doing this because it should not be my concern at this level as to how quicly this operation works. It should be the internals of the language which worries about realising that a particular module has or has not been included already and to cache it somehow.
Simon, Posted November 16, 2010, 8:40 pm
What defining about an include and extend method?
class Class
def include_and_extend(the_module)
include the_module
extend the_module::ClassMethods
end
end
class Article < ActiveRecord::Base
include_and_extend Yaffle
end
http://glosoli.blogspot.com/2010/11/includeandextend-method-for-ruby.html
Wojciech Kruszewski, Posted January 20, 2011, 4:23 am
Yehuda, you *can’t* do it like this. acts_as_something with ClassMethods and InstanceMethods and abstraction layer is a well established programming patter. It’s enterprise, you see :P