0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Active Recordの設計 ― メタプログラミングRubyで読み解くORMの仕組み

0
Last updated at Posted at 2026-03-22

Active Recordの設計

概要

この記事は、「メタプログラミングRuby 第2版」第9章「Active Recordの設計」を読み学習した内容を個人学習用にまとめ直したものです。

Active RecordはRailsの中核をなすORMであり、その内部はメタプログラミングの技法で満ちています。本記事では、オートローディング、ActiveRecord::Baseのモジュール構成、ゴーストメソッドによるアトリビュートアクセスなど、メタプログラミングの観点からActive Recordの内部設計を読み解きます。

Active Recordとは

Active Recordは、Railsの中核をなすORM(Object-Relational Mapping)ライブラリ。

Railsを経由せずに、Active Record単体で利用するためには以下のようにrequireしてデータベースとのコネクションを開く必要がある。

require 'active_record'

# sqlite3のgemを先にインストールしておくこと
ActiveRecord::Base.establish_connection :adapter => "sqlite3",
                                        :database => "dbfile"

# railsアプリケーションでは自動的に設定される

そして、Active Recordの規約に従っていれば、自動的に以下のようなDuckクラスをducksテーブルにマッピングしてくれる。

さらに、DBスキーマでducksテーブルにnameが含まれることを発見し、そのアトリビュートにアクセスするゴーストメソッドを定義してくれる。

ActiveRecordのクラス
# 全てのマッピングクラスはActiveRecord::Baseを継承する
class Duck < ActiveRecord::Base
  validate do # ブロックを受け取るクラスマクロ
    errors.add(:base, "Illegal duck name.") unless name[0] == 'D'
  end
end

my_duck = Duck.new
my_duck.name = "Donald" # nameはアトリビュートにアクセスするゴーストメソッド
my_duck.valid? # => true
my_duck.save!

Active Recordの仕組み

Active Recordの実装とオートローディング

前項のコード例における、require 'active_record'によって読み込まれるActiveRecordの内容は以下のようになっている。

require 'active_support'
require 'active_model'
# ...

module ActiveRecord
  extend ActiveSupport::Autoload # extendしてクラスメソッドを利用可能なように拡張している

  # autoloadはActiveSupport::Autoloadのクラスマクロ
  # モジュール名を最初に使った際に、自動的にモジュール・クラスのソースを探索しrequireする
  autoload :Base 
  autoload :NoTouching
  autoload :Persistence
  autoload :QueryCache
  autoload :Querying
  autoload :Validations
  # ...

ActiveRecord::Base

ActiveRecordにおいてautoload :Baseによって読み込まれるActiveRecord::Baseの内容は以下のようになっている。

Baseクラスは、多数のモジュールをinclude/extendすることで機能を組み立てている。各モジュールが永続化・バリデーション・コールバック・アソシエーションといった個別の責務を担い、それらを動的に合成することでBaseクラスの豊富な機能が実現されている。

ActiveRecordで使用されているautoloadにより、include Persistenceextend Queryingで定数が参照された時点で対応するソースファイルが自動的にrequireされるため、Baseクラスはinclude/extendの宣言だけで簡潔に構成できている。

module ActiveRecord
  class Base
    include ActiveModel::API

    extend ActiveSupport::Benchmarkable
    extend ActiveSupport::DescendantsTracker
    extend ConnectionHandling
    extend QueryCache::ClassMethods
    extend Querying
    extend Translation
    extend DynamicMatchers
    extend DelegatedType
    extend Explain
    extend Enum
    extend Delegation::DelegateCache
    extend Aggregations::ClassMethods

    include Core
    include Persistence
    include ReadonlyAttributes
    include ModelSchema
    include Inheritance
    include Scoping
    include Sanitization
    include AttributeAssignment
    include Integration
    include Validations
    include CounterCache
    include Attributes
    include Locking::Optimistic
    include Locking::Pessimistic
    include Encryption::EncryptableRecord
    include AttributeMethods
    include Callbacks
    include Timestamp
    include Associations
    include SecurePassword
    include AutosaveAssociation
    include NestedAttributes
    include Transactions
    include TouchLater
    include NoTouching
    include Reflection
    include Serialization
    include Store
    include SecureToken
    include TokenFor
    include SignedId
    include Suppressor
    include Marshalling::Methods

    self.param_delimiter = "_"
  end

  # Baseの読み込み完了を通知し、外部のgemやinitializerがBaseを拡張できるようにする
  # これにより、上記でincludeされた各モジュールの振る舞いも拡張・上書きが可能になる
  ActiveSupport.run_load_hooks(:active_record, Base)
end

このように、ActiveRecord::Baseextendincludeで多数のモジュールを取り込んでいる。

前項のコード例でDuckクラスがActiveRecord::Baseを継承した瞬間に、これらすべてのモジュールの機能が利用可能になる。

例えば、save!メソッドはPersistenceモジュールから、nameのようなゴーストメソッドはAttributeMethodsモジュールから提供されている。

Validationsモジュール

バリデーション機能はActiveModel::ValidationsActiveRecord::Validationsの2つのモジュールから責務を分けてincludeされている。

  • ActiveModel::Validationsinclude ActiveModel::API経由): validateクラスマクロやvalid?の基本実装など、オブジェクトモデルとしてのバリデーション基盤を提供。DBに依存しない汎用的なバリデーションの仕組みを担当
  • ActiveRecord::Validationsinclude Validations): valid?をオーバーライドして:create/:updateのコンテキスト判定を追加し、save/save!時に自動でバリデーションを実行するなど、保存や読み込みなどのDB操作に関連するバリデーション拡張を担当

ActiveRecordの各モジュールの再利用性

ActiveRecord::Baseは何百ものメソッドをまとめた巨大なクラスだが、疎結合、テスト容易性、モジュールの再利用性を組み合わせて、実行時にクラスが構成されるような設計となっている。

例えば、妥当性確認の機能を利用する必要があれば、ActiveRecord::Baseや他のモジュールを無視して、以下のようにActiveModel::Validationsをインクルードすれば良い。

require 'active_model'

class User
  include ActiveModel::Validations

  attr_accesor :password

  validate do
    errors.add(:base, "Don't let choose the password") if password == '1234'
  end
end

user = User.new
user.password = "12345"
user.valid? # => true


user.password = "1234"
user.valid? # => false

まとめ

  • Active RecordはRailsの中核をなすORMで、規約に従えばクラスとテーブルを自動マッピングする
  • ActiveRecord::Baseextend/includeで多数のモジュールを合成し、永続化・バリデーション・コールバック等の機能を実現している。オートローディングによりrequire不要で宣言だけで構成できている
  • DBスキーマからカラム情報を読み取り、ゴーストメソッドで動的にアトリビュートアクセサを提供する
  • 各モジュールは疎結合に設計されており、ActiveRecord::Baseを経由せず個別にincludeして再利用できる

参考文献

この記事は以下の情報を参考にして執筆しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?