require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe Pupa::Model do module Music class Band include Pupa::Model self.schema = { '$schema' => 'http://json-schema.org/draft-03/schema#', 'properties' => { 'links' => { 'items' => { 'properties' => { 'url' => { 'type' => 'string', 'format' => 'uri', }, }, }, }, }, } attr_accessor :name, :label, :founding_date, :inactive, :label_id, :manager_id, :links dump :name, :label, :founding_date, :inactive, :label_id, :manager_id, :links foreign_key :label_id, :manager_id foreign_object :label end end let :properties do {name: 'Moderat', label: {name: 'Mute'}, inactive: false, manager_id: '1', links: [{url: 'http://moderat.fm/'}]} end let :object do Music::Band.new(properties) end describe '.dump' do it 'should add properties' do [:_id, :_type, :extras, :name, :label, :founding_date, :inactive, :label_id, :manager_id, :links].each do |property| expect(Music::Band.properties.to_a).to include(property) end end end describe '.foreign_key' do it 'should add foreign keys' do expect(Music::Band.foreign_keys.to_a).to eq([:label_id, :manager_id]) end end describe '.foreign_object' do it 'should add foreign objects' do expect(Music::Band.foreign_objects.to_a).to eq([:label]) end end describe '.schema=' do let :klass_with_absolute_path do Class.new do include Pupa::Model self.schema = '/path/to/schema.json' end end let :klass_with_relative_path do Class.new do include Pupa::Model self.schema = 'schema.json' end end it 'should accept a hash' do expect(Music::Band.json_schema).to eq({ '$schema' => 'http://json-schema.org/draft-03/schema#', 'properties' => { 'links' => { 'items' => { 'properties' => { 'url' => { 'type' => 'string', 'format' => 'uri', }, }, }, }, }, }) end it 'should accept an absolute path' do expect(File).to receive(:read).and_return('{}') expect(klass_with_absolute_path.json_schema).to eq('/path/to/schema.json') end it 'should accept a relative path' do expect(File).to receive(:read).and_return('{}') expect(klass_with_relative_path.json_schema).to eq('schema.json') end end describe '#initialize' do it 'should set the _type' do expect(object._type).to eq('music/band') end it 'should set the _id' do expect(object._id).to match(/\A[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/) end it 'should set properties' do expect(object.name).to eq('Moderat') expect(object.label).to eq({name: 'Mute'}) expect(object.inactive).to eq(false) expect(object.manager_id).to eq('1') expect(object.links).to eq([{url: 'http://moderat.fm/'}]) end end describe '#[]' do it 'should get a property' do expect(object[:name]).to eq('Moderat') end it 'should raise an error if the class is missing the property' do expect{object[:nonexistent]}.to raise_error(Pupa::Errors::MissingAttributeError) end end describe '#[]=' do it 'should set a property' do object[:name] = 'Apparat' expect(object[:name]).to eq('Apparat') end it 'should raise an error if the class is missing the property' do expect{object[:nonexistent] = 1}.to raise_error(Pupa::Errors::MissingAttributeError) end end describe '#_id=' do it 'should set the _id' do object._id = '1' expect(object._id).to eq('1') end it 'should coerce the _id to a string' do object._id = BSON::ObjectId.new expect(object._id).to be_a(String) end end describe '#links' do it 'should symbolize keys' do object.extras = {'age' => 10} expect(object.extras).to eq({age: 10}) end end describe '#add_extra' do it 'should add an extra property' do object.add_extra(:age, 10) expect(object.extras).to eq({age: 10}) end end describe '#fingerprint' do it 'should return the fingerprint' do expect(object.fingerprint).to eq({_type: 'music/band', name: 'Moderat', inactive: false, manager_id: '1', links: [{url: 'http://moderat.fm/'}]}) end end describe '#foreign_properties' do it 'should return the foreign keys and foreign objects' do expect(object.foreign_properties).to eq({label: {name: 'Mute'}, manager_id: '1'}) end end describe '#validate!' do let :klass_without_schema do Class.new do include Pupa::Model end end it 'should do nothing if the schema is not set' do expect(klass_without_schema.new.validate!).to eq(nil) end it 'should return true if the object is valid' do expect(object.validate!).to eq(true) end it 'should raise an error if the object is invalid' do object[:links][0][:url] = 'invalid' expect{object.validate!}.to raise_error(JSON::Schema::ValidationError) end end describe '#to_h' do it 'should include all properties by default' do expect(object.to_h).to eq({_id: object._id, _type: 'music/band', name: 'Moderat', label: {name: 'Mute'}, inactive: false, manager_id: '1', links: [{url: 'http://moderat.fm/'}]}) end it 'should exclude foreign objects if persisting' do expect(object.to_h(persist: true)).to eq({_id: object._id, _type: 'music/band', name: 'Moderat', inactive: false, manager_id: '1', links: [{url: 'http://moderat.fm/'}]}) end it 'should not include blank properties' do expect(object.to_h).not_to have_key(:founding_date) end it 'should include false properties' do expect(object.to_h).to have_key(:inactive) end end describe '#==' do it 'should return true if two objects are equal' do expect(object).to eq(Music::Band.new(properties)) end it 'should return false if two objects are unequal' do expect(object).not_to eq(Music::Band.new(properties.merge(name: 'Apparat'))) end end end