2010-07-14 56 views
4

(大编辑,我有部分的方式...) 我一直在黑客攻击,我想出了这个作为一种方式来指定需要完成的事情属性读取之前:自定义attr_reader做属性的懒惰实例化

class Class 
    def attr_reader(*params) 
    if block_given? 
     params.each do |sym| 
     define_method(sym) do 
      yield 
      self.instance_variable_get("@#{sym}") 
     end 
     end 
    else 
     params.each do |sym| 
     attr sym 
     end 
    end 
    end 
end 

class Test 
    attr_reader :normal 
    attr_reader(:jp,:nope) { changethings if @nope.nil? } 

    def initialize 
    @normal = "Normal" 
    @jp = "JP" 
    @done = false 
    end 

    def changethings 
    p "doing" 
    @jp = "Haha!" 
    @nope = "poop" 
    end 

end 

j = Test.new 

p j.normal 
p j.jp 

changethings没有被公认的方法 - 任何人有任何想法?

+0

它看起来像在类对象的范围正在执行我的attr_reader块,而不是它的实例。关于如何强制实例的任何想法? – 2010-07-14 16:09:48

+0

您可以使用'instance_eval'而不是yield,并将block作为参数。但它闻起来有点。 – 2010-07-14 16:18:43

回答

3

您需要的背景下,以评估该块的情况。 yield默认情况下会评估它的本地环境。

class Class 
    def attr_reader(*params, &blk) 
    if block_given? 
     params.each do |sym| 
     define_method(sym) do 
      self.instance_eval(&blk) 
      self.instance_variable_get("@#{sym}") 
     end 
     end 
    else 
     params.each do |sym| 
     attr sym 
     end 
    end 
    end 
end 
+0

这样做绝妙! – 2010-07-14 17:23:16

1

这是另一种可供选择的方法。这并不像您使用define_method所做的那样优雅,但它可能值得一看。

添加一个新方法lazy_attr_readerClass

class Class 
    def lazy_attr_reader(*vars) 
    options = vars.last.is_a?(::Hash) ? vars.pop : {} 
    # get the name of the method that will populate the attribute from options 
    # default to 'get_things' 
    init_method = options[:via] || 'get_things' 
    vars.each do |var| 
     class_eval("def #{var}; #{init_method} if !defined? @#{var}; @#{var}; end") 
    end 
    end 
end 

然后使用它是这样的:

class Test 
    lazy_attr_reader :name, :via => "name_loader" 

    def name_loader 
    @name = "Bob" 
    end 
end 

在行动:

irb(main):145:0> t = Test.new 
=> #<Test:0x2d6291c> 
irb(main):146:0> t.name 
=> "Bob" 
1

恕我直言更改块的情况下是非常反直觉的,从别人的透视谁还会类固醇使用这种attr_reader

也许你应该考虑纯醇”的方法‘使用可选参数指定方法名’:

def lazy_attr_reader(*args, params) 
    args.each do |e| 
    define_method(e) do 
     send(params[:init]) if params[:init] && !instance_variable_get("@#{e}") 
     instance_variable_get("@#{e}") 
    end 
    end 
end 

class Foo 
    lazy_attr_reader :foo, :bar, :init => :load 

    def load 
    @foo = 'foo' 
    @bar = 'bar' 
    end 
end 

f = Foo.new 
puts f.bar 
#=> bar