ボクココ

サービス開発を成功させるまでの歩み

Rails コントローラ内の define_method 活用例

ども、@kimihom です。

今日はちょっとした Rails の小ネタ。Ruby にはご存知の通りメタプログラミングというトリッキーな開発方法があって、これが良いだ悪いだの議論が絶えないわけだけども、ちょっとしたことで便利に使える時がある。

今回紹介する define_method は、コードでメソッドを定義できる手法だ。以下のような感じで class 内でメソッド定義できる。

class MyClass

  define_method(:say_hello) do
    puts "hello world."
  end

end

MyClass.new.say_hello #=> hello world.

さてこの define_method だけど、 Rails でもそれなりに便利に使える時があるので活用例をご紹介する。

同じ(似た)実行内容のメソッドを一括定義

例えば Rails でランディングページを作っていた場合、大抵は View の表示だけ変わる、至ってシンプルなコントローラができるはずだ。しかし、その中で例えば “og:title などのメタタグなどは layout 内の @og-title をセットしないといけないので、コントローラ内でそれを定義したい” といったことが出てきたとしよう。今回の記事ではこういうニーズが出てきたっていうことにしておいてほしい。

そん時に define_method を使うとこんな感じでかける。

class LandingPagesController < ApplicationController

  [:welcome, :feature, :terms, :privacy].each do |m|
    define_method(m) do
      @og_title = t("landing.#{m}.og_title")
      # ...
    end
  end

end

このサンプルでの"before_action" に書けばよくね?のツッコミはナシでお願いしたい。 わお、これで View に @og_title を渡すことができた!しかもバッチリとアクション別の文言に変えることができている。

このくらいシンプルな共通処理だったら同じようなメソッド定義がつらつらと書かれているよりも、スッキリして見やすくなるかと思う。こんな感じでちょっとした手間を省く上でも、define_method をチョロっと活用してみることからお薦めしたい。

メタプログラミングを学ぶ

Ruby のメタプログラミングといえば、以下の本が有名だ。当然、私も本書を読んで Ruby の魔力に惹かれたものである。

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

この本を読んだ途端、きっとmethod_missing などの Ruby の魔力を使いたくなるだろう。「オレ、Ruby 使いこなしてるぜ」感が出てくるからだ。しかし、一旦落ち着いてほしい。メタプログラミングを乱用すると、メソッド定義がどうなっているのか、誰も理解することのできない状態 を生みかねない。

Ruby にはインスタンスメソッド一覧を表示してくれる methods という大変便利なメソッドが用意されている。メタプログラミングを乱用すると、methods などの出力にそれらが出てこなくなり、後でどうやって使えば良いのか、そもそもそのメソッドは存在するのかを確認することが困難になる。

なので、メタプログラミングの乱用は自分の趣味の範囲で実験で使ってみて、実際のプロダクションのコードでもちょっとだけ入れてみる程度から始めてみよう。いきなり Rails の Model でメタプログラミングを多用すると確実に痛い目を見ることになる。ここまで言い切れるのはなぜかというと、実際の経験者がここにいるからである。

終わりに

今回は Ruby のメタプログラミングの最初のとっかかりだけ紹介した。

「大いなる力には大いなる責任が伴う」とはよく言ったもので、メタプログラミングを活用すれば一部のコード量が劇的に下げられたり、今まで実現不可能だったほどの柔軟性のあるコードを実現できる。メタプログラミングのトレードオフを理解した上で、確実安全なプログラムを書いていこう。

そしたら、一段階上の Ruby プログラマになることができよう。