From: Alexandre Mutel on
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/.

From: Robert Klemme on
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?

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


--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
From: Alexandre Mutel on
Robert Klemme wrote:
> On 16.11.2009 22:01, Alexandre Mutel 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...)
--
Posted via http://www.ruby-forum.com/.

From: Robert Klemme on
On 17.11.2009 00:30, Alexandre Mutel wrote:
> Robert Klemme wrote:
>> On 16.11.2009 22:01, Alexandre Mutel 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

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
From: Alexandre Mutel on
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/.