[prev in list] [next in list] [prev in thread] [next in thread] 

List:       ruby-talk
Subject:    Re: class methods like attr:
From:       "Robert Klemme" <bob.news () gmx ! net>
Date:       2006-01-24 15:47:26
Message-ID: 43jj2sF1ms5ebU1 () individual ! net
[Download RAW message or body]

Gioele Barabucci wrote:
> How can I create methods like "attr:" that can create methods and
> instance variables?
>
> I'd like to do this
>
>   class B < A
>     my_property :k, "K"
>     def test
>       p @k # should print "K"
>       p @@my_properties # should print ['k']
>     end
>   end
>
> I tried with this (non-) solution
>
>   class A
>     def A.my_property(sym, val)
>       self.instance_eval {
>         @@my_properties ||= Array.new()
>         @@my_properties << sym.to_s
>         eval "@#{sym.id2name} = \"#{val}\""
>         # p @k1 # << this shows "K1.B" or "K1.C" as expected
>       }
>     end
>   end
>
> but these tests showed me that I was wrong
>
>   class A
>     def test
>       p @@my_properties
>     end
>   end
>
>   class B < A
>     my_property :k1, "K1.B" # k1 is a string fixed to "K1"
>     my_property :k2, "K2.B"
>     def test
>       super  # should be ['k1', 'k2']
>       p @k1  # should be "K1"
>     end
>   end
>
>   class C < A
>     my_property :k1, "K1.C"
>     def test
>       super  # should be ['k1']
>       p @k1  # should be "K1.C"
>     end
>   end
>
> A.new.test (should print nil or a warning) => ["k1", "k2", "k1"]
>
> B.new.test (should print ['k1', 'k2'] 'K1.B') => ["k1", "k2", "k1"]
> /at.rb:28: warning: instance variable @k1 not initialized
> nil
>
> A.new.test (should print nil or a warning) => ["k1", "k2", "k1"]
>
> C.new.test (should print ['k1'] 'K1.C') => ["k1", "k2", "k1"]
> /at.rb:36: warning: instance variable @k1 not initialized
> nil
>
> Any suggestion or idea?

One way is to define them as instance methods of class Module.

09:36:59 [~]: irb
irb(main):001:0> class Module
irb(main):002:1> def foo(sym)
irb(main):003:2> define_method(sym) {|*a| p a}
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> class Foo
irb(main):007:1> foo :bar
irb(main):008:1> end
=> #<Proc:0x04aa36d0@(irb):3>
irb(main):009:0> Foo.new.bar 1,2,3
[1, 2, 3]
=> nil

Then you can use them in the complete class hierarchy.  If you want only
part of the class hiearchy of classes to be able to use them, you should
define them as instance methods of the starting class's singleton class:

09:38:02 [~]: irb
irb(main):001:0> class Foo
irb(main):002:1> class << self
irb(main):003:2> def foo(sym)
irb(main):004:3> define_method(sym) {|*a| p a}
irb(main):005:3> end
irb(main):006:2> end
irb(main):007:1> end
=> nil
irb(main):008:0> class Bar < Foo
irb(main):009:1> foo :bar
irb(main):010:1> end
=> #<Proc:0x04aa6bc8@(irb):4>
irb(main):011:0> Bar.new.bar
[]
=> nil
irb(main):012:0> Bar.new.bar 1,2,3
[1, 2, 3]
=> nil

Kind regards

    robert



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic