::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
又检查了下,好像和 ActiveRecord 的延迟执行有关,应该 page 最后还是在 AR::Base 上调用的,所以 AR::Relation 可以不定义。看源码求证下...
好吧,我表达能力有问题,我是想问为什么 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 的代码,却找不到他定义在哪里!求各位看过这里源码的指导下!
#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
@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 中