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

jjym · 发布于 2012年08月15日 · 最后由 jjym 回复于 2012年08月19日 · 3558 次阅读
2622

::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是在哪里

共收到 16 条回复
De6df3

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

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

162

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

2622

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

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

8

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

2622

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

96

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

2622

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

96

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

96

我记得在::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类代为处理

2622

@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的代码,却找不到他定义在哪里!求各位看过这里源码的指导下!

2622

#11楼 @hhuai

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

96

#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
2622

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

96

@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中

2622

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

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


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

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