2010/07/31

attr_accessor

『プログラミングRuby1.9 言語編』(ISBN: 978-4-274-068096) を読みながら試してみたこと
# P.351 にクラスレベルのアクセサを定義する方法と解説がありました。

class Test
@var = 99
class << self
attr_accessor :var
end
end

puts "Original value = #{Test.var}"
Test.var = "cat"
puts "New value = #{Test.var}"

# 実行結果

Original value = 99
New value = cat


# 少し書き換えself を表示してみると

class Test
  p self
  @var = 99
  class << self
    p self
    attr_accessor :var
  end
end

# 実行結果

Test
#<Class:Test>
Original value = 99
New value = cat


# P.351 には

... 次のように特異クラス内で attr_accessor を呼び出す必要があります。

# とあるのだからして
# <Class:Test> が特異クラスなのだと理解していいのだろうか?
# #<Class:#<MyClass:xxxxx>> のような形式になっていないのが気になる...
# という疑問は一旦おいといて

# で、この class << self を使わないかぎり attr は定義できないのだろうか?
# class << self と書くのとは異なる方法はないのかな?

class Test1
  @var = 89
  self.class.attr_accessor :var
end

実行結果

# =>
./test.rb:30:in `<class:Test1>': private method `attr_accessor' called for Class:Class (NoMethodError)
  from ./test.rb:28:in `<main>'

# エラーだ。
# attr_accessor は <class:Test1> にとって private method だから外から呼べないよと言われてしまった。
# そんなメソッドは知らない、とは言われてない、てことは?

class Test2
  @var = 123
  self.class.send(:attr_accessor, :var)
end

puts "Original value = #{Test2.var}"
Test2.var = "dog"
puts "New value = #{Test2.var}"

# 実行結果

Original value = 123
New value = dog

# やったー
# では、これをinitialize の中でも使えるかな?

class Test3
  def initialize
    @var = "99-dayo"
    self.class.send(:attr_accessor, :var)
  end
end

t = Test3.new
puts "Original value = #{t.var}"
t.var = "apple"
puts "New value = #{t.var}"

# 実行結果

Original value = 99-dayo
New value = apple


# 使えた!ので ...

# @varなど予め定義されたインスタンス変数が不要な場合で(というか用意したハッシュの値を既定値にしたい)
# 尚かつインスタンス変数を定義する時になんら制限をもたせなくてよい条件下。
# ハッシュをうけとった class Test4 が
# 受け取ったハッシュをもとにインスタンス変数とアクセサを定義するには?

class Test4
  def initialize(h)
    @now ||= Time.now
    h.each{|k,v|
      ks = k.downcase.to_sym
      self.class.send(:attr_accessor, ks)
      self.send("#{ks}=", v)
    }
  end
end

h = {"Now"=>"123","Cat"=>"meow", "Dog"=>"bowwow"}
t = Test4.new(h)
p t, t.cat, t.dog

puts "----\n"
print "Instance_variables: #{t.instance_variables}\n"
print "Singleton: #{t.singleton_methods} \n"
print "Singleton_class: #{t.singleton_class}\n"

# 実行結果

#<Test4:0xxxxxx @now="123", @cat="meow", @dog="bowwow">
"meow"
"bowwow"
----
Instance_variables: [:@now, :@cat, :@dog]
Singleton: []
Singleton_class: #<Class:#<Test4:0xxxxxx>>


# あれーTest4 の中に singleton_class が存在しているのはなぜかしら
# self.class してる時点で自動的に特異クラスができあがっているのだろうか。

# 一瞬 object_id 同じなのか!?とぎょっとしたけど
# print "Object_id: #{t.object_id}, #{t.singleton_class.object_id}\n"
# を実行してみたら同じじゃなかった。

+++ memo +++

これ <http://midorex.blogspot.com/2010/05/selfsend2.html> を書いた頃...
ハッシュを受け取ったクラスがインスタンス変数を定義する際、アクセサによる一定の制限があればいいと考えた。
なぜなら Amazon から受け取る値の[全て]が必要ではないだろうと判断したから。

でも、全ての値をinstanceにしてもいっかと考えれば
アクセサによる制限は必要なくなるので上記のように書き換えてもいいのかもしれない..がよくわかっていない...。

<http://github.com/midore/get_item_amzon>

0 件のコメント: