From 311ea94f73c95be87f9a474da122719eebee3f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Mon, 2 Aug 2010 11:07:43 +0200 Subject: added failing touch propagation test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/timestamp_test.rb | 17 +++++++++++++++++ activerecord/test/schema/schema.rb | 2 ++ 2 files changed, 19 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index f765540808..e3d12f6214 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -82,4 +82,21 @@ class TimestampTest < ActiveRecord::TestCase ensure Pet.belongs_to :owner, :touch => true end + + def test_touching_a_record_touches_parent_record_and_grandparent_record + Toy.belongs_to :pet, :touch => true + Pet.belongs_to :owner, :touch => true + + toy = Toy.first + pet = toy.pet + owner = pet.owner + + previously_owner_updated_at = owner.updated_at + + toy.touch + + assert_not_equal previously_owner_updated_at, owner.updated_at + ensure + Toy.belongs_to :pet + end end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index f3fd37cd61..a0e620c2ef 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -398,6 +398,7 @@ ActiveRecord::Schema.define do create_table :pets, :primary_key => :pet_id ,:force => true do |t| t.string :name t.integer :owner_id, :integer + t.timestamps end create_table :pirates, :force => true do |t| @@ -530,6 +531,7 @@ ActiveRecord::Schema.define do create_table :toys, :primary_key => :toy_id ,:force => true do |t| t.string :name t.integer :pet_id, :integer + t.timestamps end create_table :traffic_lights, :force => true do |t| -- cgit v1.2.3 From b613c3cc7b08b00595e33d1b5302cd5d42687d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 Aug 2010 16:16:02 +0200 Subject: Add an internal (private API) after_touch callback. [#5271 state:resolved] --- activerecord/lib/active_record/associations.rb | 1 + activerecord/lib/active_record/callbacks.rb | 8 ++++++-- activerecord/lib/active_record/persistence.rb | 13 +++++++++++++ activerecord/lib/active_record/timestamp.rb | 13 ------------- activerecord/test/cases/timestamp_test.rb | 8 ++++---- 5 files changed, 24 insertions(+), 19 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index f540aa7f25..9663a36edf 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1498,6 +1498,7 @@ module ActiveRecord end end after_save(method_name) + after_touch(method_name) after_destroy(method_name) end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 637dac450b..82c45a41b0 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -228,7 +228,7 @@ module ActiveRecord extend ActiveSupport::Concern CALLBACKS = [ - :after_initialize, :after_find, :before_validation, :after_validation, + :after_initialize, :after_find, :after_touch, :before_validation, :after_validation, :before_save, :around_save, :after_save, :before_create, :around_create, :after_create, :before_update, :around_update, :after_update, :before_destroy, :around_destroy, :after_destroy @@ -238,7 +238,7 @@ module ActiveRecord extend ActiveModel::Callbacks include ActiveModel::Validations::Callbacks - define_model_callbacks :initialize, :find, :only => :after + define_model_callbacks :initialize, :find, :touch, :only => :after define_model_callbacks :save, :create, :update, :destroy end @@ -256,6 +256,10 @@ module ActiveRecord _run_destroy_callbacks { super } end + def touch(*) #:nodoc: + _run_touch_callbacks { super } + end + def deprecated_callback_method(symbol) #:nodoc: if respond_to?(symbol, true) ActiveSupport::Deprecation.warn("Overwriting #{symbol} in your models has been deprecated, please use Base##{symbol} :method_name instead") diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 38b91652ee..cbc2220e96 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -218,6 +218,19 @@ module ActiveRecord self end + # Saves the record with the updated_at/on attributes set to the current time. + # Please note that no validation is performed and no callbacks are executed. + # If an attribute name is passed, that attribute is updated along with + # updated_at/on attributes. + # + # Examples: + # + # product.touch # updates updated_at/on + # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on + def touch(attribute = nil) + update_attribute(attribute, current_time_from_proper_timezone) + end + private def create_or_update raise ReadOnlyRecord if readonly? diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 92f7a7753d..32b3f03f13 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -31,19 +31,6 @@ module ActiveRecord class_inheritable_accessor :record_timestamps, :instance_writer => false self.record_timestamps = true end - - # Saves the record with the updated_at/on attributes set to the current time. - # Please note that no validation is performed and no callbacks are executed. - # If an attribute name is passed, that attribute is updated along with - # updated_at/on attributes. - # - # Examples: - # - # product.touch # updates updated_at/on - # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on - def touch(attribute = nil) - update_attribute(attribute, current_time_from_proper_timezone) - end private diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index e3d12f6214..06ab7aa9c7 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -2,9 +2,10 @@ require 'cases/helper' require 'models/developer' require 'models/owner' require 'models/pet' +require 'models/toy' class TimestampTest < ActiveRecord::TestCase - fixtures :developers, :owners, :pets + fixtures :developers, :owners, :pets, :toys def setup @developer = Developer.first @@ -91,11 +92,10 @@ class TimestampTest < ActiveRecord::TestCase pet = toy.pet owner = pet.owner - previously_owner_updated_at = owner.updated_at - + owner.update_attribute(:updated_at, (time = 3.days.ago)) toy.touch - assert_not_equal previously_owner_updated_at, owner.updated_at + assert_not_equal time, owner.updated_at ensure Toy.belongs_to :pet end -- cgit v1.2.3 From f8b53f35b9cbf2a134a7d9184a044ce95764acfa Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Tue, 27 Jul 2010 22:57:27 +0200 Subject: test and fix collection_singular_ids= with string primary keys [#5125 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/associations.rb | 4 ++- .../has_many_through_associations_test.rb | 40 +++++++++++++++++++++- activerecord/test/fixtures/subscriptions.yml | 2 +- activerecord/test/models/book.rb | 3 ++ 4 files changed, 46 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9663a36edf..bd90cfc5d5 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1439,7 +1439,9 @@ module ActiveRecord end redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| - ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i) + pk_column = reflection.klass.columns.find{|c| c.name == reflection.klass.primary_key } + ids = (new_value || []).reject { |nid| nid.blank? } + ids.map!{|i| pk_column.type_cast(i)} send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index e4dd810732..3940e75ad6 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -14,9 +14,14 @@ require 'models/toy' require 'models/contract' require 'models/company' require 'models/developer' +require 'models/subscriber' +require 'models/book' +require 'models/subscription' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies + fixtures :posts, :readers, :people, :comments, :authors, + :owners, :pets, :toys, :jobs, :references, :companies, + :subscribers, :books, :subscriptions # Dummies to force column loads so query counts are clean. def setup @@ -383,4 +388,37 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) }, ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) } end + + def test_collection_singular_ids_getter_with_string_primary_keys + book = books(:awdr) + assert_equal 2, book.subscriber_ids.size + assert_equal [subscribers(:first).nick, subscribers(:second).nick].sort, book.subscriber_ids.sort + end + + def test_collection_singular_ids_setter + company = companies(:rails_core) + dev = Developer.find(:first) + + company.developer_ids = [dev.id] + assert_equal [dev], company.developers + end + + def test_collection_singular_ids_setter_with_string_primary_keys + assert_nothing_raised do + book = books(:awdr) + book.subscriber_ids = [subscribers(:second).nick] + assert_equal [subscribers(:second)], book.subscribers(true) + + book.subscriber_ids = [] + assert_equal [], book.subscribers(true) + end + + end + + def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set + company = companies(:rails_core) + ids = [Developer.find(:first).id, -9999] + assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids} + end + end diff --git a/activerecord/test/fixtures/subscriptions.yml b/activerecord/test/fixtures/subscriptions.yml index 371bfd3422..5a93c12193 100644 --- a/activerecord/test/fixtures/subscriptions.yml +++ b/activerecord/test/fixtures/subscriptions.yml @@ -9,4 +9,4 @@ webster_rfr: alterself_awdr: id: 3 subscriber_id: alterself - book_id: 3 \ No newline at end of file + book_id: 1 diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb index cfd07abddc..1e030b4f59 100644 --- a/activerecord/test/models/book.rb +++ b/activerecord/test/models/book.rb @@ -1,4 +1,7 @@ class Book < ActiveRecord::Base has_many :citations, :foreign_key => 'book1_id' has_many :references, :through => :citations, :source => :reference_of, :uniq => true + + has_many :subscriptions + has_many :subscribers, :through => :subscriptions end -- cgit v1.2.3 From e1344bf5048934b0f231974c3597dfbc1c76154f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 Aug 2010 16:51:08 +0200 Subject: Tidy up previous commit. --- activerecord/lib/active_record/associations.rb | 6 ++--- activerecord/lib/active_record/reflection.rb | 26 ++++++++++------------ .../has_many_through_associations_test.rb | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index bd90cfc5d5..1dc094b893 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1437,11 +1437,11 @@ module ActiveRecord association.replace(new_value) association end - + redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| - pk_column = reflection.klass.columns.find{|c| c.name == reflection.klass.primary_key } + pk_column = reflection.primary_key_column ids = (new_value || []).reject { |nid| nid.blank? } - ids.map!{|i| pk_column.type_cast(i)} + ids.map!{ |i| pk_column.type_cast(i) } send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 03a932f642..7f47a812eb 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -91,25 +91,19 @@ module ActiveRecord # # composed_of :balance, :class_name => 'Money' returns :balance # has_many :clients returns :clients - def name - @name - end + attr_reader :name # Returns the macro type. # # composed_of :balance, :class_name => 'Money' returns :composed_of # has_many :clients returns :has_many - def macro - @macro - end + attr_reader :macro # Returns the hash of options used for the macro. # # composed_of :balance, :class_name => 'Money' returns { :class_name => "Money" } # has_many :clients returns +{}+ - def options - @options - end + attr_reader :options # Returns the class for the macro. # @@ -137,11 +131,6 @@ module ActiveRecord @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions] end - # Returns +true+ if +self+ is a +belongs_to+ reflection. - def belongs_to? - macro == :belongs_to - end - private def derive_class_name name.to_s.camelize @@ -213,6 +202,10 @@ module ActiveRecord @primary_key_name ||= options[:foreign_key] || derive_primary_key_name end + def primary_key_column + @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key } + end + def association_foreign_key @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key end @@ -307,6 +300,11 @@ module ActiveRecord dependent_conditions end + # Returns +true+ if +self+ is a +belongs_to+ reflection. + def belongs_to? + macro == :belongs_to + end + private def derive_class_name class_name = name.to_s.camelize diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 3940e75ad6..0eaadac5ae 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -21,7 +21,7 @@ require 'models/subscription' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies, - :subscribers, :books, :subscriptions + :subscribers, :books, :subscriptions, :developers # Dummies to force column loads so query counts are clean. def setup -- cgit v1.2.3 From 009aa8825b6932b006f005ac351b82ad8100d7f1 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 26 Jul 2010 15:12:36 -0400 Subject: Eager loading an association should not change the count of children MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4971 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/associations.rb | 4 ++++ activerecord/test/cases/associations_test.rb | 13 +++++++++++++ activerecord/test/models/electron.rb | 3 +++ activerecord/test/models/liquid.rb | 5 +++++ activerecord/test/models/molecule.rb | 4 ++++ activerecord/test/schema/schema.rb | 13 +++++++++++++ 6 files changed, 42 insertions(+) create mode 100644 activerecord/test/models/electron.rb create mode 100644 activerecord/test/models/liquid.rb create mode 100644 activerecord/test/models/molecule.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1dc094b893..9cd3b9662f 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1815,6 +1815,10 @@ module ActiveRecord when Hash associations.keys.each do |name| reflection = base.reflections[name] + + if records.any? && reflection.options && reflection.options[:uniq] + records.each { |record| record.send(reflection.name).target.uniq! } + end parent_records = [] records.each do |record| diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index a1c794c084..d328ca630b 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -14,11 +14,24 @@ require 'models/reader' require 'models/parrot' require 'models/ship_part' require 'models/ship' +require 'models/liquid' +require 'models/molecule' +require 'models/electron' class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, :computers, :people, :readers + def test_eager_loading_should_not_change_count_of_children + liquid = Liquid.create(:name => 'salty') + molecule = liquid.molecules.create(:name => 'molecule_1') + molecule.electrons.create(:name => 'electron_1') + molecule.electrons.create(:name => 'electron_2') + + liquids = Liquid.includes(:molecules => :electrons).where('molecules.id is not null') + assert_equal 1, liquids[0].molecules.length + end + def test_loading_the_association_target_should_keep_child_records_marked_for_destruction ship = Ship.create!(:name => "The good ship Dollypop") part = ship.parts.create!(:name => "Mast") diff --git a/activerecord/test/models/electron.rb b/activerecord/test/models/electron.rb new file mode 100644 index 0000000000..35af9f679b --- /dev/null +++ b/activerecord/test/models/electron.rb @@ -0,0 +1,3 @@ +class Electron < ActiveRecord::Base + belongs_to :molecule +end diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb new file mode 100644 index 0000000000..b96c054f6c --- /dev/null +++ b/activerecord/test/models/liquid.rb @@ -0,0 +1,5 @@ +class Liquid < ActiveRecord::Base + set_table_name :liquid + has_many :molecules, :uniq => true +end + diff --git a/activerecord/test/models/molecule.rb b/activerecord/test/models/molecule.rb new file mode 100644 index 0000000000..69325b8d29 --- /dev/null +++ b/activerecord/test/models/molecule.rb @@ -0,0 +1,4 @@ +class Molecule < ActiveRecord::Base + belongs_to :liquid + has_many :electrons +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a0e620c2ef..fc3810f82b 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -618,6 +618,19 @@ ActiveRecord::Schema.define do t.datetime :updated_at end + create_table :liquid, :force => true do |t| + t.string :name + end + create_table :molecules, :force => true do |t| + t.integer :liquid_id + t.string :name + end + create_table :electrons, :force => true do |t| + t.integer :molecule_id + t.string :name + end + + except 'SQLite' do # fk_test_has_fk should be before fk_test_has_pk create_table :fk_test_has_fk, :force => true do |t| -- cgit v1.2.3 From 9effe3cc18182abbcf0a8b01635a34ca77b67e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 Aug 2010 17:20:17 +0200 Subject: Remove duplicated logic. --- activerecord/lib/active_record/associations.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9cd3b9662f..fdc203e298 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1805,9 +1805,7 @@ module ActiveRecord case associations when Symbol, String reflection = base.reflections[associations] - if reflection && reflection.collection? - records.each { |record| record.send(reflection.name).target.uniq! } - end + remove_uniq_by_reflection(reflection, records) when Array associations.each do |association| remove_duplicate_results!(base, records, association) @@ -1815,10 +1813,7 @@ module ActiveRecord when Hash associations.keys.each do |name| reflection = base.reflections[name] - - if records.any? && reflection.options && reflection.options[:uniq] - records.each { |record| record.send(reflection.name).target.uniq! } - end + remove_uniq_by_reflection(reflection, records) parent_records = [] records.each do |record| @@ -1837,6 +1832,7 @@ module ActiveRecord end protected + def build(associations, parent = nil, join_class = Arel::InnerJoin) parent ||= @joins.last case associations @@ -1859,6 +1855,12 @@ module ActiveRecord end end + def remove_uniq_by_reflection(reflection, records) + if reflection && reflection.collection? + records.each { |record| record.send(reflection.name).target.uniq! } + end + end + def build_join_association(reflection, parent) JoinAssociation.new(reflection, self, parent) end -- cgit v1.2.3