Skip to content

Commit 20d156d

Browse files
committed
Introduce ActiveModel::AttributeAssignment#attribute_writer_missing
Provide instances with an opportunity to gracefully handle assigning to an unknown attribute: ```ruby class Rectangle include ActiveModel::AttributeAssignment attr_accessor :length, :width def attribute_writer_missing(name, value) Rails.logger.warn "Tried to assign to unknown attribute #{name}" end end rectangle = Rectangle.new rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'" ``` By default, classes that do not override `#attribute_writer_missing` will raise an `ActiveModel::UnknownAttributeError`. The `attribute_writer_missing` aims to mimic the naming of `BasicObject#method_missing`. There is also an [ActiveModel::AttributeMethods#attribute_missing][] method, but that pertains to attribute _access_. [ActiveModel::AttributeMethods#attribute_missing]: https://edgeapi.rubyonrails.org/classes/ActiveModel/AttributeMethods.html#method-i-attribute_missing
1 parent 290b806 commit 20d156d

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

activemodel/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,23 @@
1+
* Introduce `ActiveModel::AttributeAssignment#attribute_writer_missing`
2+
3+
Provide instances with an opportunity to gracefully handle assigning to an
4+
unknown attribute:
5+
6+
```ruby
7+
class Rectangle
8+
include ActiveModel::AttributeAssignment
9+
10+
attr_accessor :length, :width
11+
12+
def attribute_writer_missing(name, value)
13+
Rails.logger.warn "Tried to assign to unknown attribute #{name}"
14+
end
15+
end
16+
17+
rectangle = Rectangle.new
18+
rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'"
19+
```
20+
21+
*Sean Doyle*
122

223
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activemodel/CHANGELOG.md) for previous changes.

activemodel/lib/active_model/attribute_assignment.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,27 @@ def assign_attributes(new_attributes)
3636

3737
alias attributes= assign_attributes
3838

39+
# Like `BasicObject#method_missing`, `#attribute_writer_missing` is invoked
40+
# when `#assign_attributes` is passed an unknown attribute name.
41+
#
42+
# By default, `#attribute_writer_missing` raises an UnknownAttributeError.
43+
#
44+
# class Rectangle
45+
# include ActiveModel::AttributeAssignment
46+
#
47+
# attr_accessor :length, :width
48+
#
49+
# def attribute_writer_missing(name, value)
50+
# Rails.logger.warn "Tried to assign to unknown attribute #{name}"
51+
# end
52+
# end
53+
#
54+
# rectangle = Rectangle.new
55+
# rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'"
56+
def attribute_writer_missing(name, value)
57+
raise UnknownAttributeError.new(self, name)
58+
end
59+
3960
private
4061
def _assign_attributes(attributes)
4162
attributes.each do |k, v|
@@ -50,7 +71,7 @@ def _assign_attribute(k, v)
5071
if respond_to?(setter)
5172
raise
5273
else
53-
raise UnknownAttributeError.new(self, k.to_s)
74+
attribute_writer_missing(k.to_s, v)
5475
end
5576
end
5677
end

activemodel/test/cases/attribute_assignment_test.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ def dup
8686
assert_equal "hz", error.attribute
8787
end
8888

89+
test "assign non-existing attribute by overriding #attribute_writer_missing" do
90+
model_class = Class.new(Model) do
91+
attr_accessor :assigned_attributes
92+
93+
def attribute_writer_missing(name, value) = @assigned_attributes[name] = value
94+
end
95+
model = model_class.new(assigned_attributes: {})
96+
97+
model.assign_attributes unknown: "attribute"
98+
99+
assert_equal({ "unknown" => "attribute" }, model.assigned_attributes)
100+
end
101+
89102
test "assign private attribute" do
90103
model = Model.new
91104
assert_raises(ActiveModel::UnknownAttributeError) do

0 commit comments

Comments
 (0)