Revert "Deprecate defining scopes with a callable (lambda, proc, etc) via the scope class method. Just define a class method yourself instead."

This reverts commit f0e198bfa1.

Conflicts:

	activerecord/test/models/post.rb
This commit is contained in:
Jon Leighton
2011-04-17 20:47:52 +01:00
parent d1f10e74ca
commit 256b363eee
6 changed files with 23 additions and 114 deletions

View File

@@ -1,23 +1,5 @@
*Rails 3.1.0 (unreleased)* *Rails 3.1.0 (unreleased)*
* Passing a proc (or other object that responds to #call) to scope is deprecated. If you need your
scope to be lazily evaluated, or takes parameters, please define it as a normal class method
instead. For example, change this:
class Post < ActiveRecord::Base
scope :unpublished, lambda { where('published_at > ?', Time.now) }
end
To this:
class Post < ActiveRecord::Base
def self.unpublished
where('published_at > ?', Time.now)
end
end
[Jon Leighton]
* Default scopes are now evaluated at the latest possible moment, to avoid problems where * Default scopes are now evaluated at the latest possible moment, to avoid problems where
scopes would be created which would implicitly contain the default scope, which would then scopes would be created which would implicitly contain the default scope, which would then
be impossible to get rid of via Model.unscoped. be impossible to get rid of via Model.unscoped.

View File

@@ -51,14 +51,6 @@ module ActiveRecord
# The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
# in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>. # in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
# #
# Note that this is simply 'syntactic sugar' for defining an actual class method:
#
# class Shirt < ActiveRecord::Base
# def self.red
# where(:color => 'red')
# end
# end
#
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
# resembles the association object constructed by a <tt>has_many</tt> declaration. For instance, # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
# you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>. # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
@@ -82,34 +74,14 @@ module ActiveRecord
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
# only shirts. # only shirts.
# #
# If you need to pass parameters to a scope, define it as a normal method: # Named \scopes can also be procedural:
# #
# class Shirt < ActiveRecord::Base # class Shirt < ActiveRecord::Base
# def self.colored(color) # scope :colored, lambda {|color| where(:color => color) }
# where(:color => color)
# end
# end # end
# #
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts. # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
# #
# Note that scopes defined with \scope will be evaluated when they are defined, rather than
# when they are used. For example, the following would be incorrect:
#
# class Post < ActiveRecord::Base
# scope :recent, where('published_at >= ?', Time.now - 1.week)
# end
#
# The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt>
# class was defined, and so the resultant SQL query would always be the same. The correct
# way to do this would be via a class method, which will re-evaluate the scope each time
# it is called:
#
# class Post < ActiveRecord::Base
# def self.recent
# where('published_at >= ?', Time.now - 1.week)
# end
# end
#
# Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations: # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
# #
# class Shirt < ActiveRecord::Base # class Shirt < ActiveRecord::Base
@@ -120,18 +92,6 @@ module ActiveRecord
# end # end
# end # end
# #
# The above could also be written as a class method like so:
#
# class Shirt < ActiveRecord::Base
# def self.red
# where(:color => 'red').extending do
# def dom_id
# 'red_shirts'
# end
# end
# end
# end
#
# Scopes can also be used while creating/building a record. # Scopes can also be used while creating/building a record.
# #
# class Article < ActiveRecord::Base # class Article < ActiveRecord::Base
@@ -168,24 +128,6 @@ module ActiveRecord
valid_scope_name?(name) valid_scope_name?(name)
extension = Module.new(&Proc.new) if block_given? extension = Module.new(&Proc.new) if block_given?
if !scope_options.is_a?(Relation) && scope_options.respond_to?(:call)
ActiveSupport::Deprecation.warn <<-WARN
Passing a proc (or other object that responds to #call) to scope is deprecated. If you need your scope to be lazily evaluated, or takes parameters, please define it as a normal class method instead. For example, change this:
class Post < ActiveRecord::Base
scope :unpublished, lambda { where('published_at > ?', Time.now) }
end
To this:
class Post < ActiveRecord::Base
def self.unpublished
where('published_at > ?', Time.now)
end
end
WARN
end
scope_proc = lambda do |*args| scope_proc = lambda do |*args|
options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options
options = scoped.apply_finder_options(options) if options.is_a?(Hash) options = scoped.apply_finder_options(options) if options.is_a?(Hash)

View File

@@ -471,12 +471,6 @@ class NamedScopeTest < ActiveRecord::TestCase
require "models/without_table" require "models/without_table"
end end
end end
def test_scopes_with_callables_are_deprecated
assert_deprecated do
Post.scope :WE_SO_EXCITED, lambda { |partyingpartyingpartying, yeah| fun!.fun!.fun! }
end
end
end end
class DynamicScopeMatchTest < ActiveRecord::TestCase class DynamicScopeMatchTest < ActiveRecord::TestCase

View File

@@ -1,8 +1,5 @@
class Comment < ActiveRecord::Base class Comment < ActiveRecord::Base
def self.limit_by(l) scope :limit_by, lambda {|l| limit(l) }
limit(l)
end
scope :containing_the_letter_e, :conditions => "comments.body LIKE '%e%'" scope :containing_the_letter_e, :conditions => "comments.body LIKE '%e%'"
scope :not_again, where("comments.body NOT LIKE '%again%'") scope :not_again, where("comments.body NOT LIKE '%again%'")
scope :for_first_post, :conditions => { :post_id => 1 } scope :for_first_post, :conditions => { :post_id => 1 }

View File

@@ -8,13 +8,12 @@ class Post < ActiveRecord::Base
scope :containing_the_letter_a, where("body LIKE '%a%'") scope :containing_the_letter_a, where("body LIKE '%a%'")
scope :ranked_by_comments, order("comments_count DESC") scope :ranked_by_comments, order("comments_count DESC")
def self.limit_by(l) scope :limit_by, lambda {|l| limit(l) }
limit(l) scope :with_authors_at_address, lambda { |address| {
end :conditions => [ 'authors.author_address_id = ?', address.id ],
:joins => 'JOIN authors ON authors.id = posts.author_id'
def self.with_authors_at_address(address) }
where('authors.author_address_id = ?', address.id).joins('JOIN authors ON authors.id = posts.author_id') }
end
belongs_to :author do belongs_to :author do
def greeting def greeting
@@ -29,10 +28,9 @@ class Post < ActiveRecord::Base
scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} } scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} }
scope :with_very_special_comments, joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) scope :with_very_special_comments, joins(:comments).where(:comments => {:type => 'VerySpecialComment'})
scope :with_post, lambda {|post_id|
def self.with_post(post_id) { :joins => :comments, :conditions => {:comments => {:post_id => post_id} } }
joins(:comments).where(:comments => { :post_id => post_id }) }
end
has_many :comments do has_many :comments do
def find_most_recent def find_most_recent

View File

@@ -1,20 +1,10 @@
class Topic < ActiveRecord::Base class Topic < ActiveRecord::Base
scope :base scope :base
scope :written_before, lambda { |time|
ActiveSupport::Deprecation.silence do if time
scope :written_before, lambda { |time| { :conditions => ['written_on < ?', time] }
if time end
{ :conditions => ['written_on < ?', time] } }
end
}
scope :with_object, Class.new(Struct.new(:klass)) {
def call
klass.where(:approved => true)
end
}.new(self)
end
scope :approved, :conditions => {:approved => true} scope :approved, :conditions => {:approved => true}
scope :rejected, :conditions => {:approved => false} scope :rejected, :conditions => {:approved => false}
@@ -29,6 +19,12 @@ class Topic < ActiveRecord::Base
end end
end end
scope :with_object, Class.new(Struct.new(:klass)) {
def call
klass.where(:approved => true)
end
}.new(self)
module NamedExtension module NamedExtension
def two def two
2 2