Rubyに慣れてきたので、メタプログラミングRuby第2版を読んでみた

こんにちは、YOUTRUSTの今井(YOUTRUST/X)です。 今回は私が最近読んでいる「メタプログラミングRuby第2版」の感想と実際にYOUTRUSTでメタプログラミングが使われている箇所の紹介をしようと思います。

メタプログラミングとは

メタプログラミングとは、コードを記述するコードを記述することである。

書籍の導入でメタプログラミングについて、上記のような説明がされています。

具体例として、Active Recordでもメタプログラミングが使用されています。

class Movie < ActiveRecord::Base
end

上記のようにActiveRecord::Baseのサブクラスを作っただけで、以下のことをメタプログラミングがやってくれます。

  • 名前がmoviesのテーブルにマッピング
  • オブジェクトのattributeにアクセスするtitle=,titleのようなメソッドを自動的に定義

つまり、以下のようなコードが記述できるようになります。

movie = Movie.create
movie.title = "博士の異常な愛情"
movie.title # => "博士の異常な愛情"

上記のコードは、moviesテーブルのレコードをラップしたMovieオブジェクトを生成し、titleフィールドにアクセスするMovie#titleMovie#title=を呼び出しています。 しかしこれらのメソッドはソースコードのどこにも見当たりません。

このメソッドの定義をメタプログラミングがやってくれていました。

つまり、ActiveRecord::Baseが実行時にテーブルのスキーマを読み取り、moviesテーブルにtitleというカラムがあることを見つけ、Movie#titleMovie#title=といったメソッドをこっそり定義してくれていたのです。

つまり、メタプログラミングをもっと厳密に定義すると、以下のように定義できると説明されています。

メタプログラミングとは、言語要素を実行時に操作するコードを記述することである。

Active Recordは、クラスのattributeごとにアクセサメソッドを書くのではなく、ActiveRecord::Baseを継承するだけで、実行時にアクセサメソッドが定義されるようなコードを書いています。 「コードを記述するコードを記述」してくれていたのです。

メタプログラミングによって、コードの重複を避け、より簡潔なコードを書くことができていることがわかります。

YOUTRUSTではどのように使われているのか

実際にYOUTRUSTでメタプログラミングを使用されている箇所を探してみました。

module FeedContent
  extend ActiveSupport::Concern

  module ClassMethods
    def feed_item_content(display_type:, publisher_method_name: :user)
      has_one :feed_item, as: :content, dependent: :restrict_with_exception

      alias_method :original_create_feed_item!, :create_feed_item!

      define_method :default_feed_item_hash do
        { display_type: display_type, publisher: public_send(publisher_method_name) }
      end

      define_method :create_feed_item! do |arg = {}|
        original_create_feed_item!(arg.merge(default_feed_item_hash))
      end
    end
  end
end

上記のFeedContentモジュールは、YOUTRUST のタイムラインで表示する各フィードのクラスで使用しています。 YOUTRUSTではタイムラインで複数の種類のフィードを表示しています。

  • 一般投稿 Postモデル
  • コミュニティ内の投稿 CommunityPostモデル
  • 募集投稿 RecruitmentPostモデル etc.

この時、各フィードで共通して使用するメソッドを、FeedContentモジュール内で定義しています。

モジュール内で使用されているdefine_methodは、メソッドをその場で定義することができるメソッドです。 上記のコードによってdefault_feed_item_hashメソッドやcreate_feed_item!メソッドは、FeedContentモジュールをincludeした各クラスのインスタンスメソッドとして定義されます。

docs.ruby-lang.org

各フィードのクラスでcreate_feed_item!メソッドを定義するのではなく、FeedContentモジュールをincludeするだけで、実行時にcreate_feed_item!メソッドを定義してくれます。

以下のようにコードを記述することが可能になります。

post = Post.new
post.create_feed_item! # create_feed_item!に渡す引数も動的に定義している

community_post = CommunityPost.new
community_post.create_feed_item!

recruitment_post = RecruitmentPost.new
recruitment_post.create_feed_item!

最後に

今回「メタプログラミングRuby第2版」を読んだことで、これまでRubyでなんとなく書いていたコードの仕組みを理解することができました。特にメソッドを呼び出すときに何が起きているのか、丁寧に説明がされていてRubyの謎(少し大袈裟な言い方ですが)がよく分かりました。

メタプログラミングRuby第2版」はRubyに慣れてきて、さらに深く理解したい人におすすめの書籍だと思います。 www.oreilly.co.jp

最後まで読んでいただきありがとうございます! 今回YOUTRUSTの一部コードを紹介しましたが、弊社ではOPEN CODEというYOUTRUSTの本番コードをNGなしでお見せするイベントを定期的に開催しております。 もし今回のブログで少しでもYOUTRUSTに興味を持っていただいた方は、ぜひご参加ください。

過去のイベントレポートがこちらです!

tech.youtrust.co.jp

絶賛採用も強化中です!

career.youtrust.co.jp