どうも。
Rubiestには少なからずブラック・マジシャンが存在するするようです。所謂メタプログラミングのことです。CとかJavaとか、別の言語出身の人だとこのRubyという魔の言語を用いたコードを読むとき、必ずと言っていいほど1度はこの「黒魔術」に悩まされることだと思います。で、チームで開発しているにも関わらずそれを当たり前のように書く奴や書いた後コメントやドキュメント等で一切フォローしない人ってやっぱりいます。糞すぎる。
コードをgrepすりゃ見つかる、なんて考えだとRubyではドツボにはまるかもしれません。期限が近くて切羽詰まっているときにこんなコードに出くわすと、確実に殺意の波動に目覚めます。アホをdisるためにも、アーンド、多少なりともコードを改善するために長々と書きます。
何が問題?
grepしづらい
つまり、影響範囲が絞りにくいことです。当然、特定に時間がかかります。
今探しているメソッドが実は動的に作っているかもしれないって気づいたところで、でもどの部分が動的に、どこで作られているのかを知る必要がありますね?しかもその糞メソッドは1つとは限りません。さあすべて影響範囲内から探し出せる自信がありますか?
メソッド変更時にリスクがでかい
grepしづらいことに通じます。影響範囲が絞りづらいので、大規模な修正中に漏れが出やすいです。大規模サイトの修正担当箇所にこんな爆弾が埋まってたら発狂もんです。
これを避けるには
既存の黒魔術へ適切なコメントを振る。
既にあるものを修正するのはリスクがでかいため、なるべくいじらずに済ませたいもの。ただ今後見る人のためにコメントをそっと振っておいてやるのが上策かと。コメントは呼び出し元。これはevalで動的に定義してる、などと書く感じで。
使うなら場所を限定するか、特定の使い方に限る
例えば必ずmodelの頭に書く、とか動的に生成されるものはそうわかるメソッド名になっている、とか。後から見て理解しやすいようにフォローは必須です。
上級者以外に使わせない。
使うのが上手な人が書けばまだいいですが、テキトーな人がテキトーに書くとわけわかめです。間違いなく。本を出せるレベルの人が、非常に読みやすい使い方をお手本のようなレベルで書くのならまだ分かりますが...それと、もしチームでコーディングルールがあるなら、このことは書いてあったほうが助かります。
実装例
定義側
eval, class_eval, define_method, instance_variable_set
# class_eval class Hoge end Hoge.class_eval {def fuga; "hogefuga"; end}
Hoge.new.fuga # ==> "hogefuga" # eval var0 = 10 var1 = 15 var2 = 20 index = 1 eval("var#{index}") # ==> 15 # define_method class Foo ["pee", "poo", "paa"].each do |word| define_method(word.to_sym) { word } end end Foo.new.poo # ==> "poo" # instance_variable_set class Bar def out @test end end bar = Bar.new bar.out # ==> nil bar.instance_variable_set(:@test, 5) bar.out # ==> 5
ザ・諸悪の根源。書いたら○されても文句は言えない。見かけたら討伐したい。
呼び出し側
send
class Baz def method_1 "method_1" end def method_2 "method_2" end end # というclassに対してsend Baz.new.send(:method_1) # ==> "method_1"
まだましです。メソッド名が直後にわかるのですから。
これが全てじゃないです。ここでは非常に簡単な例を書きました。でも、初めて見た人は戦慄するのではないでしょうか。実際、この時点でかなり探しづらいです。
書き方次第でいくらでも上等な料理にハチミツをぶちまけることが出来ます。
考えるべきこと
便利だから使う、も大事ですが単に便利だからあ~じゃなくて便利だし使うことによるデメリットをメリットが上回っているとか、開発そのものに支障が出ないかをよく考えて問題ないなら使う。そうすれば、別の人がそのコードを触っても大した苦労はしないはずです。
結論、オナニーは1人でやれ
チーム開発で頻繁にこんなNBCを、さも当たり前のように用いる奴は余程の乱交マニアかオナニー依存症の糞野郎だと思ってますが、対策を練れば今後そいつの頭にカラシニコフ銃を突きつける必要はなくなります。
仮にどうしても使いたくなったとしても、一歩下がって考えてみます。すると本当にそんな危険物を取り扱う必要があるのか、検討してみたらそんなことなかったりします。仮にメソッド定義が増えて多少ファイルが長くなったとしてもそこまで悪影響するでしょうか?同じようなメソッドが数十個数百個単位で並ぶことって通常ありえますか?個人的にはNO。それでもDRYは守れるでしょ。Fat Model?純粋にModule分割すれば済む話(それもRails4からconcern使える)。黒魔術じゃないと駄目って、作ろうとしている物が元からそういうテーマのコードならまだしも、一般のシステムにこんなものを採用しなければならないなんて、そもそも設計からしておかしいに決まってます。
それでもどうしても使いたければ、迷惑だから1人でおとなしくローカルで勝手に何か作ってればいいんじゃないかな?
他の言語ではこんな危険なことをしなくても間に合っています。Rubyだとなまじ自由度が高すぎるからこその弊害というべきか。
誰かが書いてるのを見たらこっそりIOCCC(言語は違うけどな!)でも勧めてみたらどうでしょう?
今回の件にかかわらず独りよがりなコードを書いたり無駄に高度な「こんな書き方あんだぞ(ドヤア)」って見せびらかすような真似はただただ面倒で嫌われるだけなので、やらないこと推奨。如何に見やすいコードを書けるかそして分かりやすい設計にするかって超絶大事です。
ところで、後で思い返してみたらJavaにもリフレクションっていう機能があったことを思い出しましたが、まぁ同じことですね。使わないにこしたことはないです。