2
1

More than 3 years have passed since last update.

Railsのscopeを読む

Posted at

Railsのscopeのソースコードを読んでいきます。

u = User.new(name: 'sample name')

scopeを読んでいくために、上記のようなオブジェクトをサンプルとして作成します。

    1: class User < ApplicationRecord
    2: 
    3: binding.pry
 => 4:   scope :recent, -> { order(id: :desc).limit(5) }
    5: end

中を読んでいきます。


    165:         # We are able to call the methods like this:
    166:         #
    167:         #   Article.published.featured.latest_article
    168:         #   Article.featured.titles
    169:         def scope(name, body, &block)
 => 170:           unless body.respond_to?(:call)
    171:             raise ArgumentError, "The scope body needs to be callable."
    172:           end
    173: 
    174:           if dangerous_class_method?(name)
    175:             raise ArgumentError, "You tried to define a scope named \"#{name}\" " \

bodyがcallを呼べなかったら、例外を返す。
今はbodyがcallを呼べるので次へ。

    169:         def scope(name, body, &block)
    170:           unless body.respond_to?(:call)
    171:             raise ArgumentError, "The scope body needs to be callable."
    172:           end
    173: 
 => 174:           if dangerous_class_method?(name)
    175:             raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
    176:               "on the model \"#{self.name}\", but Active Record already defined " \
    177:               "a class method with the same name."
    178:           end
    179: 

nameがクラスメソッドとして定義されていないか判定。
今は定義されていないので次へ。

    175:             raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
    176:               "on the model \"#{self.name}\", but Active Record already defined " \
    177:               "a class method with the same name."
    178:           end
    179: 
 => 180:           if method_defined_within?(name, Relation)
    181:             raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
    182:               "on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \
    183:               "an instance method with the same name."
    184:           end
    185: 

nameがインスタンスメソッドとして定義されていないか判定。
今は定義されていないので次へ。

    181:             raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
    182:               "on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \
    183:               "an instance method with the same name."
    184:           end
    185: 
 => 186:           valid_scope_name?(name)
    187:           extension = Module.new(&block) if block
    188: 
    189:           if body.respond_to?(:to_proc)
    190:             singleton_class.define_method(name) do |*args|
    191:               scope = all._exec_scope(name, *args, &body)

scopeの名前として有効かどうか判定する。nameがメソッドとして定義されていないので、次へ。

    182:               "on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \
    183:               "an instance method with the same name."
    184:           end
    185: 
    186:           valid_scope_name?(name)
 => 187:           extension = Module.new(&block) if block
    188: 
    189:           if body.respond_to?(:to_proc)
    190:             singleton_class.define_method(name) do |*args|
    191:               scope = all._exec_scope(name, *args, &body)
    192:               scope = scope.extending(extension) if extension

blockが存在しているなら、blockを引数としたてModuleを新しく作る。今回はblockが存在しないので、次へ。

    184:           end
    185: 
    186:           valid_scope_name?(name)
    187:           extension = Module.new(&block) if block
    188: 
 => 189:           if body.respond_to?(:to_proc)
    190:             singleton_class.define_method(name) do |*args|
    191:               scope = all._exec_scope(name, *args, &body)
    192:               scope = scope.extending(extension) if extension
    193:               scope
    194:             end

bodyがto_procメソッドをを呼べるか判定。呼べるので、if分の中へ。

    185: 
    186:           valid_scope_name?(name)
    187:           extension = Module.new(&block) if block
    188: 
    189:           if body.respond_to?(:to_proc)
 => 190:             singleton_class.define_method(name) do |*args|
    191:               scope = all._exec_scope(name, *args, &body)
    192:               scope = scope.extending(extension) if extension
    193:               scope
    194:             end
    195:           else

bodyで与えられた処理を実装する、nameというクラスメソッドを定義する。

    198:               scope = scope.extending(extension) if extension
    199:               scope
    200:             end
    201:           end
    202: 
 => 203:           generate_relation_method(name)
    204:         end
2
1
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
2
1