diff --git a/lib/structural.rb b/lib/structural.rb index 5929b88..68127ce 100644 --- a/lib/structural.rb +++ b/lib/structural.rb @@ -3,6 +3,7 @@ require 'structural/version' require 'structural/missing_attribute_error' +require 'structural/invalid_default_type_error' require 'structural/hashifier' require 'structural/model/definer' require 'structural/model/descriptor' diff --git a/lib/structural/invalid_default_type_error.rb b/lib/structural/invalid_default_type_error.rb new file mode 100644 index 0000000..760bff6 --- /dev/null +++ b/lib/structural/invalid_default_type_error.rb @@ -0,0 +1,4 @@ +module Structural + class InvalidDefaultTypeError < StandardError + end +end diff --git a/lib/structural/model/has_one.rb b/lib/structural/model/has_one.rb index 015b9c3..a1300db 100644 --- a/lib/structural/model/has_one.rb +++ b/lib/structural/model/has_one.rb @@ -2,9 +2,22 @@ module Structural module Model class HasOne < Association def value_of(data) - child = data.fetch(key) { raise MissingAttributeError, key } + child = data.fetch(key, &default_value) type.new(child) unless child.nil? end + + def default + valid_type_check(super) + end + + private + + def valid_type_check(v) + case v + when Hash then v + when Proc then valid_type_check(v.call) + else raise Structural::InvalidDefaultTypeError end + end end end end diff --git a/lib/structural/version.rb b/lib/structural/version.rb index 72e1437..b228c23 100644 --- a/lib/structural/version.rb +++ b/lib/structural/version.rb @@ -1,3 +1,3 @@ module Structural - VERSION = '0.1.0' + VERSION = '0.2.0' end diff --git a/spec/lib/structural/model_spec.rb b/spec/lib/structural/model_spec.rb index 32476f9..32ba74b 100644 --- a/spec/lib/structural/model_spec.rb +++ b/spec/lib/structural/model_spec.rb @@ -3,6 +3,14 @@ class NestedModel include Structural::Model field :yak + field :name, :default => nil + field :surname, :default => nil +end + +class SeparateClass + def self.as_hash + { :name => 'Michael' } + end end class TestModel @@ -20,6 +28,11 @@ class TestModel has_one :aliased_model, :type => NestedModel has_one :nested_model, :key => 'aliased_model' has_one :extra_nested_model + has_one :nested_with_invalid_default, :default => nil + has_one :nested_with_hash_default, :default => { name: 'Michael' }, :type => NestedModel + has_one :nested_with_hash_proc_default, :default => Proc.new { SeparateClass.as_hash }, :type => NestedModel + has_one :nested_with_invalid_proc_default, :default => Proc.new { SeparateClass.new }, :type => NestedModel + has_one :missing_nested_without_default has_one :test_model has_many :nested_models @@ -87,20 +100,55 @@ class ExtraNestedModel end describe ".has_one" do - it "allows nested models" do + it 'allows nested models' do model.aliased_model.should be_a NestedModel end - it "allows nested models" do + + it 'allows nested models' do model.nested_model.should be_a NestedModel model.nested_model.yak.should eq 11 end - it "allows associations to be nested within the class" do + + it 'allows associations to be nested within the class' do model.extra_nested_model.should be_a TestModel::ExtraNestedModel model.extra_nested_model.cats.should eq 'MIAOW' end - it "allows recursively defined models" do + + it 'allows recursively defined models' do model.test_model.should be_a TestModel end + + it 'allows default values as a hash' do + model.nested_with_hash_default.name.should eq 'Michael' + model.nested_with_hash_default.surname.should be_nil + end + + context 'when passing a Proc as a default' do + it 'allows the default' do + model.nested_with_hash_proc_default.name.should eq 'Michael' + model.nested_with_hash_default.surname.should be_nil + end + + context 'when the Proc does not return a Hash' do + it 'raises an error' do + expect { + model.nested_with_invalid_proc_default + }.to raise_error(Structural::InvalidDefaultTypeError) + end + end + end + + it 'fails if passed a non-hash as default' do + expect { + model.nested_with_invalid_default + }.to raise_error(Structural::InvalidDefaultTypeError) + end + + it 'fails for missing associations without defaults' do + expect { + model.missing_nested_without_default + }.to raise_error(Structural::MissingAttributeError) + end end describe ".has_many" do