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!
--
Posted via http://www.ruby-forum.com/.

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

$

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

Kind regards

  robert

···

On 16.11.2009 22:01, Alexandre Mutel wrote:

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?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme 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 16.11.2009 22:01, Alexandre Mutel wrote:

--
Posted via http://www.ruby-forum.com/.

Robert Klemme 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

···

On 17.11.2009 00:30, Alexandre Mutel wrote:

On 16.11.2009 22:01, Alexandre Mutel wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme 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?

--
Posted via http://www.ruby-forum.com/.

Robert Klemme 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:

<snip/>

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

···

2009/11/18 Alexandre Mutel <alexandre_mutel@yahoo.fr>:
--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme wrote:

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

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

···

--
Posted via http://www.ruby-forum.com/.