aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/scoping/named.rb
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2012-03-21 22:18:18 +0000
committerJon Leighton <j@jonathanleighton.com>2012-03-21 22:18:18 +0000
commit0a12a5f8169685915cbb7bf4d0a7bb482f7f2fd2 (patch)
tree77ac1032a9ff780fbf3ac92549b0a9de84fbdcba /activerecord/lib/active_record/scoping/named.rb
parentfd68bd23b602ef2a7b038b66e787604df9192c6d (diff)
downloadrails-0a12a5f8169685915cbb7bf4d0a7bb482f7f2fd2.tar.gz
rails-0a12a5f8169685915cbb7bf4d0a7bb482f7f2fd2.tar.bz2
rails-0a12a5f8169685915cbb7bf4d0a7bb482f7f2fd2.zip
Deprecate eager-evaluated scopes.
Don't use this: scope :red, where(color: 'red') default_scope where(color: 'red') Use this: scope :red, -> { where(color: 'red') } default_scope { where(color: 'red') } The former has numerous issues. It is a common newbie gotcha to do the following: scope :recent, where(published_at: Time.now - 2.weeks) Or a more subtle variant: scope :recent, -> { where(published_at: Time.now - 2.weeks) } scope :recent_red, recent.where(color: 'red') Eager scopes are also very complex to implement within Active Record, and there are still bugs. For example, the following does not do what you expect: scope :remove_conditions, except(:where) where(...).remove_conditions # => still has conditions
Diffstat (limited to 'activerecord/lib/active_record/scoping/named.rb')
-rw-r--r--activerecord/lib/active_record/scoping/named.rb18
1 files changed, 16 insertions, 2 deletions
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 2f266eff79..077e2d067e 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -3,6 +3,7 @@ require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/class/attribute'
+require 'active_support/deprecation'
module ActiveRecord
# = Active Record Named \Scopes
@@ -171,11 +172,24 @@ module ActiveRecord
# Article.published.featured.latest_article
# Article.featured.titles
- def scope(name, scope_options = {}, &block)
+ def scope(name, body = {}, &block)
extension = Module.new(&block) if block
+ # Check body.is_a?(Relation) to prevent the relation actually being
+ # loaded by respond_to?
+ if body.is_a?(Relation) || !body.respond_to?(:call)
+ ActiveSupport::Deprecation.warn(
+ "Using #scope without passing a callable object is deprecated. For " \
+ "example `scope :red, where(color: 'red')` should be changed to " \
+ "`scope :red, -> { where(color: 'red') }`. There are numerous gotchas " \
+ "in the former usage and it makes the implementation more complicated " \
+ "and buggy. (If you prefer, you can just define a class method named " \
+ "`self.red`.)"
+ )
+ end
+
singleton_class.send(:define_method, name) do |*args|
- options = scope_options.respond_to?(:call) ? unscoped { scope_options.call(*args) } : scope_options
+ options = body.respond_to?(:call) ? unscoped { body.call(*args) } : body
options = scoped.apply_finder_options(options) if options.is_a?(Hash)
relation = scoped.merge(options)