diff --git a/Gemfile.lock b/Gemfile.lock
index 2cddea7..10aee23 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -27,7 +27,7 @@ GIT
PATH
remote: .
specs:
- active_dry_form (1.2.1)
+ active_dry_form (1.4.0)
actionpack
activerecord
dry-configurable
@@ -198,6 +198,7 @@ GEM
PLATFORMS
arm64-darwin-21
+ arm64-darwin-24
x86_64-linux
DEPENDENCIES
diff --git a/lib/active_dry_form/base_form.rb b/lib/active_dry_form/base_form.rb
index 0cc0d48..2014c8b 100644
--- a/lib/active_dry_form/base_form.rb
+++ b/lib/active_dry_form/base_form.rb
@@ -48,6 +48,10 @@ def attributes=(attrs)
attrs.each do |attr, v|
next if !ActiveDryForm.config.strict_param_keys && !respond_to?(:"#{attr}=")
+ # Params for nested forms comes as a hash from frontend, convert them to array
+ # { '0' => { url: 'https://omniplatform.ru' }, '1' => { url: 'https://google.com' } } => [{ ... }, { ... }]
+ v = v.values if v.is_a?(Hash) && self.class::NESTED_FORM_ARRAYS.include?(attr.to_sym)
+
public_send(:"#{attr}=", v)
end
end
@@ -143,6 +147,7 @@ def []=(key, value)
def self.define_methods
const_set :NESTED_FORM_KEYS, []
+ const_set :NESTED_FORM_ARRAYS, Set.new
self::FIELDS_INFO[:properties].each do |key, value|
define_method :"#{key}=" do |v|
@@ -168,6 +173,9 @@ def self.define_methods
namespace: key,
is_array: value[:type] == 'array',
}
+ if value[:type] == 'array'
+ self::NESTED_FORM_ARRAYS << key.to_sym
+ end
nested_key = key
end
diff --git a/lib/active_dry_form/builder.rb b/lib/active_dry_form/builder.rb
index a364892..70aa48e 100644
--- a/lib/active_dry_form/builder.rb
+++ b/lib/active_dry_form/builder.rb
@@ -81,13 +81,9 @@ def fields_for(association_name, fields_options = {}, &block)
if association.is_a?(BaseForm)
fields_for_nested_model("#{@object_name}[#{association_name}]", association, fields_options, block)
elsif association.respond_to?(:to_ary)
- field_name_regexp = Regexp.new(Regexp.escape("#{@object_name}[#{association_name}][") << '\d+\]') # хак для замены хеша на массив
- output = ActiveSupport::SafeBuffer.new
- Array.wrap(association).each do |child|
- output << fields_for_nested_model("#{@object_name}[#{association_name}][]", child, fields_options, block)
- .gsub(field_name_regexp, "#{@object_name}[#{association_name}][]").html_safe
+ Array.wrap(association).each_with_index.reduce(ActiveSupport::SafeBuffer.new) do |output, (child, i)|
+ output << fields_for_nested_model("#{@object_name}[#{association_name}][#{i}]", child, fields_options, block)
end
- output
end
end
diff --git a/lib/active_dry_form/version.rb b/lib/active_dry_form/version.rb
index 52cba17..602a810 100644
--- a/lib/active_dry_form/version.rb
+++ b/lib/active_dry_form/version.rb
@@ -2,6 +2,6 @@
module ActiveDryForm
- VERSION = '1.3.0'
+ VERSION = '1.4.0'
end
diff --git a/spec/active_dry_form/form_helper_spec.rb b/spec/active_dry_form/form_helper_spec.rb
index 8b63d8d..38193f0 100644
--- a/spec/active_dry_form/form_helper_spec.rb
+++ b/spec/active_dry_form/form_helper_spec.rb
@@ -488,8 +488,8 @@ def polymorphic_path(*)
expected_html = <<-HTML
-
-
+
+
HTML
@@ -509,8 +509,8 @@ def polymorphic_path(*)
expected_html = <<-HTML
HTML
@@ -533,8 +533,8 @@ def polymorphic_path(*)
expected_html = <<-HTML
-
-
+
+
HTML
@@ -554,8 +554,8 @@ def polymorphic_path(*)
expected_html = <<-HTML
HTML
diff --git a/spec/active_dry_form_nested_dry_form_spec.rb b/spec/active_dry_form_nested_dry_form_spec.rb
index 3739ebe..c24e2ac 100644
--- a/spec/active_dry_form_nested_dry_form_spec.rb
+++ b/spec/active_dry_form_nested_dry_form_spec.rb
@@ -9,20 +9,22 @@
let(:user) { User.create!(name: 'Ivan') }
context 'when form is valid' do
- it 'creates nested model' do
- bookmarks_attributes = [{ url: 'https://omniplatform.ru' }]
+ it 'creates nested models' do
+ bookmarks_attributes = { 0 => { url: 'https://omniplatform.ru' }, 1 => { url: 'https://google.com' } }
personal_info_attributes = { age: 25 }
form = NestedDryForm.new(record: user)
- form.attributes = { bookmarks: bookmarks_attributes, personal_info: personal_info_attributes }
+ form.attributes = { 'bookmarks' => bookmarks_attributes, 'personal_info' => personal_info_attributes }
form.update
expect(form.valid?).to be(true)
+ expect(user.bookmarks.length).to eq(2)
expect(user.bookmarks[0].url).to eq('https://omniplatform.ru')
+ expect(user.bookmarks[1].url).to eq('https://google.com')
expect(user.personal_info.age).to eq 25
end
it 'updates nested model' do
bookmark = user.bookmarks.create!(url: 'https://google.com')
- bookmarks_attributes = [{ url: 'https://omniplatform.ru', id: bookmark.id }]
+ bookmarks_attributes = { 0 => { url: 'https://omniplatform.ru', id: bookmark.id } }
user.build_personal_info(age: 18)
user.personal_info.save!
personal_info_attributes = { age: 25, id: user.personal_info.id }
@@ -36,7 +38,7 @@
context 'when form is invalid' do
it 'returns validation errors' do
- form = NestedDryForm.new(record: user, params: { bookmarks: [{ url: '' }], personal_info: { age: 17 } })
+ form = NestedDryForm.new(record: user, params: { bookmarks: { 0 => { url: '' } }, personal_info: { age: 17 } })
form.update
expect(form.valid?).to be(false)
expect(form.personal_info.errors).to eq(age: ['должно быть больше или равным 18'])
@@ -44,7 +46,7 @@
end
it 'returns validation errors nested form' do
- form = NestedDryForm.new(params: { bookmarks: [{ url: 'http://omniplatform.ru' }] })
+ form = NestedDryForm.new(params: { bookmarks: { 0 => { url: 'http://omniplatform.ru' } } })
form.update
expect(form.bookmarks[0].omniplatform?).to be(true)
expect(form.bookmarks[0].errors).to eq(url: ['url is not https'])
diff --git a/spec/active_dry_form_nested_many_spec.rb b/spec/active_dry_form_nested_many_spec.rb
index 5b7e870..e5759eb 100644
--- a/spec/active_dry_form_nested_many_spec.rb
+++ b/spec/active_dry_form_nested_many_spec.rb
@@ -10,10 +10,10 @@
context 'when nested record is an association' do
context 'when form is invalid' do
it 'returns validation errors' do
- bookmarks_attributes = [
- { url: '', name: 'First' },
- { url: '', name: 'Second' },
- ]
+ bookmarks_attributes = {
+ 0 => { url: '', name: 'First' },
+ 1 => { url: '', name: 'Second' },
+ }
form = NestedHasManyForm.new(record: user, params: { user: { bookmarks: bookmarks_attributes } })
form.update
expect(form.valid?).to be(false)
@@ -21,7 +21,7 @@
end
it 'returns typecasted value after validation' do
- form = NestedHasManyForm.new(record: user, params: { user: { bookmarks: [{ added_on: Date.current.to_s }] } })
+ form = NestedHasManyForm.new(record: user, params: { user: { bookmarks: { 0 => { added_on: Date.current.to_s } } } })
form.update
expect(form.bookmarks[0].added_on).to eq Date.current
end
@@ -42,9 +42,9 @@
it 'updates nested model' do
bookmark = user.bookmarks.create!(url: '/first')
- bookmarks_attributes = [
- { url: '/second', id: bookmark.id },
- ]
+ bookmarks_attributes = {
+ 0 => { url: '/second', id: bookmark.id },
+ }
form = NestedHasManyForm.new(record: user, params: { user: { bookmarks: bookmarks_attributes } })
expect { form.update }.not_to change(Bookmark, :count)
expect(bookmark.url).to eq('/second')
@@ -55,10 +55,10 @@
context 'when nested record is a hash' do
context 'when form is invalid' do
it 'returns validation errors' do
- favorites_attributes = [
- { kind: '', name: 'First' },
- { kind: '', name: 'Second' },
- ]
+ favorites_attributes = {
+ 0 => { kind: '', name: 'First' },
+ 1 => { kind: '', name: 'Second' },
+ }
form = NestedHasManyForm.new(record: user, params: { user: { favorites: favorites_attributes } })
form.update
expect(form.valid?).to be(false)
@@ -69,10 +69,10 @@
context 'when form is valid' do
it 'creates nested model' do
favorites_attributes =
- [
- { kind: 'book', name: '1984' },
- { kind: 'movie', name: 'Planet of Monkeys' },
- ]
+ {
+ 0 => { kind: 'book', name: '1984' },
+ 1 => { kind: 'movie', name: 'Planet of Monkeys' },
+ }
form = NestedHasManyForm.new(record: user, params: { user: { favorites: favorites_attributes } })
form.update
expect(form.valid?).to be(true)