Module private methods inheritance?

Hi list,

while playing with ruby, I've hit a point I'm not really clear about.

code :

module A
  def initialize; puts "From A" end
end

module B
  def initialize; puts "From B" end
end

module A
  include B
end

class C
  include A
end

c = C.new
From A
=> #<C:0x...>

Now, let's say that the only thing I can change in that code it the B
module. How would you do so that I get "From B" instead of "From A" ?

···

--
Cheers,
  zimbatm

http://zimbatm.oree.ch

Jonas Pfenniger wrote:

Hi list,

while playing with ruby, I've hit a point I'm not really clear about.

code :

module A
  def initialize; puts "From A" end
end

module B
  def initialize; puts "From B" end
end

module A
  include B
end

class C
  include A
end

c = C.new
From A
=> #<C:0x...>

Now, let's say that the only thing I can change in that code it the B
module. How would you do so that I get "From B" instead of "From A" ?

see Facets' Kernel#as

T.

This might do what you want:

module A
def initialize
   puts "From A"
end
end

module B
def initialize
   puts "From B"
end
def self.included(obj)
   obj.class_eval {
     def initialize
       super # call this module's initialize
     end
   }
end
end

module A
include B
end

class C
include A
end

c = C.new
__END__
From B

Regards,
Sean

···

On 7/11/06, Jonas Pfenniger <zimba.tm@gmail.com> wrote:

Hi list,

while playing with ruby, I've hit a point I'm not really clear about.

code :

module A
  def initialize; puts "From A" end
end

module B
  def initialize; puts "From B" end
end

module A
  include B
end

class C
  include A
end

c = C.new
From A
=> #<C:0x...>

Now, let's say that the only thing I can change in that code it the B
module. How would you do so that I get "From B" instead of "From A" ?

--
Cheers,
  zimbatm

http://zimbatm.oree.ch

Sean O'Halpin wrote:

This might do what you want:

module A
def initialize
   puts "From A"
end
end

module B
def initialize
   puts "From B"
end
def self.included(obj)
   obj.class_eval {
     def initialize
       super # call this module's initialize
     end
   }
end
end

module A
include B
end

class C
include A
end

c = C.new
__END__
From B

Execpt now you've just changed A which could break anything else using
it.

Actually I think J's use of initialize, rather then some other method
was in a err. Yes? You can skip over ancestors using something like
facet/kernel/as but initialize is a touchy matter --it's not something
one generally defines in a module in the first place.

Even so, if that's really wha is wanted....

  require 'facet/kernel/as'

  class C
    include A
    def initialize
      as(B).initialize
    end
  end

What does #as do? It get the UnboundMethod in B and binds it to C, and
offers it up in a nice little Functor. You can bypass the Functor if
you want with #send_as(B,:initialize).

T.

I see this idea mentioned often (avoid initialize in modules). Some modules
need initialization, some don't. I don't think it is helpful
to generalize beyond that. I often see:

   module A
     def foo
       @foo ||= {}
     end
   end

which is basically a lazy initialization of @foo by the module instead of:

   module A
     def initialize
       @foo = {}
     end
   end

   class B
     def initialize
       super
       # init for B here
     end
   end

You can use Module#included to handle class/module level initialization but
I sometimes wish there was a well-defined instance initialization hook for
included modules instead of relying on the appropriate use of super() within
initialize(). I'm not sure what it would look like though, maybe:

module M
   def self.instantiated(x)
      x.initialize_M
   end
   def initialize_M
      #initialization for M goes here
   end
end

Presumably Class#new would trigger Module.instantiated for all included modules
in some well defined order (appearence in ancestors list?).

These seems kind of inelegant though. There must be a better solution. Or maybe
it is a solution looking for a problem.

Gary Wright

···

On Jul 11, 2006, at 10:21 AM, transfire@gmail.com wrote:

You can skip over ancestors using something like
facet/kernel/as but initialize is a touchy matter --it's not something
one generally defines in a module in the first place.

Indeed - but isn't that what Jonas wanted?

module A
  include B
end

with B overriding A?

However, this could well have unwanted side-effects on other classes
which expect A's behaviour. Caveat preemptor...

Regards,
Sean

···

On 7/11/06, transfire@gmail.com <transfire@gmail.com> wrote:

Sean O'Halpin wrote:

>
> This might do what you want:

[snip code]

Execpt now you've just changed A which could break anything else using
it.

Thanks for your help,

I finally decided to create a new Module#insert method :

class Module
  def insert(new_mod)
    new_mod = new_mod.dup
    old_mod = self
    mod_name = self.basename

    new_mod.module_eval do
      include old_mod
    end

    (nesting[-2] || Object).module_eval do
      remove_const mod_name
      const_set mod_name, new_mod
    end
  end

  # From the Facets package
  def nesting
    n = []
    name.split(/::/).inject(self){ |mod, name| c = mod.const_get(name)
; n << c ; c }
    return n
  end

  # From the Facets package
  def basename
    if name and not name.empty?
      name.gsub(/^.*::/, '')
    else
      nil #inspect.gsub('#<','').gsub('>','').sub(':', '_')
    end
  end
end

I know that I can have unwanted effects but in my case it is ok
because the loading order is pretty strict.

module A
  def initialize; puts "From A"; end
end

module B
  def initialize; puts "From B"; super; end
end

A.insert(B)

class C
  include A
end

C.new
#=> From B
#=> From A

:slight_smile:

···

--
Cheers,
  zimbatm

http://zimbatm.oree.ch

Jonas Pfenniger wrote:

Thanks for your help,

I finally decided to create a new Module#insert method :

class Module
  def insert(new_mod)
    new_mod = new_mod.dup
    old_mod = self
    mod_name = self.basename

    new_mod.module_eval do
      include old_mod
    end

    (nesting[-2] || Object).module_eval do
      remove_const mod_name
      const_set mod_name, new_mod
    end
  end

  # From the Facets package
  def nesting
    n =
    name.split(/::/).inject(self){ |mod, name| c = mod.const_get(name)
; n << c ; c }
    return n
  end

  # From the Facets package
  def basename
    if name and not name.empty?
      name.gsub(/^.*::/, '')
    else
      nil #inspect.gsub('#<','').gsub('>','').sub(':', '_')
    end
  end
end

I know that I can have unwanted effects but in my case it is ok
because the loading order is pretty strict.

module A
  def initialize; puts "From A"; end
end

module B
  def initialize; puts "From B"; super; end
end

A.insert(B)

class C
  include A
end

C.new
#=> From B
#=> From A

This code essentially (or at least partially succeeds at) reversing the
inclusion order of two modules. Ie.

  module B; end
  module A; include B; end

becomes

  module B; include A; end

Are you sure you want to do that? That can have crazy effects! In fact
it's a very bad idea unless you have full control coding over these
modules, and if that's the case you wouldn't need to do it anyway. So
as I say, I'm highly suspect. Looking at exacly what this does it
appears that:

  A.insert(B)

translates into

  Bp = B.dup

  module Bp
    include A
  end

  remove_const A
  A = Bp

Why do you need this?

T.

Why go through such convoluted hoops? The problem is simple: the original module has a bug (it doesn't call super). All you have to do is fix the bug:

   module A
     alias buggy_initialize initialize
     def initialize
       super
       buggy_initialize
     end
   end

That's a lot cleaner than undefining and redefining constants.

BTW I suggest that a module's initialize should always pass through parameters to the upper level:

   def initialize(*a,&b)
     super
     #do your stuff...
   end

And if Matz is reading this, I wish that 'call super' would be the *default* behavior. :slight_smile:

Daniel

Jonas Pfenniger wrote:

···

Thanks for your help,

I finally decided to create a new Module#insert method :

class Module
def insert(new_mod)
   new_mod = new_mod.dup
   old_mod = self
   mod_name = self.basename

   new_mod.module_eval do
     include old_mod
   end

   (nesting[-2] || Object).module_eval do
     remove_const mod_name
     const_set mod_name, new_mod
   end
end

This code essentially (or at least partially succeeds at) reversing the
inclusion order of two modules. Ie.

  module B; end
  module A; include B; end

becomes

  module B; include A; end

exactly

Are you sure you want to do that? That can have crazy effects! In fact
it's a very bad idea unless you have full control coding over these
modules, and if that's the case you wouldn't need to do it anyway. So
as I say, I'm highly suspect. Looking at exacly what this does it
appears that:

  A.insert(B)

translates into

  Bp = B.dup

  module Bp
    include A
  end

  remove_const A
  A = Bp

Why do you need this?

Yeah I know it is stange but this is because of Camping !

I can't change the Camping code so I need to find ways to sneak myself in :slight_smile:

···

On 11/07/06, transfire@gmail.com <transfire@gmail.com> wrote:

--
Cheers,
  zimbatm

http://zimbatm.oree.ch

Daniel DeLorme wrote:

And if Matz is reading this, I wish that 'call super' would be the *default* behavior. :slight_smile:

Hm. Sometimes the subclass needs to process the arguments that were passed to initialize before letting the superclass see them.

Also, since instance variables are not inherited, it may not be desirable to let the superclass initialize them.

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Jonas Pfenniger wrote:

Yeah I know it is stange but this is because of Camping !

I can't change the Camping code so I need to find ways to sneak myself in :slight_smile:

Why can't you change Camping code? It should be no different extending
Ruby itself.

T.