When you want to override methods defined by a library,
super is more convenient than
1 2 3 4 5 6 7
But as these things are commonly implemented, you need to use
alias_method (or Rails’
alias_method_chain) instead, which is less convenient:
1 2 3 4 5 6 7 8 9
I would encourage library writers to make their dynamically defined methods
super-overridable whenever possible; this post explains how.
The common implementation uses
define_method straight on your own class:
1 2 3 4 5 6 7 8 9
The library puts the method right in your class; right in
Post. So if you define your own method by the same name in
Post, you completely replace that old method, with no way to reach it. Thus you need
But if the library could instead put the method further up the inheritance chain, we could define our own method and just call
So what’s further up the inheritance chain? The
SomeORM superclass of course, and its chain of superclasses. But we can’t define methods there, or those methods would be available to every
SomeORM subclass, not just
What else is further up the inheritance chain? Modules.
Say we have this Ruby class:
1 2 3
The (slightly simplified) inheritance chain would then be
[Post, SomeModule, SomeORM, Object]. Included modules go between the class itself and its parent class, and
super will look in those modules before it gets to the parent class.
So we simply create a module, define methods on that, and include it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
That’s all it takes.
Polishing the inheritance chain
What does the inheritance chain look like now? Something along the lines of
[Post, #<Module:0x007fa0fea7fcf0>, SomeORM, Object].
We defined an anonymous module, which works fine but could be confusing in a later debugging session.
Also, the current implementation will create a separate module for each
attributes declaration. If you define ten attributes each on their own line, you’ll add ten modules to the inheritance chain.
While this works, it makes for a messier inheritance chain.
We can solve both these issues in one fell stroke:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The added code does a few things. It names the constant
Post::DynamicAttributes, so now the inheritance chain will be something like
[Post, Post::DynamicAttributes, SomeORM, Object].
It checks if
Post::DynamicAttributes already exists. If it does, it’s reused. This means we only add a single module to the inheritance chain even if
Post declares ten attributes each on their own line.
The second argument in
const_defined?(MODULE_NAME, _search_ancestors = false) is important, by the way. Say we introduce a
VideoPost that inherits from
1 2 3
Since we passed
false as the second argument of
const_defined?, we will not reuse
Post::DynamicAttributes from the superclass, but will instead get a new
VideoPost::DynamicAttributes module. This ensures attributes inherit correctly.
You could of course write just
const_defined?(MODULE_NAME, false) without the
_search_ancestors local variable. I feel it makes a cryptic argument more obvious, but it does trigger an “unused variable” warning in Ruby <2.0 with the
Examples in the wild
Implementing it in Traco last December was inspired by points brought up in Ruby Rogues episode 80, in turn based on the talk “The Polite Programmer’s Guide to Ruby Etiquette” by Jim Weirich et al. (which I haven’t watched).
I’d love to hear about other libraries that get this right, alternative solutions, or problems with this approach.