mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #44141 from drewtempelmeyer/activerecord-update-attributes-exclamation
Add ActiveRecord::Persistence#update_attribute!
This commit is contained in:
commit
0638d35a78
3 changed files with 109 additions and 1 deletions
|
@ -1,3 +1,26 @@
|
||||||
|
* Add `update_attribute!` to `ActiveRecord::Persistence`
|
||||||
|
|
||||||
|
Similar to `update_attribute`, but raises `ActiveRecord::RecordNotSaved` when a `before_*` callback throws `:abort`.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Topic < ActiveRecord::Base
|
||||||
|
before_save :check_title
|
||||||
|
|
||||||
|
def check_title
|
||||||
|
throw(:abort) if title == "abort"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
topic = Topic.create(title: "Test Title")
|
||||||
|
# #=> #<Topic title: "Test Title">
|
||||||
|
topic.update_attribute!(:title, "Another Title")
|
||||||
|
# #=> #<Topic title: "Another Title">
|
||||||
|
topic.update_attribute!(:title, "abort")
|
||||||
|
# raises ActiveRecord::RecordNotSaved
|
||||||
|
```
|
||||||
|
|
||||||
|
*Drew Tempelmeyer*
|
||||||
|
|
||||||
* Avoid loading every record in `ActiveRecord::Relation#pretty_print`
|
* Avoid loading every record in `ActiveRecord::Relation#pretty_print`
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
|
|
@ -747,7 +747,7 @@ module ActiveRecord
|
||||||
# * updated_at/updated_on column is updated if that column is available.
|
# * updated_at/updated_on column is updated if that column is available.
|
||||||
# * Updates all the attributes that are dirty in this object.
|
# * Updates all the attributes that are dirty in this object.
|
||||||
#
|
#
|
||||||
# This method raises an ActiveRecord::ActiveRecordError if the
|
# This method raises an ActiveRecord::ActiveRecordError if the
|
||||||
# attribute is marked as readonly.
|
# attribute is marked as readonly.
|
||||||
#
|
#
|
||||||
# Also see #update_column.
|
# Also see #update_column.
|
||||||
|
@ -759,6 +759,28 @@ module ActiveRecord
|
||||||
save(validate: false)
|
save(validate: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Updates a single attribute and saves the record.
|
||||||
|
# This is especially useful for boolean flags on existing records. Also note that
|
||||||
|
#
|
||||||
|
# * Validation is skipped.
|
||||||
|
# * \Callbacks are invoked.
|
||||||
|
# * updated_at/updated_on column is updated if that column is available.
|
||||||
|
# * Updates all the attributes that are dirty in this object.
|
||||||
|
#
|
||||||
|
# This method raises an ActiveRecord::ActiveRecordError if the
|
||||||
|
# attribute is marked as readonly.
|
||||||
|
#
|
||||||
|
# If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
|
||||||
|
# and #update_attribute! raises ActiveRecord::RecordNotSaved. See
|
||||||
|
# ActiveRecord::Callbacks for further details.
|
||||||
|
def update_attribute!(name, value)
|
||||||
|
name = name.to_s
|
||||||
|
verify_readonly_attribute(name)
|
||||||
|
public_send("#{name}=", value)
|
||||||
|
|
||||||
|
save!(validate: false)
|
||||||
|
end
|
||||||
|
|
||||||
# Updates the attributes of the model from the passed-in hash and saves the
|
# Updates the attributes of the model from the passed-in hash and saves the
|
||||||
# record, all wrapped in a transaction. If the object is invalid, the saving
|
# record, all wrapped in a transaction. If the object is invalid, the saving
|
||||||
# will fail and false will be returned.
|
# will fail and false will be returned.
|
||||||
|
|
|
@ -813,6 +813,69 @@ class PersistenceTest < ActiveRecord::TestCase
|
||||||
assert_not_equal prev_month, developer.updated_at
|
assert_not_equal prev_month, developer.updated_at
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_update_attribute!
|
||||||
|
assert_not_predicate Topic.find(1), :approved?
|
||||||
|
Topic.find(1).update_attribute!("approved", true)
|
||||||
|
assert_predicate Topic.find(1), :approved?
|
||||||
|
|
||||||
|
Topic.find(1).update_attribute!(:approved, false)
|
||||||
|
assert_not_predicate Topic.find(1), :approved?
|
||||||
|
|
||||||
|
Topic.find(1).update_attribute!(:change_approved_before_save, true)
|
||||||
|
assert_predicate Topic.find(1), :approved?
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_update_attribute_for_readonly_attribute!
|
||||||
|
minivan = Minivan.find("m1")
|
||||||
|
assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute!(:color, "black") }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_update_attribute_with_one_updated!
|
||||||
|
t = Topic.first
|
||||||
|
t.update_attribute!(:title, "super_title")
|
||||||
|
assert_equal "super_title", t.title
|
||||||
|
assert_not t.changed?, "topic should not have changed"
|
||||||
|
assert_not t.title_changed?, "title should not have changed"
|
||||||
|
assert_nil t.title_change, "title change should be nil"
|
||||||
|
|
||||||
|
t.reload
|
||||||
|
assert_equal "super_title", t.title
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_update_attribute_for_updated_at_on!
|
||||||
|
developer = Developer.find(1)
|
||||||
|
prev_month = Time.now.prev_month.change(usec: 0)
|
||||||
|
|
||||||
|
developer.update_attribute!(:updated_at, prev_month)
|
||||||
|
assert_equal prev_month, developer.updated_at
|
||||||
|
|
||||||
|
developer.update_attribute!(:salary, 80001)
|
||||||
|
assert_not_equal prev_month, developer.updated_at
|
||||||
|
|
||||||
|
developer.reload
|
||||||
|
assert_not_equal prev_month, developer.updated_at
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_update_attribute_for_aborted_callback!
|
||||||
|
klass = Class.new(Topic) do
|
||||||
|
def self.name; "Topic"; end
|
||||||
|
|
||||||
|
before_update :throw_abort
|
||||||
|
|
||||||
|
def throw_abort
|
||||||
|
throw(:abort)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
t = klass.create(title: "New Topic", author_name: "Not David")
|
||||||
|
|
||||||
|
assert_raises(ActiveRecord::RecordNotSaved) { t.update_attribute!(:title, "super_title") }
|
||||||
|
|
||||||
|
t_reloaded = Topic.find(t.id)
|
||||||
|
|
||||||
|
assert_equal "New Topic", t_reloaded.title
|
||||||
|
end
|
||||||
|
|
||||||
def test_update_column
|
def test_update_column
|
||||||
topic = Topic.find(1)
|
topic = Topic.find(1)
|
||||||
topic.update_column("approved", true)
|
topic.update_column("approved", true)
|
||||||
|
|
Loading…
Reference in a new issue