I'm trying to define s.t. like attr_accessor, but one that takes
an optional block defining a function that filters the value on
the setter. Perhaps that will be clear if I show you the snippet:
class Object
def add_property(sym, &block)
n = sym.id2name
code = %Q{
def #{n}()
@#{n} ||= nil
end
def #{n}=(value)
@#{n} = block ? block.call(value) : value
end
}
eval(code)
end
end
# A test object:
d = 3
# Add a getter and custom setter to 3:
d.add_property(:do_it) {|v|
# Custom setter method
puts "d.do_it here, v = #{v.inspect}"
v # No filtering this time, just return the value.
}
# Assign the property:
d.do_it = "a value"
Ok, I can see why "block" isn't defined. How can I pass add_property's
block in as a block to be used *inside* the evaluated code?
I'm trying to define s.t. like attr_accessor, but one that takes
an optional block defining a function that filters the value on
the setter. Perhaps that will be clear if I show you the snippet:
class Object
def add_property(sym, &block)
n = sym.id2name
code = %Q{
def #{n}()
@#{n} ||= nil
end
def #{n}=(value)
@#{n} = block ? block.call(value) : value
end
}
eval(code)
end
end
# A test object:
d = 3
# Add a getter and custom setter to 3:
d.add_property(:do_it) {|v|
# Custom setter method
puts "d.do_it here, v = #{v.inspect}"
v # No filtering this time, just return the value.
}
# Assign the property:
d.do_it = "a value"
Ok, I can see why "block" isn't defined. How can I pass add_property's
block in as a block to be used *inside* the evaluated code?
You can use define_method:
class Module
def add_property(sym,&test)
define_method("#{sym}=") do |val|
test && test[val]
instance_variable_set("@#{sym}", val)
end
define_method(sym) do |val|
instance_variable_get("@#{sym}")
end
end
end
class Foo
add_property :age do |x|
raise "Illegal negative" unless x >= 0
end
end
Foo.new.age = -2
RuntimeError: Illegal negative
from (irb):55
from (irb):54:in `'
from (irb):43:in `age='
from (irb):42:in `age='
from (irb):59
Here's *a* working solution. The big string + eval feels like a serious hack, but I'm not cool enough to know how to deal with it properly.
class Object
def add_property(sym, &block)
n = sym.id2name
(@_property_callbacks||={})[ n ] = block
code = %Q{
def #{n}()
@#{n} ||= nil
end
def #{n}=(value)
if block = @_property_callbacks[ '#{n}' ]
@#{n} = block.call(value)
else
@#{n} = value
end
end
}
eval(code)
end
end
# A test object:
d = 3
# Add a getter and custom setter to 3:
d.add_property(:do_it) {|v|
# Custom setter method
puts "d.do_it here, v = #{v.inspect}"
v # No filtering this time, just return the value.
}
# Assign the property:
d.do_it = "a value"
···
On Sep 19, 2005, at 6:16 AM, Clifford Heath wrote:
Ok, I can see why "block" isn't defined. How can I pass add_property's
block in as a block to be used *inside* the evaluated code?
Doh! I went searching for that, since it wasn't in my
(ruby 1.6) HTML manual and I expected it to have been
added in 1.8 :-)... But I tried
p Object.methods
p Class.methods
p Module.methods
p Kernel.methods
without success, so I stopped looking
Thanks for the help. I have the 1.8 book, both paper
and PDF, but the HTML is so convenient :-).
Clifford Heath.
An additional remark: you can improve performance if you extract the test
for the block's presence from the method and use your approach of code
generation for the getter and for the setter (for the case where there is
no block).