Skip to content

Commit 57fe7df

Browse files
xtr3meSjors Baltuszzak
authored
Add accepts_nested_attributes_for support when using delegated_type (rails#41717)
Co-Authored-By: Zachary Scott <[email protected]> Co-authored-by: Sjors Baltus <[email protected]> Co-authored-by: Zachary Scott <[email protected]>
1 parent 457feda commit 57fe7df

File tree

4 files changed

+69
-0
lines changed

4 files changed

+69
-0
lines changed

activerecord/CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
* Add nested_attributes_for support for `delegated_type`
2+
3+
```ruby
4+
class Entry < ApplicationRecord
5+
delegated_type :entryable, types: %w[ Message Comment ]
6+
accepts_nested_attributes_for :entryable
7+
end
8+
9+
entry = Entry.create(entryable_type: 'Message', entryable_attributes: { content: 'Hello world' })
10+
# => #<Entry:0x00>
11+
# id: 1
12+
# entryable_id: 1,
13+
# entryable_type: 'Message'
14+
# ...>
15+
16+
entry.entryable
17+
# => #<Message:0x01>
18+
# id: 1
19+
# content: 'Hello world'
20+
# ...>
21+
```
22+
23+
Previously it would raise an error:
24+
25+
```ruby
26+
Entry.create(entryable_type: 'Message', entryable_attributes: { content: 'Hello world' })
27+
# ArgumentError: Cannot build association `entryable'. Are you trying to build a polymorphic one-to-one association?
28+
```
29+
30+
*Sjors Baltus*
31+
132
* Use subquery for DELETE with GROUP_BY and HAVING clauses.
233
334
Prior to this change, deletes with GROUP_BY and HAVING were returning an error.

activerecord/lib/active_record/delegated_type.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ module ActiveRecord
137137
# end
138138
#
139139
# Now you can list a bunch of entries, call +Entry#title+, and polymorphism will provide you with the answer.
140+
#
141+
# == Nested Attributes
142+
#
143+
# Enabling nested attributes on a delegated_type association allows you to
144+
# create the entry and message in one go:
145+
#
146+
# class Entry < ApplicationRecord
147+
# delegated_type :entryable, types: %w[ Message Comment ]
148+
# accepts_nested_attributes_for :entryable
149+
# end
150+
#
151+
# params = { entry: { entryable_type: 'Message', entryable_attributes: { subject: 'Smiling' } } }
152+
# entry = Entry.create(params[:entry])
153+
# entry.entryable.id # => 2
154+
# entry.entryable.subject # => 'Smiling'
140155
module DelegatedType
141156
# Defines this as a class that'll delegate its type for the passed +role+ to the class references in +types+.
142157
# That'll create a polymorphic +belongs_to+ relationship to that +role+, and it'll add all the delegated
@@ -207,6 +222,10 @@ def define_delegated_type_methods(role, types:, options:)
207222
public_send("#{role}_class").model_name.singular.inquiry
208223
end
209224

225+
define_method "build_#{role}" do |*params|
226+
public_send("#{role}=", public_send("#{role}_class").new(*params))
227+
end
228+
210229
types.each do |type|
211230
scope_name = type.tableize.tr("/", "_")
212231
singular = scope_name.singularize

activerecord/test/cases/delegated_type_test.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,9 @@ class DelegatedTypeTest < ActiveRecord::TestCase
7272
assert_equal @uuid_entry_with_comment.entryable_uuid, @uuid_entry_with_comment.uuid_comment_uuid
7373
assert_nil @uuid_entry_with_comment.uuid_message_uuid
7474
end
75+
76+
test "builder method" do
77+
assert_respond_to Entry.new, :build_entryable
78+
assert_equal Message, Entry.new(entryable_type: "Message").build_entryable.class
79+
end
7580
end

activerecord/test/cases/nested_attributes_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
require "models/interest"
1212
require "models/owner"
1313
require "models/pet"
14+
require "models/entry"
15+
require "models/message"
1416
require "active_support/hash_with_indifferent_access"
1517

1618
class TestNestedAttributesInGeneral < ActiveRecord::TestCase
@@ -1117,3 +1119,15 @@ def test_extend_affects_nested_attributes
11171119
assert_equal "from extension", pirate.treasures[0].name
11181120
end
11191121
end
1122+
1123+
class TestNestedAttributesForDelegatedType < ActiveRecord::TestCase
1124+
setup do
1125+
Entry.accepts_nested_attributes_for :entryable
1126+
@entry = Entry.new(entryable_type: "Message", entryable_attributes: { subject: "Hello world!" })
1127+
end
1128+
1129+
def test_should_build_a_new_record_based_on_the_delegated_type
1130+
assert_not_predicate @entry.entryable, :persisted?
1131+
assert_equal "Hello world!", @entry.entryable.subject
1132+
end
1133+
end

0 commit comments

Comments
 (0)