Goodness, such strong but mixed feelings provoked by this article: Modules called, they want their integrity back. On the one hand: yes! But on the other: no!
Having used Ruby for upwards of ten years, I have no trouble thinking of
#include (and its callback partner-in-crime
#included) as just another method. To some degree I think it’s bringing biases in from less dynamic languages to expect Ruby built-in methods like
#include to behave like immutable keywords.
To that end, I think I have a looser idea of the “correct” semantics of
#include than Josh Cheek does.
#included includes new functionality into a class (or module). How they do that is typically to add the referenced module to the inheritance chain and copy in constants. But I have no fundamental problem with more exotic methods of bringing in new functionality.
On the other hand, I do have a slight problem with adding zillions of public class methods in order to establish a some kind of declarative mini-language at class level; methods which then overstay their welcome and clutter up the class API. A chief offender is my usual punching-bag, ActiveRecord:
class Foo; end
class Bar < ActiveRecord::Base; end
Foo.public_methods.size # => 153
Bar.public_methods.size # => 481
Holy mackeral! A whopping 328 methods added to the public class API! And that’s calculated after taking into account all the methods which ActiveSupport adds to every class in the system; before requiring
active_record the public class method count is 97.
Most of those methods are declarative “macros” which will only ever be used in the class definition: methods like
validates. But they linger around long after the class is fully defined, like that guy who’s still snoring on your couch at noon the day after a party.
I’ve started to experiment with alternatives to littering classes with “construction macros” which stick around after they have outlived their usefulness. Here’s one approach I’ve been playing with:
validates :title, :unique => true
# has_many, belongs_to etc. are not accessible here
At the risk of being a little bit more verbose, this pattern controls the context in which the additional “macro” methods are active. As an added bonus, it gives the implementor a convenient point, at the end of the block execution, to do any post-processing of the data structures that were set up by the builder macros.
Some of Josh’s alternatives reminded me of these experiments. I definitely think we share some concerns, although I think we’re coming at it from slightly different angles. For me, it’s more about avoiding namespace pollution: staying out of the global namespace, avoiding adding methods to base classes (cough
acts_as_* cough), and not cluttering up classes with methods which serve no purpose once the class setup is finished.