2013-10-18 63 views
1

考慮這個簡單的代碼:如何做到真正的只讀屬性(存取=> attributs)

class Yeah 
    attr_reader :foo 
    attr_reader :fool 
    attr_reader :feel 
    def initialize(foo: "test", fool: {}, feel: []) 
    @foo = foo 
    @fool = fool 
    end 
end 

test = Yeah::new 
pp test 
test.fool[:one] = 10 
pp test 

輸出:

#<Yeah:0x000008019a84a0 @foo="test", @fool={}> 
#<Yeah:0x000008019a84a0 @foo="test", @fool={:one=>10}> 

我的問題是,有一個 「簡單」, 「乾淨」方式,讀取accesors真正的只讀數組,哈希attributs或我需要繼承數組或哈希很多難以寫入鎖定,(undef,別名)或使用代理,委託或其他模式是這樣的?

+1

標準ruby中沒有隻讀數組或哈希。 –

+0

非常好的問題雖然(* + 1 *) –

+0

我想說,在任何情況下,我爲字符串讀取器或寫入器創建存取器都有一個意義,當您嘗試執行obj.accessor =您有很好的異常,對於散列或者數組在每種情況下我都使用[]來訪問,並且沒有什麼可阻止的,但是使用freeze的解決方案似乎很好,謝謝! – Romain

回答

2

你能想到的東西象下面這樣:

class Yeah 
    def self.reader_meth 
    %i(foo fool feel).each do |m| 
     define_method(m){instance_variable_get("@#{m}").dup.freeze} 
    end 
    end 
    def initialize(foo: "test", fool: {}, feel: []) 
    @foo = foo 
    @fool = fool 
    @feel =feel 
    end 
    reader_meth 
end 

test = Yeah.new 
test # => #<Yeah:0x8975498 @foo="test", @fool={}, @feel=[]> 
test.fool[:one] = 10 # can't modify frozen Hash (RuntimeError) 
test # => #<Yeah:0x8975498 @foo="test", @fool={}, @feel=[]> 
+1

是的,我可以返回@#{m} .dup.freeze if我想要一個例外,如果試圖訪問,謝謝!! – Romain

+0

@Romain是的,你是完全正確的。 –

+0

你可以使用'instance_variable_get'去除醜陋的'eval'。 – Max

0

什麼Object#freeze

class Yeah 
    def fool 
    @fool.freeze 
    end 
    def initialize(fool={}) 
    @fool = fool 
    end 
end 
+0

這不可能是一個好的解決方案... –

+1

但你凍結@fool像奧雅納解釋在評論 – Romain

1

,因爲我想概括這個解決方案,以防止 「惡」 evals:

我最後,從奧雅納解決方案到達此:

class Module 
    def attr_readonly *syms 
    syms.each do |method| 
     define_method(method){ 
     return self.instance_variable_get("@#{method.to_s}").dup.freeze 
     } 
    end 
    end 
end 

class Yeah 

    attr_reader :foo 
    attr_readonly :fool 
    attr_reader :feel 
    def initialize(foo: "test", fool: {}, feel: []) 
    @foo = foo 
    @fool = fool 
    @feel = feel 
    end 

end