2010-07-14 59 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