我正在尝试为这样的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?
答案 0 :(得分:14)
尝试在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)
答案 1 :(得分:4)
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中,默认情况下所有模型都从ApplicationRecord继承。如果您只想将此范围应用于特定模型集,请仅将include语句添加到这些模型类。这适用于连接查询和链接范围。
答案 2 :(得分:1)
以下其他有用范围:
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)
答案 3 :(得分:1)
这是一个更新的 Rails4 兼容解决方案 我被告知定义像这样的全球范围可能会导致冲突,告诫emptor 以及所有这些,但有时你只需要在所有模型上使用简单范围,对吧?
定义模块。
# 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并将查询链接到它。