メタプログラミングRuby 3章メモ
https://scrapbox.io/files/6599eda8173c1e00254bf9dc.png
メソッド
ボイラープレートメソッド
ボイラープレートコード は、コンピュータプログラミングでは、殆ど、または全く変化することなく、複数の場所で繰り返される定型コードのセクションのこと
Rubyはこれを回避できる
シチュエーション
高いコンピュータを使っている人を検知する。
元々用意されていたクラスがデータソースを把握する役割をしているので、Computerクラスを用意する。
Computerクラスを用意すれば、コンピューターの情報を管理できる。
コンピュータクラスとラップ
マウスやキーボードなどのメソッドを持っている。
メソッドの責務:データソースを使って各情報を取得する。
つまり、コンピュータークラスはデータソースクラスをラップしている。クラスを利用するようなイメージ。
ラップすることによって、既存のクラスを変更せずに、そのクラスの機能を拡張したり、異なるインターフェースを提供したりすることができる。
問題点:コンピューターメソッドとキーボードメソッドで、重複するメソッドが多発。(金額見たりとか)
→そのため、動的メソッドとmethod_missingという機能を使ってみようという話になった。
/icons/hr.icon
動的メソッドの話
メソッドはsendを使っても実行できる
code: ruby
Object.send(:method_name, arg) # シンボルまたは文字列が使える
これを動的ディスパッチと呼ぶ
余談:シンボルのメリット
イミュータブルだからキーの変更とかの影響を受けない
同じオブジェクトを参照するからメモリの節約ができる
慣習的にメタプログラミングで使用される
アトリビュートとして保持とは、インスタンス変数としてもつのようにオブジェクトやデータ構造に関連付けられた特性や状態を保持するということ
code: ruby
class Dog
def run(meter)
puts "#{meter}m走った"
end
private
def walk(meter)
puts "#{meter}m歩いた"
end
end
dog = Dog.new
method_hash.each do |hash|
dog.send(hash, 5)
end
# 5m走った
# 5m歩いた
コードの実行時に呼び出すメソッドを変えられる
このように、呼び出す際に動的にメソッドを変えることもできるし、define_methodという動的に定義する方法もある。
スモークテストとは
スモークテストとは、開発途上のソフトウェアをテスト(試験)する手法の一つで、開発・修正したソフトウェアを実行可能な状態に組み立て、起動するかどうかや基本的な機能が動作するかなどをざっと確認すること。
イントロスペクション
プログラミング言語においてのintrospectionは、プログラムの実行時にオブジェクトの性質や型を調べること
Array#grepにブロックを渡すと、正規表現にマッチした要素全てに対してブロックが評価される!
code: ruby
matched_names = names.grep(/e/) { |name| name.upcase }
puts matched_names
# ALICE
# DAVE
# EVE
method_missing
NoMethodErrorに慣れ親しんでいるが、これはmethod_missingというメソッドが実行されている
これをオーバーライドすると、存在しないメソッドも呼び出せてしまう
👻
ゴーストメソッド
無理なことを言われたら、これをやっといてねみたいなもん
存在しないメソッドだが、呼び出された時にmethod_missingのオーバーライドによって定義されるもの。
動的プロキシ
参考
GheeというGemはResourceProxyというサブクラスを定義している
Proxyクラスに継承している
内部的にはHasieも使っている(NoMethodの時にエラーにならないようにしてくれる君)
つまり動的プロキシとは、ゴーストメソッドを捕捉して、他のオブジェクトに転送するオブジェクト
code: ruby
class DynamicHash
def initialize
@hash = {}
end
def method_missing(name, *args, &block)
method_name = name.to_s
if method_name.end_with?('=')
key = method_name.chomp('=')
elsif @hash.key?(method_name)
else
super
end
end
# method_missingを使うなら一緒に実装する
def respond_to_missing?(name, include_private = false)
true
end
end
code: ruby
hash = DynamicHash.new
hash.name = 'John'
hash.age = 25
puts hash.name # "John"
puts hash.age # 25
puts hash.city # NoMethodErrorになる
const_missing
↑と似たような話で、定数の実装がない場合もエラーにさせない仕組み
おまけ
不用意なmethod_missingの使用は、変数として呼び出したつもりの変数すらmethod_missingとして実行してしまう
superを使用してBasicObjectのmethod_missingを呼び出す設計にしないと、無限ループに入ることもあるので注意
ブランクスレート
他にも起こりうるレアなエラーとして、Objectクラスのメソッドを意図せず呼び出してしまうなどがある。
そのためにBasicObjectのような最小限のメソッドしかない「ブランクスレート」を継承するという方法がある。
code: ruby
class BlankSlate
instance_methods.each do |method|
undef_method(method) unless method =~ /^__|^object_id$/
end
end
class MyClass < BlankSlate
def hello
puts "Hello, world!"
end
end
obj = MyClass.new
obj.hello # NoMethodError
可能であれば動的メソッド、ダメならゴーストメソッド