aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/scoping
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/scoping')
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb118
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb81
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb34
3 files changed, 194 insertions, 39 deletions
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 71754cf0a2..0738df1b54 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -1,13 +1,15 @@
require 'cases/helper'
require 'models/post'
+require 'models/comment'
require 'models/developer'
+require 'models/computer'
class DefaultScopingTest < ActiveRecord::TestCase
- fixtures :developers, :posts
+ fixtures :developers, :posts, :comments
def test_default_scope
- expected = Developer.all.merge!(:order => 'salary DESC').to_a.collect { |dev| dev.salary }
- received = DeveloperOrderedBySalary.all.collect { |dev| dev.salary }
+ expected = Developer.all.merge!(:order => 'salary DESC').to_a.collect(&:salary)
+ received = DeveloperOrderedBySalary.all.collect(&:salary)
assert_equal expected, received
end
@@ -84,14 +86,14 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_scope_overwrites_default
- expected = Developer.all.merge!(order: 'salary DESC, name DESC').to_a.collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.by_name.to_a.collect { |dev| dev.name }
+ expected = Developer.all.merge!(order: 'salary DESC, name DESC').to_a.collect(&:name)
+ received = DeveloperOrderedBySalary.by_name.to_a.collect(&:name)
assert_equal expected, received
end
def test_reorder_overrides_default_scope_order
- expected = Developer.order('name DESC').collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name }
+ expected = Developer.order('name DESC').collect(&:name)
+ received = DeveloperOrderedBySalary.reorder('name DESC').collect(&:name)
assert_equal expected, received
end
@@ -141,27 +143,45 @@ class DefaultScopingTest < ActiveRecord::TestCase
expected_5 = Developer.order('salary DESC').collect(&:name)
received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name)
assert_equal expected_5, received_5
+
+ expected_6 = Developer.order('salary DESC').collect(&:name)
+ received_6 = DeveloperOrderedBySalary.where(Developer.arel_table['name'].eq('David')).unscope(where: :name).collect(&:name)
+ assert_equal expected_6, received_6
+
+ expected_7 = Developer.order('salary DESC').collect(&:name)
+ received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq('David')).unscope(where: :name).collect(&:name)
+ assert_equal expected_7, received_7
end
def test_unscope_multiple_where_clauses
- expected = Developer.order('salary DESC').collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect { |dev| dev.name }
+ expected = Developer.order('salary DESC').collect(&:name)
+ received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect(&:name)
+ assert_equal expected, received
+ end
+
+ def test_unscope_string_where_clauses_involved
+ dev_relation = Developer.order('salary DESC').where("created_at > ?", 1.year.ago)
+ expected = dev_relation.collect(&:name)
+
+ dev_ordered_relation = DeveloperOrderedBySalary.where(name: 'Jamis').where("created_at > ?", 1.year.ago)
+ received = dev_ordered_relation.unscope(where: [:name]).collect(&:name)
+
assert_equal expected, received
end
def test_unscope_with_grouping_attributes
- expected = Developer.order('salary DESC').collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect { |dev| dev.name }
+ expected = Developer.order('salary DESC').collect(&:name)
+ received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name)
assert_equal expected, received
- expected_2 = Developer.order('salary DESC').collect { |dev| dev.name }
- received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect { |dev| dev.name }
+ expected_2 = Developer.order('salary DESC').collect(&:name)
+ received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name)
assert_equal expected_2, received_2
end
def test_unscope_with_limit_in_query
- expected = Developer.order('salary DESC').collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect { |dev| dev.name }
+ expected = Developer.order('salary DESC').collect(&:name)
+ received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name)
assert_equal expected, received
end
@@ -171,42 +191,42 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_unscope_reverse_order
- expected = Developer.all.collect { |dev| dev.name }
- received = Developer.order('salary DESC').reverse_order.unscope(:order).collect { |dev| dev.name }
+ expected = Developer.all.collect(&:name)
+ received = Developer.order('salary DESC').reverse_order.unscope(:order).collect(&:name)
assert_equal expected, received
end
def test_unscope_select
- expected = Developer.order('salary ASC').collect { |dev| dev.name }
- received = Developer.order('salary DESC').reverse_order.select(:name).unscope(:select).collect { |dev| dev.name }
+ expected = Developer.order('salary ASC').collect(&:name)
+ received = Developer.order('salary DESC').reverse_order.select(:name).unscope(:select).collect(&:name)
assert_equal expected, received
- expected_2 = Developer.all.collect { |dev| dev.id }
- received_2 = Developer.select(:name).unscope(:select).collect { |dev| dev.id }
+ expected_2 = Developer.all.collect(&:id)
+ received_2 = Developer.select(:name).unscope(:select).collect(&:id)
assert_equal expected_2, received_2
end
def test_unscope_offset
- expected = Developer.all.collect { |dev| dev.name }
- received = Developer.offset(5).unscope(:offset).collect { |dev| dev.name }
+ expected = Developer.all.collect(&:name)
+ received = Developer.offset(5).unscope(:offset).collect(&:name)
assert_equal expected, received
end
def test_unscope_joins_and_select_on_developers_projects
- expected = Developer.all.collect { |dev| dev.name }
- received = Developer.joins('JOIN developers_projects ON id = developer_id').select(:id).unscope(:joins, :select).collect { |dev| dev.name }
+ expected = Developer.all.collect(&:name)
+ received = Developer.joins('JOIN developers_projects ON id = developer_id').select(:id).unscope(:joins, :select).collect(&:name)
assert_equal expected, received
end
def test_unscope_includes
- expected = Developer.all.collect { |dev| dev.name }
- received = Developer.includes(:projects).select(:id).unscope(:includes, :select).collect { |dev| dev.name }
+ expected = Developer.all.collect(&:name)
+ received = Developer.includes(:projects).select(:id).unscope(:includes, :select).collect(&:name)
assert_equal expected, received
end
def test_unscope_having
- expected = DeveloperOrderedBySalary.all.collect { |dev| dev.name }
- received = DeveloperOrderedBySalary.having("name IN ('Jamis', 'David')").unscope(:having).collect { |dev| dev.name }
+ expected = DeveloperOrderedBySalary.all.collect(&:name)
+ received = DeveloperOrderedBySalary.having("name IN ('Jamis', 'David')").unscope(:having).collect(&:name)
assert_equal expected, received
end
@@ -269,8 +289,8 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_order_in_default_scope_should_not_prevail
- expected = Developer.all.merge!(order: 'salary desc').to_a.collect { |dev| dev.salary }
- received = DeveloperOrderedBySalary.all.merge!(order: 'salary').to_a.collect { |dev| dev.salary }
+ expected = Developer.all.merge!(order: 'salary desc').to_a.collect(&:salary)
+ received = DeveloperOrderedBySalary.all.merge!(order: 'salary').to_a.collect(&:salary)
assert_equal expected, received
end
@@ -368,6 +388,24 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal 1, DeveloperWithIncludes.where(:audit_logs => { :message => 'foo' }).count
end
+ def test_default_scope_with_references_works_through_collection_association
+ post = PostWithCommentWithDefaultScopeReferencesAssociation.create!(title: "Hello World", body: "Here we go.")
+ comment = post.comment_with_default_scope_references_associations.create!(body: "Great post.", developer_id: Developer.first.id)
+ assert_equal comment, post.comment_with_default_scope_references_associations.to_a.first
+ end
+
+ def test_default_scope_with_references_works_through_association
+ post = PostWithCommentWithDefaultScopeReferencesAssociation.create!(title: "Hello World", body: "Here we go.")
+ comment = post.comment_with_default_scope_references_associations.create!(body: "Great post.", developer_id: Developer.first.id)
+ assert_equal comment, post.first_comment
+ end
+
+ def test_default_scope_with_references_works_with_find_by
+ post = PostWithCommentWithDefaultScopeReferencesAssociation.create!(title: "Hello World", body: "Here we go.")
+ comment = post.comment_with_default_scope_references_associations.create!(body: "Great post.", developer_id: Developer.first.id)
+ assert_equal comment, CommentWithDefaultScopeReferencesAssociation.find_by(id: comment.id)
+ end
+
unless in_memory_db?
def test_default_scope_is_threadsafe
threads = []
@@ -385,4 +423,22 @@ class DefaultScopingTest < ActiveRecord::TestCase
threads.each(&:join)
end
end
+
+ test "additional conditions are ANDed with the default scope" do
+ scope = DeveloperCalledJamis.where(name: "David")
+ assert_equal 2, scope.where_values.length
+ assert_equal [], scope.to_a
+ end
+
+ test "additional conditions in a scope are ANDed with the default scope" do
+ scope = DeveloperCalledJamis.david
+ assert_equal 2, scope.where_values.length
+ assert_equal [], scope.to_a
+ end
+
+ test "a scope can remove the condition from the default scope" do
+ scope = DeveloperCalledJamis.david2
+ assert_equal 1, scope.where_values.length
+ assert_equal Developer.where(name: "David").map(&:id), scope.map(&:id)
+ end
end
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 72c9787b84..41f3449828 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -5,6 +5,7 @@ require 'models/comment'
require 'models/reply'
require 'models/author'
require 'models/developer'
+require 'models/computer'
class NamedScopingTest < ActiveRecord::TestCase
fixtures :posts, :authors, :topics, :comments, :author_addresses
@@ -132,6 +133,13 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5)
end
+ def test_scopes_body_is_a_callable
+ e = assert_raises ArgumentError do
+ Class.new(Post).class_eval { scope :containing_the_letter_z, where("body LIKE '%z%'") }
+ end
+ assert_equal "The scope body needs to be callable.", e.message
+ end
+
def test_active_records_have_scope_named__all__
assert !Topic.all.empty?
@@ -266,6 +274,71 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal 'lifo', topic.author_name
end
+ def test_reserved_scope_names
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+
+ scope :approved, -> { where(approved: true) }
+
+ class << self
+ public
+ def pub; end
+
+ private
+ def pri; end
+
+ protected
+ def pro; end
+ end
+ end
+
+ subklass = Class.new(klass)
+
+ conflicts = [
+ :create, # public class method on AR::Base
+ :relation, # private class method on AR::Base
+ :new, # redefined class method on AR::Base
+ :all, # a default scope
+ :public, # some imporant methods on Module and Class
+ :protected,
+ :private,
+ :name,
+ :parent,
+ :superclass
+ ]
+
+ non_conflicts = [
+ :find_by_title, # dynamic finder method
+ :approved, # existing scope
+ :pub, # existing public class method
+ :pri, # existing private class method
+ :pro, # existing protected class method
+ :open, # a ::Kernel method
+ ]
+
+ conflicts.each do |name|
+ assert_raises(ArgumentError, "scope `#{name}` should not be allowed") do
+ klass.class_eval { scope name, ->{ where(approved: true) } }
+ end
+
+ assert_raises(ArgumentError, "scope `#{name}` should not be allowed") do
+ subklass.class_eval { scope name, ->{ where(approved: true) } }
+ end
+ end
+
+ non_conflicts.each do |name|
+ assert_nothing_raised do
+ silence_warnings do
+ klass.class_eval { scope name, ->{ where(approved: true) } }
+ end
+ end
+
+ assert_nothing_raised do
+ subklass.class_eval { scope name, ->{ where(approved: true) } }
+ end
+ end
+ end
+
# Method delegation for scope names which look like /\A[a-zA-Z_]\w*[!?]?\z/
# has been done by evaluating a string with a plain def statement. For scope
# names which contain spaces this approach doesn't work.
@@ -344,13 +417,13 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_scopes_batch_finders
- assert_equal 3, Topic.approved.count
+ assert_equal 4, Topic.approved.count
- assert_queries(4) do
+ assert_queries(5) do
Topic.approved.find_each(:batch_size => 1) {|t| assert t.approved? }
end
- assert_queries(2) do
+ assert_queries(3) do
Topic.approved.find_in_batches(:batch_size => 2) do |group|
group.each {|t| assert t.approved? }
end
@@ -366,7 +439,7 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_scopes_on_relations
# Topic.replied
approved_topics = Topic.all.approved.order('id DESC')
- assert_equal topics(:fourth), approved_topics.first
+ assert_equal topics(:fifth), approved_topics.first
replied_approved_topics = approved_topics.replied
assert_equal topics(:third), replied_approved_topics.first
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index 0018fc06f2..d7bcbf6203 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -2,6 +2,7 @@ require "cases/helper"
require 'models/post'
require 'models/author'
require 'models/developer'
+require 'models/computer'
require 'models/project'
require 'models/comment'
require 'models/category'
@@ -11,6 +12,30 @@ require 'models/reference'
class RelationScopingTest < ActiveRecord::TestCase
fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
+ setup do
+ developers(:david)
+ end
+
+ def test_unscoped_breaks_caching
+ author = authors :mary
+ assert_nil author.first_post
+ post = FirstPost.unscoped do
+ author.reload.first_post
+ end
+ assert post
+ end
+
+ def test_scope_breaks_caching_on_collections
+ author = authors :david
+ ids = author.reload.special_posts_with_default_scope.map(&:id)
+ assert_equal [1,5,6], ids.sort
+ scoped_posts = SpecialPostWithDefaultScope.unscoped do
+ author = authors :david
+ author.reload.special_posts_with_default_scope.to_a
+ end
+ assert_equal author.posts.map(&:id).sort, scoped_posts.map(&:id).sort
+ end
+
def test_reverse_order
assert_equal Developer.order("id DESC").to_a.reverse, Developer.order("id DESC").reverse_order
end
@@ -192,8 +217,9 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
Developer.where('salary = 80000').scoping do
Developer.limit(10).scoping do
devs = Developer.all
- assert_match '(salary = 80000)', devs.to_sql
- assert_equal 10, devs.taken
+ sql = devs.to_sql
+ assert_match '(salary = 80000)', sql
+ assert_match 'LIMIT 10', sql
end
end
end
@@ -259,7 +285,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
end
end
-class HasManyScopingTest< ActiveRecord::TestCase
+class HasManyScopingTest < ActiveRecord::TestCase
fixtures :comments, :posts, :people, :references
def setup
@@ -305,7 +331,7 @@ class HasManyScopingTest< ActiveRecord::TestCase
end
end
-class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
+class HasAndBelongsToManyScopingTest < ActiveRecord::TestCase
fixtures :posts, :categories, :categories_posts
def setup