Reputation: 461
Considering this simple code :
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
Output :
#<Yeah:0x000008019a84a0 @foo="test", @fool={}>
#<Yeah:0x000008019a84a0 @foo="test", @fool={:one=>10}>
My Question is, there is a "simple","clean" way, to do read accesors to real readonly Array,Hash attributs or I need to inherit Array or Hash with a lot of locking hard to write, (undef,alias) or using Proxy, delegate or others patterns like this ?
Upvotes: 1
Views: 348
Reputation: 118289
You can think of something like below :
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=[]>
Upvotes: 2
Reputation: 461
Because i want to generalize this solution and to prevent "evil" evals :
i finally, from the Arup solution arrived to this :
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
Upvotes: 1
Reputation: 13921
What about Object#freeze
:
class Yeah
def fool
@fool.freeze
end
def initialize(fool={})
@fool = fool
end
end
Upvotes: 0