Deprecated support for passing hashes and relations to default_scope, in favour of defining a 'default_scope' class method in the model. See the CHANGELOG for more details.
This commit is contained in:
parent
fc9a04b6a6
commit
5740d4ec0c
@ -1,5 +1,26 @@
|
||||
*Rails 3.1.0 (unreleased)*
|
||||
|
||||
* Deprecated support for passing hashes and relations to 'default_scope'. Please create a class
|
||||
method for your scope instead. For example, change this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
default_scope where(:published => true)
|
||||
end
|
||||
|
||||
To this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
def self.default_scope
|
||||
where(:published => true)
|
||||
end
|
||||
end
|
||||
|
||||
Rationale: It will make the implementation simpler because we can simply use inheritance to
|
||||
handle inheritance scenarios, rather than trying to make up our own rules about what should
|
||||
happen when you call default_scope multiple times or in subclasses.
|
||||
|
||||
[Jon Leighton]
|
||||
|
||||
* PostgreSQL adapter only supports PostgreSQL version 8.2 and higher.
|
||||
|
||||
* ConnectionManagement middleware is changed to clean up the connection pool
|
||||
|
@ -425,8 +425,8 @@ class Base
|
||||
self.store_full_sti_class = true
|
||||
|
||||
# Stores the default scope for the class
|
||||
class_attribute :default_scoping, :instance_writer => false
|
||||
self.default_scoping = []
|
||||
class_attribute :default_scopes, :instance_writer => false
|
||||
self.default_scopes = []
|
||||
|
||||
# Returns a hash of all the attributes that have been specified for serialization as
|
||||
# keys and their class restriction as values.
|
||||
@ -870,7 +870,9 @@ def arel_engine
|
||||
# Returns a scope for this class without taking into account the default_scope.
|
||||
#
|
||||
# class Post < ActiveRecord::Base
|
||||
# default_scope :published => true
|
||||
# def self.default_scope
|
||||
# where :published => true
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
|
||||
@ -892,13 +894,8 @@ def unscoped #:nodoc:
|
||||
block_given? ? relation.scoping { yield } : relation
|
||||
end
|
||||
|
||||
def scoped_methods #:nodoc:
|
||||
key = :"#{self}_scoped_methods"
|
||||
Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
|
||||
end
|
||||
|
||||
def before_remove_const #:nodoc:
|
||||
reset_scoped_methods
|
||||
self.current_scope = nil
|
||||
end
|
||||
|
||||
# Specifies how the record is loaded by +Marshal+.
|
||||
@ -1020,7 +1017,7 @@ def method_missing(method_id, *arguments, &block)
|
||||
super unless all_attributes_exists?(attribute_names)
|
||||
if match.finder?
|
||||
options = arguments.extract_options!
|
||||
relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped
|
||||
relation = options.any? ? scoped(options) : scoped
|
||||
relation.send :find_by_attributes, match, attribute_names, *arguments
|
||||
elsif match.instantiator?
|
||||
scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
|
||||
@ -1109,43 +1106,48 @@ def all_attributes_exists?(attribute_names)
|
||||
# end
|
||||
#
|
||||
# *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
|
||||
def with_scope(method_scoping = {}, action = :merge, &block)
|
||||
method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
|
||||
def with_scope(scope = {}, action = :merge, &block)
|
||||
# If another Active Record class has been passed in, get its current scope
|
||||
scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
|
||||
|
||||
if method_scoping.is_a?(Hash)
|
||||
# Cannot use self.current_scope, because that could trigger build_default_scope, which
|
||||
# could in turn result in with_scope being called and hence an infinite loop.
|
||||
previous_scope = Thread.current[:"#{self}_current_scope"]
|
||||
|
||||
if scope.is_a?(Hash)
|
||||
# Dup first and second level of hash (method and params).
|
||||
method_scoping = method_scoping.dup
|
||||
method_scoping.each do |method, params|
|
||||
method_scoping[method] = params.dup unless params == true
|
||||
scope = scope.dup
|
||||
scope.each do |method, params|
|
||||
scope[method] = params.dup unless params == true
|
||||
end
|
||||
|
||||
method_scoping.assert_valid_keys([ :find, :create ])
|
||||
relation = construct_finder_arel(method_scoping[:find] || {})
|
||||
scope.assert_valid_keys([ :find, :create ])
|
||||
relation = construct_finder_arel(scope[:find] || {})
|
||||
|
||||
if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create]
|
||||
if previous_scope && previous_scope.create_with_value && scope[:create]
|
||||
scope_for_create = if action == :merge
|
||||
current_scoped_methods.create_with_value.merge(method_scoping[:create])
|
||||
previous_scope.create_with_value.merge(scope[:create])
|
||||
else
|
||||
method_scoping[:create]
|
||||
scope[:create]
|
||||
end
|
||||
|
||||
relation = relation.create_with(scope_for_create)
|
||||
else
|
||||
scope_for_create = method_scoping[:create]
|
||||
scope_for_create ||= current_scoped_methods.create_with_value if current_scoped_methods
|
||||
scope_for_create = scope[:create]
|
||||
scope_for_create ||= previous_scope.create_with_value if previous_scope
|
||||
relation = relation.create_with(scope_for_create) if scope_for_create
|
||||
end
|
||||
|
||||
method_scoping = relation
|
||||
scope = relation
|
||||
end
|
||||
|
||||
method_scoping = current_scoped_methods.merge(method_scoping) if current_scoped_methods && action == :merge
|
||||
scope = previous_scope.merge(scope) if previous_scope && action == :merge
|
||||
|
||||
self.scoped_methods << method_scoping
|
||||
self.current_scope = scope
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
self.scoped_methods.pop
|
||||
self.current_scope = previous_scope
|
||||
end
|
||||
end
|
||||
|
||||
@ -1168,41 +1170,84 @@ def with_exclusive_scope(method_scoping = {}, &block)
|
||||
with_scope(method_scoping, :overwrite, &block)
|
||||
end
|
||||
|
||||
# Sets the default options for the model. The format of the
|
||||
# <tt>options</tt> argument is the same as in find.
|
||||
def current_scope #:nodoc:
|
||||
Thread.current[:"#{self}_current_scope"] ||= build_default_scope
|
||||
end
|
||||
|
||||
def current_scope=(scope) #:nodoc:
|
||||
Thread.current[:"#{self}_current_scope"] = scope
|
||||
end
|
||||
|
||||
# Implement this method in your model to set a default scope for all operations on
|
||||
# the model.
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# def self.default_scope
|
||||
# order('last_name, first_name')
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Person.all # => SELECT * FROM people ORDER BY last_name, first_name
|
||||
#
|
||||
# The <tt>default_scope</tt> is also applied while creating/building a record. It is not
|
||||
# applied while updating a record.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# def self.default_scope
|
||||
# where(:published => true)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Article.new.published # => true
|
||||
# Article.create.published # => true
|
||||
#
|
||||
# === Deprecation warning
|
||||
#
|
||||
# There is an alternative syntax as follows:
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# default_scope order('last_name, first_name')
|
||||
# end
|
||||
#
|
||||
# <tt>default_scope</tt> is also applied while creating/building a record. It is not
|
||||
# applied while updating a record.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# default_scope where(:published => true)
|
||||
# end
|
||||
#
|
||||
# Article.new.published # => true
|
||||
# Article.create.published # => true
|
||||
def default_scope(options = {})
|
||||
reset_scoped_methods
|
||||
default_scoping = self.default_scoping.dup
|
||||
self.default_scoping = default_scoping << construct_finder_arel(options, default_scoping.pop)
|
||||
# This is now deprecated and will be removed in Rails 3.2.
|
||||
def default_scope(scope = {})
|
||||
ActiveSupport::Deprecation.warn <<-WARN
|
||||
Passing a hash or scope to default_scope is deprecated and will be removed in Rails 3.2. You should create a class method for your scope instead. For example, change this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
default_scope where(:published => true)
|
||||
end
|
||||
|
||||
To this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
def self.default_scope
|
||||
where(:published => true)
|
||||
end
|
||||
end
|
||||
WARN
|
||||
|
||||
# Reset the current scope as it may contain scopes based on a now-invalid default scope
|
||||
self.current_scope = nil
|
||||
self.default_scopes = default_scopes.dup << scope
|
||||
end
|
||||
|
||||
def current_scoped_methods #:nodoc:
|
||||
method = scoped_methods.last
|
||||
if method.respond_to?(:call)
|
||||
relation.scoping { method.call }
|
||||
else
|
||||
method
|
||||
def build_default_scope #:nodoc:
|
||||
if method(:default_scope).owner != Base.singleton_class
|
||||
# Exclusively scope to just the relation, to avoid infinite recursion where the
|
||||
# default scope tries to use the default scope tries to use the default scope...
|
||||
relation.scoping { default_scope }
|
||||
elsif default_scopes.any?
|
||||
default_scopes.inject(relation) do |default_scope, scope|
|
||||
if scope.is_a?(Hash)
|
||||
default_scope.apply_finder_options(scope)
|
||||
else
|
||||
default_scope.merge(scope)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def reset_scoped_methods #:nodoc:
|
||||
Thread.current[:"#{self}_scoped_methods"] = nil
|
||||
end
|
||||
|
||||
# Returns the class type of the record using the current module as a prefix. So descendants of
|
||||
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
||||
def compute_type(type_name)
|
||||
@ -1916,11 +1961,8 @@ def convert_number_column_value(value)
|
||||
end
|
||||
|
||||
def populate_with_current_scope_attributes
|
||||
if scope = self.class.send(:current_scoped_methods)
|
||||
create_with = scope.scope_for_create
|
||||
create_with.each { |att,value|
|
||||
respond_to?("#{att}=") && send("#{att}=", value)
|
||||
}
|
||||
self.class.scoped.scope_for_create.each do |att,value|
|
||||
respond_to?("#{att}=") && send("#{att}=", value)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -35,7 +35,7 @@ def scoped(options = nil)
|
||||
if options
|
||||
scoped.apply_finder_options(options)
|
||||
else
|
||||
current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone
|
||||
(current_scope || relation).clone
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -154,12 +154,7 @@ def many?
|
||||
# Please check unscoped if you want to remove all previous scopes (including
|
||||
# the default_scope) during the execution of a block.
|
||||
def scoping
|
||||
@klass.scoped_methods << self
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
@klass.scoped_methods.pop
|
||||
end
|
||||
@klass.send(:with_scope, self, :overwrite) { yield }
|
||||
end
|
||||
|
||||
# Updates all records with details given if they match a set of conditions supplied, limits and order can
|
||||
|
@ -70,16 +70,16 @@ def test_create_from_association_should_respect_default_scope
|
||||
# would be convenient), because this would cause that scope to be applied to any callbacks etc.
|
||||
def test_build_and_create_should_not_happen_within_scope
|
||||
car = cars(:honda)
|
||||
original_scoped_methods = Bulb.scoped_methods
|
||||
scoped_count = car.foo_bulbs.scoped.where_values.count
|
||||
|
||||
bulb = car.bulbs.build
|
||||
assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
|
||||
bulb = car.foo_bulbs.build
|
||||
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
|
||||
|
||||
bulb = car.bulbs.create
|
||||
assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
|
||||
bulb = car.foo_bulbs.create
|
||||
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
|
||||
|
||||
bulb = car.bulbs.create!
|
||||
assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
|
||||
bulb = car.foo_bulbs.create!
|
||||
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
|
||||
end
|
||||
|
||||
def test_no_sql_should_be_fired_if_association_already_loaded
|
||||
|
@ -165,16 +165,16 @@ def test_successful_build_association
|
||||
|
||||
def test_build_and_create_should_not_happen_within_scope
|
||||
pirate = pirates(:blackbeard)
|
||||
original_scoped_methods = Bulb.scoped_methods.dup
|
||||
scoped_count = pirate.association(:foo_bulb).scoped.where_values.count
|
||||
|
||||
bulb = pirate.build_bulb
|
||||
assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
|
||||
bulb = pirate.build_foo_bulb
|
||||
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
|
||||
|
||||
bulb = pirate.create_bulb
|
||||
assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
|
||||
bulb = pirate.create_foo_bulb
|
||||
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
|
||||
|
||||
bulb = pirate.create_bulb!
|
||||
assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
|
||||
bulb = pirate.create_foo_bulb!
|
||||
assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
|
||||
end
|
||||
|
||||
def test_create_association
|
||||
|
@ -1630,14 +1630,18 @@ def test_default_scope_is_reset
|
||||
Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
|
||||
UnloadablePost.table_name = 'posts'
|
||||
UnloadablePost.class_eval do
|
||||
default_scope order('posts.comments_count ASC')
|
||||
class << self
|
||||
def default_scope
|
||||
order('posts.comments_count ASC')
|
||||
end
|
||||
end
|
||||
end
|
||||
UnloadablePost.scoped_methods # make Thread.current[:UnloadablePost_scoped_methods] not nil
|
||||
UnloadablePost.scoped # make Thread.current[:UnloadablePost_scoped_methods] not nil
|
||||
|
||||
UnloadablePost.unloadable
|
||||
assert_not_nil Thread.current[:UnloadablePost_scoped_methods]
|
||||
assert_not_nil Thread.current[:UnloadablePost_current_scope]
|
||||
ActiveSupport::Dependencies.remove_unloadable_constants!
|
||||
assert_nil Thread.current[:UnloadablePost_scoped_methods]
|
||||
assert_nil Thread.current[:UnloadablePost_current_scope]
|
||||
ensure
|
||||
Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
|
||||
end
|
||||
|
@ -249,22 +249,21 @@ def test_immutable_scope
|
||||
end
|
||||
|
||||
def test_scoped_with_duck_typing
|
||||
scoping = Struct.new(:method_scoping).new(:find => { :conditions => ["name = ?", 'David'] })
|
||||
scoping = Struct.new(:current_scope).new(:find => { :conditions => ["name = ?", 'David'] })
|
||||
Developer.send(:with_scope, scoping) do
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
end
|
||||
|
||||
def test_ensure_that_method_scoping_is_correctly_restored
|
||||
scoped_methods = Developer.instance_eval('current_scoped_methods')
|
||||
|
||||
begin
|
||||
Developer.send(:with_scope, :find => { :conditions => "name = 'Jamis'" }) do
|
||||
raise "an exception"
|
||||
end
|
||||
rescue
|
||||
end
|
||||
assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
|
||||
|
||||
assert !Developer.scoped.where_values.include?("name = 'Jamis'")
|
||||
end
|
||||
end
|
||||
|
||||
@ -509,14 +508,15 @@ def test_immutable_merged_scope
|
||||
|
||||
def test_ensure_that_method_scoping_is_correctly_restored
|
||||
Developer.send(:with_scope, :find => { :conditions => "name = 'David'" }) do
|
||||
scoped_methods = Developer.instance_eval('current_scoped_methods')
|
||||
begin
|
||||
Developer.send(:with_scope, :find => { :conditions => "name = 'Maiha'" }) do
|
||||
raise "an exception"
|
||||
end
|
||||
rescue
|
||||
end
|
||||
assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
|
||||
|
||||
assert Developer.scoped.where_values.include?("name = 'David'")
|
||||
assert !Developer.scoped.where_values.include?("name = 'Maiha'")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -132,8 +132,6 @@ def test_scoped_create_with_create_with_has_higher_priority
|
||||
end
|
||||
|
||||
def test_ensure_that_method_scoping_is_correctly_restored
|
||||
scoped_methods = Developer.send(:current_scoped_methods)
|
||||
|
||||
begin
|
||||
Developer.where("name = 'Jamis'").scoping do
|
||||
raise "an exception"
|
||||
@ -141,7 +139,7 @@ def test_ensure_that_method_scoping_is_correctly_restored
|
||||
rescue
|
||||
end
|
||||
|
||||
assert_equal scoped_methods, Developer.send(:current_scoped_methods)
|
||||
assert !Developer.scoped.where_values.include?("name = 'Jamis'")
|
||||
end
|
||||
end
|
||||
|
||||
@ -310,35 +308,6 @@ def test_default_scope
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_with_lambda
|
||||
expected = Post.find_all_by_author_id(2)
|
||||
PostForAuthor.selected_author = 2
|
||||
received = PostForAuthor.all
|
||||
assert_equal expected, received
|
||||
expected = Post.find_all_by_author_id(1)
|
||||
PostForAuthor.selected_author = 1
|
||||
received = PostForAuthor.all
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_with_thing_that_responds_to_call
|
||||
klass = Class.new(ActiveRecord::Base) do
|
||||
self.table_name = 'posts'
|
||||
end
|
||||
|
||||
klass.class_eval do
|
||||
default_scope Class.new(Struct.new(:klass)) {
|
||||
def call
|
||||
klass.where(:author_id => 2)
|
||||
end
|
||||
}.new(self)
|
||||
end
|
||||
|
||||
records = klass.all
|
||||
assert_equal 3, records.length
|
||||
assert_equal 2, records.first.author_id
|
||||
end
|
||||
|
||||
def test_default_scope_is_unscoped_on_find
|
||||
assert_equal 1, DeveloperCalledDavid.count
|
||||
assert_equal 11, DeveloperCalledDavid.unscoped.count
|
||||
@ -364,49 +333,10 @@ def test_default_scoping_with_threads
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_scoping_with_inheritance
|
||||
# Inherit a class having a default scope and define a new default scope
|
||||
klass = Class.new(DeveloperOrderedBySalary)
|
||||
klass.send :default_scope, :limit => 1
|
||||
|
||||
# Scopes added on children should append to parent scope
|
||||
assert_equal 1, klass.scoped.limit_value
|
||||
assert_equal ['salary DESC'], klass.scoped.order_values
|
||||
|
||||
# Parent should still have the original scope
|
||||
assert_nil DeveloperOrderedBySalary.scoped.limit_value
|
||||
assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values
|
||||
end
|
||||
|
||||
def test_default_scope_called_twice_merges_conditions
|
||||
Developer.destroy_all
|
||||
Developer.create!(:name => "David", :salary => 80000)
|
||||
Developer.create!(:name => "David", :salary => 100000)
|
||||
Developer.create!(:name => "Brian", :salary => 100000)
|
||||
|
||||
klass = Class.new(Developer)
|
||||
klass.__send__ :default_scope, :conditions => { :name => "David" }
|
||||
klass.__send__ :default_scope, :conditions => { :salary => 100000 }
|
||||
assert_equal 1, klass.count
|
||||
assert_equal "David", klass.first.name
|
||||
assert_equal 100000, klass.first.salary
|
||||
end
|
||||
|
||||
def test_default_scope_called_twice_in_different_place_merges_where_clause
|
||||
Developer.destroy_all
|
||||
Developer.create!(:name => "David", :salary => 80000)
|
||||
Developer.create!(:name => "David", :salary => 100000)
|
||||
Developer.create!(:name => "Brian", :salary => 100000)
|
||||
|
||||
klass = Class.new(Developer)
|
||||
klass.class_eval do
|
||||
default_scope where("name = 'David'")
|
||||
default_scope where("salary = 100000")
|
||||
end
|
||||
|
||||
assert_equal 1, klass.count
|
||||
assert_equal "David", klass.first.name
|
||||
assert_equal 100000, klass.first.salary
|
||||
def test_default_scope_with_inheritance
|
||||
wheres = InheritedPoorDeveloperCalledJamis.scoped.where_values_hash
|
||||
assert_equal "Jamis", wheres[:name]
|
||||
assert_equal 50000, wheres[:salary]
|
||||
end
|
||||
|
||||
def test_method_scope
|
||||
@ -449,12 +379,6 @@ def test_order_in_default_scope_should_prevail
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_using_relation
|
||||
posts = PostWithComment.scoped
|
||||
assert_equal 2, posts.count
|
||||
assert_equal posts(:thinking), posts.first
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_scoping
|
||||
assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name
|
||||
assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
|
||||
@ -504,3 +428,176 @@ def test_create_with_reset
|
||||
assert_equal 'Jamis', jamis.name
|
||||
end
|
||||
end
|
||||
|
||||
class DeprecatedDefaultScopingTest < ActiveRecord::TestCase
|
||||
fixtures :developers, :posts
|
||||
|
||||
def test_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary }
|
||||
received = DeprecatedDeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_is_unscoped_on_find
|
||||
assert_equal 1, DeprecatedDeveloperCalledDavid.count
|
||||
assert_equal 11, DeprecatedDeveloperCalledDavid.unscoped.count
|
||||
end
|
||||
|
||||
def test_default_scope_is_unscoped_on_create
|
||||
assert_nil DeprecatedDeveloperCalledJamis.unscoped.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_string
|
||||
assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeprecatedDeveloperCalledDavid.find(:all).map(&:id).sort
|
||||
assert_equal nil, DeprecatedDeveloperCalledDavid.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_hash
|
||||
assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeprecatedDeveloperCalledJamis.find(:all).map(&:id).sort
|
||||
assert_equal 'Jamis', DeprecatedDeveloperCalledJamis.create!.name
|
||||
end
|
||||
|
||||
def test_default_scoping_with_threads
|
||||
2.times do
|
||||
Thread.new { assert_equal ['salary DESC'], DeprecatedDeveloperOrderedBySalary.scoped.order_values }.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_scoping_with_inheritance
|
||||
# Inherit a class having a default scope and define a new default scope
|
||||
klass = Class.new(DeprecatedDeveloperOrderedBySalary)
|
||||
ActiveSupport::Deprecation.silence { klass.send :default_scope, :limit => 1 }
|
||||
|
||||
# Scopes added on children should append to parent scope
|
||||
assert_equal 1, klass.scoped.limit_value
|
||||
assert_equal ['salary DESC'], klass.scoped.order_values
|
||||
|
||||
# Parent should still have the original scope
|
||||
assert_nil DeprecatedDeveloperOrderedBySalary.scoped.limit_value
|
||||
assert_equal ['salary DESC'], DeprecatedDeveloperOrderedBySalary.scoped.order_values
|
||||
end
|
||||
|
||||
def test_default_scope_called_twice_merges_conditions
|
||||
Developer.destroy_all
|
||||
Developer.create!(:name => "David", :salary => 80000)
|
||||
Developer.create!(:name => "David", :salary => 100000)
|
||||
Developer.create!(:name => "Brian", :salary => 100000)
|
||||
|
||||
klass = Class.new(Developer)
|
||||
ActiveSupport::Deprecation.silence do
|
||||
klass.__send__ :default_scope, :conditions => { :name => "David" }
|
||||
klass.__send__ :default_scope, :conditions => { :salary => 100000 }
|
||||
end
|
||||
assert_equal 1, klass.count
|
||||
assert_equal "David", klass.first.name
|
||||
assert_equal 100000, klass.first.salary
|
||||
end
|
||||
|
||||
def test_default_scope_called_twice_in_different_place_merges_where_clause
|
||||
Developer.destroy_all
|
||||
Developer.create!(:name => "David", :salary => 80000)
|
||||
Developer.create!(:name => "David", :salary => 100000)
|
||||
Developer.create!(:name => "Brian", :salary => 100000)
|
||||
|
||||
klass = Class.new(Developer)
|
||||
ActiveSupport::Deprecation.silence do
|
||||
klass.class_eval do
|
||||
default_scope where("name = 'David'")
|
||||
default_scope where("salary = 100000")
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 1, klass.count
|
||||
assert_equal "David", klass.first.name
|
||||
assert_equal 100000, klass.first.salary
|
||||
end
|
||||
|
||||
def test_method_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary }
|
||||
received = DeprecatedDeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary }
|
||||
received = DeprecatedDeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do
|
||||
DeprecatedDeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_scope_overwrites_default
|
||||
expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.name }
|
||||
received = DeprecatedDeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_reorder_overrides_default_scope_order
|
||||
expected = Developer.order('name DESC').collect { |dev| dev.name }
|
||||
received = DeprecatedDeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope
|
||||
expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary }
|
||||
received = DeprecatedDeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do
|
||||
DeprecatedDeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_order_in_default_scope_should_prevail
|
||||
expected = Developer.find(:all, :order => 'salary desc').collect { |dev| dev.salary }
|
||||
received = DeprecatedDeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_using_relation
|
||||
posts = DeprecatedPostWithComment.scoped
|
||||
assert_equal 2, posts.to_a.length
|
||||
assert_equal posts(:thinking), posts.first
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_scoping
|
||||
assert_equal 'David', DeprecatedPoorDeveloperCalledJamis.create!(:name => 'David').name
|
||||
assert_equal 200000, DeprecatedPoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_values
|
||||
assert_equal nil, DeprecatedPoorDeveloperCalledJamis.create!(:salary => nil).salary
|
||||
assert_equal 50000, DeprecatedPoorDeveloperCalledJamis.create!(:name => 'David').salary
|
||||
end
|
||||
|
||||
def test_default_scope_attribute
|
||||
jamis = DeprecatedPoorDeveloperCalledJamis.new(:name => 'David')
|
||||
assert_equal 50000, jamis.salary
|
||||
end
|
||||
|
||||
def test_where_attribute
|
||||
aaron = DeprecatedPoorDeveloperCalledJamis.where(:salary => 20).new(:name => 'Aaron')
|
||||
assert_equal 20, aaron.salary
|
||||
assert_equal 'Aaron', aaron.name
|
||||
end
|
||||
|
||||
def test_where_attribute_merge
|
||||
aaron = DeprecatedPoorDeveloperCalledJamis.where(:name => 'foo').new(:name => 'Aaron')
|
||||
assert_equal 'Aaron', aaron.name
|
||||
end
|
||||
|
||||
def test_create_with_merge
|
||||
aaron = DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'foo', :salary => 20).merge(
|
||||
DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'Aaron')).new
|
||||
assert_equal 20, aaron.salary
|
||||
assert_equal 'Aaron', aaron.name
|
||||
|
||||
aaron = DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'foo', :salary => 20).
|
||||
create_with(:name => 'Aaron').new
|
||||
assert_equal 20, aaron.salary
|
||||
assert_equal 'Aaron', aaron.name
|
||||
end
|
||||
|
||||
def test_create_with_reset
|
||||
jamis = DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'Aaron').create_with(nil).new
|
||||
assert_equal 'Jamis', jamis.name
|
||||
end
|
||||
end
|
||||
|
@ -1,14 +1,15 @@
|
||||
class Bulb < ActiveRecord::Base
|
||||
|
||||
default_scope :conditions => {:name => 'defaulty' }
|
||||
def self.default_scope
|
||||
where :name => 'defaulty'
|
||||
end
|
||||
|
||||
belongs_to :car
|
||||
|
||||
attr_reader :scoped_methods_after_initialize
|
||||
attr_reader :scope_after_initialize
|
||||
|
||||
after_initialize :record_scoped_methods_after_initialize
|
||||
def record_scoped_methods_after_initialize
|
||||
@scoped_methods_after_initialize = self.class.scoped_methods.dup
|
||||
after_initialize :record_scope_after_initialize
|
||||
def record_scope_after_initialize
|
||||
@scope_after_initialize = self.class.scoped
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,6 +1,7 @@
|
||||
class Car < ActiveRecord::Base
|
||||
|
||||
has_many :bulbs
|
||||
has_many :foo_bulbs, :class_name => "Bulb", :conditions => { :name => 'foo' }
|
||||
has_many :tyres
|
||||
has_many :engines
|
||||
has_many :wheels, :as => :wheelable
|
||||
@ -14,9 +15,13 @@ class Car < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class CoolCar < Car
|
||||
default_scope :order => 'name desc'
|
||||
def self.default_scope
|
||||
order 'name desc'
|
||||
end
|
||||
end
|
||||
|
||||
class FastCar < Car
|
||||
default_scope order('name desc')
|
||||
def self.default_scope
|
||||
order 'name desc'
|
||||
end
|
||||
end
|
||||
|
@ -13,7 +13,9 @@ class Categorization < ActiveRecord::Base
|
||||
class SpecialCategorization < ActiveRecord::Base
|
||||
self.table_name = 'categorizations'
|
||||
|
||||
default_scope where(:special => true)
|
||||
def self.default_scope
|
||||
where(:special => true)
|
||||
end
|
||||
|
||||
belongs_to :author
|
||||
belongs_to :category
|
||||
|
@ -86,7 +86,11 @@ def raise_if_projects_empty!
|
||||
|
||||
class DeveloperOrderedBySalary < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :order => 'salary DESC'
|
||||
|
||||
def self.default_scope
|
||||
order('salary DESC')
|
||||
end
|
||||
|
||||
scope :by_name, order('name DESC')
|
||||
|
||||
def self.all_ordered_by_name
|
||||
@ -98,15 +102,72 @@ def self.all_ordered_by_name
|
||||
|
||||
class DeveloperCalledDavid < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => "name = 'David'"
|
||||
|
||||
def self.default_scope
|
||||
where "name = 'David'"
|
||||
end
|
||||
end
|
||||
|
||||
class DeveloperCalledJamis < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => { :name => 'Jamis' }
|
||||
|
||||
def self.default_scope
|
||||
where :name => 'Jamis'
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractDeveloperCalledJamis < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
|
||||
def self.default_scope
|
||||
where :name => 'Jamis'
|
||||
end
|
||||
end
|
||||
|
||||
class PoorDeveloperCalledJamis < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => { :name => 'Jamis', :salary => 50000 }
|
||||
|
||||
def self.default_scope
|
||||
where :name => 'Jamis', :salary => 50000
|
||||
end
|
||||
end
|
||||
|
||||
class InheritedPoorDeveloperCalledJamis < DeveloperCalledJamis
|
||||
self.table_name = 'developers'
|
||||
|
||||
def self.default_scope
|
||||
super.where :salary => 50000
|
||||
end
|
||||
end
|
||||
|
||||
ActiveSupport::Deprecation.silence do
|
||||
class DeprecatedDeveloperOrderedBySalary < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :order => 'salary DESC'
|
||||
|
||||
def self.by_name
|
||||
order('name DESC')
|
||||
end
|
||||
|
||||
def self.all_ordered_by_name
|
||||
with_scope(:find => { :order => 'name DESC' }) do
|
||||
find(:all)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DeprecatedDeveloperCalledDavid < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => "name = 'David'"
|
||||
end
|
||||
|
||||
class DeprecatedDeveloperCalledJamis < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => { :name => 'Jamis' }
|
||||
end
|
||||
|
||||
class DeprecatedPoorDeveloperCalledJamis < ActiveRecord::Base
|
||||
self.table_name = 'developers'
|
||||
default_scope :conditions => { :name => 'Jamis', :salary => 50000 }
|
||||
end
|
||||
end
|
||||
|
@ -34,7 +34,7 @@ class Pirate < ActiveRecord::Base
|
||||
:after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
|
||||
has_many :birds_with_reject_all_blank, :class_name => "Bird"
|
||||
|
||||
has_one :bulb, :foreign_key => :car_id
|
||||
has_one :foo_bulb, :foreign_key => :car_id, :class_name => "Bulb", :conditions => { :name => 'foo' }
|
||||
|
||||
accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
|
@ -142,20 +142,25 @@ class SubStiPost < StiPost
|
||||
self.table_name = Post.table_name
|
||||
end
|
||||
|
||||
class PostWithComment < ActiveRecord::Base
|
||||
self.table_name = 'posts'
|
||||
default_scope where("posts.comments_count > 0").order("posts.comments_count ASC")
|
||||
ActiveSupport::Deprecation.silence do
|
||||
class DeprecatedPostWithComment < ActiveRecord::Base
|
||||
self.table_name = 'posts'
|
||||
default_scope where("posts.comments_count > 0").order("posts.comments_count ASC")
|
||||
end
|
||||
end
|
||||
|
||||
class PostForAuthor < ActiveRecord::Base
|
||||
self.table_name = 'posts'
|
||||
cattr_accessor :selected_author
|
||||
default_scope lambda { where(:author_id => PostForAuthor.selected_author) }
|
||||
end
|
||||
|
||||
class FirstPost < ActiveRecord::Base
|
||||
self.table_name = 'posts'
|
||||
default_scope where(:id => 1)
|
||||
|
||||
def self.default_scope
|
||||
where(:id => 1)
|
||||
end
|
||||
|
||||
has_many :comments, :foreign_key => :post_id
|
||||
has_one :comment, :foreign_key => :post_id
|
||||
end
|
||||
|
@ -18,6 +18,9 @@ def make_comments
|
||||
end
|
||||
|
||||
class BadReference < ActiveRecord::Base
|
||||
self.table_name ='references'
|
||||
default_scope :conditions => {:favourite => false }
|
||||
self.table_name = 'references'
|
||||
|
||||
def self.default_scope
|
||||
where :favourite => false
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
class WithoutTable < ActiveRecord::Base
|
||||
default_scope where(:published => true)
|
||||
end
|
||||
def self.default_scope
|
||||
where(:published => true)
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user