define_method method_name do |new_child|
instance_variable_set(child_list_name, []) if instance_variable_get(child_list_name).nil?
unless instance_variable_get(child_list_name).include? new_child
instance_variable_set(child_list_name, (instance_variable_get(child_list_name) << new_child))
if new_child.respond_to? self_method
new_child.send self_method, self
end
end
end
This is what I'd really like the new method to look like (for child== :personnel):
def add_personnel (new_personnel)
unless (@personnel_list||=[]).include? new_personnel @personnel_list << new_personnel
self_method= (self.name.downcase+'=').to_sym
if new_personnel.respond_to? self_method
new_personnel.send self_method, self
end
end
end
I make two possible suggestions. Create yourself something shorter
than instance_variable_set/get. Eg.
def __iv(name,value=Exception)
if value==Exception
instance_variable_get("@#{name}")
else
instance_variable_set("@#{name}", value)
end
end
That will make it easier to read:
define_method method_name do |new_child|
__iv(child_list_name, ) if __iv(child_list_name).nil?
unless __iv(child_list_name).include? new_child
__iv(child_list_name, (__iv(child_list_name) << new_child))
if new_child.respond_to? self_method
new_child.send self_method, self
end
end
end
Secondly, use eval if it makes the code easier to maintain.
T.
···
On Nov 28, 4:28 am, Sharon Phillips <phillip...@yahoo.co.uk> wrote:
Hi,
This problem has been driving me nuts, so I've finally given in and
decided to ask for help.
I'm just cutting my teeth on metaprogramming but am stuck referencing
instance variables within define_method
Here's the code snippet in question. It works, but is extremely ugly
with its constant usage of instance_variable_get and set.
define_method method_name do |new_child|
instance_variable_set(child_list_name, ) if
instance_variable_get(child_list_name).nil?
unless instance_variable_get(child_list_name).include? new_child
instance_variable_set(child_list_name,
(instance_variable_get(child_list_name) << new_child))
if new_child.respond_to? self_method
new_child.send self_method, self
end
end
end
This is what I'd really like the new method to look like (for
child== :personnel):
def add_personnel (new_personnel)
unless (@personnel_list||=).include? new_personnel @personnel_list << new_personnel
self_method= (self.name.downcase+'=').to_sym
if new_personnel.respond_to? self_method
new_personnel.send self_method, self
end
end
end
It looks like you are maintaining multiple lists but I don't
understand why you are encoding the name of the list in the method
names and in the instance variables. Why not just use a hash
and methods that take a list name parameter?
def add(list, obj)
unless (@lists[list]||=).include? obj @lists[list] << obj
if obj.respond_to? :add_container
obj.add_container(self)
end
end
end
Now if you really want something like 'add_personnel' it is easy:
listname='personnel'
define_method('add_#{listname}') do |obj|
add(listname, obj)
end
The added object can derive a name directly from 'self' (self.name.downcase)
there is no need to figure that out for the object.
Note: I didn't test this code, but I hope you get the idea.
Bottom line: use a hash and access the key/value pairs instead of
trying to construct and dereference instance variable names.
Gary Wright
···
On Nov 28, 2007, at 4:28 AM, Sharon Phillips wrote:
This is what I'd really like the new method to look like (for child== :personnel):
def add_personnel (new_personnel)
unless (@personnel_list||=).include? new_personnel @personnel_list << new_personnel
self_method= (self.name.downcase+'=').to_sym
if new_personnel.respond_to? self_method
new_personnel.send self_method, self
end
end
end
eval strings, it's faster to debug by light years since you can dump it out to the screen. it also lets you use blocks. the cool kids use 'define_method', but then their methods cease to take blocks, enclose scope which cannot be freed, and are a pain in the butt to debug. starting with your sample method, which is the best way to start of course, i'd do
cfp:~ > cat a.rb
module Adder
def attr_adder *names
names.each do |name|
code = <<-code
def add_#{ name } (new_#{ name })
unless (@#{ name }||=).include? new_#{ name }
@#{ name } << new_#{ name }
self_method= (self.name.downcase+'=').to_sym
if new_#{ name }.respond_to? self_method
new_#{ name }.send self_method, self
end
end
end
code
puts code if $DEBUG
module_eval code
end
end
def self.included other
other.extend self
end
end
class C
include Adder
attr_adder :personnel, :foobar
end
obviously writing this is quite easy: just start with your template and do some string substitution being able to dump the code out is HUGE when it comes to metaprogramming. i've even had libs that dumped it out so it could be, gasp, rdoc'd.
define_method is quite useful at times, but i'd say this isn't it.
define_method method_name do |new_child|
instance_variable_set(child_list_name, ) if instance_variable_get(child_list_name).nil?
unless instance_variable_get(child_list_name).include? new_child
instance_variable_set(child_list_name, (instance_variable_get(child_list_name) << new_child))
if new_child.respond_to? self_method
new_child.send self_method, self
end
end
end
This is what I'd really like the new method to look like (for child== :personnel):
def add_personnel (new_personnel)
unless (@personnel_list||=).include? new_personnel @personnel_list << new_personnel
self_method= (self.name.downcase+'=').to_sym
if new_personnel.respond_to? self_method
new_personnel.send self_method, self
end
end
end
Any help appreciated.
--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama
eval strings, it's faster to debug by light years since you can dump it out to the screen. it also lets you use blocks. the cool kids use 'define_method', but then their methods cease to take blocks, enclose scope which cannot be freed, and are a pain in the butt to debug. starting with your sample method, which is the best way to start of course, i'd do
Funnily enough, that's how I'd done this originally, including calling my code variable 'code', using 'CODE' as the terminator and printing it to screen for debugging. I changed because I thought I was doing it the wrong way, and that the 'proper' way to do this sort of this was to use define_method.
However... I really like Garry Wright's suggestion of using a hash to manage these lists. Much neater design than mine.
Thanks for the suggestions.
Cheers,
Dave
···
On 29/11/2007, at 3:22 AM, ara.t.howard wrote:
On Nov 28, 2007, at 2:28 AM, Sharon Phillips wrote:
Here's the code snippet in question. It works, but is extremely ugly with its constant usage of instance_variable_get and set.
define_method method_name do |new_child|
instance_variable_set(child_list_name, ) if instance_variable_get(child_list_name).nil?
unless instance_variable_get(child_list_name).include? new_child
instance_variable_set(child_list_name, (instance_variable_get(child_list_name) << new_child))
if new_child.respond_to? self_method
new_child.send self_method, self
end
end
end
This is what I'd really like the new method to look like (for child== :personnel):
def add_personnel (new_personnel)
unless (@personnel_list||=).include? new_personnel @personnel_list << new_personnel
self_method= (self.name.downcase+'=').to_sym
if new_personnel.respond_to? self_method
new_personnel.send self_method, self
end
end
end
Any help appreciated.
eval strings, it's faster to debug by light years since you can dump it out to the screen. it also lets you use blocks. the cool kids use 'define_method', but then their methods cease to take blocks, enclose scope which cannot be freed, and are a pain in the butt to debug. starting with your sample method, which is the best way to start of course, i'd do
cfp:~ > cat a.rb
module Adder
def attr_adder *names
names.each do |name|
code = <<-code
def add_#{ name } (new_#{ name })
unless (@#{ name }||=).include? new_#{ name }
@#{ name } << new_#{ name }
self_method= (self.name.downcase+'=').to_sym
if new_#{ name }.respond_to? self_method
new_#{ name }.send self_method, self
end
end
end
code
puts code if $DEBUG
module_eval code
end
end
def self.included other
other.extend self
end
end
class C
include Adder
attr_adder :personnel, :foobar
end
obviously writing this is quite easy: just start with your template and do some string substitution being able to dump the code out is HUGE when it comes to metaprogramming. i've even had libs that dumped it out so it could be, gasp, rdoc'd.
define_method is quite useful at times, but i'd say this isn't it.
kind regards.
a @ http://codeforpeople.com/
--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama