EDITED to fix bug: See Scope Kills for details.
While ruby has attr_accessor there's no good way to specify a default value which comes as part of the stock language. Thank goodness for metaprogramming. Based off of Create getter and setter on a valorized variable, I have a solution which takes either a value or a block as the default. It's based, in part, upon my solution for Ruby Quiz #67 Metakoans.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Object def self.attribute(*arg,&block) (name,default) = arg self.send(:define_method, name) { if instance_variables.include? "@#{name}" self.instance_eval "@#{name}" else if block_given? instance_eval &block else default end end } self.send(:define_method, "#{name}="){ |value| self.instance_eval "@#{name} = value" } end end |
Given the following class:
1 2 3 4 5 6 |
class Foo attribute :bar attribute(:fud) {instance_variables.include?("@bar") ? @bar : ' '} attribute :fi, 10 end |
we can test as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
>> t=Foo.new => #<Foo:0xb7b9cadc> >> t.bar => nil >> t.fud => " " >> t.fi => 10 >> t.bar = 5 => 5 >> t.fud => 5 >> t.fud = 20 => 20 >> t.fud => 20 >> |
I imagine it can be simplified some more, but it works, and I think it's kinda nifty.....
