Skip to content

Commit 0a72aef

Browse files
committed
Mix ActiveModel::AttributeAssignment into Base
By including the [ActiveModel::AttributeAssignment][], the `Base` class can access the [assign_attributes][] method for bulk assignment of attributes **without** saving them to the server (like through `Base#update_attributes`). ```ruby person = Person.new person.id # => nil person.name # => nil person.assign_attributes id: 1, name: "Matz" person.id # => 1 person.name # => "Matz" ``` Support for versions prior to 7.2.0 --- This commit includes a conditional monkey-patch of the `_assign_attribute` method when Active Model's version is < 7.2.0: ```ruby # 7.1.5.2 def _assign_attribute(k, v) setter = :"#{k}=" if respond_to?(setter) public_send(setter, v) else raise UnknownAttributeError.new(self, k.to_s) end end # 7.2.0 def _assign_attribute(k, v) setter = :"#{k}=" public_send(setter, v) rescue NoMethodError if respond_to?(setter) raise else raise UnknownAttributeError.new(self, k.to_s) end end ``` The change is necessary because the [7.1.5.2][] version queries the instance with `respond_to?`, whereas the [7.2.0][] version skips the query and sends the method first, falling back to a `NoMethodError` rescue in case that it doesn't. [#450][] is an alternative to including the monkey patch. If merged, it'd make `respond_to?` behavior more like `method_missing`, so the monkey patch would be unnecessary. [ActiveModel::AttributeAssignment]: https://edgeapi.rubyonrails.org/classes/ActiveModel/AttributeAssignment.html [assign_attributes]: https://edgeapi.rubyonrails.org/classes/ActiveModel/AttributeAssignment.html#method-i-assign_attributes [7.1.5.2]: https://github.com/rails/rails/blob/v7.1.5.2/activemodel/lib/active_model/attribute_assignment.rb [7.2.0]: https://github.com/rails/rails/blob/v7.2.0/activemodel/lib/active_model/attribute_assignment.rb [#450]: #450
1 parent b549319 commit 0a72aef

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

lib/active_resource/base.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ def load(attributes, remove_root = false, persisted = false)
15891589
attributes = Formats.remove_root(attributes) if remove_root
15901590

15911591
attributes.each do |key, value|
1592-
@attributes[key.to_s] =
1592+
assign_attributes key.to_s => (
15931593
case value
15941594
when Array
15951595
resource = nil
@@ -1607,6 +1607,7 @@ def load(attributes, remove_root = false, persisted = false)
16071607
else
16081608
value.duplicable? ? value.dup : value
16091609
end
1610+
)
16101611
end
16111612
self
16121613
end
@@ -1827,6 +1828,19 @@ def method_missing(method_symbol, *arguments) # :nodoc:
18271828
super
18281829
end
18291830
end
1831+
1832+
if ActiveModel::VERSION::MAJOR < 8 && ActiveModel::VERSION::MINOR < 2
1833+
def _assign_attribute(k, v)
1834+
setter = :"#{k}="
1835+
public_send(setter, v)
1836+
rescue NoMethodError
1837+
if respond_to?(setter)
1838+
raise
1839+
else
1840+
raise UnknownAttributeError.new(self, k.to_s)
1841+
end
1842+
end
1843+
end
18301844
end
18311845

18321846
class Base
@@ -1835,7 +1849,7 @@ class Base
18351849
extend ActiveResource::Associations
18361850

18371851
include Callbacks, CustomMethods, Validations, Serialization
1838-
include ActiveModel::Conversion
1852+
include ActiveModel::Conversion, ActiveModel::AttributeAssignment
18391853
include ActiveModel::ForbiddenAttributesProtection
18401854
include ActiveModel::Serializers::JSON
18411855
include ActiveModel::Serializers::Xml

test/cases/base_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,15 @@ def test_complex_clone
11791179
assert_not_equal matz.non_ar_hash, matz_c.non_ar_hash
11801180
end
11811181

1182+
def test_assign_attributes
1183+
matz = Person.new
1184+
1185+
matz.assign_attributes "id" => 1, name: "Matz"
1186+
1187+
assert_equal 1, matz.id
1188+
assert_equal "Matz", matz.name
1189+
end
1190+
11821191
def test_update
11831192
matz = Person.find(:first)
11841193
matz.name = "David"

0 commit comments

Comments
 (0)