ruby picture

RCR 301: Dynamic module includes

Submitted by gmosx (Fri Apr 15 04:00:56 UTC 2005)

Abstract

A method to allow the programmer to parametrize the modules included in Classes or other Mnodules.

Problem

Some times you can't clearly define a behaviour or concept as a simple Module you can later include into your Class. As an example consider the Orderable 'mixin' for the Og library. You want to allow the programmer to redefine the name of the attribute used for ordering. The name is a parameter that dynamically modifies the code appended to the Class.

example:

class ListItem

  include Orderable, :name => :position
end

module Orderable

  def self.append_dynamic_features(base, options)
    ...
    base.module_eval %{
      attr_accessor :{options[:name]}
      ...
    }
  end
end

the method append_dynamic_features is called by the modified include method to append parametrized functionality.

Proposal

Extend the standard Module#include method to accept an optional hash of parameters. The new include method will try to call the append_dynamic_features method of the Module before falling back to the original implementation.

Analysis

The example implementation adds a useful feature to the Ruby language that allows advanced meta programming without changing Ruby's semantics. The implementation is fully backwards compatible and needs no change for existing code.

An altenative name could be parametrized include.

Implementation

class Module

  alias_method :__include_without_options__, :include
  def include(*args)
    options = args.last.is_a?(Hash) ? args.pop : {}
    for mod in args 
      if mod.respond_to?(:append_dynamic_features)
        mod.append_dynamic_features(self, options)
      end     
    end
    __include_without_options__(*args)
  end
  alias_method :dynamic_include, :include

end

ruby picture
Comments Current voting
Ah, I see this has made it into an RCR. I think its a good idea, but I beleive the implementation will have to be a bit different than that suggested. Besides, it would certainly be better if Ruby was more "aware" of this capability, then the example implementation allows. The exmaple implementation is also, IMHO, overly verbose. Furthermore, it has a scoping problem with string iterpolation.

  def self.append_dynamic_features(base, options)
    ...
    base.module_eval %{
      p "#{self}"
      ...
    }
  end

Will not result in the self one would normally want.

If the implemenation is improved I am in favor.

T.


I think that should be:

p "\#{self}"

or

p "#{base}"

this way you would get your expected behaviour. IMHO, p "#{self}" gives the expected result.

George


Just pointing to the "traits" mechanism :

Traits give fine control to the including entity over what gets included from a mixin, so you get a similar kind of flexibility than with parameterized includes, but without having to write the mixin in a templatized way.

What do you think ? should I post a separate RCR for traits or is this one the place to discuss it ?

-- Damien


Fair points George. Certainly it is workable and thus useful the way you have implemented. But as an RCR, the implementation should be more integerated into Ruby. IN other words, there should be away to achieve this functionality without the string interpolation.

BTW I have a module method that I think would be better as a paramerterized module:

  def key_attributes(*fields)
    code = ""
    code << "def ==(o) " << fields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") << " end\n"
    code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?(o.#{f})" }.join(" && ") << " end\n"
    code << "def hash() " << fields.map {|f| "self.#{f}.hash" }.join(" ^ ") << " end\n"
    # puts code
    class_eval code
    fields
  end

So others agree that this is a good use case?

T.


Strongly opposed 0
Opposed 0
Neutral 5
In favor 3
Strongly advocate 1
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .