How to dynamically include a module and update top level?

Hi,
I’m new to Ruby programming, and I’m having some trouble to dynamically
include a module into another module. This question may have been
already posted, I tried to find it without any success, sorry to ask it
probably again (I found Ruby really powerful, but there are some obscure
behavior that i still don’t understand!)

So my problem is simple. When trying with “static” include, it’s working
as i understand it:

irb(main):001:0> module A
irb(main):002:1> def test()
irb(main):003:2> “test”
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> module B
irb(main):007:1> include A
irb(main):008:1> end
=> B
irb(main):009:0> include B
=> Object
irb(main):010:0> test
=> “test”

But when I’m trying to load the module A through a module_eval on module
B, this is not working:

module A
def self.included(mod)
puts “#{self} included in #{mod}”
end

def testA()
    "testA"
end

end

module B
class Includer
def self.include_dyn(name)
B.module_eval “include #{name}”
end
end
end

irb(main):017:0> include B
=> Object
irb(main):018:0> Includer.include_dyn “A”
A included in B
=> B
irb(main):019:0> testA
NameError: undefined local variable or method `testA’ for main:Object
from (irb):19
from :0


The weird thing is the included callback is saying that A is include in
B… so why the testA method is not expanded to the top level?
after this, if i try to include B again, and call testA, it’s working…
but i thought that as soon as a module is mixed-in, every method added
later are available to the top-includer…

It’s possible to dynamically mixin A into B, and automatically having
the includer of B (the top level in my example), being updated?

Thanks!

On 16.11.2009 22:01, Alexandre M. wrote:

irb(main):001:0> module A
=> Object

end

from :0
the includer of B (the top level in my example), being updated?
The point in time of inclusion is important: your dynamic inclusion
comes after you said “include B”. If you create a new class, which
includes B you will also see A’s methods.

Try this

module A
def self.included(x)
printf “%p included in %p\n”, x, self
end

def foo
123
end
end

module B
end

class T
include B
end

puts “initially”
p T.ancestors, B === T.new, A === T.new

module B
include A
end

puts “inclusion of A”
p T.ancestors, B === T.new, A === T.new

class S
include B
end

puts “new class with B”
p S.ancestors, B === S.new, A === S.new

You will see

$ ruby19 incl.rb
initially
[T, B, Object, Kernel, BasicObject]
true
false
B included in A
inclusion of A
[T, B, Object, Kernel, BasicObject]
true
false
new class with B
[S, B, A, Object, Kernel, BasicObject]
true
true

$

Basically “include” behaves as if the inheritance chain at the time of
inclusion is copied.

Kind regards

robert

Robert K. wrote:

On 16.11.2009 22:01, Alexandre M. wrote:
Basically “include” behaves as if the inheritance chain at the time of
inclusion is copied.

Thanks for your response. So it seems that every time a module is
mixed-in another top-module, i have to reinclude the top-module into its
includers?

I tried the following “hack” and seems to work, I would be glad to have
some feedback… not sure it’s a good ruby habit… even if it’s
working!

module ModuleIncluderTracker
def includers()
@includers
end

def included(mod)
puts “#{self} included in #{mod}”
if !(@includers.include? mod)
@includers << mod
end
if (mod.respond_to? :reinclude)
mod.reinclude
end
end

def reinclude()
@includers.each { |mod| puts “try to reinclude #{self} in #{mod}”;
mod.module_eval “include #{self}”; }
end
end

module A
@includers = []
extend ModuleIncluderTracker
def toto()
“toto”
end
end

module B
@includers = []
extend ModuleIncluderTracker

def self.late_include(mod)
module_eval “include #{mod}”
end
end

irb(main):037:0> include B
B included in Object
=> Object

irb(main):038:0> B.late_include “A”
A included in B
try to reinclude B in Object
B included in Object
=> B

irb(main):039:0> toto
=> “toto”

With this, it keeps all the includers up-to-date with new module
included (although i have not managed any kind of circular references…
not sure if it’s possible…)

On 17.11.2009 00:30, Alexandre M. wrote:

Robert K. wrote:

On 16.11.2009 22:01, Alexandre M. wrote:
Basically “include” behaves as if the inheritance chain at the time of
inclusion is copied.

Thanks for your response. So it seems that every time a module is
mixed-in another top-module, i have to reinclude the top-module into its
includers?

Apparently:

irb(main):001:0> module A; end
=> nil
irb(main):002:0> c=Class.new { include A }
=> #Class:0x1021d37c
irb(main):003:0> c.ancestors
=> [#Class:0x1021d37c, A, Object, PP::ObjectMixin, Kernel,
BasicObject]
irb(main):004:0> module B; end
=> nil
irb(main):005:0> module A; include B; end
=> A
irb(main):006:0> c.ancestors
=> [#Class:0x1021d37c, A, Object, PP::ObjectMixin, Kernel,
BasicObject]
irb(main):007:0> c.class_eval { include A }
=> #Class:0x1021d37c
irb(main):008:0> c.ancestors
=> [#Class:0x1021d37c, A, B, Object, PP::ObjectMixin, Kernel,
BasicObject]
irb(main):009:0>

I tried the following “hack” and seems to work, I would be glad to have
some feedback… not sure it’s a good ruby habit… even if it’s
working!

Personally I find that the more interesting question. Do you want to
have the side effect of updating potentially many classes (and objects
via their class and #extend)? Maybe there is a better design choice? I
don’t know your use case or what you need that behavior for. Generally
Matz pics very reasonable choices so I tend to assume that the
aforementioned side effect is usually not wanted. Which does not mean
that there is no use case for this.

Kind regards

robert

Robert K. wrote:

Personally I find that the more interesting question. Do you want to
have the side effect of updating potentially many classes (and objects
via their class and #extend)? Maybe there is a better design choice? I
don’t know your use case or what you need that behavior for. Generally
Matz pics very reasonable choices so I tend to assume that the
aforementioned side effect is usually not wanted. Which does not mean
that there is no use case for this.

Thanks for you response! You are right. I have resolved this without
using this trick and using plain .extends/include as you mentioned.

My use case is simple : i would like to develop a DSL language that
provides sub-DSL language that you can switch at runtime.

For example, the top DSL is inside module ALang and i have two sub
language extension in ALang_B and ALang_C like this:

module ALang
def use(sublang)
# remove all previously defined sublanguage method
# …
# Load new sub language here
instance_eval { require “alang_#{sublang}” }
instance_eval “extend ALang_#{sublang}”
end

here others A Language methods…

end

module ALang_B
def myABFunction()
“myAB”
end

def self.extended(base)
# perform init (declare dynamic methods…etc.)
end
end

module ALang_C
def myACFunction()
“myAC”
end

def self.extended(base)
# perform init (declare dynamic methods…etc.)
end
end


Using it like this:

require “alang”
include alang

use :B
myABFunction

use :C

myABFunction should not be usable

myACFunction


Do you any project that use sub DSL language loaded/unloaded like this?

2009/11/18 Alexandre M. [email protected]:

using this trick and using plain .extends/include as you mentioned.

My use case is simple : i would like to develop a DSL language that
provides sub-DSL language that you can switch at runtime.

For example, the top DSL is inside module ALang and i have two sub
language extension in ALang_B and ALang_C like this:

Do you any project that use sub DSL language loaded/unloaded like this?

No, and I’d rather resort to a different approach, e.g.

module ALang_B
def myABFunction()
puts “myAB”
end
end

module ALang_C
def myACFunction()
puts “myAC”
end
end

module ALang
@langs = {:B => ALang_B, :C => ALang_C}
def self.lang(x) @langs.fetch(x) end

def use(sublang)
# printf “use %p\n”, self
@lg = Object.new.extend(ALang.lang(sublang))
end

def self.included(x)
# printf “included %p %p %s %p\n”, self, x, x.to_s, x.class
end

def method_missing(*a,&b)
# printf “missing %p\n”, self
if @lg
@lg.send(*a,&b)
else
super
end
end
end

include ALang

use :B
myABFunction

begin
myACFunction
rescue => e
puts e
end

use :C

myABFunction should not be usable

myACFunction

begin
myABFunction
rescue => e
puts e
end

Or pick a completely different approach where use receives a block in
which you can use the particular language, e.g.

module ALang

def use(sublang)
# printf “use %p\n”, self
@lg = Object.new.extend(ALang.lang(sublang))
end
end

use2 :B do
myABFunction

begin
myACFunction
rescue => e
puts e
end
end

use2 :C do

myABFunction should not be usable

myACFunction

begin
myABFunction
rescue => e
puts e
end
end

Kind regards

robert

Robert K. wrote:

No, and I’d rather resort to a different approach, e.g.

Woo, thanks Robert, this is a really clean approach!