From 281edce6db8accc7d4a0e9ab01892631d9d0ebc3 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 20 May 2008 20:50:46 +0100 Subject: Ensure nil to '' doesn't get recorded by dirty for nullable integer columns. [#150 state:resolved] [Jason Dew, Pratik] --- activerecord/lib/active_record/dirty.rb | 24 ++++++++++++++++-------- activerecord/test/cases/dirty_test.rb | 10 ++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index 6034963811..49f1204fa1 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -117,14 +117,7 @@ module ActiveRecord # The attribute already has an unsaved change. unless changed_attributes.include?(attr) old = clone_attribute_value(:read_attribute, attr) - - # Remember the original value if it's different. - typecasted = if column = column_for_attribute(attr) - column.type_cast(value) - else - value - end - changed_attributes[attr] = old unless old == typecasted + changed_attributes[attr] = old if field_changed?(attr, old, value) end # Carry on. @@ -138,5 +131,20 @@ module ActiveRecord update_without_dirty end end + + def field_changed?(attr, old, value) + if column = column_for_attribute(attr) + if column.type == :integer && column.null && old.nil? + # For nullable integer columns, NULL gets stored in database for blank (i.e. '') values. + # Hence we don't record it as a change if the value changes from nil to ''. + value = nil if value.blank? + else + value = column.type_cast(value) + end + end + + old != value + end + end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 1266eb5036..62178977f9 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -44,6 +44,16 @@ class DirtyTest < ActiveRecord::TestCase assert_nil pirate.catchphrase_change end + def test_nullable_integer_not_marked_as_changed_if_new_value_is_blank + pirate = Pirate.new + + ["", nil].each do |value| + pirate.parrot_id = value + assert !pirate.parrot_id_changed? + assert_nil pirate.parrot_id_change + end + end + def test_object_should_be_changed_if_any_attribute_is_changed pirate = Pirate.new assert !pirate.changed? -- cgit v1.2.3 From 19d7b1d22bd68af00244ddc3e1f35cec187e9120 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 21 May 2008 10:46:28 +0100 Subject: Verbose ActiveRecord::AssociationTypeMismatch exception message. [#189 state:resolved] --- activerecord/lib/active_record/associations/association_proxy.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index ec16af3897..11c64243a2 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -210,7 +210,8 @@ module ActiveRecord def raise_on_type_mismatch(record) unless record.is_a?(@reflection.klass) - raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.klass} expected, got #{record.class}" + message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})" + raise ActiveRecord::AssociationTypeMismatch, message end end -- cgit v1.2.3 From 262d23d763c05bbe5f433a99cb9f02e48a8cdd4a Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Wed, 21 May 2008 08:27:20 -0700 Subject: ActiveRecord::Base#reload should clear dirty attributes. [#231 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/dirty.rb | 8 ++++++++ activerecord/test/cases/dirty_test.rb | 8 ++++++++ 2 files changed, 16 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index 49f1204fa1..8fdc763292 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -40,6 +40,7 @@ module ActiveRecord base.alias_method_chain :save, :dirty base.alias_method_chain :save!, :dirty base.alias_method_chain :update, :dirty + base.alias_method_chain :reload, :dirty base.superclass_delegating_accessor :partial_updates base.partial_updates = false @@ -84,6 +85,13 @@ module ActiveRecord status end + # reload the record and clears changed attributes. + def reload_with_dirty(*args) #:nodoc: + record = reload_without_dirty(*args) + changed_attributes.clear + record + end + private # Map of change attr => original value. def changed_attributes diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 62178977f9..c011ffaf57 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -137,6 +137,14 @@ class DirtyTest < ActiveRecord::TestCase check_pirate_after_save_failure(pirate) end + def test_reload_should_clear_changed_attributes + pirate = Pirate.create!(:catchphrase => "shiver me timbers") + pirate.catchphrase = "*hic*" + assert pirate.changed? + pirate.reload + assert !pirate.changed? + end + private def with_partial_updates(klass, on = true) old = klass.partial_updates? -- cgit v1.2.3 From dd9938a44ee3a7bb6c42527a1be6fcec70bf4772 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Wed, 21 May 2008 21:03:38 +0100 Subject: Fix mysql 4.1 incompatibility in the active record schema tests. Signed-off-by: Michael Koziarski [#233 state:resolved ] --- .../test/cases/active_schema_test_mysql.rb | 73 ++++++++++++---------- 1 file changed, 41 insertions(+), 32 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index ddf3e82162..2a42dc3517 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.rb @@ -40,47 +40,56 @@ class ActiveSchemaTest < ActiveRecord::TestCase end def test_add_timestamps - #we need to actually modify some data, so we make execute to point to the original method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute_with_stub, :execute - alias_method :execute, :execute_without_stub - end - ActiveRecord::Base.connection.create_table :delete_me do |t| - end - ActiveRecord::Base.connection.add_timestamps :delete_me - assert_equal ActiveRecord::Base.connection.execute("SHOW FIELDS FROM delete_me where FIELD='updated_at' AND TYPE='datetime'").num_rows, 1 - assert_equal ActiveRecord::Base.connection.execute("SHOW FIELDS FROM delete_me where FIELD='created_at' AND TYPE='datetime'").num_rows, 1 - ensure - ActiveRecord::Base.connection.drop_table :delete_me rescue nil - #before finishing, we restore the alias to the mock-up method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute, :execute_with_stub + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + end + ActiveRecord::Base.connection.add_timestamps :delete_me + assert column_present?('delete_me', 'updated_at', 'datetime') + assert column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end end end def test_remove_timestamps - #we need to actually modify some data, so we make execute to point to the original method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute_with_stub, :execute - alias_method :execute, :execute_without_stub - end - ActiveRecord::Base.connection.create_table :delete_me do |t| - t.timestamps - end - ActiveRecord::Base.connection.remove_timestamps :delete_me - assert_equal ActiveRecord::Base.connection.execute("SHOW FIELDS FROM delete_me where FIELD='updated_at' AND TYPE='datetime'").num_rows, 0 - assert_equal ActiveRecord::Base.connection.execute("SHOW FIELDS FROM delete_me where FIELD='created_at' AND TYPE='datetime'").num_rows, 0 - ensure - ActiveRecord::Base.connection.drop_table :delete_me rescue nil - #before finishing, we restore the alias to the mock-up method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute, :execute_with_stub + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + t.timestamps + end + ActiveRecord::Base.connection.remove_timestamps :delete_me + assert !column_present?('delete_me', 'updated_at', 'datetime') + assert !column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end end end - private + def with_real_execute + #we need to actually modify some data, so we make execute point to the original method + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + alias_method :execute_with_stub, :execute + alias_method :execute, :execute_without_stub + end + yield + ensure + #before finishing, we restore the alias to the mock-up method + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + alias_method :execute, :execute_with_stub + end + end + + def method_missing(method_symbol, *arguments) ActiveRecord::Base.connection.send(method_symbol, *arguments) end + + def column_present?(table_name, column_name, type) + results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") + results.first && results.first['Type'] == type + end end -- cgit v1.2.3 From cff2291df5d1df106ae8cf116655f0703f53f8c3 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Thu, 22 May 2008 11:19:10 +0100 Subject: Fix failing tests on mysql 4.1 & ruby 1.8.4. [#237 state:resolved] Signed-off-by: Pratik Naik --- activerecord/test/cases/migration_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 527856b4c0..f36255e209 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -281,7 +281,7 @@ if ActiveRecord::Base.connection.supports_migrations? # Do a manual insertion if current_adapter?(:OracleAdapter) Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)" - elsif current_adapter?(:OpenBaseAdapter) + elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')" else Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)" @@ -384,7 +384,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_not_equal "Z", bob.moment_of_truth.zone # US/Eastern is -5 hours from GMT assert_equal Rational(-5, 24), bob.moment_of_truth.offset - assert_equal "-05:00", bob.moment_of_truth.zone + assert_match /\A-05:?00\Z/, bob.moment_of_truth.zone #ruby 1.8.6 uses HH:MM, prior versions use HHMM assert_equal DateTime::ITALY, bob.moment_of_truth.start end end -- cgit v1.2.3