Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ GIT
PATH
remote: .
specs:
active_dry_form (1.2.1)
active_dry_form (1.4.0)
actionpack
activerecord
dry-configurable
Expand Down Expand Up @@ -198,6 +198,7 @@ GEM

PLATFORMS
arm64-darwin-21
arm64-darwin-24
x86_64-linux

DEPENDENCIES
Expand Down
8 changes: 8 additions & 0 deletions lib/active_dry_form/base_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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|
Expand All @@ -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

Expand Down
8 changes: 2 additions & 6 deletions lib/active_dry_form/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/active_dry_form/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

module ActiveDryForm

VERSION = '1.3.0'
VERSION = '1.4.0'

end
16 changes: 8 additions & 8 deletions spec/active_dry_form/form_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ def polymorphic_path(*)

expected_html = <<-HTML
<div class="form-input input_url form-input-required">
<label for="user_bookmarks__url">Bookmarks URL</label>
<input required="required" type="url" value="https://example.com" name="user[bookmarks][][url]" id="user_bookmarks__url" />
<label for="user_bookmarks_0_url">Bookmarks URL</label>
<input required="required" type="url" value="https://example.com" name="user[bookmarks][0][url]" id="user_bookmarks_0_url" />
</div>
HTML

Expand All @@ -509,8 +509,8 @@ def polymorphic_path(*)

expected_html = <<-HTML
<div class="form-input input_url form-input-required form-input-error">
<label for="user_bookmarks__url">Bookmarks URL</label>
<input required="required" type="url" name="user[bookmarks][][url]" id="user_bookmarks__url" />
<label for="user_bookmarks_0_url">Bookmarks URL</label>
<input required="required" type="url" name="user[bookmarks][0][url]" id="user_bookmarks_0_url" />
<div class="form-error">должно быть заполнено</div>
</div>
HTML
Expand All @@ -533,8 +533,8 @@ def polymorphic_path(*)

expected_html = <<-HTML
<div class="form-input input_text form-input-required">
<label for="user_favorites__kind">Favorites Kind</label>
<input required="required" type="text" value="book" name="user[favorites][][kind]" id="user_favorites__kind" />
<label for="user_favorites_0_kind">Favorites Kind</label>
<input required="required" type="text" value="book" name="user[favorites][0][kind]" id="user_favorites_0_kind" />
</div>
HTML

Expand All @@ -554,8 +554,8 @@ def polymorphic_path(*)

expected_html = <<-HTML
<div class="form-input input_text form-input-required form-input-error">
<label for="user_favorites__kind">Favorites Kind</label>
<input required="required" type="text" name="user[favorites][][kind]" id="user_favorites__kind" />
<label for="user_favorites_0_kind">Favorites Kind</label>
<input required="required" type="text" name="user[favorites][0][kind]" id="user_favorites_0_kind" />
<div class="form-error">должно быть заполнено</div>
</div>
HTML
Expand Down
14 changes: 8 additions & 6 deletions spec/active_dry_form_nested_dry_form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -36,15 +38,15 @@

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'])
expect(form.bookmarks[0].errors).to eq(url: ['должно быть заполнено'])
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'])
Expand Down
32 changes: 16 additions & 16 deletions spec/active_dry_form_nested_many_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@
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)
expect(form.errors).to eq({ bookmarks: { 0 => { url: ['должно быть заполнено'] }, 1 => { url: ['должно быть заполнено'] } } })
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
Expand All @@ -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')
Expand All @@ -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)
Expand All @@ -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)
Expand Down