さらっと、というよりは一発では分かりそうになかったので、というほうが正しいw。ちなみに本書を読む前の自分が持ってた知識レベルは動的ディスパッチ*1とevalの初歩ってくらいです。

- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: KADOKAWA/アスキー・メディアワークス
- 発売日: 2010/08/28
- メディア: 大型本
- 購入: 18人 クリック: 533回
- この商品を含むブログ (127件) を見る
Railsの付近の章はまだ全然読んでないけど、それ以外のところで「おおっ」と思ったようなところをメモ。特にスコープのフラット化の付近は読んでて一人ですごいすごい言っていたw。
クラス再訪
- 「クラスはオブジェクトである」
- オブジェクトについて当てはまるものはクラスにも当てはまる
- クラスはClassクラスのインスタンスなのだ
- なっ、なんだってー
動的メソッド
以下のコードは面白い。動的にメソッドが定義されていく。class Computer def initialize(computer_id, data_source) @id = computer_id @data_source = data_source end def self.define_component(name) define_method(name) do info = @data_source.send "get_#{name}_info", @id price = @data_source.send "get_#{name}_price", @id result = "#{name.to_s.capitalize}: #{info} ($#{price})" return "* #{result}" if price >= 100 result end end define_component :mouse define_component :cpu define_component :keyboard end
selfが付いていないとエラーで死ぬ。
NoMethodError: undefined method `define_component' for Computer:Class from (irb):56:in `<class:Computer>' from (irb):40 from /opt/local/bin/irb1.9:12:in `<main>'
これはクラスがClassクラスのインスタンスであるということが理解できていれば大丈夫なはず。クラスを定義しているときのselfはインスタンスではなく、クラスに対して付いているので、Classクラスに対してdefine_componentをやろうとして死んでしまっているのである。
Computerクラスのオブジェクトがレシーバーになっているんではなくて、Computerクラスがレシーバーになっている、と考えるとしっくりくるかも。
スコープのフラット化
これは結構すごい。外のスコープにある変数をコードの本質を変えずにいかに触れるようにするか。my_var = "Success" MyClass = Class.new do puts "#{my_var} in the class definition!" define_method :my_method do puts "#{my_var} in the method!" end end MyClass.new.my_method
技術的には「入れ子構造のレキスカルスコープ」とかフラットスコープと読んだりするらしい。
スコープの共有化
- 複数のメソッド間で変数を共有したいが、その他からは見えないようにしたいということが可能になる(すごい)
def define_methods shared = 0 Kernel.send :define_method, :counter do shared end Kernel.send :define_method, :inc do |x| shared += x end end define_methods counter # => 0 inc(4) counter # => 4
instance_eval
オブジェクトのコンテキストでブロックを評価するもの。これもフラットスコープで評価されるために外の変数にアクセスできるようになる。class MyClass def initialize @v = 1 end end obj = MyClass.new obj.instance_eval do self # => #<MyClass:0x3340dc @v=1> @v # => 1 end v = 2 obj.instance_eval { @v = v } obj.instance_eval { @v } # => 2
こやつを使うとprivateな変数にも触れるようになってしまうため、「カプセル化って何だったの...」って感じになってしまいそうだが、irbからさくっと試したいときやテストのときには便利なので、そういうときだけ触るようにすればよい。