problem with Module#append_features

F

Ferenc Engard

Hi all,

I need to write a constructor-like method for a module. From the help I
have found the Module#append_features method, which look like is the one
I want. The problem is that apparently this method does not exist! At
least, it do not work for me (i.e. is not called automatically), and
there is no such method in the Object#private_instance_methods list.

What is wrong?

Thanks:
Circum
 
T

ts

F> I need to write a constructor-like method for a module. From the help I
F> have found the Module#append_features method, which look like is the one
F> I want. The problem is that apparently this method does not exist! At

svg% ruby -e 'p Module.private_instance_methods(false).grep(/app/)'
["append_features"]
svg%

F> least, it do not work for me (i.e. is not called automatically), and

Well, it depend what you want to do

svg% cat b.rb
#!/usr/bin/ruby
module M
def self.append_features(kl)
puts "append_features #{kl}"
super
end
end

class A
include M
end
svg%

svg% b.rb
append_features A
svg%


Guy Decoux
 
F

Ferenc Engard

svg% ruby -e 'p Module.private_instance_methods(false).grep(/app/)'
["append_features"]
svg%

I was totally confused. I tried Object.private_instance_methods, and of
course, there was no such method there. :-/
module M
def self.append_features(kl)
puts "append_features #{kl}"
super
end
end

[...]

That is what I wanted to. Thank you, now it works. Well, I had no idea
that I need to write "self.append_features" instead of just
"append_features". Anyway, after you showed me, it looks
straightforward. :)

"self.anything" is the same as "M.anything"?

Circum
 
F

Ferenc Engard

Hello,

Sorry, I pushed "send" too early. That is not exactly what I want. I
want a method which will be called when an object is instantiated, if
the object's Class includes this Module. I.e., I do not want to write a
'init_x_module' call into each classes' constructor, which includes this
module, but want the system call that method. Something like multiple
inheritance in C++.

Circum
module M
def self.append_features(kl)
puts "append_features #{kl}"
super
end
end

[...]

That is what I wanted to. Thank you, now it works. Well, I had no idea
 
D

Dan Doel

Here's some black magic to do something like you want to do. If there's
an easier way, I'm sure
someone will post it as a follow-up to my message. :)

class Foo
def initialize
puts "Foo!"
end
end

module Bar
def self.append_features(cls)

class << cls # Trap the
initialize
proc { |x| @init = x } # method in an
unbound
end.call(cls.instance_method:)initialize)) # method object

cls.class_eval do remove_method :initialize end # Remove the old
# initialize

super # Make sure to call super
end

def initialize
unbound = class << self.class; @init; end # Retrieve the old method

init = unbound.bind self # Bind it to the current
# object
init.call
puts "Bar!" # new stuff goes here
end
end

class Foo
include Bar
end

Foo.new
 
T

ts

F> Sorry, I pushed "send" too early. That is not exactly what I want. I
F> want a method which will be called when an object is instantiated, if
F> the object's Class includes this Module. I.e., I do not want to write a
F> 'init_x_module' call into each classes' constructor, which includes this
F> module, but want the system call that method. Something like multiple
F> inheritance in C++.

This ?

svg% cat b.rb
#!./ruby

module M
def initialize#before
puts "M#initialize#before"
end
end

class A
include M
end

A.new
svg%

svg% ./ruby b.rb
M#initialize#before
svg%



p.s. : don't try it, this is a modified version of ruby which exist only at
moulon :))


Guy Decoux
 
T

Tobias Peters

Ferenc said:
Hello,

I
want a method which will be called when an object is instantiated, if
the object's Class includes this Module. I.e., I do not want to write a
'init_x_module' call into each classes' constructor, which includes this
module, but want the system call that method. Something like multiple
inheritance in C++.

In standard ruby:

class Module
def before(*method_names)
@before = method_names
end
alias pre_beforehack_append_features append_features
def append_features(klass)
@before ||= []
@before.each{|method_name|
klass.module_eval <<-END_OF_ADVICE
alias pre_include_#{self}_#{method_name} #{method_name}
def #{method_name}(*args,&block)
#{method_name}_#{self}(*args,&block)
pre_include_#{self}_#{method_name}(*args,&block)
end
END_OF_ADVICE
}
pre_beforehack_append_features(klass)
end
end
module M
before :initialize
def initialize_M(*args)
puts "#{self}: initializing M part"
end
end
class A
def initialize
puts "#{self}: initializing A part"
end
include M
end

A.new

=> #<A:0x25cb5d8>: initializing M part
#<A:0x25cb5d8>: initializing A part

The reader may now:
- implement Module#after
- do something intelligent to preserve method arity
- investigate if all of this is not already implemented in AspectR
 
M

Mauricio Fernández

This ?

svg% cat b.rb
#!./ruby

module M
def initialize#before
puts "M#initialize#before"
end
end

class A
include M
end

A.new
svg%

svg% ./ruby b.rb
M#initialize#before
svg%



p.s. : don't try it, this is a modified version of ruby which exist only at
moulon :))

You like teasing people, don't you ;-)

--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Make it idiot-proof, and someone will breed a better idiot.
-- Oliver Elphick
 
T

ts

T> The reader may now:
T> - implement Module#after
T> - do something intelligent to preserve method arity
T> - investigate if all of this is not already implemented in AspectR

Well, it's not as simple as you think. To give you an example, when
plruby run with a timeout, it don't give the possibility to the user code
to create threads. It's really easy to do it in C (it just need to undef
Thread.new, ...), try to write it in ruby but don't forget :
* you must be able to create Thread when $SAFE <= 3
* it must not exist a possibility to do the same when $SAFE >= 4

With hook, you just need to write

class Thread
class << self
def new:before(*args)
raise if $SAFE >= 4
end
# etc, for start ...
end
end


Guy Decoux
 
F

Ferenc Engard

Hello,
module Bar
def self.append_features(cls)

class << cls # Trap the
initialize
proc { |x| @init = x } # method in an
unbound
end.call(cls.instance_method:)initialize)) # method object

cls.class_eval do remove_method :initialize end # Remove the old
# initialize

super # Make sure to call super
end

def initialize
unbound = class << self.class; @init; end # Retrieve the old method

init = unbound.bind self # Bind it to the current
# object
init.call
puts "Bar!" # new stuff goes here
end
end

Somehow I have understand your solution. Anyway, I have some questions
which I cannot figure out with my knowledge from Pragmatic Programmer's
guide.

I do not understand the relation between the body of a class definition,
and the singleton class body.

Let's take this snippet:

class A
@x=1
def A.fn1
puts @x
end
def fn2
puts @x
end
end

class << A
@x=2
end

a=A.new
a.instance_eval "@x=3"
class << a
@x=4
end

A.fn1 # 1
class << A
puts @x # 2
end
a.fn2 # 3
class << a
puts @x # 4
end

This prints out 1,2,3,4. For values for 4 @x'es (two for the object and
two for the class). And I didn't used class variables (@@x). If I get
just one object (e.g. the "A" object of type Class), where do these two
instance variables belong? Also, what other techniques are available to
get/set these variables? Maybe there are more @x'es? :)

Thank you:

Circum, who are getting confused in these classes and metaclasses and
meta-metaclasses... :)
 
D

Dan Doel

Let's see:

class A; end
a = A.new

Defines two objects. A is an object whose proper class is Class, and a is
an object whose proper class is A. You can access these objects with:

A.class
a.class

a.class is A for all instances of the A class. A.class is Class, and that's
the same for all classes.

However, every object in Ruby also has a singleton class. In a sense, this
class is a subclass of the object's proper class, so any changes you make
to it are reflected in the object, but not other objects with the same
proper
class. So the hierarchy looks something like:

Class
|
A-Singleton -> A
|
a-singleton -> a

If vertical means inheritence, and horizontal means "instance of".

Of course, it's more complicated than that, because a-singleton is an
instance of Class, so it has Class as its proper class, and
a-singleton-singleton as its singleton class. And Likewise
a-singleton-singleton is an instance of Class and has a proper class
and a singleton class.

So, about your examples:

for @x = 1, that's a class instance variable of the A class. It's
accessible using A.class_eval { @x } or a.class.class_eval { @x }
You can make attribute writers/readers by doing:

class << A
attr_reader :x
end

for @x = 2, it's also a class instance variable, but it uses the
singleton class of A instead of A itself. Since the singleton class
is exclusively related to A, it's no better than a class instance
variable of A, but in reality it's a class instance variable of the
singleton class of A. To write an accessor, you do:

def A.x
class << self
@x
end
end

@x = 3 is a normal instance variable

@x = 4 is an instance variable of the singleton class of a, so it's
technically a class instance variable. However, since the singleton
class of a is only related to a, it's no better than an instance
variable of a in a different namespace.

Of course, you can go on as far as you want and make instance variables
like so:

def a.xN(N)
return @x if N < 1

cls = self

N.times do
cls = class << cls; self; end
end

cls.class_eval { @x }
end

So a.xN(1) would read the @x variable in a's singleton class, while
a.xN(2) would read the @x variable in the singleton class of a's
singleton class (I haven't tested this. It assumes that singleton
classes go up infinitely as necessary).

Of course, this would be pretty crazy, and I wouldn't recommend it.

Anyway, this has gone on very long, so I should stop. If you have
any more questions, I'll do my best to answer them, especially if
they're about the code I wrote.

Hope this has helped some.

- Dan
 
F

Ferenc Engard

Class
|
A-Singleton -> A
|
a-singleton -> a

If vertical means inheritence, and horizontal means "instance of".

Now it is a bit clearer.
def a.xN(N)
return @x if N < 1

cls = self

N.times do
cls = class << cls; self; end
end

cls.class_eval { @x }
end

This is weird. :) What a language... :)

If I write

class A
end
a=A.new

class << a
# now self is a-singleton, which is child of A?
self.inspect # returns: "A"
end

class A
# now self is A?
self.inspect # returns: "A"
end

This is truly not the same A, isn't it?

So, it is getting clearer, I am starting to learn what
class << self means...

Thanks for the lesson:
Circum
 
F

Ferenc Engard

In standard ruby:
class Module
def before(*method_names)
@before = method_names
end
alias pre_beforehack_append_features append_features
def append_features(klass)
@before ||= []
@before.each{|method_name|
klass.module_eval <<-END_OF_ADVICE
alias pre_include_#{self}_#{method_name} #{method_name}
def #{method_name}(*args,&block)
#{method_name}_#{self}(*args,&block)
pre_include_#{self}_#{method_name}(*args,&block)
end
END_OF_ADVICE
}
pre_beforehack_append_features(klass)
end
end
[...]

I have used this solution, and the small problem is that I must define
the method to wrap _before_ I include the mixin. Is it possible to get
rid of this constraint?

Thanks:
Circum
 
M

Mauricio Fernández

I have used this solution, and the small problem is that I must define
the method to wrap _before_ I include the mixin. Is it possible to get
rid of this constraint?

Use Module#method_added.


--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

- long f_ffree; /* free file nodes in fs */
+ long f_ffree; /* freie Dateiknoten im Dateisystem */
-- Seen in a translation
 
F

Ferenc Engard

Mauricio said:
Use Module#method_added.

I have tried to do this, but unfortunately I have a bad experience. This
testprogram works:

--------------------------------
#!/usr/bin/env ruby
class Module
def append_features(klass)
puts "append_features: #{klass}"
if klass.method_defined?:)initialize2) # The method is already
defined
puts "method exists"
else # The method is not yet defined
puts "method does not exist"
end
end
end

module M
end

class A
def initialize2
end
include M
end
--------------------------------

Output:

append_features: A
method exists

But, if I change everywhere (i.e., two places) "initialize2" to
"initialize", then it will output

append_features: A
method does not exist

The problem is, that if I want before() to work both before and after
the method definition, then I have to decide whether it is defined
already in append_features(). And it looks that ruby handles the
'initialize' method specially in this case. :-(

Can I consider it as a bug?

Ferenc
 
T

ts

F> Can I consider it as a bug?

No, this is because #initialize is a private method. Try it with
private_method_defined?

Guy Decoux
 
F

Ferenc Engard

ts said:
No, this is because #initialize is a private method. Try it with
private_method_defined?

Well, it was still a pain, as the 'initialize' methods seems always be
defined:

fery@domesticus:~$ irb1.8
irb(main):001:0> class A
irb(main):002:1> puts "init?: #{private_method_defined?:)initialize)}"
irb(main):003:1> end
init?: true

So I decided to always re-hack a method when it is added to a class,
anyway, it is a logical thing to do.

Below is my solution, as this list do not accept even ascii attachments,
and wait for the simpler/shorter implementations, as I am always happy
to learn.

Ferenc

PS: I have looked aspectR, but its source was too long (9K :), so I
didn't look into its implementation details.

--------------------------------------------->8 before.rb
 
F

Ferenc Engard

ts said:
No, this is because #initialize is a private method. Try it with
private_method_defined?

Well, it was still a pain, as the 'initialize' methods seems always be
defined:

fery@domesticus:~$ irb1.8
irb(main):001:0> class A
irb(main):002:1> puts "init?: #{private_method_defined?:)initialize)}"
irb(main):003:1> end
init?: true

So I decided to always re-hack a method when it is added to a class,
anyway, it is a logical thing to do.

Below is my solution, as this list do not accept even ascii attachments,
and wait for the simpler/shorter implementations, as I am always happy
to learn.

I hope the long rows will not break...

Ferenc

PS: I have looked aspectR, but its source was too long (9K :), so I
didn't look into its implementation details.

---------------------------------------->8---- before.rb

#!/usr/bin/env ruby
def debug(*args)
$stderr.puts(args[0])
end

# Extension of Module to define methods in modules which run before the
# appropriate method in the class which mixes the module.
class Module
def before_init
@before = []
end
def before(*method_names)
before_init() if !@before
@before += method_names
debug "#{self}.@before==#{@before}", :before
end
def before_get_before() @before end
def before_check_new_method(mod,aSymbol)
if !(mod.before_get_before.nil?) && (mod.before_get_before.include?
aSymbol)
debug "#{self}.before_check_new_method(#{mod},#{aSymbol}):
#{aSymbol} is in #{mod}.@before.", :before
debug "Executing
beforehack_insertBeforeWrapper(#{self},#{aSymbol})", :before
mod.beforehack_insertBeforeWrapper(self,aSymbol)
end
end

alias pre_beforehack_append_features append_features
def append_features(klass)
before_init() if !@before
@before.each {|method_name|
#~ if klass.method_defined?(method_name) ||
klass.private_method_defined?(method_name) # Only with ruby 1.8
if klass.method_defined?(method_name) ||
klass.private_instance_methods.include?(method_name.id2name) # The
method is already defined
debug "#{self}.append_features(#{klass}): checking method
'#{method_name}': exists.", :before
debug "Executing
beforehack_insertBeforeWrapper(#{klass},#{method_name})", :before
beforehack_insertBeforeWrapper(klass,method_name)
else
debug "#{self}.append_features(#{klass}): checking method
'#{method_name}': do not exist.", :before
end
}
pre_beforehack_append_features(klass)
end

def beforehack_insertBeforeWrapper(klass,method_name)
debug "beforehack_insertBeforeWrapper: hacking
#{klass}.#{method_name} to exec #{method_name}_#{self}", :before
klass.module_eval <<-END_OF_ADVICE
@before_in_beforehack_semaphore=true
alias pre_include_#{self}_#{method_name} #{method_name}
def #{method_name}(*args,&block)
#{method_name}_#{self}(*args,&block)
pre_include_#{self}_#{method_name}(*args,&block)
end
@before_in_beforehack_semaphore=false
END_OF_ADVICE
end

alias pre_beforehack_method_added method_added if self.respond_to?
:method_added
def method_added(aSymbol)
if !@before_in_beforehack_semaphore
self.included_modules.each {|mod|
before_check_new_method(mod,aSymbol)
}
end
self.pre_beforehack_method_added if self.respond_to?
:pre_beforehack_method_added
end
end

# Usage example:

module Mod
before :fnname
def fnname_Mod
puts "fnname_Mod called"
end
end

class Klass
include Mod
def fnname
puts "fnname called"
end
end

k=Klass.new
k.fnname
 
T

ts

----------------------------------------> 8---- before.rb

try this

F> module Mod
F> before :fnname
F> def fnname_Mod
F> puts "fnname_Mod called"
F> end
F> end

F> class Klass
F> include Mod
F> def fnname
F> puts "fnname called"
F> end
F> end

F> k=Klass.new
F> k.fnname

class A < Klass
def fnname
puts "A#fnname called"
super
end
end

A.new.fnname



Guy Decoux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top