質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.05%

ActiveRecordではwhereが実行された際にはどうやってrelation/query_methodsのwhereが呼ばれるのでしょうか?

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 2,360

score 13

ActiveRecordのwhereの仕組み

ActiveRecordのwhereの仕組みに関して教えてください。ActiveRecordのソースを読み込みpryで動作を止めてひとつひとつ確認しています。

User.where(id: 1)


を実行するとrails/activerecord/lib/active_record/querying.rbのdelegateでwhereがto: allになっています。

delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
         :where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly,
         :having, :create_with, :distinct, :references, :none, :unscope, :merge, to: :all

したがってrails/activerecord/lib/active_record/scoping/named.rb
のallに処理が渡されています。

def all
  if current_scope
   current_scope.clone
  else
    default_scoped
  end
end


ここのdefault_scopeの処理のあと
rails/activerecord/lib/active_record/relation/query_methods.rb
のwhereに処理が行きます。

def where(opts = :chain, *rest)
  if :chain == opts
    WhereChain.new(spawn)
  elsif opts.blank?
    self
  else
    spawn.where!(opts, *rest)
  end
end


これはどのようにして実現しているのでしょうか?つまりどうやってdefault_scopeの処理の後にrelation/query_methods.rbのwhereへ行くようにしているのでしょうか?

最終的にはこちらの記事のThe Beauty of ActiveRecord::RelationにあるようにActiveRecord::Relationを使ったメソッドの数珠つなぎのような物を実装したいと考えております。

ただその前にActiveRecordのwhereが呼ばれた際にwhere->all->where(relation/query_methods)とつなげている仕組みを理解しなければ実現できそうにありません。

ソースのここを読めば分かるかも、といったヒントでも構いませんので教えていただけますでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

coffee_manということはjava使い様でしょうか、javaでrubyという黒魔術を
説明するのは難しいのですが。
delegateは、委譲ですmethodを引数に渡せないjavaで説明するのは難しいので
delegateされているmethodを実行する前にallが実行されるぐらいのイメージでいてください

つまりどうやってdefault_scopeの処理の後にrelation/query_methods.rbのwhereへ行くようにしているのでしょうか?
ではなく、relation/query_methods.rbのwhereへ行く前にdefault_scopeを実行するようにあらかじめ義務付けられているのです。

で、そのあとの処理も書いておきますが
current_scope
ですが、すでに、これにはwhere(id: 1)が入っておりますので
current_scopeにはActiveRecord::Relationを返し
実行されるのはcurrent_scope.cloneです。
で、rubyにおいては、method内で最後に評価された式が戻り値になりますので
current_scope.cloneが戻り値になります。
で、このcloneが曲者で、元のcurrent_scopeと別インスタンスなのですが
attributeは共有しているという…。
※これで、任意のタイミングまでmethodchainしたクエリが自由に発行できるわけですが…
で、各methodでWhereChainなどにストックしていって、最後にarelというライブラリを使って
SQLに変更するのがactive_recordの大まかな流れです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/28 17:47

    mokeさん、ご回答ありがとうございます。ここ数時間、ずっとmokeさんの回答を何度も読んでおります。
    なんとなく「relation/query_methods.rbのwhereへ行く前にdefault_scopeを実行するようにあらかじめ義務付けられている」のあたりの理解が不足していたことが分かりました。

    こちらの[ドキュメント](http://api.rubyonrails.org/classes/Module.html#method-i-delegate)ではFoo.new.helloがGreeterのhelloに委譲されているだけのように見えます。

    仮にFooにhelloメソッドが存在した場合、これをmokeさんのおっしゃるようにFooのhelloへ行く前にGreater内にある処理を実行するように義務付けるにするはどのように書けばいいのでしょうか?
    ActiveRecordで「***へ行く前に***の実行を義務つけ」というのがdelegateで行われているという理解ですが、ここがそもそも間違っているのでしょうか?なんとなくdelegateのような気がするだけで本当の理解にはいたっておりません。

    キャンセル

  • 2017/04/28 18:27 編集

    もちろん、それが、一般的なdelegateなのですが、この場合の
    allはclassでもなんでもなくただの、methodです、コードを読むとわかるのですが
    railsのdelegateはtoの先と合わせた新たなmethodを作ってmodule_evalしてるだけなんですよ。
    引数にmethodを取れ、さらにmethodを定義して実行できるというrubyの静的言語からは
    考えつかないような特殊性が垣間見れます。
    でもこれによって、Rails3までの不便さがなくなっているのでありがたい限りなのですが。
    まあ、気持ち悪いと思う人がいてもおかしくないと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Rubyに関する質問
  • ActiveRecordではwhereが実行された際にはどうやってrelation/query_methodsのwhereが呼ばれるのでしょうか?