diff --git a/lib/lego/value.rb b/lib/lego/value.rb index 88a8708..eafc7f4 100644 --- a/lib/lego/value.rb +++ b/lib/lego/value.rb @@ -16,3 +16,4 @@ class Boolean; end require_relative 'value/integer' require_relative 'value/float' require_relative 'value/boolean' +require_relative 'value/hash' diff --git a/lib/lego/value/array.rb b/lib/lego/value/array.rb index 9939994..2e7b1eb 100644 --- a/lib/lego/value/array.rb +++ b/lib/lego/value/array.rb @@ -2,7 +2,7 @@ module Lego::Value class Array < Base def initialize(type, opts={}) - @_item_parser = Lego.value_parser(type) + @_item_parser = Lego.value_parser(type, opts[:children_opts] || {} ) super(opts) end diff --git a/lib/lego/value/hash.rb b/lib/lego/value/hash.rb new file mode 100644 index 0000000..e8949e8 --- /dev/null +++ b/lib/lego/value/hash.rb @@ -0,0 +1,16 @@ +module Lego::Value + class Hash < Base + def parsers + [ + ->(v) { v.respond_to?(:to_hash) ? Lego.just(v.to_hash.freeze) : Lego.fail("invalid hash: #{v.inspect}") }, + ->(v) { (not allow_empty? and v.empty?) ? Lego.none : Lego.just(v) } + ] + end + + private + + def allow_empty? + @opts.fetch(:allow_empty, false) + end + end +end diff --git a/spec/lego/value/array_spec.rb b/spec/lego/value/array_spec.rb index 21fe178..9b40f48 100644 --- a/spec/lego/value/array_spec.rb +++ b/spec/lego/value/array_spec.rb @@ -37,6 +37,11 @@ specify { subject.parse([1, 2, 3]).should be_error("length not 2: '[1, 2, 3]'") } specify { subject.parse([42, 24]).should be_just([42, 24]) } end + + context 'passess children_opts to children parser' do + subject { Lego::Value::Array.new(String, children_opts: { allow_blank: true, default: -> { 'default' }} )} + specify { subject.parse(["abc", "", nil]).should be_just(["abc", "", "default"])} + end end describe '#coerce' do diff --git a/spec/lego/value/hash_spec.rb b/spec/lego/value/hash_spec.rb new file mode 100644 index 0000000..2d8f175 --- /dev/null +++ b/spec/lego/value/hash_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Lego::Value::Hash do + describe '#parse' do + context 'parses nil' do + subject { Lego::Value::Hash.new(allow_empty:false, default: -> { {key: 'value' } }) } + specify { subject.parse(nil).should be_just({ key: 'value' }) } + end + + context 'allow empty' do + subject { Lego::Value::Hash.new(allow_empty: true) } + + it 'parses empty hash' do + subject.parse({}).should be_just({}) + end + end + + context 'disallow empty' do + subject { Lego::Value::Hash.new(allow_empty:false, default: -> { {key: 'value' } }) } + specify { subject.parse({}).should be_just({key: 'value' }) } + end + + let(:hash) {{ + key: 'value', + key2: 3, + key3: { + nested_key: Object.new, + nested_key2: 'nested value' + } + }} + + it 'parses non-empty hash' do + result = subject.parse(hash) + result.should be_just(hash) + result.value.should be_frozen + end + + context 'returns error when invalid hash' do + specify { subject.parse('').should be_error('invalid hash: ""') } + specify { subject.parse([]).should be_error('invalid hash: []') } + specify { subject.parse(6).should be_error('invalid hash: 6') } + end + end + + describe '#coerce' do + context 'missing' do + it 'raises error' do + expect{ subject.coerce(nil) }.to raise_error(Lego::CoerceError, 'missing value') + expect{ subject.coerce({}) }.to raise_error(Lego::CoerceError, 'missing value') + end + end + + context 'with :default handler' do + subject { Lego::Value::Hash.new(default: handler) } + + context 'nil handler' do + let(:handler) { -> { nil } } + specify { subject.coerce(nil).should be_nil } + end + + context 'lambda handler' do + let(:default_hash) { { key: 'value'} } + let(:handler) { -> { default_hash } } + specify { subject.coerce(nil).should == default_hash } + end + end + + context 'failure' do + it 'raises error' do + expect { subject.coerce(nil) }.to raise_error(Lego::CoerceError, 'missing value') + end + end + + context 'success' do + it 'returns hash' do + subject.coerce({key: 'value' }).should == { key: 'value' } + end + end + end +end