Factory   A
last analyzed

Complexity

Total Complexity 6

Size/Duplication

Total Lines 71
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 6
c 2
b 0
f 0
dl 0
loc 71
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 9 3
A handler_factory() 0 15 1
A create_internal() 0 11 1
A wrap() 0 9 1
1
# frozen_string_literal: true
2
3
require_relative '../../mixin/errors'
4
require_relative '../../mixin/reflection'
5
require_relative '../../path/segment'
6
7
module AMA
8
  module Entity
9
    class Mapper
10
      module Handler
11
        module Entity
12
          # Default entity factory
13
          class Factory
14
            include Mixin::Errors
15
16
            INSTANCE = new
17
18
            # @param [AMA::Entity::Mapper::Type] type
19
            # @param [Object] _data
20
            # @param [AMA::Entity::Mapper::Context] context
21
            def create(type, _data, context)
22
              create_internal(type, context)
23
            rescue StandardError => e
24
              message = "Failed to instantiate #{type} directly from class"
25
              if e.is_a?(ArgumentError)
26
                message += '. Does it have parameterless #initialize() method?'
27
              end
28
              mapping_error(message, parent: e, context: context)
29
            end
30
31
            private
32
33
            # @param [AMA::Entity::Mapper::Type] type
34
            # @param [AMA::Entity::Mapper::Context] context
35
            def create_internal(type, context)
36
              entity = type.type.new
37
              type.attributes.values.each do |attribute|
38
                next if attribute.default.nil? || attribute.virtual
39
                segment = Path::Segment.attribute(attribute.name)
40
                ctx = context.advance(segment)
41
                value = attribute.default
42
                type.injector.inject(entity, type, attribute, value, ctx)
43
              end
44
              entity
45
            end
46
47
            class << self
48
              include Mixin::Reflection
49
50
              # @param [Factory] implementation
51
              # @return [Factory]
52
              def wrap(implementation)
53
                handler = handler_factory(implementation, INSTANCE)
54
                description = "Safety wrapper for #{implementation}"
55
                wrapper = method_object(:create, to_s: description, &handler)
56
                wrapper.singleton_class.instance_eval do
57
                  include Mixin::Errors
58
                end
59
                wrapper
60
              end
61
62
              private
63
64
              # @param [Factory] implementation
65
              # @param [Factory] fallback
66
              # @return [Factory]
67
              def handler_factory(implementation, fallback)
68
                lambda do |type, data, ctx|
69
                  begin
70
                    implementation.create(type, data, ctx) do |t, d, c|
71
                      fallback.create(t, d, c)
72
                    end
73
                  rescue StandardError => e
74
                    raise_if_internal(e)
75
                    message = "Unexpected error from factory #{implementation}"
76
                    signature = '(type, data, context)'
77
                    options = { parent: e, context: ctx, signature: signature }
78
                    compliance_error(message, options)
79
                  end
80
                end
81
              end
82
            end
83
          end
84
        end
85
      end
86
    end
87
  end
88
end
89