Robert Klemme
1/18/2006 10:53:00 PM
Schüle Daniel <uval@rz.uni-karlsruhe.de> wrote:
> [...]
>
>> Not as far as I know. What do you need that for?
>
> I am learning Ruby and was trying to explore if there exist
> something Pythonlike
>
>>>> class X:
> ... pass
> ...
>>>> x=X()
>>>> x.foo = 1
>>>> x.foo
> 1
>>>> del x.foo
>>>> x.foo
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> AttributeError: X instance has no attribute 'foo'
>>>>
>>>> x
> <__main__.X instance at 0x403e3bec>
>>>> del x
>>>> x
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> NameError: name 'x' is not defined
>>>>
Usually it's sufficient to set the attribute value to nil. You'll soon
learn that the attribute had the wrong value - at that point when someone
invokes a method on nil that is not defined for NilClass. Personally I
never felt the need to actually take attribute accessors away from an
instance.
>> Of course you can create your own class.
>>
>> class ChangingFields
>> def method_missing(s,*a,&b)
>> case s.to_s
>> when /^(.*)=$/
>> field = $1
>> class << self;self;end.class_eval do
>> attr_accessor field
>> end
>> send(s,*a,&b)
>> else
>> super
>> end
>> end
>>
>> def remove_field(field)
>> class << self;self;end.class_eval do
>> remove_method( field )
>> remove_method( "#{field}=" )
>> end
>> instance_variable_set("@#{field}", nil)
>> end
>> end
>
> metaprogramming is still one of my weak points
It's one of the areas that takes a little longer to feel at home with - at
least longer than the usual OO stuff.
> why do you use
> class << self;self;end.class_eval do ... end
> and not just
> ChangingFields.class_eval do ... end
Because if I understood you correctly you want to add and remove fields on
an per instance basis. If you do it with the class object, you'll define
instance methods for all instances (effective immediately).
>> irb(main):052:0> c=ChangingFields.new
>> => #<ChangingFields:0x101d0b88>
>> irb(main):053:0> c.foo = 1
>> => 1
>> irb(main):054:0> c
>> => #<ChangingFields:0x101d0b88 @foo=1>
>> irb(main):055:0> c.foo
>> => 1
>> irb(main):056:0> c.remove_field :foo
>> => nil
>> irb(main):057:0> c
>> => #<ChangingFields:0x101d0b88 @foo=nil>
>> irb(main):058:0> c.foo
>> NoMethodError: undefined method `foo' for #<ChangingFields:0x101d0b88
>> @foo=nil>
>> from (irb):37:in `method_missing'
>> from (irb):58
>> from (null):0
>
> thx for this example, I will try to digest it :)
You're welcome! While I think about it, a better approach might be to
define explicit methods that create and remove attributes on an per instance
basis:
class Changing
def def_attr(sym)
class<<self;self;end.class_eval do
attr_accessor sym
end
end
def undef_attr(sym)
instance_variable_set("@#{sym}", nil)
class<<self;self;end.class_eval "undef #{sym};undef #{sym}="
end
end
>> c=Changing.new
=> #<Changing:0x1019d390>
>> c.foo
NoMethodError: undefined method `foo' for #<Changing:0x1019d390>
from (irb):73
from :0
>> c.def_attr :foo
=> nil
>> c.foo
=> nil
>> c.foo=10
=> 10
>> c.foo
=> 10
>> c.undef_attr :foo
=> nil
>> c.foo
NoMethodError: undefined method `foo' for #<Changing:0x1019d390 @foo=nil>
from (irb):79
Kind regards
robert