Rails 源码中 ActiveRelation::Base 的 all 方法 (?) 定义在哪里???

jjym · 2012年08月15日 · 最后由 jjym 回复于 2012年08月19日 · 5528 次阅读

::ActiveRecord::Base.send :include, Kaminari::ActiveRecordExtension` 在 hooks.rb 中看到这行代码

在 AR::Base 里定义一个 page(默认)scope(scope 里的块是干嘛的?Rails API 没有很详细的说明。是在调用 scope 后执行的?),这样 page 后的对象也是 include 了分页的模块。

但是我找了半天就是找不到 AR::Relation 在哪里 include 了 Model.where(xxx).page 是确实有这个方法,而且 Relation 也 include 了 Kaminari 的模块,但是我找了一个小时没有看到在哪里。。求指点

简单来说

1,我看到 AR::Base include 了 2,我没看到 AR::Relation include 3,AR::Relation 的确 可以调用 page 4,我想知道这是怎么做到的


这个问题已经由@xxx(名字忘了)解决。现在的问题是 Base 中的 all 是定义在哪里的?? 我用 Base.method :all 看是定义在 querying.rb 中,但是 querying.rb 中一堆代理到 all 的,却诶有定义 all,求高手指点下 all 是在哪里

据我的理解,include 是 Ruby 的方法,用于给类实现多继承的

Class.send :include, NewModule 这种写法时常用在 Gem 对某些类做 hack 的场景

ActiveRecord::Base => ActiveRecord::Model => ActiveRecord::Core private relation method

不知道我理解错你们的意思了,还是你们理解错我的意思了

我是找不到 AR::Relation 在哪里 include

真的没看懂你在说什么...

又检查了下,好像和 ActiveRecord 的延迟执行有关,应该 page 最后还是在 AR::Base 上调用的,所以 AR::Relation 可以不定义。看源码求证下...

#4 楼 @hooopo 我也没看懂.... 我猜猜楼主是在找 lib/kaminari/models/active_record_model_extension.rb , lib/kaminari/config.rb里面定义的page方法?

好吧,我表达能力有问题,我是想问为什么 AR::Relation 中可以调用 page 方法?因为我没有找到 AR::Relation 在哪里 include 模块了 我知道调用 page 这个 scope 后的 Relation 会 extending 模块 但是比如 User.where(xx).page 这个是怎么做到的??

::ActiveRecord::Base.send :include, Kaminari::ActiveRecordExtension 我的理解是 ActiveRecord include 了 Kaminari 所以就能使调用 page 这个方法。而 ActiveRecord::Relation 只是 ActiveRecord::Base 内部的中间层。所以并不需要在插件去 include ActiveRecord::Relation。

我记得在::ActiveRecord::Base 定义的 class method 是可以在 ActiveRecord::Relation 下面调用的,估计 Kaminari 要做的事情应该是这样:

class User
  def self.page(number = 0)
    offset(number * DEFAULT_PER_PAGE).limit(DEFAULT_PER_PAGE)
  end
end

User.order(:first_name).page

上面那段代码中,User.order(:first_name) 返回的是一个 ActiveRecord::Relation 的对象,但是它还是可以调用User#page

具体原因不清楚,但最简单的实现就是在实例化 ActiveRecord::Relation 的时候把 User 传进去,然后如果在 ActiveRecord::Relation 里面找不到对应的 scope 或者方法,就让 User 类代为处理

@leomao10 正解 relation 中初始化有这个

relation.rb
def initialize(klass, table, values = {})
      @klass             = klass
      @table             = table
      @values            = values
      @implicit_readonly = nil
      @loaded            = false
      @default_scoped    = false
    end

这个是 relation include 的 delegate 模块中的代码

def method_missing(method, *args, &block)
      if @klass.respond_to?(method)
        ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
        scoping { @klass.send(method, *args, &block) }
      elsif Array.method_defined?(method)
        ::ActiveRecord::Delegation.delegate method, :to => :to_a
        to_a.send(method, *args, &block)
      elsif arel.respond_to?(method)
        ::ActiveRecord::Delegation.delegate method, :to => :arel
        arel.send(method, *args, &block)
      else
        super
      end
    end

这个是 Base include 的 core 模块的代码,其中传了 self 作为 Relation 实例的 klass 并且,relation 在 method_missing 中调用,这样可以解释为何只在 Base 里 include 模块即可

def relation #:nodoc:
        relation = Relation.new(self, arel_table)

        if finder_needs_type_condition?
          relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
        else
          relation
        end
      end

但是有个很严重的问题!!!我找了一下午不知道 relaiton 是在哪里调用的!! Base 的查询方法都在 querying 中

delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :all
    delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :all
    delegate :find_by, :find_by!, :to => :all
    delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :all
    delegate :find_each, :find_in_batches, :to => :all
    delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
             :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
             :having, :create_with, :uniq, :references, :none, :to => :all
    delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :all

但是我怎么找都找不到 all 在哪里,用屁股想的话会知道 all 内调用了 relation 方法,但是我想看 all 的代码,却找不到他定义在哪里!求各位看过这里源码的指导下!

#11 楼 @hhuai

这个是 Relation 的我知道 AR::Base 是在哪里定义或 include 的真心找不到。。

#12 楼 @jjym 你是什么版 本啊,我这是 delegate 到 scoped, scoped 再对应到 relation 中。

delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
    delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped
    delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
    delegate :find_each, :find_in_batches, :to => :scoped
    delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
             :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
             :having, :create_with, :uniq, :to => :scoped
# File activerecord/lib/active_record/scoping/named.rb, line 30
def scoped(options = nil)
  if options
    scoped.apply_finder_options(options)
  else
    if current_scope
      current_scope.clone
    else
      scope = relation.clone
      scope.default_scoped = true
      scope
    end
  end
end

#13 楼 @hhuai 我这 3.2.3 和 3.2.8 都是 all。github 上也是 all...

@jjym 你上面找到你那段代码应该是在 Master branch 的,现在 rails master branch 应该是 4.0 不是 3.2.8

3.2.8 的代码是在这里: https://github.com/rails/rails/blob/v3.2.8/activerecord/lib/active_record/querying.rb

@hhuai 说的是对的,基本和 scope 有关的方法都 delegate 到 scoped, scoped 再对应到 relation 中

#15 楼 @leomao10 原来如此啊。。@hhuai thk 两位

不过我本机是 3.2.3,用 pry 代码也是 all..不知为何


顺便能不能问下 4.0 的 all 是在哪?

需要 登录 后方可回复, 如果你还没有账号请 注册新账号