メタプログラミングRuby第2版 / 第3章メソッド[勉強メモ]


メタプログラミングRubyを読んでいます。完全自分用のメモ記事です。
第3章で特に参考になった部分を中心に書いています。


😎 3章メソッド

動的言語Rubyと静的言語

  • JavaとかCはコンパイラがメソッドのチェックを行う
  • Rubyは実行する際にメソッドを探す。な。ればエラーになる

Rubyの動的言語の特徴を活かす

Rubyの動的言語の特徴を利用して、Rubyは重複を排除するために、次の様な方法を取ることができる。

- 動的にメソッドを定義
- method_missingを使う

動的メソッドを使ったリファクタリング

まずは修正前の変更。

class Computer
def initialize(computer_id, data_source)
@id = computer_id
@data_source = data_source
end

def mouse
info = @data_source.get_mouse_info(@id)
price = @data_source.get_mouse_price(@id)
result = "Mouse: #{info} ($#{price})"
return "* #{result}" if price >= 100
result
end

def cpu
info = @data_source.get_cpu_info(@id)
price = @data_source.get_cpu_price(@id)
result = "Cpu: #{info} ($#{price})"
return "* #{result}" if price >= 100
result
end

def keyboard
info = @data_source.get_keyboard_info(@id)
price = @data_source.get_keyboard_price(@id)
result = "keyboard: #{info} ($#{price})"
return "* #{result}" if price >= 100
result
end
end

動的メソッドを使ってリファクタリングを行った結果。

class Computer
def initialize(computer_id, data_source)
@id = computer_id
@data_source = data_source
data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_component $1 }
end

def self.define_component(name)
define_method(name) do
info = @data_source.get_keyboard_info(@id)
price = @data_source.get_keyboard_price(@id)
result = "keyboard: #{info} ($#{price})"
price >= 100 ? "* #{result}" : result
end
end
end

method_missingを使ったリファクタリング

さきほどの Computer クラスを method_missing でリファクタリングした場合。

class Computer
def initialize(computer_id, data_source)
@id = computer_id
@data_source = data_source
end

def method_missing(name)
super if !@data_source.respond_to?("get_#{name}_info")
info = @data_source.send("get_#{name}_info", @id)
price = @data_source.send("get_#{name}_price", @id)
result = "#{name.capitalize}: #{info} ($#{price})"
price >= 100 ? "* #{result}" : result
end

def respond_to_missing?(method, include_private = false)
@data_source.respond_to?("get_#{method}_info") || super
end
end

ただし、 method_missing はバグが発生した場合に気付きにくい特徴があるので利用には注意が必要。
あと、method_missing よりも、respond_to_missing? のほうが適切というお話もあります。

Always Define respond_to_missing? When Overriding method_missing

ゴーストメソッドの最大の問題点は、本物のメソッドではないという点だ。
振る舞いはメソッドなのに、 Object#methods に含まれないのだ。

🗻 Pryのコード

def refresh(options={})
defaults = {}
attributes = [ :input, :output, :commands, :print, :quiet,
:exception_handler, :hooks, :custom_completions,
:prompt, :memory_size, :extra_sticky_locals ]

attributes.each do |attribute|
defaults[attribute] = Pry.send attribute
end

# ...

defaults.merge!(options).each do |key, value|
send("#{key}=", value) if respond_to?("#{key}=")
end

true
end

Excerpt From: Paolo Perrotta. “メタプログラミングRuby 第2版.” iBooks.

😀 サンプルソース

何かの役に立つこともあるかもですので、リポジトリも公開しておきます。

morizyun/meta_programming_ruby2 - GitHub

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍