Changeset 9157
- Timestamp:
- 03/31/08 01:10:04 (2 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/aggregations.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/base.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/callbacks.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/dirty.rb (modified) (4 diffs)
- trunk/activerecord/lib/active_record/locking/optimistic.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/timestamp.rb (modified) (2 diffs)
- trunk/activerecord/test/cases/associations_test.rb (modified) (1 diff)
- trunk/activerecord/test/cases/base_test.rb (modified) (1 diff)
- trunk/activerecord/test/cases/dirty_test.rb (modified) (5 diffs)
- trunk/activerecord/test/cases/query_cache_test.rb (modified) (1 diff)
- trunk/railties/configs/initializers/new_in_rails_3.rb (added)
- trunk/railties/lib/rails_generator/generators/applications/app/app_generator.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r9150 r9157 1 1 *SVN* 2 3 * Partial updates include only unsaved attributes. Off by default; set YourClass.partial_updates = true to enable. [Jeremy Kemper] 2 4 3 5 * Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled [Geoff Buesing] trunk/activerecord/lib/active_record/aggregations.rb
r8671 r9157 175 175 define_method("#{name}=") do |part| 176 176 if part.nil? && allow_nil 177 mapping.each { |pair| @attributes[pair.first] = nil }177 mapping.each { |pair| self[pair.first] = nil } 178 178 instance_variable_set("@#{name}", nil) 179 179 else 180 180 part = conversion.call(part) unless part.is_a?(class_name.constantize) || conversion.nil? 181 mapping.each { |pair| @attributes[pair.first] = part.send(pair.last) }181 mapping.each { |pair| self[pair.first] = part.send(pair.last) } 182 182 instance_variable_set("@#{name}", part.freeze) 183 183 end trunk/activerecord/lib/active_record/base.rb
r9122 r9157 2408 2408 # Updates the associated record with values matching those of the instance attributes. 2409 2409 # Returns the number of affected rows. 2410 def update 2411 quoted_attributes = attributes_with_quotes(false, false )2410 def update(attribute_names = @attributes.keys) 2411 quoted_attributes = attributes_with_quotes(false, false, attribute_names) 2412 2412 return 0 if quoted_attributes.empty? 2413 2413 connection.update( … … 2501 2501 # Returns a copy of the attributes hash where all the values have been safely quoted for use in 2502 2502 # an SQL statement. 2503 def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true )2503 def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) 2504 2504 quoted = {} 2505 2505 connection = self.class.connection 2506 @attributes.each_pair do |name, value|2506 attribute_names.each do |name| 2507 2507 if column = column_for_attribute(name) 2508 2508 quoted[name] = connection.quote(read_attribute(name), column) unless !include_primary_key && column.primary trunk/activerecord/lib/active_record/callbacks.rb
r8664 r9157 230 230 def after_update() end 231 231 232 def update_with_callbacks #:nodoc:232 def update_with_callbacks(*args) #:nodoc: 233 233 return false if callback(:before_update) == false 234 result = update_without_callbacks 234 result = update_without_callbacks(*args) 235 235 callback(:after_update) 236 236 result trunk/activerecord/lib/active_record/dirty.rb
r9139 r9157 29 29 # person.changed # => ['name'] 30 30 # person.changes # => { 'name' => ['Bill', 'bob'] } 31 # 32 # Before modifying an attribute in-place: 33 # person.name_will_change! 34 # person.name << 'by' 35 # person.name_change # => ['uncle bob', 'uncle bobby'] 31 36 module Dirty 32 37 def self.included(base) 33 base.attribute_method_suffix '_changed?', '_change', '_w as'38 base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was' 34 39 base.alias_method_chain :write_attribute, :dirty 35 40 base.alias_method_chain :save, :dirty 36 41 base.alias_method_chain :save!, :dirty 42 base.alias_method_chain :update, :dirty 43 44 base.superclass_delegating_accessor :partial_updates 45 base.partial_updates = true 37 46 end 38 47 … … 82 91 end 83 92 93 # Handle *_changed? for method_missing. 94 def attribute_changed?(attr) 95 changed_attributes.include?(attr) 96 end 97 98 # Handle *_change for method_missing. 99 def attribute_change(attr) 100 [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr) 101 end 102 103 # Handle *_was for method_missing. 104 def attribute_was(attr) 105 attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr) 106 end 107 108 # Handle *_will_change! for method_missing. 109 def attribute_will_change!(attr) 110 changed_attributes[attr] = clone_attribute_value(:read_attribute, attr) 111 end 84 112 85 113 # Wrap write_attribute to remember original attribute value. … … 89 117 # The attribute already has an unsaved change. 90 118 unless changed_attributes.include?(attr) 91 old = read_attribute(attr)119 old = clone_attribute_value(:read_attribute, attr) 92 120 93 121 # Remember the original value if it's different. … … 104 132 end 105 133 106 107 # Handle *_changed? for method_missing. 108 def attribute_changed?(attr) 109 changed_attributes.include?(attr) 110 end 111 112 # Handle *_change for method_missing. 113 def attribute_change(attr) 114 [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr) 115 end 116 117 # Handle *_was for method_missing. 118 def attribute_was(attr) 119 attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr) 134 def update_with_dirty 135 if partial_updates? 136 update_without_dirty(changed) 137 else 138 update_without_dirty 139 end 120 140 end 121 141 end trunk/activerecord/lib/active_record/locking/optimistic.rb
r8395 r9157 67 67 end 68 68 69 def update_with_lock #:nodoc:70 return update_without_lock unless locking_enabled?69 def update_with_lock(attribute_names = @attributes.keys) #:nodoc: 70 return update_without_lock(attribute_names) unless locking_enabled? 71 71 72 72 lock_col = self.class.locking_column … … 74 74 send(lock_col + '=', previous_value + 1) 75 75 76 attribute_names += [lock_col] 77 attribute_names.uniq! 78 76 79 begin 77 80 affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking") 78 81 UPDATE #{self.class.table_name} 79 SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false ))}82 SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false, attribute_names))} 80 83 WHERE #{self.class.primary_key} = #{quote_value(id)} 81 84 AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)} trunk/activerecord/lib/active_record/timestamp.rb
r9093 r9157 30 30 end 31 31 32 def update_with_timestamps #:nodoc:32 def update_with_timestamps(*args) #:nodoc: 33 33 if record_timestamps 34 34 t = self.class.default_timezone == :utc ? Time.now.utc : Time.now … … 36 36 write_attribute('updated_on', t) if respond_to?(:updated_on) 37 37 end 38 update_without_timestamps 38 update_without_timestamps(*args) 39 39 end 40 40 end trunk/activerecord/test/cases/associations_test.rb
r9110 r9157 450 450 def test_not_resaved_when_unchanged 451 451 firm = Firm.find(:first, :include => :account) 452 firm.name += '-changed' 452 453 assert_queries(1) { firm.save! } 453 454 454 455 firm = Firm.find(:first) 455 456 firm.account = Account.find(:first) 456 assert_queries( 1) { firm.save! }457 assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! } 457 458 458 459 firm = Firm.find(:first).clone trunk/activerecord/test/cases/base_test.rb
r9150 r9157 154 154 assert_equal 2, Topic.find(topic.id).content["two"] 155 155 156 topic.content_will_change! 156 157 topic.content["three"] = 3 157 158 topic.save trunk/activerecord/test/cases/dirty_test.rb
r9139 r9157 2 2 require 'models/topic' # For booleans 3 3 require 'models/pirate' # For timestamps 4 require 'models/parrot' 4 5 5 6 class Pirate # Just reopening it, not defining it … … 20 21 end 21 22 22 class DirtyTest < Test::Unit::TestCase23 class DirtyTest < ActiveRecord::TestCase 23 24 def test_attribute_changes 24 25 # New record - no changes. … … 44 45 end 45 46 46 # Rewritten from original tests to use AR47 47 def test_object_should_be_changed_if_any_attribute_is_changed 48 48 pirate = Pirate.new … … 63 63 end 64 64 65 def test_attribute_will_change! 66 pirate = Pirate.create!(:catchphrase => 'arr') 67 68 pirate.catchphrase << ' matey' 69 assert !pirate.catchphrase_changed? 70 71 assert pirate.catchphrase_will_change! 72 assert pirate.catchphrase_changed? 73 assert_equal ['arr matey', 'arr matey'], pirate.catchphrase_change 74 75 pirate.catchphrase << '!' 76 assert pirate.catchphrase_changed? 77 assert_equal ['arr matey', 'arr matey!'], pirate.catchphrase_change 78 end 79 80 def test_association_assignment_changes_foreign_key 81 pirate = Pirate.create! 82 pirate.parrot = Parrot.create! 83 assert pirate.changed? 84 assert_equal %w(parrot_id), pirate.changed 85 end 86 65 87 def test_attribute_should_be_compared_with_type_cast 66 88 topic = Topic.new … … 75 97 assert !topic.approved_changed? 76 98 end 99 100 def test_partial_update 101 pirate = Pirate.new(:catchphrase => 'foo') 102 103 with_partial_updates Pirate, false do 104 assert_queries(2) { 2.times { pirate.save! } } 105 end 106 107 with_partial_updates Pirate, true do 108 assert_queries(0) { 2.times { pirate.save! } } 109 end 110 end 111 112 private 113 def with_partial_updates(klass, on = true) 114 old = klass.partial_updates? 115 klass.partial_updates = on 116 yield 117 ensure 118 klass.partial_updates = old 119 end 77 120 end trunk/activerecord/test/cases/query_cache_test.rb
r8681 r9157 83 83 84 84 Task.cache do 85 Task.find(1).save! 85 task = Task.find(1) 86 task.starting = Time.now.utc 87 task.save! 86 88 end 87 89 end trunk/railties/lib/rails_generator/generators/applications/app/app_generator.rb
r9134 r9157 62 62 # Initializers 63 63 m.template "configs/initializers/inflections.rb", "config/initializers/inflections.rb" 64 m.template "configs/initializers/mime_types.rb", "config/initializers/mime_types.rb" 64 m.template "configs/initializers/mime_types.rb", "config/initializers/mime_types.rb" 65 m.template "configs/initializers/new_in_rails_3.rb", "config/initializers/new_in_rails_3.rb" 65 66 66 67 # Environments