activerecord 3.0.0.beta3 → 3.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (51) hide show
  1. data/CHANGELOG +27 -0
  2. data/lib/active_record.rb +5 -1
  3. data/lib/active_record/aggregations.rb +1 -0
  4. data/lib/active_record/association_preload.rb +1 -1
  5. data/lib/active_record/associations.rb +88 -33
  6. data/lib/active_record/associations/association_collection.rb +12 -11
  7. data/lib/active_record/associations/association_proxy.rb +1 -1
  8. data/lib/active_record/attribute_methods.rb +10 -1
  9. data/lib/active_record/attribute_methods/dirty.rb +50 -50
  10. data/lib/active_record/attribute_methods/primary_key.rb +1 -1
  11. data/lib/active_record/autosave_association.rb +20 -5
  12. data/lib/active_record/base.rb +28 -343
  13. data/lib/active_record/callbacks.rb +23 -34
  14. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  15. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
  16. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  17. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -0
  18. data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -18
  19. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
  20. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +3 -3
  21. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +61 -7
  22. data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
  23. data/lib/active_record/connection_adapters/mysql_adapter.rb +22 -50
  24. data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -143
  25. data/lib/active_record/connection_adapters/sqlite_adapter.rb +6 -2
  26. data/lib/active_record/counter_cache.rb +105 -0
  27. data/lib/active_record/fixtures.rb +16 -15
  28. data/lib/active_record/locale/en.yml +2 -2
  29. data/lib/active_record/locking/optimistic.rb +37 -16
  30. data/lib/active_record/migration.rb +7 -3
  31. data/lib/active_record/named_scope.rb +1 -5
  32. data/lib/active_record/nested_attributes.rb +13 -2
  33. data/lib/active_record/observer.rb +19 -7
  34. data/lib/active_record/persistence.rb +230 -0
  35. data/lib/active_record/railtie.rb +17 -23
  36. data/lib/active_record/railties/databases.rake +3 -2
  37. data/lib/active_record/relation.rb +5 -2
  38. data/lib/active_record/relation/batches.rb +6 -1
  39. data/lib/active_record/relation/calculations.rb +12 -9
  40. data/lib/active_record/relation/finder_methods.rb +14 -10
  41. data/lib/active_record/relation/query_methods.rb +62 -44
  42. data/lib/active_record/relation/spawn_methods.rb +6 -1
  43. data/lib/active_record/schema_dumper.rb +3 -0
  44. data/lib/active_record/serializers/xml_serializer.rb +30 -56
  45. data/lib/active_record/session_store.rb +1 -1
  46. data/lib/active_record/timestamp.rb +22 -26
  47. data/lib/active_record/transactions.rb +168 -50
  48. data/lib/active_record/validations.rb +33 -49
  49. data/lib/active_record/version.rb +1 -1
  50. data/lib/rails/generators/active_record.rb +6 -9
  51. metadata +27 -10
data/CHANGELOG CHANGED
@@ -1,3 +1,30 @@
1
+ *Rails 3.0.0 [beta 4] (June 8th, 2010)*
2
+
3
+ * Fixed that ActiveRecord::Base.compute_type would swallow NoMethodError #4751 [Andrew Bloomgarden, Andrew White]
4
+
5
+ * Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik]
6
+
7
+ Example:
8
+
9
+ add_index(:accounts, :name, :name => 'by_name', :length => 10)
10
+ => CREATE INDEX by_name ON accounts(name(10))
11
+
12
+ add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
13
+ => CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
14
+
15
+ * find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune]
16
+
17
+ * New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 [Brian Durand]
18
+
19
+ * Serialized attributes are not converted to YAML if they are any of the formats that can be serialized to XML (like Hash, Array and Strings). [José Valim]
20
+
21
+ * Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne]
22
+
23
+ * PostgreSQL: drop support for old postgres driver. Use pg 0.9.0 or later. [Jeremy Kemper]
24
+
25
+ * Observers can prevent records from saving by returning false, just like before_save and friends. #4087 [Mislav Marohnić]
26
+
27
+
1
28
  *Rails 3.0.0 [beta 3] (April 13th, 2010)*
2
29
 
3
30
  * Add Relation extensions. [Pratik Naik]
@@ -29,7 +29,9 @@ activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
29
29
  $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
30
30
 
31
31
  require 'active_support'
32
+ require 'active_support/i18n'
32
33
  require 'active_model'
34
+ require 'arel'
33
35
 
34
36
  module ActiveRecord
35
37
  extend ActiveSupport::Autoload
@@ -59,6 +61,7 @@ module ActiveRecord
59
61
 
60
62
  autoload :Base
61
63
  autoload :Callbacks
64
+ autoload :CounterCache
62
65
  autoload :DynamicFinderMatch
63
66
  autoload :DynamicScopeMatch
64
67
  autoload :Migration
@@ -66,6 +69,7 @@ module ActiveRecord
66
69
  autoload :NamedScope
67
70
  autoload :NestedAttributes
68
71
  autoload :Observer
72
+ autoload :Persistence
69
73
  autoload :QueryCache
70
74
  autoload :Reflection
71
75
  autoload :Schema
@@ -117,4 +121,4 @@ ActiveSupport.on_load(:active_record) do
117
121
  Arel::Table.engine = Arel::Sql::Engine.new(self)
118
122
  end
119
123
 
120
- I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
124
+ I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
@@ -253,6 +253,7 @@ module ActiveRecord
253
253
  raise ArgumentError, 'Converter must be a symbol denoting the converter method to call or a Proc to be invoked.'
254
254
  end
255
255
  end
256
+
256
257
  mapping.each { |pair| self[pair.first] = part.send(pair.last) }
257
258
  instance_variable_set("@#{name}", part.freeze)
258
259
  end
@@ -378,7 +378,7 @@ module ActiveRecord
378
378
 
379
379
 
380
380
  def interpolate_sql_for_preload(sql)
381
- instance_eval("%@#{sql.gsub('@', '\@')}@")
381
+ instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
382
382
  end
383
383
 
384
384
  def append_conditions(reflection, preload_options)
@@ -2,6 +2,7 @@ require 'active_support/core_ext/array/wrap'
2
2
  require 'active_support/core_ext/enumerable'
3
3
  require 'active_support/core_ext/module/delegation'
4
4
  require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/string/conversions'
5
6
 
6
7
  module ActiveRecord
7
8
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
@@ -839,10 +840,11 @@ module ActiveRecord
839
840
  # If set to <tt>:destroy</tt> all the associated objects are destroyed
840
841
  # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
841
842
  # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
842
- # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
843
- # the <tt>:through</tt> option.
844
- # the <tt>:through</tt> option. If set to <tt>:restrict</tt>
845
- # this object cannot be deleted if it has any associated object.
843
+ # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
844
+ # <tt>:restrict</tt> this object cannot be deleted if it has any associated object.
845
+ #
846
+ # *Warning:* This option is ignored when used with <tt>:through</tt> option.
847
+ #
846
848
  # [:finder_sql]
847
849
  # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
848
850
  # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
@@ -1304,14 +1306,14 @@ module ActiveRecord
1304
1306
 
1305
1307
  # Don't use a before_destroy callback since users' before_destroy
1306
1308
  # callbacks will be executed after the association is wiped out.
1307
- old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
1308
- class_eval <<-end_eval unless method_defined?(old_method)
1309
- alias_method :#{old_method}, :destroy_without_callbacks # alias_method :destroy_without_habtm_shim_for_posts, :destroy_without_callbacks
1310
- def destroy_without_callbacks # def destroy_without_callbacks
1311
- #{reflection.name}.clear # posts.clear
1312
- #{old_method} # destroy_without_habtm_shim_for_posts
1313
- end # end
1314
- end_eval
1309
+ include Module.new {
1310
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
1311
+ def destroy # def destroy
1312
+ super # super
1313
+ #{reflection.name}.clear # posts.clear
1314
+ end # end
1315
+ RUBY
1316
+ }
1315
1317
 
1316
1318
  add_association_callbacks(reflection.name, options)
1317
1319
  end
@@ -1398,7 +1400,7 @@ module ActiveRecord
1398
1400
  primary_key = reflection.source_reflection.primary_key_name
1399
1401
  send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
1400
1402
  else
1401
- send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id)
1403
+ send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").except(:includes).map!(&:id)
1402
1404
  end
1403
1405
  end
1404
1406
  end
@@ -1460,7 +1462,7 @@ module ActiveRecord
1460
1462
  before_destroy(method_name)
1461
1463
 
1462
1464
  module_eval(
1463
- "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
1465
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
1464
1466
  )
1465
1467
  end
1466
1468
 
@@ -1500,7 +1502,16 @@ module ActiveRecord
1500
1502
  when :destroy
1501
1503
  method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
1502
1504
  define_method(method_name) do
1503
- send(reflection.name).each { |o| o.destroy }
1505
+ send(reflection.name).each do |o|
1506
+ # No point in executing the counter update since we're going to destroy the parent anyway
1507
+ counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
1508
+ if(o.respond_to? counter_method) then
1509
+ class << o
1510
+ self
1511
+ end.send(:define_method, counter_method, Proc.new {})
1512
+ end
1513
+ o.destroy
1514
+ end
1504
1515
  end
1505
1516
  before_destroy method_name
1506
1517
  when :delete_all
@@ -1728,6 +1739,14 @@ module ActiveRecord
1728
1739
  build(associations)
1729
1740
  end
1730
1741
 
1742
+ def graft(*associations)
1743
+ associations.each do |association|
1744
+ join_associations.detect {|a| association == a} ||
1745
+ build(association.reflection.name, association.find_parent_in(self), association.join_class)
1746
+ end
1747
+ self
1748
+ end
1749
+
1731
1750
  def join_associations
1732
1751
  @joins[1..-1].to_a
1733
1752
  end
@@ -1736,6 +1755,17 @@ module ActiveRecord
1736
1755
  @joins[0]
1737
1756
  end
1738
1757
 
1758
+ def count_aliases_from_table_joins(name)
1759
+ # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
1760
+ quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase
1761
+ join_sql = join_base.table_joins.to_s.downcase
1762
+ join_sql.blank? ? 0 :
1763
+ # Table names
1764
+ join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size +
1765
+ # Table aliases
1766
+ join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size
1767
+ end
1768
+
1739
1769
  def instantiate(rows)
1740
1770
  rows.each_with_index do |row, i|
1741
1771
  primary_id = join_base.record_id(row)
@@ -1780,22 +1810,22 @@ module ActiveRecord
1780
1810
  end
1781
1811
 
1782
1812
  protected
1783
- def build(associations, parent = nil)
1813
+ def build(associations, parent = nil, join_class = Arel::InnerJoin)
1784
1814
  parent ||= @joins.last
1785
1815
  case associations
1786
1816
  when Symbol, String
1787
1817
  reflection = parent.reflections[associations.to_s.intern] or
1788
1818
  raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
1789
1819
  @reflections << reflection
1790
- @joins << build_join_association(reflection, parent)
1820
+ @joins << build_join_association(reflection, parent).with_join_class(join_class)
1791
1821
  when Array
1792
1822
  associations.each do |association|
1793
- build(association, parent)
1823
+ build(association, parent, join_class)
1794
1824
  end
1795
1825
  when Hash
1796
1826
  associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
1797
- build(name, parent)
1798
- build(associations[name])
1827
+ build(name, parent, join_class)
1828
+ build(associations[name], nil, join_class)
1799
1829
  end
1800
1830
  else
1801
1831
  raise ConfigurationError, associations.inspect
@@ -1872,6 +1902,12 @@ module ActiveRecord
1872
1902
  @table_joins = joins
1873
1903
  end
1874
1904
 
1905
+ def ==(other)
1906
+ other.class == self.class &&
1907
+ other.active_record == active_record &&
1908
+ other.table_joins == table_joins
1909
+ end
1910
+
1875
1911
  def aliased_prefix
1876
1912
  "t0"
1877
1913
  end
@@ -1937,6 +1973,27 @@ module ActiveRecord
1937
1973
  end
1938
1974
  end
1939
1975
 
1976
+ def ==(other)
1977
+ other.class == self.class &&
1978
+ other.reflection == reflection &&
1979
+ other.parent == parent
1980
+ end
1981
+
1982
+ def find_parent_in(other_join_dependency)
1983
+ other_join_dependency.joins.detect do |join|
1984
+ self.parent == join
1985
+ end
1986
+ end
1987
+
1988
+ def join_class
1989
+ @join_class ||= Arel::InnerJoin
1990
+ end
1991
+
1992
+ def with_join_class(join_class)
1993
+ @join_class = join_class
1994
+ self
1995
+ end
1996
+
1940
1997
  def association_join
1941
1998
  return @join if @join
1942
1999
 
@@ -2036,27 +2093,25 @@ module ActiveRecord
2036
2093
  end
2037
2094
 
2038
2095
  def join_relation(joining_relation, join = nil)
2039
- if (relations = relation).is_a?(Array)
2040
- joining_relation.joins(Relation::JoinOperation.new(relations.first, Arel::OuterJoin, association_join.first)).
2041
- joins(Relation::JoinOperation.new(relations.last, Arel::OuterJoin, association_join.last))
2042
- else
2043
- joining_relation.joins(Relation::JoinOperation.new(relations, Arel::OuterJoin, association_join))
2044
- end
2096
+ joining_relation.joins(self.with_join_class(Arel::OuterJoin))
2045
2097
  end
2046
2098
 
2047
2099
  protected
2048
2100
 
2049
2101
  def aliased_table_name_for(name, suffix = nil)
2050
- if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{active_record.connection.quote_table_name name.downcase}\son}
2051
- @join_dependency.table_aliases[name] += 1
2102
+ if @join_dependency.table_aliases[name].zero?
2103
+ @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name)
2052
2104
  end
2053
2105
 
2054
- unless @join_dependency.table_aliases[name].zero?
2055
- # if the table name has been used, then use an alias
2106
+ if !@join_dependency.table_aliases[name].zero? # We need an alias
2056
2107
  name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
2057
- table_index = @join_dependency.table_aliases[name]
2058
2108
  @join_dependency.table_aliases[name] += 1
2059
- name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
2109
+ if @join_dependency.table_aliases[name] == 1 # First time we've seen this name
2110
+ # Also need to count the aliases from the table_aliases to avoid incorrect count
2111
+ @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name)
2112
+ end
2113
+ table_index = @join_dependency.table_aliases[name]
2114
+ name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1
2060
2115
  else
2061
2116
  @join_dependency.table_aliases[name] += 1
2062
2117
  end
@@ -2077,7 +2132,7 @@ module ActiveRecord
2077
2132
  end
2078
2133
 
2079
2134
  def interpolate_sql(sql)
2080
- instance_eval("%@#{sql.gsub('@', '\@')}@")
2135
+ instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
2081
2136
  end
2082
2137
  end
2083
2138
  end
@@ -411,7 +411,8 @@ module ActiveRecord
411
411
  end
412
412
  elsif @reflection.klass.scopes[method]
413
413
  @_named_scopes_cache ||= {}
414
- @_named_scopes_cache[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
414
+ @_named_scopes_cache[method] ||= {}
415
+ @_named_scopes_cache[method][args] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
415
416
  else
416
417
  with_scope(construct_scope) do
417
418
  if block_given?
@@ -451,6 +452,16 @@ module ActiveRecord
451
452
  records
452
453
  end
453
454
 
455
+ def add_record_to_target_with_callbacks(record)
456
+ callback(:before_add, record)
457
+ yield(record) if block_given?
458
+ @target ||= [] unless loaded?
459
+ @target << record unless @reflection.options[:uniq] && @target.include?(record)
460
+ callback(:after_add, record)
461
+ set_inverse_instance(record, @owner)
462
+ record
463
+ end
464
+
454
465
  private
455
466
  def create_record(attrs)
456
467
  attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
@@ -475,16 +486,6 @@ module ActiveRecord
475
486
  end
476
487
  end
477
488
 
478
- def add_record_to_target_with_callbacks(record)
479
- callback(:before_add, record)
480
- yield(record) if block_given?
481
- @target ||= [] unless loaded?
482
- @target << record unless @reflection.options[:uniq] && @target.include?(record)
483
- callback(:after_add, record)
484
- set_inverse_instance(record, @owner)
485
- record
486
- end
487
-
488
489
  def remove_records(*records)
489
490
  records = flatten_deeper(records)
490
491
  records.each { |record| raise_on_type_mismatch(record) }
@@ -51,7 +51,7 @@ module ActiveRecord
51
51
  alias_method :proxy_respond_to?, :respond_to?
52
52
  alias_method :proxy_extend, :extend
53
53
  delegate :to_param, :to => :proxy_target
54
- instance_methods.each { |m| undef_method m unless m =~ /^(?:nil\?|send|object_id|to_a)$|^__|proxy_/ }
54
+ instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|proxy_/ }
55
55
 
56
56
  def initialize(owner, reflection)
57
57
  @owner, @reflection = owner, reflection
@@ -18,10 +18,19 @@ module ActiveRecord
18
18
  def instance_method_already_implemented?(method_name)
19
19
  method_name = method_name.to_s
20
20
  @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
21
- @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
21
+ @@_defined_activerecord_methods ||= defined_activerecord_methods
22
22
  raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
23
23
  @_defined_class_methods.include?(method_name)
24
24
  end
25
+
26
+ def defined_activerecord_methods
27
+ active_record = ActiveRecord::Base
28
+ super_klass = ActiveRecord::Base.superclass
29
+ methods = active_record.public_instance_methods - super_klass.public_instance_methods
30
+ methods += active_record.private_instance_methods - super_klass.private_instance_methods
31
+ methods += active_record.protected_instance_methods - super_klass.protected_instance_methods
32
+ methods.map {|m| m.to_s }.to_set
33
+ end
25
34
  end
26
35
 
27
36
  def method_missing(method_id, *args, &block)
@@ -5,20 +5,20 @@ module ActiveRecord
5
5
  module Dirty
6
6
  extend ActiveSupport::Concern
7
7
  include ActiveModel::Dirty
8
+ include AttributeMethods::Write
8
9
 
9
10
  included do
10
- alias_method_chain :save, :dirty
11
- alias_method_chain :save!, :dirty
12
- alias_method_chain :update, :dirty
13
- alias_method_chain :reload, :dirty
11
+ if self < ::ActiveRecord::Timestamp
12
+ raise "You cannot include Dirty after Timestamp"
13
+ end
14
14
 
15
15
  superclass_delegating_accessor :partial_updates
16
16
  self.partial_updates = true
17
17
  end
18
18
 
19
19
  # Attempts to +save+ the record and clears changed attributes if successful.
20
- def save_with_dirty(*args) #:nodoc:
21
- if status = save_without_dirty(*args)
20
+ def save(*) #:nodoc:
21
+ if status = super
22
22
  @previously_changed = changes
23
23
  @changed_attributes.clear
24
24
  end
@@ -26,70 +26,70 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
29
- def save_with_dirty!(*args) #:nodoc:
30
- save_without_dirty!(*args).tap do
29
+ def save!(*) #:nodoc:
30
+ super.tap do
31
31
  @previously_changed = changes
32
32
  @changed_attributes.clear
33
33
  end
34
34
  end
35
35
 
36
36
  # <tt>reload</tt> the record and clears changed attributes.
37
- def reload_with_dirty(*args) #:nodoc:
38
- reload_without_dirty(*args).tap do
37
+ def reload(*) #:nodoc:
38
+ super.tap do
39
39
  @previously_changed.clear
40
40
  @changed_attributes.clear
41
41
  end
42
42
  end
43
43
 
44
- private
45
- # Wrap write_attribute to remember original attribute value.
46
- def write_attribute(attr, value)
47
- attr = attr.to_s
44
+ private
45
+ # Wrap write_attribute to remember original attribute value.
46
+ def write_attribute(attr, value)
47
+ attr = attr.to_s
48
48
 
49
- # The attribute already has an unsaved change.
50
- if attribute_changed?(attr)
51
- old = @changed_attributes[attr]
52
- @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
53
- else
54
- old = clone_attribute_value(:read_attribute, attr)
55
- # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
56
- old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
57
- @changed_attributes[attr] = old if field_changed?(attr, old, value)
58
- end
49
+ # The attribute already has an unsaved change.
50
+ if attribute_changed?(attr)
51
+ old = @changed_attributes[attr]
52
+ @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
53
+ else
54
+ old = clone_attribute_value(:read_attribute, attr)
55
+ # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
56
+ old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
57
+ @changed_attributes[attr] = old if field_changed?(attr, old, value)
58
+ end
59
59
 
60
- # Carry on.
61
- super(attr, value)
60
+ # Carry on.
61
+ super(attr, value)
62
+ end
63
+
64
+ def update(*)
65
+ if partial_updates?
66
+ # Serialized attributes should always be written in case they've been
67
+ # changed in place.
68
+ super(changed | (attributes.keys & self.class.serialized_attributes.keys))
69
+ else
70
+ super
62
71
  end
72
+ end
63
73
 
64
- def update_with_dirty
65
- if partial_updates?
66
- # Serialized attributes should always be written in case they've been
67
- # changed in place.
68
- update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
74
+ def field_changed?(attr, old, value)
75
+ if column = column_for_attribute(attr)
76
+ if column.number? && column.null && (old.nil? || old == 0) && value.blank?
77
+ # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
78
+ # Hence we don't record it as a change if the value changes from nil to ''.
79
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
80
+ # be typecast back to 0 (''.to_i => 0)
81
+ value = nil
69
82
  else
70
- update_without_dirty
83
+ value = column.type_cast(value)
71
84
  end
72
85
  end
73
86
 
74
- def field_changed?(attr, old, value)
75
- if column = column_for_attribute(attr)
76
- if column.number? && column.null && (old.nil? || old == 0) && value.blank?
77
- # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
78
- # Hence we don't record it as a change if the value changes from nil to ''.
79
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
80
- # be typecast back to 0 (''.to_i => 0)
81
- value = nil
82
- else
83
- value = column.type_cast(value)
84
- end
85
- end
86
-
87
- old != value
88
- end
87
+ old != value
88
+ end
89
89
 
90
- def clone_with_time_zone_conversion_attribute?(attr, old)
91
- old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
92
- end
90
+ def clone_with_time_zone_conversion_attribute?(attr, old)
91
+ old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
92
+ end
93
93
  end
94
94
  end
95
95
  end