diff options
author | Petr Balaban <balaban.petr@gmail.com> | 2014-10-07 18:25:55 +0300 |
---|---|---|
committer | Petr Balaban <balaban.petr@gmail.com> | 2014-10-07 18:25:55 +0300 |
commit | 903026ee68075950ba04fec044e1b631426a69d7 (patch) | |
tree | 0cc30d7929a5684687e5310c70499d8e952e6146 | |
parent | 72306d654decdb139023f14452c3dc68d64639a6 (diff) | |
download | hashie-903026ee68075950ba04fec044e1b631426a69d7.tar.gz |
Custom error messages for required properties in Hashie::Dash subclasses
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | lib/hashie/dash.rb | 16 | ||||
-rw-r--r-- | spec/hashie/dash_spec.rb | 29 |
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). @@ -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 |