Skip to content

Commit

Permalink
Implement IgnoreRequired
Browse files Browse the repository at this point in the history
  • Loading branch information
maxlinc committed Jan 9, 2015
1 parent 700aed2 commit 503697d
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/hashie.rb
Expand Up @@ -12,6 +12,7 @@ module Extensions
autoload :Coercion, 'hashie/extensions/coercion'
autoload :DeepMerge, 'hashie/extensions/deep_merge'
autoload :IgnoreUndeclared, 'hashie/extensions/ignore_undeclared'
autoload :IgnoreRequired, 'hashie/extensions/ignore_required'
autoload :IndifferentAccess, 'hashie/extensions/indifferent_access'
autoload :MergeInitializer, 'hashie/extensions/merge_initializer'
autoload :MethodAccess, 'hashie/extensions/method_access'
Expand Down
42 changes: 42 additions & 0 deletions lib/hashie/extensions/ignore_required.rb
@@ -0,0 +1,42 @@
module Hashie
module Extensions
# IgnoreRequired is a simple mixin that silently ignores
# required properties on initialization and assignment instead of
# raising an error. This is useful when using a building an object
# that will eventually be match a Dash but is temporarily incomplete.
#
# @example
# class Person < Hashie::Dash
#
# property :first_name, required: true
# property :last_name, required: true
# property :email
# end
#
# class PartialPerson < Person
# include Hashie::Extensions::IgnoreRequired
# end
#
# user_data = {
# :first_name => 'Freddy',
# }
#
# p = Person.new(user_data) # ArgumentError: The property 'last_name' is required for Person.
#
# p = PartialPerson.new(user_data)
# p.last_name = 'Nostrils'
# p.first_name # => 'Freddy'
# p.first_name # => 'Nostrils'
# p.email # => nil
# p.foo # => NoMethodError
module IgnoreRequired
def assert_property_required!(_property, _value)
# do nothing
end

def assert_property_set!(_property)
# do nothing
end
end
end
end
65 changes: 65 additions & 0 deletions spec/hashie/extensions/ignore_required_spec.rb
@@ -0,0 +1,65 @@
require 'spec_helper'

describe Hashie::Extensions::IgnoreRequired do
context 'included in Dash' do
class ForgivingDash < Hashie::Dash
include Hashie::Extensions::IgnoreRequired
property :city, required: true
property :state, required: true, from: :province
property :zip, required: true
end

subject { ForgivingDash }

it 'silently ignores required properties on initialization' do
expect { subject.new(city: 'New York') }.to_not raise_error
end

it 'raises errors for undefined properties on initialization' do
expect { subject.new(city: 'Toronto', province: 'Ontario') }.to raise_error(NoMethodError, /property 'province' is not defined/)
end

it 'requires properties to be declared on assignment' do
hash = subject.new(city: 'Toronto')
expect { hash.country = 'Canada' }.to raise_error(NoMethodError)
end

it 'requires properties to be declared on access' do
hash = subject.new(city: 'Toronto')
expect { hash.country }.to raise_error(NoMethodError)
end
end

context 'combined with Coercion' do
class ForgivingDashWithCoercion < ForgivingDash
include Hashie::Extensions::Coercion
coerce_key :zip, ->(v) { format('%05d', v) }
end

subject { ForgivingDashWithCoercion }

it 'works with coerced properties' do
expect(subject.new(zip: 501).zip).to eq('00501')
end

context 'with nested, coerced Dashes' do
class Address < Hashie::Dash
property :number, required: true
property :street, required: true
property :apartment
end

class ForgivingDashWithAddress < ForgivingDashWithCoercion
property :address, required: true
coerce_key :address, Address
end

subject { ForgivingDashWithAddress }

it 'does not work propagate to nested, coercable properties' do
address = { street: 'Pennsylvania Avenue' }
expect { subject.new(address: address) }.to raise_error(ArgumentError, /property 'number' is required for Address/)
end
end
end
end

0 comments on commit 503697d

Please sign in to comment.