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

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

More than 3 years have passed since last update.

@yukihirop

class Order < ActiveRecord::Base; end;が実行されると何が起こるのか?

この記事は、2017/09/30(土)に書きました。

はじめに

Orderは適当な名前です。なんでもいいです。
railsを使ったことがあるひとなら、ActiveRecord::Base を継承したモデルをつくったことが必ずあると思いますが、モデルを作った時に何が行われているかは普段気にしないとおもいます。だが、その部分を理解することはActiveRecord::Baseを継承したモデルの理解をする上で重要だと思い今回しっかり追ってみました。

class Order < ActiveRecord::Base
end

これが実行されると何が行われるのか?

diffを強調のニュアンスで使っています。

動作環境

  • rails 5.1.0
  • ruby 2.4.1p11



ActiveRecord::Baseクラスを継承するだけで実行されるメソッドがある?

ActiveRecord::Baseクラスの中でDelegatte::DelegateCacheがextendされている。
[activerecord-5.1.0/lib/active_record/base.rb]

 class Base
    extend ActiveModel::Naming

    extend ActiveSupport::Benchmarkable
    extend ActiveSupport::DescendantsTracker

    extend ConnectionHandling
    extend QueryCache::ClassMethods
    extend Querying
    extend Translation
    extend DynamicMatchers
    extend Explain
    extend Enum
+   extend Delegate::DelegateCache

つまり、Delegate::DelegateCacheモジュールに定義されているメソッドをクラス・メソッドとして使えるようになる。


どのようなメソッドが実行されるの?(inheritedメソッド)

Delegate::DelegateCache モジュールにrubyのinheritedメソッドがオーバーライドしてある。

[activerecord-5.1.0/lib/active_record/relation/delegation.rb]

def inherited(child_class)
+   child_class.initialize_relation_delegate_cache
    delegate = child_class.relation_delegate_class(ActiveRecord::Associations::CollectionProxy)
    delegate.include ActiveRecord::Associations::CollectionProxy::DelegateExtending
    super
end

# child_class => Order

child_classはOrderクラスを指している。このメソッドはchild_class.initialize_relation_delegate_cache が肝。
これは何をするのかというと、 child_classが所有しているインスタンス変数@@relation_delegate_cacheに以下のようなhashを保持する。

@relation_delegate_cache = {
    ActiveRecord::Relation                      => Order::ActiveRecord_Relation,
    ActiveRecord::Associations::CollectionProxy => Order::ActiveRecord_Associations_CollectionProxy,
    ActiveRecord::AssociationRelation           => Order::ActiveRecordAssociation
}

例えばOrder::ActiveRecord_Relationはどのようなクラスかというと、

  1. ClassSpecificRelationモジュールの持つメソッドをクラス・メソッドに持つ
  2. ActiveRecord::Relationをsuperclassに持つ

という大事な性質のあるクラスである。


動的に生成されるクラス(Order::ActiveRecord_Relation)がどのように生成されるか?

child_class.initialize_relation_delegate_cache が肝だと先程いったのでその中身を見てみると、

[activerecord-5.1.0/lib/active_record/relation/delegation.rb]

def initialize_relation_delegate_cache
    @relation_delegate_cache = cache = {}
    [
        ActiveRecord::Relation,
       ActiveRecord::Associations::CollectionProxy,
       ActiveRecord::AssociationRelation
    ].each do |klass|
+       delegate = Class.new(klass) {
+           include ClassSpecificRelation
+       }

+       mangled_name = klass.name.gsub("::".freeze, "_".freeze)

+       # ここでOrder::ActiveRecord_Relationの中身がdlegateになる
+       const_set mangled_name, delegate
        private_constant mangled_name
        cache[klass] = delegate
    end
end

説明すると、klass=ActiveRecord::Relationの時、このクラスを継承してなおかつ、ClassSpecificReflationモジュールをインクルードしたクラスdelegateを動的に生成し、selfがOrderなのでconst_setでdelegateに名前をつけると、
delegateにOrder::ActiveRecord_Relationという名前がつくわけである。あとは生成したクラス(Order::ActiveRecord_Relation)をキーがActiveRecord::Relationでhashとしてキャッシュしている。
ActiveRecord::Baseが継承されたときだけ1度だけ行われるメソッドなので使いまわすにはキャッシュしておく必要があるのである。

以上のような仕組みでOrder::ActiveRecord_Relationクラスが動的に生成される。

ではこのように動的に生成されたOrder::ActiveRecord_Relationクラスはどのように使われるのか?

一例としては、

Order.where(id:1)のようにメソッドが呼ばれた時、内部的にOrder::ActiveRecord_Relationのインスタンス(relation)が生成され、それに対してwhereが実行されるのである。
※Order.whereとrelation.whereのwhereは別物のメソッドである。

この例は、
Order.where(name: "name_1", description: "description_1")はどのような順番でメソッドが実行されるのか?
で説明します。たぶん。。。。


まとめ

class Order < ActiveRecord::Base; end; が実行されると、Order::ActiveRecord_RelationOrder::ActiveRecord_Associations_CollectionProxyOrder::ActiveRecordAssociationクラスが動的に生成される。例えば、Order::ActiveRecord_Relationクラスのインスタンスは、Order.where(id:1) を語る上で重要なインスタンスである。

Orderクラスがこの3つのクラスをインスタンス変数@relation_delegate_cacheに保持している。

・Order::ActiveRecord_Relation
・Order::ActiveRecord_Associations_CollectionProxy
・Order::ActiveRecordAssociation

以上。

1
Help us understand the problem. What is going on with this article?
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
1
Help us understand the problem. What is going on with this article?