Clifford Heath wrote:
> 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
>> Foo.new.age = 1
=> 1
>> Foo.new.age = 0
=> 0

Kind regards

    robert