attr_reader

class Hoge
  def initialize
    @data = [*1..10]
  end

  attr_reader :data
end

h = Hoge.new
hoge.data = 1 #=>Error

p hoge.data #=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
hoge.data[0] = 0
p hoge.data #=>[0, 2, 3, 4, 5, 6, 7, 8, 9, 10]


hoge.data = 1 がエラーになるのは当り前。
でも hoge.data[0] = 0 がエラーならない。
自分が期待しているのとは動作がちがう。


色々考えた結論は、前者は@dataそのものを変更しようとするので、
data= メソッドが無いからエラーとなる。
後者は@dataの内部のオブジェクトを変えるだけで、
data= メソッドを使用しないので問題が無いかな。


理由が分かっても今期待しているのとは動作が異なるので困る。
自分が期待している動作になるようなattr_readerを作ってみた。

class Object
  def attr_reader_dup(*val)
    val.each do |name|
      name = name.id2name
      module_eval <<-END
        def #{name}
          @#{name}.dup
        end
      END
    end
  end
end

class Hoge
  def initialize
    @data = [*1..10]
  end

  attr_reader_dup :data
end

h = Hoge.new

p hoge.data #=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
hoge.data[0] = 0
p hoge.data #=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


これで問題無く動く。
ただ、@dataが大きすぎると重くなるかも。
[ruby-list:22115] デフォルト付き attr_reader を参考にした。


もっとまともな回避策はないかな?