黑客ActiveRecord:添加全局命名范围

我试图为ActiveRecord模型提供一组非常通用的命名范围,如下所示:

module Scopes def self.included(base) base.class_eval do named_scope :not_older_than, lambda {|interval| {:conditions => ["#{table_name}.created_at >= ?", interval.ago] } end end end ActiveRecord::Base.send(:include, Scopes) class User < ActiveRecord::Base end 

如果命名范围应该是通用的,我们需要指定* table_name *以防止命名问题,如果它们是来自其他链式命名范围的连接。

问题是我们无法获取table_name,因为它在ActiveRecord :: Base上调用,而不是在User上调用。

 User.not_older_than(1.week) NoMethodError: undefined method `abstract_class?' for Object:Class from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2207:in `class_of_active_record_descendant' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1462:in `base_class' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1138:in `reset_table_name' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1134:in `table_name' from /home/bogdan/makabu/railsware/startwire/repository/lib/core_ext/active_record/base.rb:15:in `included' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:92:in `call' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:92:in `named_scope' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:97:in `call' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:97:in `not_older_than' 

如何在Scopes模块中获取实际的table_name?

Rails 5,ApplicationRecord(希望对其他人有帮助)

 # app/models/concerns/not_older_than.rb module NotOlderThan extend ActiveSupport::Concern included do scope :not_older_than, -> (time, table = self.table_name){ where("#{table}.created_at >= ?", time.ago) } end end # app/models/application_record.rb class ApplicationRecord < ActiveRecord::Base self.abstract_class = true include NotOlderThan end # app/models/user.rb class User < ApplicationRecord # Code end # Usage User.not_older_than(1.week) 

在Rails 5中,默认情况下所有模型都inheritance自ApplicationRecord。 如果您只想将此范围应用于特定模型集,请仅将include语句添加到这些模型类。 这适用于连接查询和链接范围。

尝试在ActiveRecord :: Base的类方法中使用#scoped方法。 这应该工作:

 module Scopes def self.included(base) base.class_eval do def self.not_older_than(interval) scoped(:conditions => ["#{table_name}.created_at > ?", interval.ago]) end end end end ActiveRecord::Base.send(:include, Scopes) 

其他有用的范围如下:

 module Scopes def self.included(base) base.class_eval do def self.created(date_start, date_end = nil) if date_start && date_end scoped(:conditions => ["#{table_name}.created_at >= ? AND #{table_name}.created_at <= ?", date_start, date_end]) elsif date_start scoped(:conditions => ["#{table_name}.created_at >= ?", date_start]) end end def self.updated(date_start, date_end = nil) if date_start && date_end scoped(:conditions => ["#{table_name}.updated_at >= ? AND #{table_name}.updated_at <= ?", date_start, date_end]) elsif date_start scoped(:conditions => ["#{table_name}.updated_at >= ?", date_start]) end end end end end ActiveRecord::Base.send(:include, Scopes) 

这是一个更新的Rails4兼容解决方案。
我被告知定义这样的全球范围可能会导致冲突, 警告和所有这些,但有时你只需要在所有模型上使用简单的范围,对吧?

定义一个模块。

 # in /app/models/concerns/global_scopes.rb module GlobalScopes def self.included(base) base.class_eval do def self.in_daterange(start_date, end_date) all.where(created_at: start_date.to_date.beginning_of_day..end_date.to_date.end_of_day) end end end end 

将模块包含在ActiveRecord::Base

 # in /config/initializers/activerecord.rb ActiveRecord::Base.send(:include, GlobalScopes) 

而已! 请注意,在Rails4中,您不必使用:scoped,而是使用:all并将查询链接到它。