require 'spec_helper' describe ROM::Mapper do subject(:mapper) do klass = Class.new(parent) options.each { |k, v| klass.send(k, v) } klass end let(:parent) { Class.new(ROM::Mapper) } let(:options) { {} } let(:header) { mapper.header } let(:expected_header) { ROM::Header.coerce(attributes) } describe '#attribute' do context 'simple attribute' do let(:attributes) { [[:name]] } it 'adds an attribute for the header' do mapper.attribute :name expect(header).to eql(expected_header) end end context 'aliased attribute' do let(:attributes) { [[:name, from: :user_name]] } it 'adds an aliased attribute for the header' do mapper.attribute :name, from: :user_name expect(header).to eql(expected_header) end end context 'prefixed attribute' do let(:attributes) { [[:name, from: :user_name]] } let(:options) { { prefix: :user } } it 'adds an aliased attribute for the header using configured :prefix' do mapper.attribute :name expect(header).to eql(expected_header) end end context 'prefixed attribute using custom separator' do let(:attributes) { [[:name, from: :'u.name']] } let(:options) { { prefix: :u, prefix_separator: '.' } } it 'adds an aliased attribute for the header using configured :prefix' do mapper.attribute :name expect(header).to eql(expected_header) end end context 'symbolized attribute' do let(:attributes) { [[:name, from: 'name']] } let(:options) { { symbolize_keys: true } } it 'adds an attribute with symbolized alias' do mapper.attribute :name expect(header).to eql(expected_header) end end end describe 'overriding inherited attributes' do context 'when name matches' do let(:attributes) { [[:name, type: :string]] } it 'excludes the inherited attribute' do parent.attribute :name mapper.attribute :name, type: :string expect(header).to eql(expected_header) end end context 'when alias matches' do let(:attributes) { [[:name, from: 'name', type: :string]] } it 'excludes the inherited attribute' do parent.attribute 'name' mapper.attribute :name, from: 'name', type: :string expect(header).to eql(expected_header) end end context 'when name in a wrapped attribute matches' do let(:attributes) do [ [:city, type: :hash, wrap: true, header: [[:name, from: :city_name]]] ] end it 'excludes the inherited attribute' do parent.attribute :city_name mapper.wrap :city do attribute :name, from: :city_name end expect(header).to eql(expected_header) end end context 'when name in a grouped attribute matches' do let(:attributes) do [ [:tags, type: :array, group: true, header: [[:name, from: :tag_name]]] ] end it 'excludes the inherited attribute' do parent.attribute :tag_name mapper.group :tags do attribute :name, from: :tag_name end expect(header).to eql(expected_header) end end context 'when name in a hash attribute matches' do let(:attributes) do [ [:city, type: :hash, header: [[:name, from: :city_name]]] ] end it 'excludes the inherited attribute' do parent.attribute :city mapper.embedded :city, type: :hash do attribute :name, from: :city_name end expect(header).to eql(expected_header) end end context 'when name of an array attribute matches' do let(:attributes) do [ [:tags, type: :array, header: [[:name, from: :tag_name]]] ] end it 'excludes the inherited attribute' do parent.attribute :tags mapper.embedded :tags, type: :array do attribute :name, from: :tag_name end expect(header).to eql(expected_header) end end end describe '#exclude' do let(:attributes) { [[:name, from: 'name']] } it 'removes an attribute from the inherited header' do mapper.attribute :name, from: 'name' expect(header).to eql(expected_header) end end describe '#embedded' do context 'when :type is set to :hash' do let(:attributes) { [[:city, type: :hash, header: [[:name]]]] } it 'adds an embedded hash attribute' do mapper.embedded :city, type: :hash do attribute :name end expect(header).to eql(expected_header) end end context 'when :type is set to :array' do let(:attributes) { [[:tags, type: :array, header: [[:name]]]] } it 'adds an embedded array attribute' do mapper.embedded :tags, type: :array do attribute :name end expect(header).to eql(expected_header) end end end describe '#wrap' do let(:attributes) { [[:city, type: :hash, wrap: true, header: [[:name]]]] } it 'adds an wrapped hash attribute using a block to define attributes' do mapper.wrap :city do attribute :name end expect(header).to eql(expected_header) end it 'adds an wrapped hash attribute using a options define attributes' do mapper.wrap city: [:name] expect(header).to eql(expected_header) end end describe '#group' do let(:attributes) { [[:tags, type: :array, group: true, header: [[:name]]]] } it 'adds a group attribute using a block to define attributes' do mapper.group :tags do attribute :name end expect(header).to eql(expected_header) end it 'adds a group attribute using a options define attributes' do mapper.group tags: [:name] expect(header).to eql(expected_header) end end describe 'top-level :prefix option' do let(:options) do { prefix: :user } end context 'when no attribute overrides top-level setting' do let(:attributes) do [ [:name, from: :user_name], [:address, from: :user_address, type: :hash, header: [ [:city, from: :user_city]] ], [:contact, type: :hash, wrap: true, header: [ [:mobile, from: :user_mobile]] ], [:tasks, type: :array, group: true, header: [ [:title, from: :user_title]] ] ] end it 'sets aliased attributes using prefix automatically' do mapper.attribute :name mapper.embedded :address, type: :hash do attribute :city end mapper.wrap :contact do attribute :mobile end mapper.group :tasks do attribute :title end expect(header).to eql(expected_header) end end context 'when an attribute overrides top-level setting' do let(:attributes) do [ [:name, from: :user_name], [:birthday, from: :user_birthday, type: :hash, header: [ [:year, from: :bd_year], [:month, from: :bd_month], [:day, from: :bd_day]] ], [:address, from: :user_address, type: :hash, header: [[:city]]], [:contact, type: :hash, wrap: true, header: [ [:mobile, from: :contact_mobile]] ], [:tasks, type: :array, group: true, header: [ [:title, from: :task_title]] ] ] end it 'excludes from aliasing the ones which override it' do mapper.attribute :name mapper.embedded :birthday, type: :hash, prefix: :bd do attribute :year attribute :month attribute :day end mapper.embedded :address, type: :hash, prefix: false do attribute :city end mapper.wrap :contact, prefix: :contact do attribute :mobile end mapper.group :tasks, prefix: :task do attribute :title end expect(header).to eql(expected_header) end end end end