voids 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -4
- data/README.md +9 -0
- data/lib/voids/base.rb +4 -6
- data/lib/voids/version.rb +1 -1
- data/spec/voids/base_spec.rb +34 -8
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 66922bf400447b157fb6b27cfc70a8e9cf7da4c60629824f5f40b5661fd5336a
|
|
4
|
+
data.tar.gz: 506f410f8806f92fdd5cd5e1d1b3289b0932ebd84d1dc7f0a85756598acbbfaf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5482cbb6ce7d7efd0729acb4274cd6fd30440ffc48b0088b15bdef99e802ab8a880c0585dacd4a28168b35499e2878f0107696c0b29db9203eed609e8bb55b82
|
|
7
|
+
data.tar.gz: 9d2d5befe25f3f6227f907144136f7b584b0d44a770bdb47d9cadda4f4cfca7bccffa14605dbd93e0f99fb9b1935b678672001f5cf2730ed45f85963ebe040b8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
# [Unreleased]
|
|
2
|
+
|
|
3
|
+
- ...
|
|
4
|
+
|
|
5
|
+
# [3.0.0] - 2026-09-21
|
|
6
|
+
|
|
7
|
+
- **Breaking:** no longer add an `:invalid` error to the association itself when a nested form is invalid. Nested errors are copied to the parent under `association.attribute` / `association[index].attribute` keys only, matching `ActiveRecord`'s autosave association behavior, which `accepts_nested_attributes_for` implies. Callers that checked `errors[:association]` should check for the copied nested keys instead.
|
|
8
|
+
- Validate each nested form exactly once per parent validation, rather than two to three times.
|
|
9
|
+
- Make `AssociationProxy#any?` block-aware (matching Ruby collection semantics).
|
|
10
|
+
|
|
11
|
+
# [2.1.0] - 2026-09-15
|
|
2
12
|
|
|
3
|
-
- Add `changes_applied:` option to `Voids::Base#assign_attributes` so callers can bulk-assign attributes without resetting dirty tracking.
|
|
4
13
|
- Propagate validation context to nested forms during parent validation.
|
|
5
14
|
- Validate nested forms even when parent validations fail, so nested errors still bubble up.
|
|
6
15
|
- Add `Voids::Base#empty?` with default emptiness semantics based on blank attributes.
|
|
7
|
-
- Make `AssociationProxy#any?` block-aware (matching Ruby collection semantics).
|
|
8
16
|
- Add `AssociationProxy#reverse` for array-like read access without explicit `to_a` conversion.
|
|
17
|
+
- Add `changes_applied:` option to `Voids::Base#assign_attributes` so callers can bulk-assign attributes without resetting dirty tracking.
|
|
18
|
+
- Add CI/coverage/version badges to README
|
|
19
|
+
|
|
20
|
+
# [2.0.0] - 2026-09-14
|
|
21
|
+
|
|
22
|
+
- Re-released as voids gem
|
|
9
23
|
|
|
10
|
-
|
|
24
|
+
# [1.0.0] - 2026-01-13
|
|
11
25
|
|
|
12
26
|
- Initial release
|
data/README.md
CHANGED
|
@@ -200,6 +200,15 @@ form.valid?(:publish) # false
|
|
|
200
200
|
form.errors.full_messages # includes nested form errors
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
+
Nested errors are copied onto the parent, keyed by the association and the nested
|
|
204
|
+
attribute. As in `ActiveRecord`, no error is added to the association itself:
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
form.errors.messages.keys # => [:"images[0].url"] (has_many)
|
|
208
|
+
form.errors.messages.keys # => [:"author.name"] (has_one)
|
|
209
|
+
form.errors[:images] # => []
|
|
210
|
+
```
|
|
211
|
+
|
|
203
212
|
### Extracting attributes for persistence
|
|
204
213
|
|
|
205
214
|
Use `model_attributes` for the form's own attributes:
|
data/lib/voids/base.rb
CHANGED
|
@@ -247,17 +247,15 @@ module Voids
|
|
|
247
247
|
when :has_one
|
|
248
248
|
next if nested_form.valid?(context)
|
|
249
249
|
|
|
250
|
-
errors.add(name.to_sym, :invalid)
|
|
251
250
|
copy_nested_errors(name, nested_form)
|
|
252
251
|
all_valid = false
|
|
253
252
|
when :has_many
|
|
254
|
-
next if nested_form.valid?(context)
|
|
255
|
-
|
|
256
|
-
errors.add(name.to_sym, :invalid)
|
|
257
253
|
nested_form.each_with_index do |form, index|
|
|
258
|
-
|
|
254
|
+
next if form.valid?(context)
|
|
255
|
+
|
|
256
|
+
copy_nested_errors("#{name}[#{index}]", form)
|
|
257
|
+
all_valid = false
|
|
259
258
|
end
|
|
260
|
-
all_valid = false
|
|
261
259
|
end
|
|
262
260
|
end
|
|
263
261
|
|
data/lib/voids/version.rb
CHANGED
data/spec/voids/base_spec.rb
CHANGED
|
@@ -386,8 +386,8 @@ RSpec.describe Voids::Base do
|
|
|
386
386
|
form.images.new
|
|
387
387
|
|
|
388
388
|
expect(form.valid?).to be(false)
|
|
389
|
-
expect(form.errors.
|
|
390
|
-
expect(form.errors
|
|
389
|
+
expect(form.errors.messages.keys).to contain_exactly(:'images[0].url')
|
|
390
|
+
expect(form.errors[:images]).to be_empty
|
|
391
391
|
end
|
|
392
392
|
|
|
393
393
|
it 'includes nested form errors in parent errors' do
|
|
@@ -407,8 +407,8 @@ RSpec.describe Voids::Base do
|
|
|
407
407
|
form.build_photo
|
|
408
408
|
form.valid?
|
|
409
409
|
|
|
410
|
-
expect(form.errors.messages.keys).to
|
|
411
|
-
expect(form.errors
|
|
410
|
+
expect(form.errors.messages.keys).to contain_exactly(:'photo.url')
|
|
411
|
+
expect(form.errors[:photo]).to be_empty
|
|
412
412
|
end
|
|
413
413
|
|
|
414
414
|
it 'includes nested form errors in parent even when parent has errors' do
|
|
@@ -429,8 +429,8 @@ RSpec.describe Voids::Base do
|
|
|
429
429
|
form.build_photo
|
|
430
430
|
form.valid?
|
|
431
431
|
|
|
432
|
-
expect(form.errors.messages.keys).to contain_exactly(:title, :
|
|
433
|
-
expect(form.errors
|
|
432
|
+
expect(form.errors.messages.keys).to contain_exactly(:title, :'photo.url')
|
|
433
|
+
expect(form.errors[:photo]).to be_empty
|
|
434
434
|
end
|
|
435
435
|
|
|
436
436
|
it 'passes the validation context to nested forms' do
|
|
@@ -452,8 +452,34 @@ RSpec.describe Voids::Base do
|
|
|
452
452
|
form.build_photo(url: 'http://myspace.com/sicily-photos')
|
|
453
453
|
form.valid?(:final_setup)
|
|
454
454
|
|
|
455
|
-
expect(form.errors.messages.keys).to contain_exactly(:
|
|
456
|
-
expect(form.errors
|
|
455
|
+
expect(form.errors.messages.keys).to contain_exactly(:'photo.url')
|
|
456
|
+
expect(form.errors[:photo]).to be_empty
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
it 'validates each nested form exactly once' do
|
|
460
|
+
validation_counts = Hash.new(0)
|
|
461
|
+
nested_class = Class.new(described_class) do
|
|
462
|
+
attribute :url, :string
|
|
463
|
+
validate do
|
|
464
|
+
validation_counts[url] += 1
|
|
465
|
+
errors.add(:url, :blank) if url.blank?
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
stub_const('ImageForm', nested_class)
|
|
469
|
+
|
|
470
|
+
form_class = Class.new(described_class) do
|
|
471
|
+
has_many :images
|
|
472
|
+
attribute :title, :string
|
|
473
|
+
end
|
|
474
|
+
stub_const('ImageGalleryForm', form_class)
|
|
475
|
+
|
|
476
|
+
form = form_class.new(title: 'test')
|
|
477
|
+
%w[first second].each { |url| form.images.new(url:) }
|
|
478
|
+
form.images.new(url: '')
|
|
479
|
+
form.images.new(url: 'fourth')
|
|
480
|
+
form.valid?
|
|
481
|
+
|
|
482
|
+
expect(validation_counts).to eq({ 'first' => 1, 'second' => 1, '' => 1, 'fourth' => 1 })
|
|
457
483
|
end
|
|
458
484
|
end
|
|
459
485
|
end
|