Hello,
Thought this was interesting for a numbner of reasons, not the least of
which is that I am suprised it even works. What it is is whay to do
dynamic module mixins, i.e. including modules with macro-like changes
in behavior. Note that the first three lines simply add those well
known methods to the Binding class.
require 'nano/binding/self'
require 'nano/binding/%5B%5D%3D' # []=
require 'nano/binding/local_variables'
class Module
def module_options
@module_options ||= {}
end
alias_method :include_without_options, :include
def include(*args)
base = self
options = args.last.is_a?(Hash) ? args.pop : {}
mods = args.collect do |mod|
mod.append_dynamic_features( self, options )
mod
end
include_without_options(*mods)
end
# Note: Is this the best name for this callback?
def append_dynamic_features( base, options )
mod = self
base.module_options[mod] = options
end
# vars is just a dummy slot, does nothing
def dynamic_mixin( space, *vars )
mod = space.self
space.local_variables.each { |v|
default = space[v.to_sym]
space[v.to_sym] = lambda { |base|
base.class.module_options[mod][v.to_sym] || default }
}
end
end
# try it
if $0 == __FILE__
module MyDynamix
dynamic_mixin( binding, name='Tom' ) # name is local var
define_method( :hello ) {
puts "Hello from #{name[self]}"
}
define_method( :goodbye ) {
puts "Goodbye from #{name[self]}"
}
end
class MyClass
include MyDynamix, :name => 'George'
end
m = MyClass.new
m.hello #=> Hello from George
m.goodbye #=> Hello from George
class MyClass2
include MyDynamix
end
m = MyClass2.new
m.hello #=> Hello from Tom
m.goodbye #=> Hello from Tom
end
What amazes me about this is that 'name' in 'name[self]' is a local
variable assigned to a lambda, yet 'self' is not and rather belongs to
the initialized instance of the class. How is that possible? How is
execution of the lambda deferred til later? I think this is a
facinating example of Ruby's "juggling" abilities.
Also, it would be nice to be able to take this one step further, if it
were possible NOT to have to explictly pass 'binding' via
#dyanmic_mixin or pass 'self' via the local var lambda, then it would
be perfect.
T.