spec/attribute_spec.rb in attributor-5.0.2 vs spec/attribute_spec.rb in attributor-5.1.0

- old
+ new

@@ -1,76 +1,71 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb') - describe Attributor::Attribute do - let(:attribute_options) { Hash.new } let(:type) { Attributor::String } subject(:attribute) { Attributor::Attribute.new(type, attribute_options) } - let(:context) { ["context"] } - let(:value) { "one" } + let(:context) { ['context'] } + let(:value) { 'one' } context 'initialize' do its(:type) { should be type } its(:options) { should be attribute_options } it 'calls check_options!' do - Attributor::Attribute.any_instance.should_receive(:check_options!) + expect_any_instance_of(Attributor::Attribute).to receive(:check_options!) Attributor::Attribute.new(type, attribute_options) end context 'for anonymous types (aka Structs)' do before do - Attributor.should_receive(:resolve_type).once.with(Struct,attribute_options, anything()).and_call_original + expect(Attributor).to receive(:resolve_type).once.with(Struct, attribute_options, anything).and_call_original end it 'generates the class' do - thing = Attributor::Attribute.new(Struct, attribute_options) do + Attributor::Attribute.new(Struct, attribute_options) do attribute :id, Integer end end - end - end context '==' do let(:other_attribute) { Attributor::Attribute.new(type, attribute_options) } - it { should == other_attribute} + it { should eq other_attribute } end context 'describe' do - let(:attribute_options) { {:required => true, :values => ["one"], :description => "something", :min => 0} } + let(:attribute_options) { { required: true, values: ['one'], description: 'something', min: 0 } } let(:expected) do - h = {type: {name: 'String', id: type.id, family: type.family}} - common = attribute_options.select{|k,v| Attributor::Attribute::TOP_LEVEL_OPTIONS.include? k } - h.merge!( common ) - h[:options] = {:min => 0 } + h = { type: { name: 'String', id: type.id, family: type.family } } + common = attribute_options.select { |k, _v| Attributor::Attribute::TOP_LEVEL_OPTIONS.include? k } + h.merge!(common) + h[:options] = { min: 0 } h end + its(:describe) { should eq expected } - its(:describe) { should == expected } - context 'with example options' do - let(:attribute_options) { {:description=> "something", :example => "ex_def"} } + let(:attribute_options) { { description: 'something', example: 'ex_def' } } its(:describe) { should have_key(:example_definition) } its(:describe) { should_not have_key(:example) } it 'should have the example value in the :example_definition key' do - subject.describe[:example_definition].should == "ex_def" + expect(subject.describe[:example_definition]).to eq 'ex_def' end end context 'with custom_data' do - let(:custom_data) { {loggable: true, visible_in_ui: false} } - let(:attribute_options) { {custom_data: custom_data} } + let(:custom_data) { { loggable: true, visible_in_ui: false } } + let(:attribute_options) { { custom_data: custom_data } } its(:describe) { should have_key(:custom_data) } it 'keep the custom data attribute' do - subject.describe[:custom_data].should == custom_data + expect(subject.describe[:custom_data]).to eq custom_data end end context 'for an anonymous type (aka: Struct)' do let(:attribute_options) { Hash.new } @@ -78,180 +73,172 @@ Attributor::Attribute.new(Struct, attribute_options) do attribute :id, Integer end end - subject(:description) { attribute.describe } - it 'uses the name of the first non-anonymous ancestor' do - description[:type][:name].should == 'Struct' + expect(description[:type][:name]).to eq 'Struct' end it 'includes sub-attributes' do - description[:type][:attributes].should have_key(:id) + expect(description[:type][:attributes]).to have_key(:id) end - end context 'with an example' do + let(:attribute_options) { {} } + let(:example) { attribute.example } + subject(:described) { attribute.describe(false, example: example) } - let(:attribute_options){ {} } - let(:example){ attribute.example } - subject(:described){ attribute.describe(false, example: example) } - context 'using a simple terminal type' do let(:type) { String } - its(:keys){ should include(:example) } + its(:keys) { should include(:example) } it 'should have the passed example value' do - described.should have_key(:example) - described[:example].should eq(example) + expect(described).to have_key(:example) + expect(described[:example]).to eq(example) end it 'should have removed the example from the :type' do - described[:type].should_not have_key(:example) + expect(described[:type]).not_to have_key(:example) end - end context 'using a complex type' do let(:type) { Cormorant } - its(:keys){ should_not include(:example) } + its(:keys) { should_not include(:example) } it 'Should see examples in the right places, depending on leaf/no-leaf types' do # String, a leaf attribute type: should have example name_attr = described[:type][:attributes][:name] - name_attr.should include(:example) - name_attr[:type].should_not include(:example) + expect(name_attr).to include(:example) + expect(name_attr[:type]).not_to include(:example) # Struct, a non-leaf attribute type: shouldn't have example - ts_attr = described[:type][:attributes][:timestamps] - ts_attr.should_not include(:example) - ts_attr[:type].should_not include(:example) + ts_attr = described[:type][:attributes][:timestamps] + expect(ts_attr).not_to include(:example) + expect(ts_attr[:type]).not_to include(:example) # DateTime inside a Struct, a nested leaf attribute type: should have example - born_attr = ts_attr[:type][:attributes][:born_at] - born_attr.should include(:example) - born_attr[:type].should_not include(:example) + born_attr = ts_attr[:type][:attributes][:born_at] + expect(born_attr).to include(:example) + expect(born_attr[:type]).not_to include(:example) end end end end - context 'parse' do - let(:loaded_object){ double("I'm loaded") } + let(:loaded_object) { double("I'm loaded") } it 'loads and validates' do - attribute.should_receive(:load).with(value,Attributor::DEFAULT_ROOT_CONTEXT).and_return(loaded_object) - attribute.should_receive(:validate).with(loaded_object,Attributor::DEFAULT_ROOT_CONTEXT).and_call_original + expect(attribute).to receive(:load).with(value, Attributor::DEFAULT_ROOT_CONTEXT).and_return(loaded_object) + expect(attribute).to receive(:validate).with(loaded_object, Attributor::DEFAULT_ROOT_CONTEXT).and_call_original attribute.parse(value) end end - context 'checking options' do it 'raises for invalid options' do - expect { + expect do Attributor::Attribute.new(Integer, unknown_opt: true) - }.to raise_error(/unsupported option/) + end.to raise_error(/unsupported option/) end it 'has a spec that we try to validate the :default value' do - expect { - Attributor::Attribute.new(Integer, default: "not an okay integer") - }.to raise_error(/Default value doesn't have the correct attribute type/) + expect do + Attributor::Attribute.new(Integer, default: 'not an okay integer') + end.to raise_error(/Default value doesn't have the correct attribute type/) end context 'custom_data' do it 'raises when not a hash' do - expect { + expect do Attributor::Attribute.new(Integer, custom_data: 1) - }.to raise_error(/custom_data must be a Hash/) + end.to raise_error(/custom_data must be a Hash/) end it 'does not raise for hashes' do - expect { - Attributor::Attribute.new(Integer, custom_data: {loggable: true}) - }.not_to raise_error + expect do + Attributor::Attribute.new(Integer, custom_data: { loggable: true }) + end.not_to raise_error end end end context 'example' do let(:example) { nil } context 'with nothing specified' do let(:attribute_options) { {} } before do - type.should_receive(:example).and_return(example) + expect(type).to receive(:example).and_return(example) end it 'defers to the type' do - attribute.example.should be example + expect(attribute.example).to be example end end context 'with an attribute that has the values option set' do - let(:values) { ["one", "two"] } - let(:attribute_options) { {:values => values} } + let(:values) { %w(one two) } + let(:attribute_options) { { values: values } } it 'picks a random value' do - values.should include subject.example + expect(values).to include subject.example end - end context 'deterministic examples' do let(:example) { /\w+/ } - let(:attribute_options) { {:example => example} } + let(:attribute_options) { { example: example } } it 'can take a context to pre-seed the random number generator' do example_1 = subject.example(['context']) example_2 = subject.example(['context']) - example_1.should eq example_2 + expect(example_1).to eq example_2 end it 'can take a context to pre-seed the random number generator' do example_1 = subject.example(['context']) example_2 = subject.example(['different context']) - example_1.should_not eq example_2 + expect(example_1).not_to eq example_2 end end context 'with an example option' do - let(:example){ "Bob" } - let(:attribute_options) { {example: example , regexp: /Bob/ } } + let(:example) { 'Bob' } + let(:attribute_options) { { example: example, regexp: /Bob/ } } - its(:example){ should == example } + its(:example) { should eq example } context 'that is not valid' do - let(:example){ "Frank" } + let(:example) { 'Frank' } it 'raises a validation error' do - expect{ + expect do subject.example - }.to raise_error(Attributor::AttributorException, /Error generating example/) + end.to raise_error(Attributor::AttributorException, /Error generating example/) end end end end context 'example_from_options' do let(:example) { nil } let(:generated_example) { example } - let(:attribute_options) { {:example => example} } - let(:parent){ nil } - let(:context){ Attributor::DEFAULT_ROOT_CONTEXT} + let(:attribute_options) { { example: example } } + let(:parent) { nil } + let(:context) { Attributor::DEFAULT_ROOT_CONTEXT } - subject(:example_result) { attribute.example_from_options( parent, context ) } + subject(:example_result) { attribute.example_from_options(parent, context) } before do - attribute.should_receive(:load).with( generated_example , an_instance_of(Array) ).and_call_original + expect(attribute).to receive(:load).with(generated_example, an_instance_of(Array)).and_call_original end context 'with a string' do - let(:example) { "example" } + let(:example) { 'example' } it { should be example } end context 'with an integer' do @@ -263,223 +250,210 @@ context 'with a regexp' do let(:example) { /\w+/ } let(:generated_example) { /\w+/.gen } it 'calls #gen on the regexp' do - example.should_receive(:gen).and_return(generated_example) + expect(example).to receive(:gen).and_return(generated_example) - example_result.should =~ example + expect(example_result).to match example end context 'for a type with a non-String native_type' do let(:type) { Attributor::Integer } let(:example) { /\d{5}/ } let(:generated_example) { /\d{5}/.gen } it 'coerces the example value properly' do - example.should_receive(:gen).and_return(generated_example) - type.should_receive(:load).and_call_original + expect(example).to receive(:gen).and_return(generated_example) + expect(type).to receive(:load).and_call_original - example_result.should be_kind_of(type.native_type) + expect(example_result).to be_kind_of(type.native_type) end end - end context 'with a proc' do - let(:parent){ Object.new } + let(:parent) { Object.new } context 'with one argument' do - let(:example) { lambda { |obj| 'ok' } } + let(:example) { ->(_obj) { 'ok' } } let(:generated_example) { 'ok' } before do - example.should_receive(:call).with(parent).and_return(generated_example) + expect(example).to receive(:call).with(parent).and_return(generated_example) end it 'passes any given parent through to the example proc' do - example_result.should == 'ok' + expect(example_result).to eq 'ok' end end context 'with two arguments' do - let(:example) { lambda { |obj, context| "#{context} ok" } } + let(:example) { ->(_obj, context) { "#{context} ok" } } let(:generated_example) { "#{context} ok" } - let(:context){ ['some_context'] } + let(:context) { ['some_context'] } before do - example.should_receive(:call).with(parent, context).and_return(generated_example) + expect(example).to receive(:call).with(parent, context).and_return(generated_example) end it 'passes any given parent through to the example proc' do - example_result.should == "#{context} ok" + expect(example_result).to eq "#{context} ok" end end - end context 'with an Collection (of Strings)' do let(:type) { Attributor::Collection.of(String) } - let(:example) { ["one"] } - it { should == example } + let(:example) { ['one'] } + it { should eq example } end - end context 'load' do - let(:context){ ['context'] } + let(:context) { ['context'] } let(:value) { '1' } it 'delegates to type.load' do - type.should_receive(:load).with(value,context, {}) - attribute.load(value,context) + expect(type).to receive(:load).with(value, context, {}) + attribute.load(value, context) end it 'passes options to type.load' do - type.should_receive(:load).with(value, context, foo: 'bar') + expect(type).to receive(:load).with(value, context, foo: 'bar') attribute.load(value, context, foo: 'bar') end context 'applying default values' do let(:value) { nil } - let(:default_value) { "default value" } - let(:attribute_options) { {:default => default_value} } + let(:default_value) { 'default value' } + let(:attribute_options) { { default: default_value } } subject(:result) { attribute.load(value) } context 'for nil' do - it { should == default_value} + it { should eq default_value } end context 'for false' do let(:type) { Attributor::Boolean } let(:default_value) { false } - it { should == default_value} - + it { should eq default_value } end context 'for a Proc-based default value' do - let(:context){ ["$"] } - subject(:result){ attribute.load(value,context) } + let(:context) { ['$'] } + subject(:result) { attribute.load(value, context) } - context 'with no arguments arguments' do - let(:default_value) { proc { "no_params" } } - it { should == default_value.call } + let(:default_value) { proc { 'no_params' } } + it { should eq default_value.call } end context 'with 1 argument (the parent)' do - let(:default_value) { proc {|parent| "parent is fake: #{parent.class}" } } - it { should == "parent is fake: Attributor::FakeParent" } + let(:default_value) { proc { |parent| "parent is fake: #{parent.class}" } } + it { should eq 'parent is fake: Attributor::FakeParent' } end context 'with 2 argument (the parent and the contents)' do - let(:default_value) { proc {|parent,context| "parent is fake: #{parent.class} and context is: #{context}" } } - it { should == "parent is fake: Attributor::FakeParent and context is: [\"$\"]"} + let(:default_value) { proc { |parent, context| "parent is fake: #{parent.class} and context is: #{context}" } } + it { should eq 'parent is fake: Attributor::FakeParent and context is: ["$"]' } end context 'which attempts to use the parent (which is not supported for the moment)' do - let(:default_value) { proc {|parent| "any parent method should spit out warning: [#{parent.something}]" } } - it "should output a warning" do + let(:default_value) { proc { |parent| "any parent method should spit out warning: [#{parent.something}]" } } + it 'should output a warning' do begin - old_verbose, $VERBOSE = $VERBOSE, nil - Kernel.should_receive(:warn).and_call_original - attribute.load(value,context).should == "any parent method should spit out warning: []" + old_verbose = $VERBOSE + $VERBOSE = nil + expect(Kernel).to receive(:warn).and_call_original + expect(attribute.load(value, context)).to eq 'any parent method should spit out warning: []' ensure $VERBOSE = old_verbose end end end end end context 'validating a value' do - context '#validate' do context 'applying attribute options' do context ':required' do - let(:attribute_options) { {:required => true} } + let(:attribute_options) { { required: true } } context 'with a nil value' do let(:value) { nil } it 'returns an error' do - attribute.validate(value, context).first.should == 'Attribute context is required' + expect(attribute.validate(value, context).first).to eq 'Attribute context is required' end end end context ':values' do - let(:values) { ['one','two'] } - let(:attribute_options) { {:values => values} } + let(:values) { %w(one two) } + let(:attribute_options) { { values: values } } let(:value) { nil } - subject(:errors) { attribute.validate(value, context)} + subject(:errors) { attribute.validate(value, context) } context 'with a value that is allowed' do - let(:value) { "one" } + let(:value) { 'one' } it 'returns no errors' do - errors.should be_empty + expect(errors).to be_empty end end context 'with a value that is not allowed' do - let(:value) { "three" } + let(:value) { 'three' } it 'returns an error indicating the problem' do - errors.first.should =~ /is not within the allowed values/ + expect(errors.first).to match(/is not within the allowed values/) end - end end - - end it 'calls the right validate_X methods?' do - attribute.should_receive(:validate_type).with(value, context).and_call_original - attribute.should_not_receive(:validate_dependency) - type.should_receive(:validate).and_call_original + expect(attribute).to receive(:validate_type).with(value, context).and_call_original + expect(attribute).not_to receive(:validate_dependency) + expect(type).to receive(:validate).and_call_original attribute.validate(value, context) end - end context '#validate_type' do - subject(:errors) { attribute.validate_type(value, context)} + subject(:errors) { attribute.validate_type(value, context) } context 'with a value of the right type' do - let(:value) { "one" } + let(:value) { 'one' } it 'returns no errors' do - errors.should be_empty + expect(errors).to be_empty end end context 'with a value of a value different than the native_type' do let(:value) { 1 } it 'returns errors' do - errors.should_not be_empty - errors.first.should =~ /is of the wrong type/ + expect(errors).not_to be_empty + expect(errors.first).to match(/is of the wrong type/) end - end - - end context '#validate_missing_value' do - let(:key) { "$.instance.ssh_key.name" } + let(:key) { '$.instance.ssh_key.name' } let(:value) { /\w+/.gen } - let(:attribute_options) { {:required_if => key} } + let(:attribute_options) { { required_if: key } } - let(:ssh_key) { double("ssh_key", :name => value) } - let(:instance) { double("instance", :ssh_key => ssh_key) } + let(:ssh_key) { double('ssh_key', name: value) } + let(:instance) { double('instance', ssh_key: ssh_key) } before { Attributor::AttributeResolver.current.register('instance', instance) } - let(:attribute_context) { ['$','params','key_material'] } + let(:attribute_context) { ['$', 'params', 'key_material'] } subject(:errors) { attribute.validate_missing_value(attribute_context) } - context 'for a simple dependency without a predicate' do context 'that is satisfied' do it { should_not be_empty } end @@ -488,118 +462,110 @@ it { should be_empty } end end context 'with a dependency that has a predicate' do - let(:value) { "default_ssh_key_name" } - #subject(:errors) { attribute.validate_missing_value('') } + let(:value) { 'default_ssh_key_name' } + # subject(:errors) { attribute.validate_missing_value('') } context 'where the target attribute exists, and matches the predicate' do - let(:attribute_options) { {:required_if => {key => /default/} } } + let(:attribute_options) { { required_if: { key => /default/ } } } it { should_not be_empty } - its(:first) { should =~ /Attribute #{Regexp.quote(Attributor.humanize_context( attribute_context ))} is required when #{Regexp.quote(key)} matches/ } + its(:first) { should match(/Attribute #{Regexp.quote(Attributor.humanize_context(attribute_context))} is required when #{Regexp.quote(key)} matches/) } end context 'where the target attribute exists, but does not match the predicate' do - let(:attribute_options) { {:required_if => {key => /other/} } } + let(:attribute_options) { { required_if: { key => /other/ } } } it { should be_empty } end context 'where the target attribute does not exist' do - let(:attribute_options) { {:required_if => {key => /default/} } } - let(:ssh_key) { double("ssh_key", :name => nil) } + let(:attribute_options) { { required_if: { key => /default/ } } } + let(:ssh_key) { double('ssh_key', name: nil) } it { should be_empty } end end - end - end - context 'for an attribute for a subclass of Model' do let(:type) { Chicken } let(:type_options) { Chicken.options } subject(:attribute) { Attributor::Attribute.new(type, attribute_options) } it 'has attributes' do - attribute.attributes.should == type.attributes + expect(attribute.attributes).to eq type.attributes end - #it 'has compiled_definition' do - # attribute.compiled_definition.should == type.definition - #end + # it 'has compiled_definition' do + # attribute.compiled_definition.should eq type.definition + # end - it 'merges its options with those of the compiled_definition' do - attribute.options.should == attribute_options.merge(type_options) + expect(attribute.options).to eq attribute_options.merge(type_options) end it 'describe handles sub-attributes nicely' do describe = attribute.describe(false) - describe[:type][:name].should == type.name - common_options = attribute_options.select{|k,v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k } - special_options = attribute_options.reject{|k,v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k } - common_options.each do |k,v| - describe[k].should == v + expect(describe[:type][:name]).to eq type.name + common_options = attribute_options.select { |k, _v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k } + special_options = attribute_options.reject { |k, _v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k } + common_options.each do |k, v| + expect(describe[k]).to eq v end - special_options.each do |k,v| - describe[:options][k].should == v + special_options.each do |k, v| + expect(describe[:options][k]).to eq v end - type_options.each do |k,v| - describe[:options][k].should == v + type_options.each do |k, v| + expect(describe[:options][k]).to eq v end - - attribute.attributes.each do |name, attr| - describe[:type][:attributes].should have_key(name) + attribute.attributes.each do |name, _attr| + expect(describe[:type][:attributes]).to have_key(name) end - end it 'supports deterministic examples' do - example_1 = attribute.example(["Chicken context"]) - example_2 = attribute.example(["Chicken context"]) + example_1 = attribute.example(['Chicken context']) + example_2 = attribute.example(['Chicken context']) - example_1.attributes.should eq(example_2.attributes) + expect(example_1.attributes).to eq(example_2.attributes) end context '#validate' do let(:chicken) { Chicken.example } let(:type_attributes) { type.attributes } it 'validates sub-attributes' do errors = attribute.validate(chicken) - errors.should be_empty + expect(errors).to be_empty end context 'with a failing validation' do - subject(:chicken) { Chicken.example(age: 150, email: "foo") } + subject(:chicken) { Chicken.example(age: 150, email: 'foo') } let(:email_validation_response) { ["$.email value \(#{chicken.email}\) does not match regexp (/@/)"] } let(:age_validation_response) { ["$.age value \(#{chicken.age}\) is larger than the allowed max (120)"] } it 'collects sub-attribute validation errors' do errors = attribute.validate(chicken) - errors.should =~ (age_validation_response | email_validation_response) + expect(errors).to match_array(age_validation_response | email_validation_response) end end - end - context '#validate_missing_value' do let(:type) { Duck } let(:attribute_name) { nil } let(:attribute) { Duck.attributes[attribute_name] } - let(:attribute_context) { ['$','duck',"#{attribute_name}"] } + let(:attribute_context) { ['$', 'duck', attribute_name.to_s] } subject(:errors) { attribute.validate_missing_value(attribute_context) } before do Attributor::AttributeResolver.current.register('duck', duck) end @@ -614,21 +580,20 @@ d end context 'where the target attribute exists, and matches the predicate' do it { should_not be_empty } - its(:first) { should == "Attribute $.duck.email is required when name (for $.duck) is present." } + its(:first) { should eq 'Attribute $.duck.email is required when name (for $.duck) is present.' } end context 'where the target attribute does not exist' do before do duck.name = nil end it { should be_empty } end end - context 'for a dependency with a predicate' do let(:attribute_name) { :age } let(:duck) do d = Duck.new @@ -637,11 +602,11 @@ d end context 'where the target attribute exists, and matches the predicate' do it { should_not be_empty } - its(:first) { should =~ /Attribute #{Regexp.quote('$.duck.age')} is required when name #{Regexp.quote('(for $.duck)')} matches/ } + its(:first) { should match(/Attribute #{Regexp.quote('$.duck.age')} is required when name #{Regexp.quote('(for $.duck)')} matches/) } end context 'where the target attribute exists, and does not match the predicate' do before do duck.name = 'Donald' @@ -653,71 +618,64 @@ before do duck.name = nil end it { should be_empty } end - end - end - end end context 'for a Collection' do context 'of non-Model (or Struct) type' do let(:member_type) { Attributor::Integer } - let(:type) { Attributor::Collection.of(member_type)} - let(:member_options) { {:max => 10} } - let(:attribute_options) { {:member_options => member_options} } + let(:type) { Attributor::Collection.of(member_type) } + let(:member_options) { { max: 10 } } + let(:attribute_options) { { member_options: member_options } } context 'the member_attribute of that type' do subject(:member_attribute) { attribute.type.member_attribute } - it { should be_kind_of(Attributor::Attribute)} + it { should be_kind_of(Attributor::Attribute) } its(:type) { should be(member_type) } its(:options) { should eq(member_options) } end - context "working with members" do - let(:values) { ['1',2,12] } + context 'working with members' do + let(:values) { ['1', 2, 12] } it 'loads' do - attribute.load(values).should =~ [1,2,12] + expect(attribute.load(values)).to match_array [1, 2, 12] end it 'validates' do object = attribute.load(values) errors = attribute.validate(object) - errors.should have(1).item - errors[0].should =~ /value \(12\) is larger/ + expect(errors).to have(1).item + expect(errors[0]).to match(/value \(12\) is larger/) end end - - end context 'of a Model (or Struct) type' do - subject(:attribute) { Attributor::Attribute.new(type, attribute_options, &attribute_block) } + subject(:attribute) { Attributor::Attribute.new(type, attribute_options, &attribute_block) } - let(:attribute_block) { Proc.new{ attribute :angry , required: true } } - let(:attribute_options) { {reference: Chicken, member_options: member_options} } + let(:attribute_block) { proc { attribute :angry, required: true } } + let(:attribute_options) { { reference: Chicken, member_options: member_options } } let(:member_type) { Attributor::Struct } let(:type) { Attributor::Collection.of(member_type) } let(:member_options) { {} } context 'the member_attribute of that type' do subject(:member_attribute) { attribute.type.member_attribute } - it { should be_kind_of(Attributor::Attribute)} + it { should be_kind_of(Attributor::Attribute) } its(:options) { should eq(member_options.merge(reference: Chicken, identity: :email)) } its(:attributes) { should have_key :angry } it 'inherited the type and options from the reference' do - member_attribute.attributes[:angry].type.should be(Chicken.attributes[:angry].type) - member_attribute.attributes[:angry].options.should eq(Chicken.attributes[:angry].options.merge(required: true)) + expect(member_attribute.attributes[:angry].type).to be(Chicken.attributes[:angry].type) + expect(member_attribute.attributes[:angry].options).to eq(Chicken.attributes[:angry].options.merge(required: true)) end end - end end - end