summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Balaban <balaban.petr@gmail.com>2014-10-07 18:25:55 +0300
committerPetr Balaban <balaban.petr@gmail.com>2014-10-07 18:25:55 +0300
commit903026ee68075950ba04fec044e1b631426a69d7 (patch)
tree0cc30d7929a5684687e5310c70499d8e952e6146
parent72306d654decdb139023f14452c3dc68d64639a6 (diff)
downloadhashie-903026ee68075950ba04fec044e1b631426a69d7.tar.gz
Custom error messages for required properties in Hashie::Dash subclasses
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md12
-rw-r--r--lib/hashie/dash.rb16
-rw-r--r--spec/hashie/dash_spec.rb29
4 files changed, 49 insertions, 9 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 43ac5ca..e85090f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
## Next Release
* Your contribution here.
+* [#233](https://github.com/intridea/hashie/pull/233): Custom error messages for required properties in Hashie::Dash subclasses - [@joss](https://github.com/joss).
* [#231](https://github.com/intridea/hashie/pull/231): Added support for coercion on class type that inherit from Hash - [@gregory](https://github.com/gregory).
* [#228](https://github.com/intridea/hashie/pull/228): Made Hashie::Extensions::Parsers::YamlErbParser pass template filename to ERB - [@jperville](https://github.com/jperville).
* [#224](https://github.com/intridea/hashie/pull/224): Merging Hashie::Mash now correctly only calls the block on duplicate values - [@amysutedja](https://github.com/amysutedja).
diff --git a/README.md b/README.md
index 4bfc875..56f5cbe 100644
--- a/README.md
+++ b/README.md
@@ -385,22 +385,26 @@ safe_mash.zip = 'Test' # => ArgumentError
## Dash
-Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset.
+Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset. Another option is message for required properties, which allow you to add custom messages for required property.
### Example:
```ruby
class Person < Hashie::Dash
property :name, required: true
+ property :age, required: true, message: 'must be set.'
property :email
property :occupation, default: 'Rubyist'
end
p = Person.new # => ArgumentError: The property 'name' is required for this Dash.
+p = Person.new(name: 'Bob') # => ArgumentError: The property 'age' must be set.
-p = Person.new(name: "Bob")
-p.name # => 'Bob'
-p.name = nil # => ArgumentError: The property 'name' is required for this Dash.
+p = Person.new(name: "Bob", age: 18)
+p.name # => 'Bob'
+p.name = nil # => ArgumentError: The property 'name' is required for this Dash.
+p.age # => 18
+p.age = nil # => ArgumentError: The property 'age' must be set.
p.email = 'abc@def.com'
p.occupation # => 'Rubyist'
p.email # => 'abc@def.com'
diff --git a/lib/hashie/dash.rb b/lib/hashie/dash.rb
index b6e7fb5..9381ddd 100644
--- a/lib/hashie/dash.rb
+++ b/lib/hashie/dash.rb
@@ -27,6 +27,7 @@ module Hashie
# * <tt>:required</tt> - Specify the value as required for this
# property, to raise an error if a value is unset in a new or
# existing Dash.
+ # * <tt>:message</tt> - Specify custom error message for required property
#
def self.property(property_name, options = {})
properties << property_name
@@ -46,7 +47,12 @@ module Hashie
if defined? @subclasses
@subclasses.each { |klass| klass.property(property_name, options) }
end
- required_properties << property_name if options.delete(:required)
+
+ if options.delete(:required)
+ required_properties[property_name] = options.delete(:message) || "is required for #{name}."
+ else
+ fail ArgumentError, 'The :message option should be used with :required option.' if options.key?(:message)
+ end
end
class << self
@@ -55,7 +61,7 @@ module Hashie
end
instance_variable_set('@properties', Set.new)
instance_variable_set('@defaults', {})
- instance_variable_set('@required_properties', Set.new)
+ instance_variable_set('@required_properties', {})
def self.inherited(klass)
super
@@ -74,7 +80,7 @@ module Hashie
# Check to see if the specified property is
# required.
def self.required?(name)
- required_properties.include? name
+ required_properties.key? name
end
# You may initialize a Dash with an attributes hash
@@ -168,7 +174,7 @@ module Hashie
end
def assert_required_attributes_set!
- self.class.required_properties.each do |required_property|
+ self.class.required_properties.each_key do |required_property|
assert_property_set!(required_property)
end
end
@@ -182,7 +188,7 @@ module Hashie
end
def fail_property_required_error!(property)
- fail ArgumentError, "The property '#{property}' is required for #{self.class.name}."
+ fail ArgumentError, "The property '#{property}' #{self.class.required_properties[property]}"
end
def fail_no_property_error!(property)
diff --git a/spec/hashie/dash_spec.rb b/spec/hashie/dash_spec.rb
index 82267d2..2e2b23f 100644
--- a/spec/hashie/dash_spec.rb
+++ b/spec/hashie/dash_spec.rb
@@ -34,6 +34,10 @@ class SubclassedTest < DashTest
property :last_name, required: true
end
+class RequiredMessageTest < DashTest
+ property :first_name, required: true, message: 'must be set.'
+end
+
class DashDefaultTest < Hashie::Dash
property :aliases, default: ['Snake']
end
@@ -47,11 +51,20 @@ describe DashTest do
[ArgumentError, "The property '#{property}' is required for #{subject.class.name}."]
end
+ def property_required_custom_error(property)
+ [ArgumentError, "The property '#{property}' must be set."]
+ end
+
+ def property_message_without_required_error
+ [ArgumentError, 'The :message option should be used with :required option.']
+ end
+
def no_property_error(property)
[NoMethodError, "The property '#{property}' is not defined for #{subject.class.name}."]
end
subject { DashTest.new(first_name: 'Bob', email: 'bob@example.com') }
+ let(:required_message) { RequiredMessageTest.new(first_name: 'Bob') }
it('subclasses Hashie::Hash') { should respond_to(:to_mash) }
@@ -83,13 +96,29 @@ describe DashTest do
expect { subject.first_name = nil }.to raise_error(*property_required_error('first_name'))
end
+ it 'errors out when message added to not required property' do
+ expect do
+ class DashMessageOptionWithoutRequiredTest < Hashie::Dash
+ property :first_name, message: 'is required.'
+ end
+ end.to raise_error(*property_message_without_required_error)
+
+ expect do
+ class DashMessageOptionWithoutRequiredTest < Hashie::Dash
+ property :first_name, required: false, message: 'is required.'
+ end
+ end.to raise_error(*property_message_without_required_error)
+ end
+
context 'writing to properties' do
it 'fails writing a required property to nil' do
expect { subject.first_name = nil }.to raise_error(*property_required_error('first_name'))
+ expect { required_message.first_name = nil }.to raise_error(*property_required_custom_error('first_name'))
end
it 'fails writing a required property to nil using []=' do
expect { subject[:first_name] = nil }.to raise_error(*property_required_error('first_name'))
+ expect { required_message[:first_name] = nil }.to raise_error(*property_required_custom_error('first_name'))
end
it 'fails writing to a non-existent property using []=' do