aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-05-22 15:16:12 +0100
committerPratik Naik <pratiknaik@gmail.com>2008-05-22 15:16:12 +0100
commite32deb595f66e8be7aeb9aeb2ef796994f524584 (patch)
treeb5ec35226d8c3c8279df332edbdd450055ffc80b /activerecord
parent4617139bcc21ccb875d25baf06c3124692061cf2 (diff)
parentcff2291df5d1df106ae8cf116655f0703f53f8c3 (diff)
downloadrails-e32deb595f66e8be7aeb9aeb2ef796994f524584.tar.gz
rails-e32deb595f66e8be7aeb9aeb2ef796994f524584.tar.bz2
rails-e32deb595f66e8be7aeb9aeb2ef796994f524584.zip
Merge commit 'mainstream/master'
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb3
-rw-r--r--activerecord/lib/active_record/dirty.rb32
-rw-r--r--activerecord/test/cases/active_schema_test_mysql.rb73
-rw-r--r--activerecord/test/cases/dirty_test.rb18
-rw-r--r--activerecord/test/cases/migration_test.rb4
5 files changed, 87 insertions, 43 deletions
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
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
index 6034963811..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
+ # <tt>reload</tt> 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
@@ -117,14 +125,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 +139,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/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
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 1266eb5036..c011ffaf57 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?
@@ -127,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?
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