Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

メタプログラミングの考えをRubyで使用するときのパターン

More than 5 years have passed since last update.

メタプログラミングとは

「コードを記述するコードを記述すること」と一般的には呼ばれている。

rubyのメソッドである「find」「find_each」「find_in_batches」も抽象化して考えることができる(メタプログラミングかどうか実際のところ、調査不足でわからないが)

どういうときに使用するのか

そもそも・・メタプログラミングすると動的にプログラムを実行することができる。プログラムの一部をパラメーター化することでコードの重複を取り除くこともできる。うまく使えばリーダブルにもなる。がしかし、使い方によっては理解しにくくなったり、メンテナンスしにくくなったりする。

処理を1箇所に局所化できるか、、、言いかえると、
「メタプログラミングでやっていることの詳細を知らなくても使えるくらい抽象化できるか」

どうやって使用していくのか

主に2パターンだと考えてよい

パターン1「モンキーパッチ」
既存のクラスを拡張し上書きすること

class Fixnum
  def +(v)
    self - v
  end
  def *(v)
    self / v
  end
end
puts 42 + 42
puts 42 * 42

パターン2「動的なメソッド定義」
似たような処理を持つメソッド群を、まとめて定義すること
(具体例でコードを出します)

そのときに気をつけるべきポイントは

パターン1
全ての文字列が保持しても問題のない汎用的な機能か見極めることが大事
→意図していない挙動になりバグにつながる

パターン2
method_missing
「未定義メソッドに対しても好きな処理を加えられる」
→バグが起こりやすい、起こっても特定しにくい
→唯一性を考えてメソッドを作成する必要がある

実際の例

パターン1に関してはあまり実行する機会がなさそうなので今回はパターン2のみ
扱います。

<% if request.path=~ /^\/top/ %>
  <%# ITエンジニア %>
  <% if @login_user.exp_job_types == "10" %>
    <% job = engineer %>
  <%# デザイナー %>
  <% else @login_user.exp_job_types == "20" %>
    <% job = designer %>
  <% end %>
<% end %>
  #経験職種(大分類)
  def experienced_job?(login_user,type)
    login_user.exp_job_types.present? && login_user.exp_job_types == number
  end
@login_user.experienced_job?(@login_user,JobType::IT_ENGINEER)

rubyは引数でどうこうする言語ではない。
つまり今回experienced_parent_job_engineer?とかexperienced_parent_job_creative_web?とかってメソッドがそれぞれ別個で存在するように作っているのはRuby的には違和感がない。。が・・・今後もこの形のメソッドを作成する可能性は十二分にあることはなんとなくわかる。また同じメソッドがいくつもあるのには違和感がある→メタプログラミングだっ!

   def experienced_job?(number)
      exp_job_types.present? && exp_job_types == number
    end

    def method_missing(method_name,*args)
      #正規表現でメソッド呼びだし
      method_name.to_s.match(/^experienced_parent_job_(\w+)\?$/) do|ms|
        super unless JobType.const_defined?("#{ms[1].upcase}")
        return experienced_job?(eval"JobType::" + ms[1].upcase)
      end
      super
    end

おまけ(マッチした複数の文字列を取得)

よく見かけるのは正規表現オブジェクトのパターンにマッチした文字列を取得するというもの。ただ1つのパターンをいくつかに分割し、それぞれの部分にマッチした文字列を取得することも可能であり、このパターンのものがメタプログラミングのときにはよく目につく。

def method_missing(method_name, *args)
    if /^(.+)_with_save(\!?)$/.match(method_name)
      if defined? $1
        send($1,*args)
        if $2.blank?
          return self.save
        else
          return self.save!
        end
      end
    end
・
・
・
・
end

マッチすると括弧の数だけ変数「\$1」や変数「\$2」に部分文字列が代入されていきます。順番はパターンの中でマッチ箇所が現れた順となります。

katsuyuki
4月から4年目をむかえました
http://katsuyukikun.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away