diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2014-01-11 17:28:43 -0800 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2014-01-11 17:28:43 -0800 |
commit | 11e8badb16876e6bd72c874631d25aec41dad293 (patch) | |
tree | 5ec87baa3a8e7652c46d6804b174b81b8f9c7745 /activerecord/test/cases/relation | |
parent | 474ebc55bd13ad58626a49dfc44c8e6407813935 (diff) | |
parent | caa981d88112f019ade868f75af6b5f399c244a4 (diff) | |
download | rails-11e8badb16876e6bd72c874631d25aec41dad293.tar.gz rails-11e8badb16876e6bd72c874631d25aec41dad293.tar.bz2 rails-11e8badb16876e6bd72c874631d25aec41dad293.zip |
Merge branch 'master' into set_binds
* master: (2794 commits)
doc, API example on how to use `Model#exists?` with multiple IDs. [ci skip]
Restore DATABASE_URL even if it's nil in connection_handler test
[ci skip] - error_messages_for has been deprecated since 2.3.8 - lets reduce any confusion for users
Ensure Active Record connection consistency
Revert "ask the fixture set for the sql statements"
Check `respond_to` before delegation due to: https://github.com/ruby/ruby/commit/d781caaf313b8649948c107bba277e5ad7307314
Adding Hash#compact and Hash#compact! methods
MySQL version 4.1 was EOL on December 31, 2009 We should at least recommend modern versions of MySQL to users.
clear cache on body close so that cache remains during rendering
add a more restricted codepath for templates fixes #13390
refactor generator tests to use block form of Tempfile
Fix typo [ci skip]
Move finish_template as the last public method in the generator
Minor typos fix [ci skip]
make `change_column_null` reversible. Closes #13576.
create/drop test and development databases only if RAILS_ENV is nil
Revert "Speedup String#to"
typo fix in test name. [ci skip].
`core_ext/string/access.rb` test what we are documenting.
Fix typo in image_tag documentation
...
Conflicts:
activerecord/lib/active_record/associations/join_dependency/join_association.rb
activerecord/lib/active_record/relation/query_methods.rb
Diffstat (limited to 'activerecord/test/cases/relation')
-rw-r--r-- | activerecord/test/cases/relation/delegation_test.rb | 68 | ||||
-rw-r--r-- | activerecord/test/cases/relation/merging_test.rb | 162 | ||||
-rw-r--r-- | activerecord/test/cases/relation/mutation_test.rb | 144 | ||||
-rw-r--r-- | activerecord/test/cases/relation/predicate_builder_test.rb | 14 | ||||
-rw-r--r-- | activerecord/test/cases/relation/where_chain_test.rb | 36 | ||||
-rw-r--r-- | activerecord/test/cases/relation/where_test.rb | 63 |
6 files changed, 487 insertions, 0 deletions
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb new file mode 100644 index 0000000000..9b2bfed039 --- /dev/null +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -0,0 +1,68 @@ +require 'cases/helper' +require 'models/post' +require 'models/comment' + +module ActiveRecord + class DelegationTest < ActiveRecord::TestCase + fixtures :posts + + def call_method(target, method) + method_arity = target.to_a.method(method).arity + + if method_arity.zero? + target.public_send(method) + elsif method_arity < 0 + if method == :shuffle! + target.public_send(method) + else + target.public_send(method, 1) + end + elsif method_arity == 1 + target.public_send(method, 1) + else + raise NotImplementedError + end + end + end + + module DelegationWhitelistBlacklistTests + ARRAY_DELEGATES = [ + :+, :-, :|, :&, :[], + :all?, :collect, :detect, :each, :each_cons, :each_with_index, + :exclude?, :find_all, :flat_map, :group_by, :include?, :length, + :map, :none?, :one?, :partition, :reject, :reverse, + :sample, :second, :sort, :sort_by, :third, + :to_ary, :to_set, :to_xml, :to_yaml + ] + + ARRAY_DELEGATES.each do |method| + define_method "test_delegates_#{method}_to_Array" do + assert_respond_to target, method + end + end + + ActiveRecord::Delegation::BLACKLISTED_ARRAY_METHODS.each do |method| + define_method "test_#{method}_is_not_delegated_to_Array" do + assert_raises(NoMethodError) { call_method(target, method) } + end + end + end + + class DelegationAssociationTest < DelegationTest + include DelegationWhitelistBlacklistTests + + def target + Post.first.comments + end + end + + class DelegationRelationTest < DelegationTest + include DelegationWhitelistBlacklistTests + + fixtures :comments + + def target + Comment.all + end + end +end diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb new file mode 100644 index 0000000000..23500bf5d8 --- /dev/null +++ b/activerecord/test/cases/relation/merging_test.rb @@ -0,0 +1,162 @@ +require 'cases/helper' +require 'models/author' +require 'models/comment' +require 'models/developer' +require 'models/post' +require 'models/project' + +class RelationMergingTest < ActiveRecord::TestCase + fixtures :developers, :comments, :authors, :posts + + def test_relation_merging + devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order('id ASC').where("id < 3")) + assert_equal [developers(:david), developers(:jamis)], devs.to_a + + dev_with_count = Developer.limit(1).merge(Developer.order('id DESC')).merge(Developer.select('developers.*')) + assert_equal [developers(:poor_jamis)], dev_with_count.to_a + end + + def test_relation_to_sql + sql = Post.connection.unprepared_statement do + Post.first.comments.to_sql + end + assert_no_match(/\?/, sql) + end + + def test_relation_merging_with_arel_equalities_keeps_last_equality + devs = Developer.where(Developer.arel_table[:salary].eq(80000)).merge( + Developer.where(Developer.arel_table[:salary].eq(9000)) + ) + assert_equal [developers(:poor_jamis)], devs.to_a + end + + def test_relation_merging_with_arel_equalities_keeps_last_equality_with_non_attribute_left_hand + salary_attr = Developer.arel_table[:salary] + devs = Developer.where( + Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(80000) + ).merge( + Developer.where( + Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(9000) + ) + ) + assert_equal [developers(:poor_jamis)], devs.to_a + end + + def test_relation_merging_with_eager_load + relations = [] + relations << Post.order('comments.id DESC').merge(Post.eager_load(:last_comment)).merge(Post.all) + relations << Post.eager_load(:last_comment).merge(Post.order('comments.id DESC')).merge(Post.all) + + relations.each do |posts| + post = posts.find { |p| p.id == 1 } + assert_equal Post.find(1).last_comment, post.last_comment + end + end + + def test_relation_merging_with_locks + devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2)) + assert devs.locked.present? + end + + def test_relation_merging_with_preload + [Post.all.merge(Post.preload(:author)), Post.preload(:author).merge(Post.all)].each do |posts| + assert_queries(2) { assert posts.first.author } + end + end + + def test_relation_merging_with_joins + comments = Comment.joins(:post).where(:body => 'Thank you for the welcome').merge(Post.where(:body => 'Such a lovely day')) + assert_equal 1, comments.count + end + + def test_relation_merging_with_association + assert_queries(2) do # one for loading post, and another one merged query + post = Post.where(:body => 'Such a lovely day').first + comments = Comment.where(:body => 'Thank you for the welcome').merge(post.comments) + assert_equal 1, comments.count + end + end + + test "merge collapses wheres from the LHS only" do + left = Post.where(title: "omg").where(comments_count: 1) + right = Post.where(title: "wtf").where(title: "bbq") + + expected = [left.where_values[1]] + right.where_values + merged = left.merge(right) + + assert_equal expected, merged.where_values + assert !merged.to_sql.include?("omg") + assert merged.to_sql.include?("wtf") + assert merged.to_sql.include?("bbq") + end + + def test_merging_removes_rhs_bind_parameters + left = Post.where(id: Arel::Nodes::BindParam.new('?')) + column = Post.columns_hash['id'] + left.bind_values += [[column, 20]] + right = Post.where(id: 10) + + merged = left.merge(right) + assert_equal [], merged.bind_values + end + + def test_merging_keeps_lhs_bind_parameters + column = Post.columns_hash['id'] + binds = [[column, 20]] + + right = Post.where(id: Arel::Nodes::BindParam.new('?')) + right.bind_values += binds + left = Post.where(id: 10) + + merged = left.merge(right) + assert_equal binds, merged.bind_values + end + + def test_merging_reorders_bind_params + post = Post.first + id_column = Post.columns_hash['id'] + title_column = Post.columns_hash['title'] + + bv = Post.connection.substitute_at id_column, 0 + + right = Post.where(id: bv) + right.bind_values += [[id_column, post.id]] + + left = Post.where(title: bv) + left.bind_values += [[title_column, post.title]] + + merged = left.merge(right) + assert_equal post, merged.first + end +end + +class MergingDifferentRelationsTest < ActiveRecord::TestCase + fixtures :posts, :authors + + test "merging where relations" do + hello_by_bob = Post.where(body: "hello").joins(:author). + merge(Author.where(name: "Bob")).order("posts.id").pluck("posts.id") + + assert_equal [posts(:misc_by_bob).id, + posts(:other_by_bob).id], hello_by_bob + end + + test "merging order relations" do + posts_by_author_name = Post.limit(3).joins(:author). + merge(Author.order(:name)).pluck("authors.name") + + assert_equal ["Bob", "Bob", "David"], posts_by_author_name + + posts_by_author_name = Post.limit(3).joins(:author). + merge(Author.order("name")).pluck("authors.name") + + assert_equal ["Bob", "Bob", "David"], posts_by_author_name + end + + test "merging order relations (using a hash argument)" do + posts_by_author_name = Post.limit(4).joins(:author). + merge(Author.order(name: :desc)).pluck("authors.name") + + assert_equal ["Mary", "Mary", "Mary", "David"], posts_by_author_name + end +end diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb new file mode 100644 index 0000000000..7cb2a19bee --- /dev/null +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -0,0 +1,144 @@ +require 'cases/helper' +require 'models/post' + +module ActiveRecord + class RelationMutationTest < ActiveSupport::TestCase + class FakeKlass < Struct.new(:table_name, :name) + extend ActiveRecord::Delegation::DelegateCache + inherited self + + def connection + Post.connection + end + + def relation_delegate_class(klass) + self.class.relation_delegate_class(klass) + end + end + + def relation + @relation ||= Relation.new FakeKlass.new('posts'), Post.arel_table + end + + (Relation::MULTI_VALUE_METHODS - [:references, :extending, :order, :unscope]).each do |method| + test "##{method}!" do + assert relation.public_send("#{method}!", :foo).equal?(relation) + assert_equal [:foo], relation.public_send("#{method}_values") + end + end + + test '#order!' do + assert relation.order!('name ASC').equal?(relation) + assert_equal ['name ASC'], relation.order_values + end + + test '#order! with symbol prepends the table name' do + assert relation.order!(:name).equal?(relation) + node = relation.order_values.first + assert node.ascending? + assert_equal :name, node.expr.name + assert_equal "posts", node.expr.relation.name + end + + test '#order! on non-string does not attempt regexp match for references' do + obj = Object.new + obj.expects(:=~).never + assert relation.order!(obj) + assert_equal [obj], relation.order_values + end + + test '#references!' do + assert relation.references!(:foo).equal?(relation) + assert relation.references_values.include?('foo') + end + + test 'extending!' do + mod, mod2 = Module.new, Module.new + + assert relation.extending!(mod).equal?(relation) + assert_equal [mod], relation.extending_values + assert relation.is_a?(mod) + + relation.extending!(mod2) + assert_equal [mod, mod2], relation.extending_values + end + + test 'extending! with empty args' do + relation.extending! + assert_equal [], relation.extending_values + end + + (Relation::SINGLE_VALUE_METHODS - [:from, :lock, :reordering, :reverse_order, :create_with]).each do |method| + test "##{method}!" do + assert relation.public_send("#{method}!", :foo).equal?(relation) + assert_equal :foo, relation.public_send("#{method}_value") + end + end + + test '#from!' do + assert relation.from!('foo').equal?(relation) + assert_equal ['foo', nil], relation.from_value + end + + test '#lock!' do + assert relation.lock!('foo').equal?(relation) + assert_equal 'foo', relation.lock_value + end + + test '#reorder!' do + relation = self.relation.order('foo') + + assert relation.reorder!('bar').equal?(relation) + assert_equal ['bar'], relation.order_values + assert relation.reordering_value + end + + test '#reorder! with symbol prepends the table name' do + assert relation.reorder!(:name).equal?(relation) + node = relation.order_values.first + + assert node.ascending? + assert_equal :name, node.expr.name + assert_equal "posts", node.expr.relation.name + end + + test 'reverse_order!' do + assert relation.reverse_order!.equal?(relation) + assert relation.reverse_order_value + relation.reverse_order! + assert !relation.reverse_order_value + end + + test 'create_with!' do + assert relation.create_with!(foo: 'bar').equal?(relation) + assert_equal({foo: 'bar'}, relation.create_with_value) + end + + test 'test_merge!' do + assert relation.merge!(where: :foo).equal?(relation) + assert_equal [:foo], relation.where_values + end + + test 'merge with a proc' do + assert_equal [:foo], relation.merge(-> { where(:foo) }).where_values + end + + test 'none!' do + assert relation.none!.equal?(relation) + assert_equal [NullRelation], relation.extending_values + assert relation.is_a?(NullRelation) + end + + test 'distinct!' do + relation.distinct! :foo + assert_equal :foo, relation.distinct_value + assert_equal :foo, relation.uniq_value # deprecated access + end + + test 'uniq! was replaced by distinct!' do + relation.uniq! :foo + assert_equal :foo, relation.distinct_value + assert_equal :foo, relation.uniq_value # deprecated access + end + end +end diff --git a/activerecord/test/cases/relation/predicate_builder_test.rb b/activerecord/test/cases/relation/predicate_builder_test.rb new file mode 100644 index 0000000000..14a8d97d36 --- /dev/null +++ b/activerecord/test/cases/relation/predicate_builder_test.rb @@ -0,0 +1,14 @@ +require "cases/helper" +require 'models/topic' + +module ActiveRecord + class PredicateBuilderTest < ActiveRecord::TestCase + def test_registering_new_handlers + PredicateBuilder.register_handler(Regexp, proc do |column, value| + Arel::Nodes::InfixOperation.new('~', column, value.source) + end) + + assert_match %r{["`]topics["`].["`]title["`] ~ 'rails'}i, Topic.where(title: /rails/).to_sql + end + end +end diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index 92d1e013e8..fd2420cb88 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -23,6 +23,12 @@ module ActiveRecord assert_equal([expected], relation.where_values) end + def test_not_with_nil + assert_raise ArgumentError do + Post.where.not(nil) + end + end + def test_not_in expected = Arel::Nodes::NotIn.new(Post.arel_table[@name], %w[hello goodbye]) relation = Post.where.not(title: %w[hello goodbye]) @@ -76,5 +82,35 @@ module ActiveRecord expected = Arel::Nodes::NotEqual.new(Post.arel_table[@name], 'ruby on rails') assert_equal(expected, relation.where_values[1]) end + + def test_rewhere_with_one_condition + relation = Post.where(title: 'hello').where(title: 'world').rewhere(title: 'alone') + + expected = Arel::Nodes::Equality.new(Post.arel_table[@name], 'alone') + assert_equal 1, relation.where_values.size + assert_equal expected, relation.where_values.first + end + + def test_rewhere_with_multiple_overwriting_conditions + relation = Post.where(title: 'hello').where(body: 'world').rewhere(title: 'alone', body: 'again') + + title_expected = Arel::Nodes::Equality.new(Post.arel_table['title'], 'alone') + body_expected = Arel::Nodes::Equality.new(Post.arel_table['body'], 'again') + + assert_equal 2, relation.where_values.size + assert_equal title_expected, relation.where_values.first + assert_equal body_expected, relation.where_values.second + end + + def test_rewhere_with_one_overwriting_condition_and_one_unrelated + relation = Post.where(title: 'hello').where(body: 'world').rewhere(title: 'alone') + + title_expected = Arel::Nodes::Equality.new(Post.arel_table['title'], 'alone') + body_expected = Arel::Nodes::Equality.new(Post.arel_table['body'], 'world') + + assert_equal 2, relation.where_values.size + assert_equal body_expected, relation.where_values.first + assert_equal title_expected, relation.where_values.second + end end end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index d333be3560..937f226b1d 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -24,6 +24,10 @@ module ActiveRecord } end + def test_rewhere_on_root + assert_equal posts(:welcome), Post.rewhere(title: 'Welcome to the weblog').first + end + def test_belongs_to_shallow_where author = Author.new author.id = 1 @@ -31,6 +35,21 @@ module ActiveRecord assert_equal Post.where(author_id: 1).to_sql, Post.where(author: author).to_sql end + def test_belongs_to_nil_where + assert_equal Post.where(author_id: nil).to_sql, Post.where(author: nil).to_sql + end + + def test_belongs_to_array_value_where + assert_equal Post.where(author_id: [1,2]).to_sql, Post.where(author: [1,2]).to_sql + end + + def test_belongs_to_nested_relation_where + expected = Post.where(author_id: Author.where(id: [1,2])).to_sql + actual = Post.where(author: Author.where(id: [1,2])).to_sql + + assert_equal expected, actual + end + def test_belongs_to_nested_where parent = Comment.new parent.id = 1 @@ -51,6 +70,25 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end + def test_polymorphic_nested_array_where + treasure = Treasure.new + treasure.id = 1 + hidden = HiddenTreasure.new + hidden.id = 2 + + expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: [treasure, hidden]) + actual = PriceEstimate.where(estimate_of: [treasure, hidden]) + + assert_equal expected.to_sql, actual.to_sql + end + + def test_polymorphic_nested_relation_where + expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: Treasure.where(id: [1,2])) + actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1,2])) + + assert_equal expected.to_sql, actual.to_sql + end + def test_polymorphic_sti_shallow_where treasure = HiddenTreasure.new treasure.id = 1 @@ -81,6 +119,31 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end + def test_decorated_polymorphic_where + treasure_decorator = Struct.new(:model) do + def self.method_missing(method, *args, &block) + Treasure.send(method, *args, &block) + end + + def is_a?(klass) + model.is_a?(klass) + end + + def method_missing(method, *args, &block) + model.send(method, *args, &block) + end + end + + treasure = Treasure.new + treasure.id = 1 + decorated_treasure = treasure_decorator.new(treasure) + + expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: 1) + actual = PriceEstimate.where(estimate_of: decorated_treasure) + + assert_equal expected.to_sql, actual.to_sql + end + def test_aliased_attribute expected = Topic.where(heading: 'The First Topic') actual = Topic.where(title: 'The First Topic') |