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)