summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Herold <michael.j.herold@gmail.com>2018-02-05 20:10:23 -0600
committerMichael Herold <michael.j.herold@gmail.com>2018-02-06 18:42:02 -0600
commit0bd18eeb5bcbbc17d476573ae8a311fc339b6c82 (patch)
tree3d5201c99fa176c2be635cf9c2b3fa1a02a3019e
parent3c6149cdd72ab8cc87cf008cc222514e30ef7470 (diff)
downloadhashie-0bd18eeb5bcbbc17d476573ae8a311fc339b6c82.tar.gz
Allow codependent Dash attributes to initialize
Definition: Codependent properties A set of two or more properties who have "required" validations that are based on each other. Example: ```ruby class OneOrMore < Hashie::Dash property :a, required: -> { b.nil? } property :b, required: -> { a.nil? } end ``` When constructing a Dash via the merge initializer, codependent properties have their "required" validation run too early when their values are set to `nil`, which causes an `ArgumentError` to be raised in the case that the first property is set to `nil`. This is an order-dependence bug that is fixed by this commit. By pruning off `nil` values only during initialization via the merge initializer, we can prevent this problem from occurring for the case of `nil` values. However, this is an indication of a larger problem with the architecture of Dash. We should be setting all the properties before running the validations. Rearchitecting this will be quite an undertaking.
-rw-r--r--CHANGELOG.md1
-rw-r--r--lib/hashie/dash.rb13
-rw-r--r--spec/hashie/dash_spec.rb25
3 files changed, 37 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a79834..8dbfc93 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@ scheme are considered to be bugs.
* [#435](https://github.com/intridea/hashie/pull/435): Mash `default_proc`s are now propagated down to nested sub-Hashes - [@michaelherold](https://github.com/michaelherold).
* [#436](https://github.com/intridea/hashie/pull/436): Ensure that `Hashie::Extensions::IndifferentAccess` injects itself after a non-destructive merge - [@michaelherold](https://github.com/michaelherold).
+* [#437](https://github.com/intridea/hashie/pull/437): Allow codependent properties to be set on Dash - [@michaelherold](https://github.com/michaelherold).
* Your contribution here.
### Security
diff --git a/lib/hashie/dash.rb b/lib/hashie/dash.rb
index 4ac7676..a111e4b 100644
--- a/lib/hashie/dash.rb
+++ b/lib/hashie/dash.rb
@@ -160,7 +160,7 @@ module Hashie
end
def update_attributes!(attributes)
- initialize_attributes(attributes)
+ update_attributes(attributes)
self.class.defaults.each_pair do |prop, value|
self[prop] = begin
@@ -175,9 +175,18 @@ module Hashie
private
def initialize_attributes(attributes)
+ return unless attributes
+
+ cleaned_attributes = attributes.reject { |_attr, value| value.nil? }
+ update_attributes(cleaned_attributes)
+ end
+
+ def update_attributes(attributes)
+ return unless attributes
+
attributes.each_pair do |att, value|
self[att] = value
- end if attributes
+ end
end
def assert_property_exists!(property)
diff --git a/spec/hashie/dash_spec.rb b/spec/hashie/dash_spec.rb
index 87e5bc3..fe789de 100644
--- a/spec/hashie/dash_spec.rb
+++ b/spec/hashie/dash_spec.rb
@@ -402,6 +402,31 @@ describe DashTest do
expect(subject.count).to eq subject.class.defaults[:count]
end
end
+
+ context 'codependent attributes' do
+ let(:codependent) do
+ Class.new(Hashie::Dash) do
+ property :a, required: -> { b.nil? }, message: 'is required if b is not set.'
+ property :b, required: -> { a.nil? }, message: 'is required if a is not set.'
+ end
+ end
+
+ it 'does not raise an error when only the first property is set' do
+ expect { codependent.new(a: 'ant', b: nil) }.not_to raise_error
+ end
+
+ it 'does not raise an error when only the second property is set' do
+ expect { codependent.new(a: nil, b: 'bat') }.not_to raise_error
+ end
+
+ it 'does not raise an error when both properties are set' do
+ expect { codependent.new(a: 'ant', b: 'bat') }.not_to raise_error
+ end
+
+ it 'raises an error when neither property is set' do
+ expect { codependent.new(a: nil, b: nil) }.to raise_error(ArgumentError)
+ end
+ end
end
end