From f1acf1cc74bb9e847a4d27e97630d1b685764a93 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 2 Jan 2010 21:16:16 +0530 Subject: Give higher preference to second relation's equality predicates when merging --- activerecord/lib/active_record/relation.rb | 38 +++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8756695d46..44e2061efa 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -18,16 +18,32 @@ module ActiveRecord def merge(r) raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass - joins(r.relation.joins(r.relation)). - group(r.send(:group_clauses).join(', ')). - order(r.send(:order_clauses).join(', ')). - where(r.send(:where_clause)). - limit(r.taken). - offset(r.skipped). - select(r.send(:select_clauses).join(', ')). - eager_load(r.eager_load_associations). - preload(r.preload_associations). - from(r.send(:sources).present? ? r.send(:from_clauses) : nil) + merged_relation = spawn(table) + + [self, r].each do |r| + merged_relation = merged_relation. + joins(r.relation.joins(r.relation)). + group(r.send(:group_clauses).join(', ')). + order(r.send(:order_clauses).join(', ')). + limit(r.taken). + offset(r.skipped). + select(r.send(:select_clauses).join(', ')). + eager_load(r.eager_load_associations). + preload(r.preload_associations). + from(r.send(:sources).present? ? r.send(:from_clauses) : nil) + end + + merged_wheres = @relation.wheres + + r.wheres.each do |w| + if w.is_a?(Arel::Predicates::Equality) + merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name } + end + + merged_wheres << w + end + + merged_relation.where(*merged_wheres) end alias :& :merge @@ -129,7 +145,7 @@ module ActiveRecord end def spawn(relation = @relation) - relation = self.class.new(@klass, relation) + relation = Relation.new(@klass, relation) relation.readonly = @readonly relation.preload_associations = @preload_associations relation.eager_load_associations = @eager_load_associations -- cgit v1.2.3 From 32b48bf419eb22514be9a75981b9b0cf51a973a0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 2 Jan 2010 22:43:40 +0530 Subject: Use arel predicates instead of strings wherever possible when merging relations --- activerecord/lib/active_record/relation.rb | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 44e2061efa..6eb1d907e6 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -18,19 +18,18 @@ module ActiveRecord def merge(r) raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass - merged_relation = spawn(table) + merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations) + merged_relation.readonly = r.readonly - [self, r].each do |r| + [self.relation, r.relation].each do |arel| merged_relation = merged_relation. - joins(r.relation.joins(r.relation)). - group(r.send(:group_clauses).join(', ')). - order(r.send(:order_clauses).join(', ')). - limit(r.taken). - offset(r.skipped). - select(r.send(:select_clauses).join(', ')). - eager_load(r.eager_load_associations). - preload(r.preload_associations). - from(r.send(:sources).present? ? r.send(:from_clauses) : nil) + joins(arel.joins(arel)). + group(arel.groupings). + order(arel.send(:order_clauses).join(', ')). + limit(arel.taken). + offset(arel.skipped). + select(arel.send(:select_clauses)). + from(arel.sources) end merged_wheres = @relation.wheres -- cgit v1.2.3 From 65200d2933bd337b8347dd32502a12c2fddf24f0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 00:08:59 +0530 Subject: Implement Relation#new --- activerecord/lib/active_record/relation.rb | 13 ++++++++++++- activerecord/test/cases/relations_test.rb | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6eb1d907e6..0319781e03 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -15,6 +15,10 @@ module ActiveRecord @loaded, @readonly = false end + def new(*args, &block) + @klass.send(:with_scope, :create => create_scope) { @klass.new(*args, &block) } + end + def merge(r) raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass @@ -138,7 +142,7 @@ module ActiveRecord end def reset - @first = @last = nil + @first = @last = @create_scope = nil @records = [] self end @@ -181,6 +185,13 @@ module ActiveRecord end end + def create_scope + @create_scope ||= wheres.inject({}) do |hash, where| + hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) + hash + end + end + def where_clause(join_string = " AND ") @relation.send(:where_clauses).join(join_string) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 6605c8bf34..c4b2c21578 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -477,4 +477,20 @@ class RelationTest < ActiveRecord::TestCase assert posts.many? assert ! posts.limit(1).many? end + + def test_build + posts = Post.scoped + + post = posts.new + assert_kind_of Post, post + end + + def test_scoped_build + posts = Post.where(:title => 'You told a lie') + + post = posts.new + assert_kind_of Post, post + assert_equal 'You told a lie', post.title + end + end -- cgit v1.2.3 From ac1df91e5eac4959c0030e2b2ea39968f7f9ba1a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 00:16:14 +0530 Subject: Implement Relation#create and Relation#create! --- activerecord/lib/active_record/relation.rb | 14 +++++++++++++- activerecord/test/cases/relations_test.rb | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 0319781e03..b445ba67b7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -16,7 +16,15 @@ module ActiveRecord end def new(*args, &block) - @klass.send(:with_scope, :create => create_scope) { @klass.new(*args, &block) } + with_create_scope { @klass.new(*args, &block) } + end + + def create(*args, &block) + with_create_scope { @klass.create(*args, &block) } + end + + def create!(*args, &block) + with_create_scope { @klass.create!(*args, &block) } end def merge(r) @@ -185,6 +193,10 @@ module ActiveRecord end end + def with_create_scope + @klass.send(:with_scope, :create => create_scope) { yield } + end + def create_scope @create_scope ||= wheres.inject({}) do |hash, where| hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index c4b2c21578..bea2946130 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -10,6 +10,7 @@ require 'models/comment' require 'models/entrant' require 'models/developer' require 'models/company' +require 'models/bird' class RelationTest < ActiveRecord::TestCase fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, @@ -493,4 +494,27 @@ class RelationTest < ActiveRecord::TestCase assert_equal 'You told a lie', post.title end + def test_create + birds = Bird.scoped + + sparrow = birds.create + assert_kind_of Bird, sparrow + assert sparrow.new_record? + + hen = birds.where(:name => 'hen').create + assert ! hen.new_record? + assert_equal 'hen', hen.name + end + + def test_create_bang + birds = Bird.scoped + + assert_raises(ActiveRecord::RecordInvalid) { birds.create! } + + hen = birds.where(:name => 'hen').create! + assert_kind_of Bird, hen + assert ! hen.new_record? + assert_equal 'hen', hen.name + end + end -- cgit v1.2.3 From dcafe995bfe51e53dd04607956be9b54073e9cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 31 Dec 2009 12:29:44 +0100 Subject: Make nested attributes behave like in 2.3.5 and add a sanity test for it with I18n. --- .../lib/active_record/autosave_association.rb | 2 +- .../test/cases/autosave_association_test.rb | 30 +++++++++++++++++----- activerecord/test/cases/nested_attributes_test.rb | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 44c668b619..98ab64537e 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -267,7 +267,7 @@ module ActiveRecord unless valid = association.valid? if reflection.options[:autosave] association.errors.each do |attribute, message| - attribute = "#{reflection.name}_#{attribute}" + attribute = "#{reflection.name}.#{attribute}" errors[attribute] << message if errors[attribute].empty? end else diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 803e5b25b1..4e8f954e81 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -786,14 +786,14 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase def test_should_automatically_validate_the_associated_model @pirate.ship.name = '' assert @pirate.invalid? - assert @pirate.errors[:ship_name].any? + assert @pirate.errors[:"ship.name"].any? end def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid @pirate.ship.name = nil @pirate.catchphrase = nil assert @pirate.invalid? - assert @pirate.errors[:ship_name].any? + assert @pirate.errors[:"ship.name"].any? assert @pirate.errors[:catchphrase].any? end @@ -886,7 +886,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase def test_should_automatically_validate_the_associated_model @ship.pirate.catchphrase = '' assert @ship.invalid? - assert @ship.errors[:pirate_catchphrase].any? + assert @ship.errors[:"pirate.catchphrase"].any? end def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid @@ -894,7 +894,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase @ship.pirate.catchphrase = nil assert @ship.invalid? assert @ship.errors[:name].any? - assert @ship.errors[:pirate_catchphrase].any? + assert @ship.errors[:"pirate.catchphrase"].any? end def test_should_still_allow_to_bypass_validations_on_the_associated_model @@ -961,7 +961,7 @@ module AutosaveAssociationOnACollectionAssociationTests @pirate.send(@association_name).each { |child| child.name = '' } assert !@pirate.valid? - assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"] + assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"] assert @pirate.errors[@association_name].empty? end @@ -969,16 +969,32 @@ module AutosaveAssociationOnACollectionAssociationTests @pirate.send(@association_name).build(:name => '') assert !@pirate.valid? - assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"] + assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"] assert @pirate.errors[@association_name].empty? end + def test_should_default_invalid_error_from_i18n + I18n.backend.store_translations(:en, :activerecord => { :errors => { :models => + { @association_name.to_s.singularize.to_sym => { :blank => "cannot be blank" } } + }}) + + @pirate.send(@association_name).build(:name => '') + + assert !@pirate.valid? + assert_equal ["cannot be blank"], @pirate.errors["#{@association_name}.name"] + assert @pirate.errors[@association_name].empty? + ensure + I18n.backend.store_translations(:en, :activerecord => { :errors => { :models => + { @association_name.to_s.singularize.to_sym => nil } + }}) + end + def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid @pirate.send(@association_name).each { |child| child.name = '' } @pirate.catchphrase = nil assert !@pirate.valid? - assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"] + assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"] assert @pirate.errors[:catchphrase].any? end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 60c5bad225..8891282915 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -592,7 +592,7 @@ module NestedAttributesOnACollectionAssociationTests assert_no_difference ['Man.count', 'Interest.count'] do man = Man.create(:name => 'John', :interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}]) - assert !man.errors[:interests_man].empty? + assert !man.errors[:"interests.man"].empty? end end # restore :inverse_of -- cgit v1.2.3 From 5fdd0e80a4db778268e80435b471090cb14f7229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 31 Dec 2009 13:44:15 +0100 Subject: Be sure to convert namespaced names to we have 'Parrots name' instead of 'Parrots.name' in error messages. --- activerecord/test/cases/autosave_association_test.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 4e8f954e81..cf763d730a 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -982,6 +982,7 @@ module AutosaveAssociationOnACollectionAssociationTests assert !@pirate.valid? assert_equal ["cannot be blank"], @pirate.errors["#{@association_name}.name"] + assert_equal ["#{@association_name.to_s.titleize} name cannot be blank"], @pirate.errors.full_messages assert @pirate.errors[@association_name].empty? ensure I18n.backend.store_translations(:en, :activerecord => { :errors => { :models => -- cgit v1.2.3 From 7cc0a4cfa1d18c011d6e41f57d25eb10ed018eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 31 Dec 2009 14:14:29 +0100 Subject: Use activerecord.errors.format as in Rails 2.3.5. --- activerecord/lib/active_record/locale/en.yml | 3 +++ 1 file changed, 3 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index 092f5f0023..e33d389f8c 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -1,6 +1,9 @@ en: activerecord: errors: + # model.errors.full_messages format. + format: "{{attribute}} {{message}}" + # The values :model, :attribute and :value are always available for interpolation # The value :count is available when applicable. Can be used for pluralization. messages: -- cgit v1.2.3 From a9c790e10ffd06294200dbbed4692fbd60424800 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:00:14 +0530 Subject: Simply methods for checking eager loaded tables references in the query --- activerecord/lib/active_record/associations.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 80e6acf34c..24ea8cef33 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1830,22 +1830,22 @@ module ActiveRecord end # Checks if the conditions reference a table other than the current model table - def include_eager_conditions?(options, tables = nil, joined_tables = nil) - ((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any? + def include_eager_conditions?(options, joined_tables) + (conditions_tables(options) - joined_tables).any? end # Checks if the query order references a table other than the current model's table. - def include_eager_order?(options, tables = nil, joined_tables = nil) - ((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any? + def include_eager_order?(options, joined_tables) + (order_tables(options) - joined_tables).any? end - def include_eager_select?(options, joined_tables = nil) - (selects_tables(options) - (joined_tables || joined_tables(options))).any? + def include_eager_select?(options, joined_tables) + (selects_tables(options) - joined_tables).any? end def references_eager_loaded_tables?(options) joined_tables = joined_tables(options) - include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables) + include_eager_order?(options, joined_tables) || include_eager_conditions?(options, joined_tables) || include_eager_select?(options, joined_tables) end def using_limitable_reflections?(reflections) -- cgit v1.2.3 From c51347152abc7d8a97fd8df3eecfdbfd07673b68 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:08:01 +0530 Subject: Get rid of Model.construct_finder_arel_with_includes. Use construct_finder_arel instead --- .../associations/association_collection.rb | 2 +- activerecord/lib/active_record/base.rb | 21 ++++++++------------- activerecord/lib/active_record/named_scope.rb | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 1ceb0dbf96..cead614d0f 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -58,7 +58,7 @@ module ActiveRecord find_scope = construct_scope[:find].slice(:conditions, :order) with_scope(:find => find_scope) do - relation = @reflection.klass.send(:construct_finder_arel_with_includes, options) + relation = @reflection.klass.send(:construct_finder_arel, options) case args.first when :first, :last, :all diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7cefba2b82..e77cc6e697 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -645,7 +645,7 @@ module ActiveRecord #:nodoc: options = args.extract_options! set_readonly_option!(options) - relation = construct_finder_arel_with_includes(options) + relation = construct_finder_arel(options) case args.first when :first, :last, :all @@ -1581,17 +1581,7 @@ module ActiveRecord #:nodoc: offset(construct_offset(options[:offset], scope)). from(options[:from]) - lock = (scope && scope[:lock]) || options[:lock] - relation = relation.lock if lock.present? - - relation = relation.readonly if options[:readonly] - - relation - end - - def construct_finder_arel_with_includes(options = {}) - relation = construct_finder_arel(options) - include_associations = merge_includes(scope(:find, :include), options[:include]) + include_associations = merge_includes(scope && scope[:include], options[:include]) if include_associations.any? if references_eager_loaded_tables?(options) @@ -1601,6 +1591,11 @@ module ActiveRecord #:nodoc: end end + lock = (scope && scope[:lock]) || options[:lock] + relation = relation.lock if lock.present? + + relation = relation.readonly if options[:readonly] + relation end @@ -1722,7 +1717,7 @@ module ActiveRecord #:nodoc: super unless all_attributes_exists?(attribute_names) if match.finder? options = arguments.extract_options! - relation = options.any? ? construct_finder_arel_with_includes(options) : scoped + relation = options.any? ? construct_finder_arel(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 diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 5959265d5e..f63b249241 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -29,7 +29,7 @@ module ActiveRecord unless scoped?(:find) finder_needs_type_condition? ? active_relation.where(type_condition) : active_relation.spawn else - construct_finder_arel_with_includes + construct_finder_arel end end end -- cgit v1.2.3 From 6f5f23aaa7c4673fe923c6ad1450baaeccb40c02 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:24:28 +0530 Subject: Add Relation#includes to be an equivalent of current finder option :include --- .../associations/association_collection.rb | 2 +- activerecord/lib/active_record/base.rb | 2 +- activerecord/lib/active_record/relation.rb | 18 +++++++++++----- .../lib/active_record/relation/query_methods.rb | 4 ++++ activerecord/test/cases/relations_test.rb | 25 +++++++++++++++++++++- 5 files changed, 43 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index cead614d0f..358db6df1d 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -21,7 +21,7 @@ module ActiveRecord construct_sql end - delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :from, :lock, :readonly, :having, :to => :scoped + delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped def select(select = nil, &block) if block_given? diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e77cc6e697..b0bc7b928e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -655,7 +655,7 @@ module ActiveRecord #:nodoc: end end - delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :from, :lock, :readonly, :having, :to => :scoped + delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index b445ba67b7..fc4d1a6960 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -5,13 +5,15 @@ module ActiveRecord delegate :to_sql, :to => :relation delegate :length, :collect, :map, :each, :all?, :to => :to_a - attr_reader :relation, :klass, :preload_associations, :eager_load_associations - attr_writer :readonly, :preload_associations, :eager_load_associations, :table + attr_reader :relation, :klass + attr_writer :readonly, :table + attr_accessor :preload_associations, :eager_load_associations, :include_associations def initialize(klass, relation) @klass, @relation = klass, relation @preload_associations = [] @eager_load_associations = [] + @include_associations = [] @loaded, @readonly = false end @@ -30,7 +32,7 @@ module ActiveRecord def merge(r) raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass - merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations) + merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.include_associations) merged_relation.readonly = r.readonly [self.relation, r.relation].each do |arel| @@ -74,7 +76,9 @@ module ActiveRecord def to_a return @records if loaded? - @records = if @eager_load_associations.any? + find_with_associations = @eager_load_associations.any? + + @records = if find_with_associations begin @klass.send(:find_with_associations, { :select => @relation.send(:select_clauses).join(', '), @@ -94,7 +98,10 @@ module ActiveRecord @klass.find_by_sql(@relation.to_sql) end - @preload_associations.each {|associations| @klass.send(:preload_associations, @records, associations) } + preload = @preload_associations + preload += @include_associations unless find_with_associations + preload.each {|associations| @klass.send(:preload_associations, @records, associations) } + @records.each { |record| record.readonly! } if @readonly @loaded = true @@ -160,6 +167,7 @@ module ActiveRecord relation.readonly = @readonly relation.preload_associations = @preload_associations relation.eager_load_associations = @eager_load_associations + relation.include_associations = @include_associations relation.table = table relation end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4600aab574..cf2cc7ba70 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -5,6 +5,10 @@ module ActiveRecord spawn.tap {|r| r.preload_associations += Array.wrap(associations) } end + def includes(*associations) + spawn.tap {|r| r.include_associations += Array.wrap(associations) } + end + def eager_load(*associations) spawn.tap {|r| r.eager_load_associations += Array.wrap(associations) } end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index bea2946130..18f6152cc0 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -178,7 +178,7 @@ class RelationTest < ActiveRecord::TestCase end end - def test_find_with_included_associations + def test_find_with_preloaded_associations assert_queries(2) do posts = Post.preload(:comments) assert posts.first.comments.first @@ -206,6 +206,29 @@ class RelationTest < ActiveRecord::TestCase end end + def test_find_with_included_associations + assert_queries(2) do + posts = Post.includes(:comments) + assert posts.first.comments.first + end + + assert_queries(2) do + posts = Post.scoped.includes(:comments) + assert posts.first.comments.first + end + + assert_queries(2) do + posts = Post.includes(:author) + assert posts.first.author + end + + assert_queries(3) do + posts = Post.includes(:author, :comments).to_a + assert posts.first.author + assert posts.first.comments.first + end + end + def test_default_scope_with_conditions_string assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.scoped.map(&:id).sort assert_equal nil, DeveloperCalledDavid.create!.name -- cgit v1.2.3 From eb7fdb94647c42e31370b7faa1a474a966053f4d Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:27:14 +0530 Subject: Make Relation#includes behave exactly like the existing :include option --- activerecord/lib/active_record/base.rb | 13 ++---------- activerecord/lib/active_record/relation.rb | 33 +++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b0bc7b928e..70776c7aa2 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1579,17 +1579,8 @@ module ActiveRecord #:nodoc: order(construct_order(options[:order], scope)). limit(construct_limit(options[:limit], scope)). offset(construct_offset(options[:offset], scope)). - from(options[:from]) - - include_associations = merge_includes(scope && scope[:include], options[:include]) - - if include_associations.any? - if references_eager_loaded_tables?(options) - relation = relation.eager_load(include_associations) - else - relation = relation.preload(include_associations) - end - end + from(options[:from]). + includes( merge_includes(scope && scope[:include], options[:include])) lock = (scope && scope[:lock]) || options[:lock] relation = relation.lock if lock.present? diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index fc4d1a6960..c828c0b751 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -76,7 +76,7 @@ module ActiveRecord def to_a return @records if loaded? - find_with_associations = @eager_load_associations.any? + find_with_associations = @eager_load_associations.any? || references_eager_loaded_tables? @records = if find_with_associations begin @@ -90,7 +90,7 @@ module ActiveRecord :offset => @relation.skipped, :from => (@relation.send(:from_clauses) if @relation.send(:sources).present?) }, - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) + ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations + @include_associations, nil)) rescue ThrowResult [] end @@ -157,7 +157,7 @@ module ActiveRecord end def reset - @first = @last = @create_scope = nil + @first = @last = @create_scope = @joined_tables = nil @records = [] self end @@ -216,5 +216,32 @@ module ActiveRecord @relation.send(:where_clauses).join(join_string) end + def references_eager_loaded_tables? + include_eager_order? || include_eager_conditions? || include_eager_select? + end + + def include_eager_order? + order_clause = @relation.send(:order_clauses).join(', ') + (tables_in_string(order_clause) - joined_tables).any? + end + + def include_eager_conditions? + (tables_in_string(where_clause) - joined_tables).any? + end + + def include_eager_select? + select_clause = @relation.send(:select_clauses).join(', ') + (tables_in_string(select_clause) - joined_tables).any? + end + + def joined_tables + @joined_tables ||= (tables_in_string(@relation.joins(relation)) + [table.name, table.table_alias]).compact.uniq + end + + def tables_in_string(string) + return [] if string.blank? + string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.uniq + end + end end -- cgit v1.2.3 From 0d1a2a3c2239ed02f39e3e4690b905b0e86e80be Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:49:10 +0530 Subject: Remove unused code from association.rb now that Relation takes care of checking the referenced tables --- activerecord/lib/active_record/associations.rb | 78 -------------------------- 1 file changed, 78 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 24ea8cef33..a2cb41f9ab 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1770,84 +1770,6 @@ module ActiveRecord relation.to_sql end - def tables_in_string(string) - return [] if string.blank? - string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten - end - - def tables_in_hash(hash) - return [] if hash.blank? - tables = hash.map do |key, value| - if value.is_a?(Hash) - key.to_s - else - tables_in_string(key) if key.is_a?(String) - end - end - tables.flatten.compact - end - - def conditions_tables(options) - # look in both sets of conditions - conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond| - case cond - when nil then all - when Array then all << tables_in_string(cond.first) - when Hash then all << tables_in_hash(cond) - else all << tables_in_string(cond) - end - end - conditions.flatten - end - - def order_tables(options) - order = [options[:order], scope(:find, :order) ].join(", ") - return [] unless order && order.is_a?(String) - tables_in_string(order) - end - - def selects_tables(options) - select = options[:select] - return [] unless select && select.is_a?(String) - tables_in_string(select) - end - - def joined_tables(options) - scope = scope(:find) - joins = options[:joins] - merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) - [table_name] + case merged_joins - when Symbol, Hash, Array - if array_of_strings?(merged_joins) - tables_in_string(merged_joins.join(' ')) - else - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_joins, nil) - join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact - end - else - tables_in_string(merged_joins) - end - end - - # Checks if the conditions reference a table other than the current model table - def include_eager_conditions?(options, joined_tables) - (conditions_tables(options) - joined_tables).any? - end - - # Checks if the query order references a table other than the current model's table. - def include_eager_order?(options, joined_tables) - (order_tables(options) - joined_tables).any? - end - - def include_eager_select?(options, joined_tables) - (selects_tables(options) - joined_tables).any? - end - - def references_eager_loaded_tables?(options) - joined_tables = joined_tables(options) - include_eager_order?(options, joined_tables) || include_eager_conditions?(options, joined_tables) || include_eager_select?(options, joined_tables) - end - def using_limitable_reflections?(reflections) reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero? end -- cgit v1.2.3 From e9ebf8b898e0dba4ca62cca7eb26987a9117a20e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:56:21 +0530 Subject: Cache Relation#to_sql --- activerecord/lib/active_record/relation.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index c828c0b751..31c7acf346 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -2,7 +2,6 @@ module ActiveRecord class Relation include QueryMethods, FinderMethods, CalculationMethods - delegate :to_sql, :to => :relation delegate :length, :collect, :map, :each, :all?, :to => :to_a attr_reader :relation, :klass @@ -157,7 +156,7 @@ module ActiveRecord end def reset - @first = @last = @create_scope = @joined_tables = nil + @first = @last = @create_scope = @joined_tables = @to_sql = nil @records = [] self end @@ -180,6 +179,10 @@ module ActiveRecord @primary_key ||= table[@klass.primary_key] end + def to_sql + @to_sql ||= @relation.to_sql + end + protected def method_missing(method, *args, &block) -- cgit v1.2.3 From 47da00e94b5b35fb98add859feae1bfc47d969ec Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 03:58:34 +0530 Subject: Further simplify Relation#references_eager_loaded_tables? --- activerecord/lib/active_record/relation.rb | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 31c7acf346..114095b7ef 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -156,7 +156,7 @@ module ActiveRecord end def reset - @first = @last = @create_scope = @joined_tables = @to_sql = nil + @first = @last = @create_scope = @to_sql = nil @records = [] self end @@ -220,25 +220,8 @@ module ActiveRecord end def references_eager_loaded_tables? - include_eager_order? || include_eager_conditions? || include_eager_select? - end - - def include_eager_order? - order_clause = @relation.send(:order_clauses).join(', ') - (tables_in_string(order_clause) - joined_tables).any? - end - - def include_eager_conditions? - (tables_in_string(where_clause) - joined_tables).any? - end - - def include_eager_select? - select_clause = @relation.send(:select_clauses).join(', ') - (tables_in_string(select_clause) - joined_tables).any? - end - - def joined_tables - @joined_tables ||= (tables_in_string(@relation.joins(relation)) + [table.name, table.table_alias]).compact.uniq + joined_tables = (tables_in_string(@relation.joins(relation)) + [table.name, table.table_alias]).compact.uniq + (tables_in_string(to_sql) - joined_tables).any? end def tables_in_string(string) -- cgit v1.2.3 From 8e57deed8b4efad6ea1c551f415b74596111f890 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 04:09:36 +0530 Subject: Remove optional join_dependency argument as Relation always supplies it --- activerecord/lib/active_record/associations.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a2cb41f9ab..d74b21b690 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1465,8 +1465,7 @@ module ActiveRecord after_destroy(method_name) end - def find_with_associations(options = {}, join_dependency = nil) - join_dependency ||= JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins]) + def find_with_associations(options = {}, join_dependency) rows = select_all_rows(options, join_dependency) join_dependency.instantiate(rows) rescue ThrowResult -- cgit v1.2.3 From 8571aa613f1707401b0fe54c0fa1687604c48e0a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 2 Jan 2010 18:39:36 -0800 Subject: Revert "Remove optional join_dependency argument as Relation always supplies it" This reverts commit 8e57deed8b4efad6ea1c551f415b74596111f890. --- activerecord/lib/active_record/associations.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d74b21b690..a2cb41f9ab 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1465,7 +1465,8 @@ module ActiveRecord after_destroy(method_name) end - def find_with_associations(options = {}, join_dependency) + def find_with_associations(options = {}, join_dependency = nil) + join_dependency ||= JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins]) rows = select_all_rows(options, join_dependency) join_dependency.instantiate(rows) rescue ThrowResult -- cgit v1.2.3 From 4939f95c9b0c1e68b442c94640831e05eff15a32 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 11:08:57 +0530 Subject: Reapply "Remove optional join_dependency argument as Relation always supplies it" - Now without syntax errors --- activerecord/lib/active_record/associations.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a2cb41f9ab..b2ae447d5e 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1465,8 +1465,7 @@ module ActiveRecord after_destroy(method_name) end - def find_with_associations(options = {}, join_dependency = nil) - join_dependency ||= JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins]) + def find_with_associations(options, join_dependency) rows = select_all_rows(options, join_dependency) join_dependency.instantiate(rows) rescue ThrowResult -- cgit v1.2.3 From 3eca0ab8388a84bf34a78395edf85e30c6943c63 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 18:47:34 +0530 Subject: Give preference to the second relation's order when merging --- activerecord/lib/active_record/relation.rb | 13 ++++++++++--- activerecord/test/cases/relations_test.rb | 1 - 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 114095b7ef..9cfd9b6d23 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -38,13 +38,16 @@ module ActiveRecord merged_relation = merged_relation. joins(arel.joins(arel)). group(arel.groupings). - order(arel.send(:order_clauses).join(', ')). limit(arel.taken). offset(arel.skipped). select(arel.send(:select_clauses)). from(arel.sources) end + relation_order = r.send(:order_clause) + merged_order = relation_order.present? ? relation_order : order_clause + merged_relation = merged_relation.order(merged_order) + merged_wheres = @relation.wheres r.wheres.each do |w| @@ -83,7 +86,7 @@ module ActiveRecord :select => @relation.send(:select_clauses).join(', '), :joins => @relation.joins(relation), :group => @relation.send(:group_clauses).join(', '), - :order => @relation.send(:order_clauses).join(', '), + :order => order_clause, :conditions => where_clause, :limit => @relation.taken, :offset => @relation.skipped, @@ -156,7 +159,7 @@ module ActiveRecord end def reset - @first = @last = @create_scope = @to_sql = nil + @first = @last = @create_scope = @to_sql = @order_clause = nil @records = [] self end @@ -219,6 +222,10 @@ module ActiveRecord @relation.send(:where_clauses).join(join_string) end + def order_clause + @order_clause ||= @relation.send(:order_clauses).join(', ') + end + def references_eager_loaded_tables? joined_tables = (tables_in_string(@relation.joins(relation)) + [table.name, table.table_alias]).compact.uniq (tables_in_string(to_sql) - joined_tables).any? diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 18f6152cc0..7046420ebd 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -539,5 +539,4 @@ class RelationTest < ActiveRecord::TestCase assert ! hen.new_record? assert_equal 'hen', hen.name end - end -- cgit v1.2.3 From 8edfa8f82fecb23af506278d510ea900e6021e6b Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 18:57:57 +0530 Subject: Move Relation#spawn and Relation#merge to a separate module --- activerecord/lib/active_record.rb | 1 + activerecord/lib/active_record/relation.rb | 47 +-------------------- .../lib/active_record/relation/spawn_methods.rb | 49 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 activerecord/lib/active_record/relation/spawn_methods.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index cf439b0dc0..728dec8925 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -55,6 +55,7 @@ module ActiveRecord autoload :FinderMethods autoload :CalculationMethods autoload :PredicateBuilder + autoload :SpawnMethods end autoload :Base diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 9cfd9b6d23..a4d11f813d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,6 +1,6 @@ module ActiveRecord class Relation - include QueryMethods, FinderMethods, CalculationMethods + include QueryMethods, FinderMethods, CalculationMethods, SpawnMethods delegate :length, :collect, :map, :each, :all?, :to => :to_a @@ -28,41 +28,6 @@ module ActiveRecord with_create_scope { @klass.create!(*args, &block) } end - def merge(r) - raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass - - merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.include_associations) - merged_relation.readonly = r.readonly - - [self.relation, r.relation].each do |arel| - merged_relation = merged_relation. - joins(arel.joins(arel)). - group(arel.groupings). - limit(arel.taken). - offset(arel.skipped). - select(arel.send(:select_clauses)). - from(arel.sources) - end - - relation_order = r.send(:order_clause) - merged_order = relation_order.present? ? relation_order : order_clause - merged_relation = merged_relation.order(merged_order) - - merged_wheres = @relation.wheres - - r.wheres.each do |w| - if w.is_a?(Arel::Predicates::Equality) - merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name } - end - - merged_wheres << w - end - - merged_relation.where(*merged_wheres) - end - - alias :& :merge - def respond_to?(method, include_private = false) return true if @relation.respond_to?(method, include_private) || Array.method_defined?(method) @@ -164,16 +129,6 @@ module ActiveRecord self end - def spawn(relation = @relation) - relation = Relation.new(@klass, relation) - relation.readonly = @readonly - relation.preload_associations = @preload_associations - relation.eager_load_associations = @eager_load_associations - relation.include_associations = @include_associations - relation.table = table - relation - end - def table @table ||= Arel::Table.new(@klass.table_name, Arel::Sql::Engine.new(@klass)) end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb new file mode 100644 index 0000000000..4a64f0c6a9 --- /dev/null +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -0,0 +1,49 @@ +module ActiveRecord + module SpawnMethods + def spawn(relation = @relation) + relation = Relation.new(@klass, relation) + relation.readonly = @readonly + relation.preload_associations = @preload_associations + relation.eager_load_associations = @eager_load_associations + relation.include_associations = @include_associations + relation.table = table + relation + end + + def merge(r) + raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass + + merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.include_associations) + merged_relation.readonly = r.readonly + + [self.relation, r.relation].each do |arel| + merged_relation = merged_relation. + joins(arel.joins(arel)). + group(arel.groupings). + limit(arel.taken). + offset(arel.skipped). + select(arel.send(:select_clauses)). + from(arel.sources) + end + + relation_order = r.send(:order_clause) + merged_order = relation_order.present? ? relation_order : order_clause + merged_relation = merged_relation.order(merged_order) + + merged_wheres = @relation.wheres + + r.wheres.each do |w| + if w.is_a?(Arel::Predicates::Equality) + merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name } + end + + merged_wheres << w + end + + merged_relation.where(*merged_wheres) + end + + alias :& :merge + + end +end -- cgit v1.2.3 From 22bfd8b09804db28e05e598e062d58fd2ab36485 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 19:20:40 +0530 Subject: Rename a variable name for consistency --- activerecord/lib/active_record/relation.rb | 8 ++++---- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/lib/active_record/relation/spawn_methods.rb | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a4d11f813d..ec8ae21726 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -6,13 +6,13 @@ module ActiveRecord attr_reader :relation, :klass attr_writer :readonly, :table - attr_accessor :preload_associations, :eager_load_associations, :include_associations + attr_accessor :preload_associations, :eager_load_associations, :includes_associations def initialize(klass, relation) @klass, @relation = klass, relation @preload_associations = [] @eager_load_associations = [] - @include_associations = [] + @includes_associations = [] @loaded, @readonly = false end @@ -57,7 +57,7 @@ module ActiveRecord :offset => @relation.skipped, :from => (@relation.send(:from_clauses) if @relation.send(:sources).present?) }, - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations + @include_associations, nil)) + ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations + @includes_associations, nil)) rescue ThrowResult [] end @@ -66,7 +66,7 @@ module ActiveRecord end preload = @preload_associations - preload += @include_associations unless find_with_associations + preload += @includes_associations unless find_with_associations preload.each {|associations| @klass.send(:preload_associations, @records, associations) } @records.each { |record| record.readonly! } if @readonly diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index cf2cc7ba70..525a9cb365 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -6,7 +6,7 @@ module ActiveRecord end def includes(*associations) - spawn.tap {|r| r.include_associations += Array.wrap(associations) } + spawn.tap {|r| r.includes_associations += Array.wrap(associations) } end def eager_load(*associations) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 4a64f0c6a9..7b15279253 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -5,7 +5,7 @@ module ActiveRecord relation.readonly = @readonly relation.preload_associations = @preload_associations relation.eager_load_associations = @eager_load_associations - relation.include_associations = @include_associations + relation.includes_associations = @includes_associations relation.table = table relation end @@ -13,7 +13,7 @@ module ActiveRecord def merge(r) raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass - merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.include_associations) + merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.includes_associations) merged_relation.readonly = r.readonly [self.relation, r.relation].each do |arel| -- cgit v1.2.3 From af5e1b4cc6fd3ae5f5e175751373a5e60934385b Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 19:42:29 +0530 Subject: Add Relation#except --- activerecord/CHANGELOG | 5 +++++ .../lib/active_record/relation/spawn_methods.rb | 22 ++++++++++++++++++++++ activerecord/test/cases/relations_test.rb | 12 ++++++++++++ 3 files changed, 39 insertions(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 4846774deb..0cfd8cdc87 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,10 @@ *Edge* +* Add Relation#except. [Pratik Naik] + + one_red_item = Item.where(:colour => 'red').limit(1) + all_items = one_red_item.except(:where, :limit) + * Add Relation#delete_all. [Pratik Naik] Item.where(:colour => 'red').delete_all diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 7b15279253..b9e3457aac 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -45,5 +45,27 @@ module ActiveRecord alias :& :merge + def except(*skips) + result = Relation.new(@klass, table) + result.table = table + + [:eager_load, :preload, :includes].each do |load_method| + result = result.send(load_method, send(:"#{load_method}_associations")) + end + + result.readonly = self.readonly unless skips.include?(:readonly) + + result = result.joins(@relation.joins(@relation)) unless skips.include?(:joins) + result = result.group(@relation.groupings) unless skips.include?(:group) + result = result.limit(@relation.taken) unless skips.include?(:limit) + result = result.offset(@relation.skipped) unless skips.include?(:offset) + result = result.select(@relation.send(:select_clauses)) unless skips.include?(:select) + result = result.from(@relation.sources) unless skips.include?(:from) + result = result.order(order_clause) unless skips.include?(:order) + result = result.where(*@relation.wheres) unless skips.include?(:where) + + result + end + end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 7046420ebd..bcecf74737 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -539,4 +539,16 @@ class RelationTest < ActiveRecord::TestCase assert ! hen.new_record? assert_equal 'hen', hen.name end + + def test_except + relation = Post.where(:author_id => 1).order('id ASC').limit(1) + assert_equal [posts(:welcome)], relation.all + + author_posts = relation.except(:order, :limit) + assert_equal Post.where(:author_id => 1).all, author_posts.all + + all_posts = relation.except(:where, :order, :limit) + assert_equal Post.all, all_posts.all + end + end -- cgit v1.2.3 From 3db876cb761837ebf9b02d22846353e277ff14cd Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 20:50:04 +0530 Subject: Relation#merge and Relation#except should respect havings --- activerecord/lib/active_record/relation/spawn_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index b9e3457aac..ea58812faa 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -23,7 +23,8 @@ module ActiveRecord limit(arel.taken). offset(arel.skipped). select(arel.send(:select_clauses)). - from(arel.sources) + from(arel.sources). + having(arel.havings) end relation_order = r.send(:order_clause) @@ -63,6 +64,7 @@ module ActiveRecord result = result.from(@relation.sources) unless skips.include?(:from) result = result.order(order_clause) unless skips.include?(:order) result = result.where(*@relation.wheres) unless skips.include?(:where) + result = result.having(*@relation.havings) unless skips.include?(:having) result end -- cgit v1.2.3 From 00f3f6dc3145f2d7e8b8dadd966008cd0fa54636 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 3 Jan 2010 21:24:09 +0530 Subject: Relation#merge and Relation#except should respect locks --- activerecord/lib/active_record/relation/spawn_methods.rb | 4 +++- activerecord/test/cases/relations_test.rb | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index ea58812faa..a637e97155 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -24,7 +24,8 @@ module ActiveRecord offset(arel.skipped). select(arel.send(:select_clauses)). from(arel.sources). - having(arel.havings) + having(arel.havings). + lock(arel.locked) end relation_order = r.send(:order_clause) @@ -65,6 +66,7 @@ module ActiveRecord result = result.order(order_clause) unless skips.include?(:order) result = result.where(*@relation.wheres) unless skips.include?(:where) result = result.having(*@relation.havings) unless skips.include?(:having) + result = result.lock(@relation.locked) unless skips.include?(:lock) result end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index bcecf74737..f895f8b8d2 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -407,6 +407,11 @@ class RelationTest < ActiveRecord::TestCase end end + def test_relation_merging_with_locks + devs = Developer.lock.where("salary >= 80000").order("id DESC") & Developer.limit(2) + assert devs.locked.present? + end + def test_relation_merging_with_preload [Post.scoped & Post.preload(:author), Post.preload(:author) & Post.scoped].each do |posts| assert_queries(2) { assert posts.first.author } -- cgit v1.2.3 From 6fbe9ef2ffb1858027130789246f3ae24a0a182f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 3 Jan 2010 20:39:42 +0100 Subject: Use namespaces in notifications. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- activerecord/lib/active_record/railtie.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index d09aa3c4d2..ba12dcd9ce 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -201,7 +201,7 @@ module ActiveRecord protected def log(sql, name) result = nil - ActiveSupport::Notifications.instrument(:sql, :sql => sql, :name => name) do + ActiveSupport::Notifications.instrument("activerecord.sql", :sql => sql, :name => name) do @runtime += Benchmark.ms { result = yield } end result diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 657ee738c0..51eb04c9d0 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -62,7 +62,7 @@ module ActiveRecord initializer "active_record.notifications" do require 'active_support/notifications' - ActiveSupport::Notifications.subscribe("sql") do |name, before, after, instrumenter_id, payload| + ActiveSupport::Notifications.subscribe("activerecord.sql") do |name, before, after, instrumenter_id, payload| ActiveRecord::Base.connection.log_info(payload[:sql], payload[:name], (after - before) * 1000) end end -- cgit v1.2.3 From a115b5d79a850bb56cd3c9db9a05d6da35e3d7be Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 4 Jan 2010 02:05:18 +0530 Subject: Ensure using proper engine for Arel::Table --- activerecord/lib/active_record/base.rb | 10 ++++++++-- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/multiple_db_test.rb | 6 ++++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 70776c7aa2..ec7725d256 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1510,11 +1510,17 @@ module ActiveRecord #:nodoc: end def active_relation_table(table_name_alias = nil) - Arel::Table.new(table_name, :as => table_name_alias) + Arel::Table.new(table_name, :as => table_name_alias, :engine => active_relation_engine) end def active_relation_engine - @active_relation_engine ||= Arel::Sql::Engine.new(self) + @active_relation_engine ||= begin + if self == ActiveRecord::Base + Arel::Table.engine + else + connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.active_relation_engine + end + end end private diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ec8ae21726..6b9925d4e7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -130,7 +130,7 @@ module ActiveRecord end def table - @table ||= Arel::Table.new(@klass.table_name, Arel::Sql::Engine.new(@klass)) + @table ||= Arel::Table.new(@klass.table_name, :engine => @klass.active_relation_engine) end def primary_key diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb index 7c3e0f2ca6..6155bfd50a 100644 --- a/activerecord/test/cases/multiple_db_test.rb +++ b/activerecord/test/cases/multiple_db_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/entrant' +require 'models/bird' # So we can test whether Course.connection survives a reload. require_dependency 'models/course' @@ -82,4 +83,9 @@ class MultipleDbTest < ActiveRecord::TestCase assert_equal "Ruby Development", Course.find(1).name assert_equal "Ruby Developer", Entrant.find(1).name end + + def test_arel_table_engines + assert_not_equal Entrant.active_relation_engine, Course.active_relation_engine + assert_equal Entrant.active_relation_engine, Bird.active_relation_engine + end end -- cgit v1.2.3 From a68165833a7ba50a1e3d731afe8934d19e3ced99 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 4 Jan 2010 03:50:16 +0530 Subject: Add Relation#create_with to explictily specify create scope --- activerecord/lib/active_record/relation.rb | 16 +++++++++------- activerecord/lib/active_record/relation/query_methods.rb | 4 ++++ activerecord/lib/active_record/relation/spawn_methods.rb | 10 ++++++++++ activerecord/test/cases/relations_test.rb | 8 ++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6b9925d4e7..487b54f27d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -6,7 +6,7 @@ module ActiveRecord attr_reader :relation, :klass attr_writer :readonly, :table - attr_accessor :preload_associations, :eager_load_associations, :includes_associations + attr_accessor :preload_associations, :eager_load_associations, :includes_associations, :create_with_attributes def initialize(klass, relation) @klass, @relation = klass, relation @@ -124,7 +124,7 @@ module ActiveRecord end def reset - @first = @last = @create_scope = @to_sql = @order_clause = nil + @first = @last = @to_sql = @order_clause = @scope_for_create = nil @records = [] self end @@ -163,13 +163,15 @@ module ActiveRecord end def with_create_scope - @klass.send(:with_scope, :create => create_scope) { yield } + @klass.send(:with_scope, :create => scope_for_create) { yield } end - def create_scope - @create_scope ||= wheres.inject({}) do |hash, where| - hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) - hash + def scope_for_create + @scope_for_create ||= begin + @create_with_attributes || wheres.inject({}) do |hash, where| + hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) + hash + end end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 525a9cb365..5d7bf0b7bc 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -17,6 +17,10 @@ module ActiveRecord spawn.tap {|r| r.readonly = status } end + def create_with(attributes = {}) + spawn.tap {|r| r.create_with_attributes = attributes } + end + def select(selects) if selects.present? relation = spawn(@relation.project(selects)) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a637e97155..4ecee8c634 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -6,6 +6,7 @@ module ActiveRecord relation.preload_associations = @preload_associations relation.eager_load_associations = @eager_load_associations relation.includes_associations = @includes_associations + relation.create_with_attributes = @create_with_attributes relation.table = table relation end @@ -32,6 +33,14 @@ module ActiveRecord merged_order = relation_order.present? ? relation_order : order_clause merged_relation = merged_relation.order(merged_order) + merged_relation.create_with_attributes = @create_with_attributes + + if @create_with_attributes && r.create_with_attributes + merged_relation.create_with_attributes = @create_with_attributes.merge(r.create_with_attributes) + else + merged_relation.create_with_attributes = r.create_with_attributes || @create_with_attributes + end + merged_wheres = @relation.wheres r.wheres.each do |w| @@ -56,6 +65,7 @@ module ActiveRecord end result.readonly = self.readonly unless skips.include?(:readonly) + result.create_with_attributes = @create_with_attributes unless skips.include?(:create_with) result = result.joins(@relation.joins(@relation)) unless skips.include?(:joins) result = result.group(@relation.groupings) unless skips.include?(:group) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index f895f8b8d2..195889f1df 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -545,6 +545,14 @@ class RelationTest < ActiveRecord::TestCase assert_equal 'hen', hen.name end + def test_explicit_create_scope + hens = Bird.where(:name => 'hen') + assert_equal 'hen', hens.new.name + + hens = hens.create_with(:name => 'cock') + assert_equal 'cock', hens.new.name + end + def test_except relation = Post.where(:author_id => 1).order('id ASC').limit(1) assert_equal [posts(:welcome)], relation.all -- cgit v1.2.3 From 3990310a2bedd0dff5753e3e9b1282e686cff0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 4 Jan 2010 00:03:56 +0100 Subject: Use underscore in notification namespaces. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- activerecord/lib/active_record/railtie.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index ba12dcd9ce..5eedf448a4 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -201,7 +201,7 @@ module ActiveRecord protected def log(sql, name) result = nil - ActiveSupport::Notifications.instrument("activerecord.sql", :sql => sql, :name => name) do + ActiveSupport::Notifications.instrument("active_record.sql", :sql => sql, :name => name) do @runtime += Benchmark.ms { result = yield } end result diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 51eb04c9d0..a35edace19 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -62,7 +62,7 @@ module ActiveRecord initializer "active_record.notifications" do require 'active_support/notifications' - ActiveSupport::Notifications.subscribe("activerecord.sql") do |name, before, after, instrumenter_id, payload| + ActiveSupport::Notifications.subscribe("active_record.sql") do |name, before, after, instrumenter_id, payload| ActiveRecord::Base.connection.log_info(payload[:sql], payload[:name], (after - before) * 1000) end end -- cgit v1.2.3