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

Jonas P. wrote:

module B

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.

On 7/11/06, Jonas P. [email protected] wrote:

module B

http://zimbatm.oree.ch

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

Sean O’Halpin wrote:

def initialize

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.

On Jul 11, 2006, at 10:21 AM, [email protected] 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.

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 W.

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

On 11/07/06, [email protected] [email protected] wrote:

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

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:


Cheers,
zimbatm

Jonas P. 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.

Jonas P. wrote:

new_mod.module_eval do

def nesting
else
def initialize; puts “From A”; end
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.

On 7/11/06, [email protected] [email protected] 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.

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

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

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.