On Module Integrity

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. #include / #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:

require 'active_record'
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 has_many, belongs_to, and 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:

require 'make_believe_active_record'
class Post
  ActiveRecord.build(self) do
    has_many :tags
    belongs_to :category
    validates :title, :unique => true
  end

  # has_many, belongs_to etc. are not accessible here
end

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.

3 comments

  1. If you go with the ‘make_believe_active_record’, what happens to ActiveRecord::Base#errors, #attributes, and the rest of the association methods?

  2. will avoiding namespace pollution bring us to the JavaScript’s (function(…) {…})(); way of doing things? I kind of see the point of avoiding pollution in the “global” namespace and can live with somewhat cumbersome “closures everywhere” patterns. Not so sure about giving up on syntax convenience to keep my class namespace “protected”

Leave a Reply

Your email address will not be published. Required fields are marked *