Skip to content

Commit

Permalink
Applying work on default values injection
Browse files Browse the repository at this point in the history
  • Loading branch information
etki committed Jul 30, 2017
2 parents 405831f + d19b244 commit ff1c911
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 52 deletions.
18 changes: 16 additions & 2 deletions lib/mapper/api/default/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative '../factory'
require_relative '../../mixin/errors'
require_relative '../../path/segment'

module AMA
module Entity
Expand All @@ -17,15 +18,28 @@ class Factory < API::Factory
# @param [AMA::Entity::Mapper::Type] type
# @param [Object] _data
# @param [AMA::Entity::Mapper::Context] context
def create(type, _data = nil, context = nil)
type.type.new
def create(type, _data, context)
create_internal(type)
rescue StandardError => e
message = "Failed to instantiate #{type} directly from class"
if e.is_a?(ArgumentError)
message += '. Does it have parameterless #initialize() method?'
end
mapping_error(message, parent: e, context: context)
end

private

def create_internal(type)
entity = type.type.new
type.attributes.values.each do |attribute|
next if attribute.default.nil? || attribute.virtual
segment = Path::Segment.attribute(attribute.name)
value = attribute.default
type.injector.inject(entity, type, attribute, value, segment)
end
entity
end
end
end
end
Expand Down
94 changes: 94 additions & 0 deletions test/suite/integration/mapper/api/default/factory.spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true

require_relative '../../../../../../lib/mapper/api/default/factory'
require_relative '../../../../../../lib/mapper/exception/mapping_error'

klass = ::AMA::Entity::Mapper::API::Default::Factory
mapping_error_class = ::AMA::Entity::Mapper::Exception::MappingError

describe klass do
let(:factory) do
klass.new
end

let(:context) do
double(
path: double
)
end

let(:type) do
double(
type: double,
attributes: {
counter: double(
name: :counter,
default: 0,
virtual: false
),
value: double(
name: :value,
default: nil,
virtual: false
),
_element: double(
name: :_element,
default: {},
virtual: true
)
},
injector: double(inject: nil)
)
end

describe '#create' do
it 'instantiates class by issuing direct #new() call' do
value = double
allow(type).to receive(:attributes).and_return({})
expect(type.type).to receive(:new).and_return(value)
expect(factory.create(type, double, context)).to eq(value)
end

it 'wraps constructor errors' do
expect(type.type).to receive(:new).and_raise
proc = lambda do
factory.create(type, double, context)
end
expect(&proc).to raise_error(mapping_error_class)
end

it 'reports invalid method signature in case of argument error' do
expect(type.type).to receive(:new).and_raise(ArgumentError)
proc = lambda do
factory.create(type, double, context)
end
expect(&proc).to raise_error(mapping_error_class, /initialize|constructor/)
end

it 'fills entity with default attribute values' do
dummy = double
expect(type.type).to receive(:new).and_return(dummy)

attribute = type.attributes[:counter]
match = receive(:inject).with(dummy, type, attribute, 0, anything)
expect(type.injector).to(match)

attribute = type.attributes[:value]
match = receive(:inject).with(dummy, type, attribute, anything, anything)
expect(type.injector).not_to(match)

factory.create(type, double, context)
end

it 'ignores virtual attributes' do
dummy = double
expect(type.type).to receive(:new).and_return(dummy)

attribute = type.attributes[:_element]
match = receive(:inject).with(dummy, type, attribute, anything, anything)
expect(type.injector).not_to(match)

factory.create(type, double, context)
end
end
end
19 changes: 11 additions & 8 deletions test/suite/integration/mapper/api/wrapper/factory.spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@

describe klass do
let(:type) do
double(type: double)
double(
type: double,
attributes: {},
injector: double(inject: nil)
)
end

describe '#create' do
it 'should provide fallback to default factory' do
it 'provides fallback to default factory' do
result = double
expect(type.type).to receive(:new).exactly(:once).and_return(result)
factory = double
Expand All @@ -22,7 +26,7 @@
expect(klass.new(factory).create(type)).to eq(result)
end

it 'should wrap exceptions in mapping error' do
it 'wraps exceptions in mapping error' do
factory = double
expect(factory).to receive(:create).exactly(:once).and_raise
proc = lambda do
Expand All @@ -31,11 +35,10 @@
expect(&proc).to raise_error(mapping_error_class)
end

it 'should provide hint about method signature' do
factory_class = Class.new do
def create; end
end
factory = factory_class.new
it 'provides hint about method signature' do
error_class = ArgumentError
factory = double
expect(factory).to receive(:create).exactly(:once).and_raise(error_class)
proc = lambda do
klass.new(factory).create(type)
end
Expand Down
4 changes: 2 additions & 2 deletions test/suite/integration/mapper/type/concrete.spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ def self.to_s
end

describe '#factory' do
it 'should provide default factory if none was set' do
it 'provides default factory if none was set' do
type = klass.new(dummy_class)
factory = type.factory
expect(factory.create(type)).to be_a(dummy_class)
expect(factory.create(type, double, double)).to be_a(dummy_class)
end
end

Expand Down
40 changes: 0 additions & 40 deletions test/suite/unit/mapper/api/default/factory.spec.rb

This file was deleted.

0 comments on commit ff1c911

Please sign in to comment.