Metaprogramming help

C

Chris Richards

I have created a module as follows which adds an initialize method to
the class it is included in. The problem is that the initializer method
could be easily overriden by the includer Class. Is there a way to
ensure that the initialize method is run even though it may have been
overriden??

module SomeModule

def self.included(base)
base.class_eval do

attr_accessor :fields, :x_position, :y_position

def initialize(x_position=0, y_position=0)
@fields=self.class.xfields.clone
self.y_position=y_position
self.x_position=x_position
for field in @fields
eval "self."+field[:var] + " = field[:value]"
end
@fields = @fields.sort_by{|f| f[:eek:rder]} if @fields.find{|f|
f[:eek:rder]}
end
end
end

end

Any help is appreciated.
Thanks, Chris
 
R

Robert Dober

I have created a module as follows which adds an initialize method to
the class it is included in. The problem is that the initializer method
could be easily overriden by the includer Class. Is there a way to
ensure that the initialize method is run even though it may have been
overriden??
No there is none and I am not sure it is a good idea, as it is the
users responsability to call super.
But if it turns out to be a good idea than one could imagine doing
something like below, which kind of hides better what you try to hide.
I believe that hiding your own init method and assure it is called in
new might be a better model of your intent, initialize belongs to the
user after all, right?module M
class << self
def included into
my_init = nil
into.module_eval do
### More sophistication can be added here to avoid naming conflicts
### in the includee (into)
define_method :my_init do puts 42 end
my_init = instance_method :my_init
remove_method :my_init
end

class << into; self end.module_eval do
alias_method :_orig_new_, :new
define_method :new do | *args | # more hocus pocus needed for &blk
o = _orig_new_( *args )
my_init.bind( o ).call( *args )
o
end
end
end
end
end


class C
include M
def my_init; puts 222 end
end

c= C.new
c.my_init

Of course a user can still mess with C#new but your code might be save
from most common reusage patterns :).

HTH
Robert
 
R

Robert Dober

Sorry for quothing myself but you know Murphy's law :(

of course it is unnecessary to alias new to _orig_new_, just replace
_orig_new_ with a call to super.

Sorry
R.
 
A

ara.t.howard

I have created a module as follows which adds an initialize method to
the class it is included in. The problem is that the initializer
method
could be easily overriden by the includer Class. Is there a way to
ensure that the initialize method is run even though it may have been
overriden??

module SomeModule

def self.included(base)
base.class_eval do

attr_accessor :fields, :x_position, :y_position

def initialize(x_position=0, y_position=0)
@fields=self.class.xfields.clone
self.y_position=y_position
self.x_position=x_position
for field in @fields
eval "self."+field[:var] + " = field[:value]"
end
@fields = @fields.sort_by{|f| f[:eek:rder]} if @fields.find{|f|
f[:eek:rder]}
end
end
end

end

Any help is appreciated.
Thanks, Chris

i'd be inclined to do something like this:

cfp:~ > cat a.rb
module M
def M.initialize obj
obj.instance_eval{
@a = 42
}
end

module ClassMethods
def new *a, &b
obj = super
ensure
M.initialize obj
end
end

module InstanceMethods
attr :a
end

def self.included other
other.send :extend, ClassMethods
other.send :include, InstanceMethods
end
end

class C
include M
end

p C.new.a


cfp:~ > ruby a.rb
42


allowing you to extend the includee's class, instances, and also keep
your private initialize private rather than dropping it into the
includee

regards
a @ http://codeforpeople.com/
 
R

Robert Dober

I have created a module as follows which adds an initialize method to
the class it is included in. The problem is that the initializer
method
could be easily overriden by the includer Class. Is there a way to
ensure that the initialize method is run even though it may have been
overriden??

module SomeModule

def self.included(base)
base.class_eval do

attr_accessor :fields, :x_position, :y_position

def initialize(x_position=0, y_position=0)
@fields=self.class.xfields.clone
self.y_position=y_position
self.x_position=x_position
for field in @fields
eval "self."+field[:var] + " = field[:value]"
end
@fields = @fields.sort_by{|f| f[:eek:rder]} if @fields.find{|f|
f[:eek:rder]}
end
end
end

end

Any help is appreciated.
Thanks, Chris

i'd be inclined to do something like this:

cfp:~ > cat a.rb
module M
def M.initialize obj
obj.instance_eval{
@a = 42
}
end

module ClassMethods
def new *a, &b
obj = super
ensure
M.initialize obj
end
end

module InstanceMethods
attr :a
end

def self.included other
other.send :extend, ClassMethods
other.send :include, InstanceMethods
end
end

class C
include M
end

p C.new.a


cfp:~ > ruby a.rb
42


allowing you to extend the includee's class, instances, and also keep
your private initialize private rather than dropping it into the
includee
Well the simplicity and elegance of your code speak for themselves, I
would like to defend myself nevertheless;)
my aim was hiding the implementation even more, but as I put it
myself, that might not make sense very easily.
But I learnt a lot from your approach here...
R.
 
A

ara.t.howard

Well the simplicity and elegance of your code speak for themselves, I
would like to defend myself nevertheless;)
my aim was hiding the implementation even more, but as I put it
myself, that might not make sense very easily.
But I learnt a lot from your approach here...



lol - my own code looks much more like yours. i go back and forth
with methodology. this week i am using modules ;-)

a @ http://codeforpeople.com/
 
T

Trans

I have created a module as follows which adds an initialize method to
the class it is included in. The problem is that the initializer method
could be easily overriden by the includer Class. Is there a way to
ensure that the initialize method is run even though it may have been
overriden??

module SomeModule

def self.included(base)
base.class_eval do

attr_accessor :fields, :x_position, :y_position

def initialize(x_position=0, y_position=0)
@fields=self.class.xfields.clone
self.y_position=y_position
self.x_position=x_position
for field in @fields
eval "self."+field[:var] + " = field[:value]"
end
@fields = @fields.sort_by{|f| f[:eek:rder]} if @fields.find{|f|
f[:eek:rder]}
end
end
end

end

This is more of an aside then an answer to your question. But it is
important to note that you should not do the above. You are
"falsifying" inclusion and actually injecting code. The reason I say
"falsify" is because you end up with a module in the hierarchy that
contains no code. If you want to inject code, then simply define a
class method that does it. Don't use the included callback.

BTW: To ensure a routine always runs on initialization despite
#initialize, one way:

class X
class << self
def new(*a, &b)
o = allocate
o.do_your_thing
o.initialize(*a,&b)
o
end
end
end

The other is AOP which is a much larger can of worms.

T.
 
C

Chris Richards

Wow, thanks for all your replies.
BTW: To ensure a routine always runs on initialization despite
#initialize, one way:

class X
class << self
def new(*a, &b)
o = allocate
o.do_your_thing
o.initialize(*a,&b)
o
end
end
end

The other is AOP which is a much larger can of worms.

so whats the simplest way to ensure this method was always run ? :

def initialize(x=0, y=0)
self.x_position=x
self.y_position=y
end

Thanks
Chris
 
T

Trans

Wow, thanks for all your replies.




so whats the simplest way to ensure this method was always run ? :

def initialize(x=0, y=0)
self.x_position=x
self.y_position=y
end

Why do you want to thwart super here? You should have very clear
reason for doing so, otherwise leave it.

But to answer your question directly:

class X
class << self
def new(*a, &b)
o = allocate
o.pre_initialize(*a, &b)
o.initialize(*a,&b)
o
end
end

def pre_initialize(x=0, y=0)
self.x_position=x
self.y_position=y
end
end

T.
 

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

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,054
Latest member
LucyCarper

Latest Threads

Top