Denormalizer.candidate_names()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
1
# frozen_string_literal: true
2
3
require_relative '../../mixin/reflection'
4
require_relative '../../mixin/errors'
5
6
module AMA
7
  module Entity
8
    class Mapper
9
      module Handler
10
        module Entity
11
          # Default denormalization processor
12
          class Denormalizer
13
            include Mixin::Reflection
14
            include Mixin::Errors
15
16
            INSTANCE = new
17
18
            # @param [Hash] source
19
            # @param [AMA::Entity::Mapper::Type] type
20
            # @param [AMA::Entity::Mapper::Context] context
21
            def denormalize(source, type, context = nil)
22
              validate_source!(source, type, context)
23
              entity = type.factory.create(type, source, context)
24
              type.attributes.values.each do |attribute|
25
                next if attribute.virtual
26
                candidate_names(attribute).each do |name|
27
                  next unless source.key?(name)
28
                  value = source[name]
29
                  break set_object_attribute(entity, attribute.name, value)
30
                end
31
              end
32
              entity
33
            end
34
35
            private
36
37
            # @param [AMA::Entity::Mapper::Type::Attribute] attribute
38
            # @return [Array<Symbol, String>]
39
            def candidate_names(attribute)
40
              [attribute.name, *attribute.aliases].flat_map do |candidate|
41
                [candidate, candidate.to_s]
42
              end
43
            end
44
45
            # @param [Hash] source
46
            # @param [AMA::Entity::Mapper::Type] type
47
            # @param [AMA::Entity::Mapper::Context] context
48
            def validate_source!(source, type, context)
49
              return if source.is_a?(Hash)
50
              message = "Expected Hash, #{source.class} provided " \
51
                "(while denormalizing #{type})"
52
              mapping_error(message, context: context)
53
            end
54
55
            class << self
56
              include Mixin::Reflection
57
58
              # @param [Denormalizer] implementation
59
              # @return [Denormalizer]
60
              def wrap(implementation)
61
                handler = handler_factory(implementation, INSTANCE)
62
                depiction = "Safety wrapper for #{implementation}"
63
                wrapper = method_object(:denormalize, to_s: depiction, &handler)
64
                wrapper.singleton_class.instance_eval do
65
                  include Mixin::Errors
66
                end
67
                wrapper
68
              end
69
70
              private
71
72
              # @param [Denormalizer] implementation
73
              # @param [Denormalizer] fallback
74
              # @return [Denormalizer]
75
              def handler_factory(implementation, fallback)
76
                lambda do |source, type, ctx|
77
                  begin
78
                    implementation.denormalize(source, type, ctx) do |s, t, c|
79
                      fallback.denormalize(s, t, c)
80
                    end
81
                  rescue StandardError => e
82
                    raise_if_internal(e)
83
                    message = 'Unexpected error from denormalizer ' \
84
                      "#{implementation}"
85
                    signature = '(source, type, context)'
86
                    options = { parent: e, context: ctx, signature: signature }
87
                    compliance_error(message, **options)
88
                  end
89
                end
90
              end
91
            end
92
          end
93
        end
94
      end
95
    end
96
  end
97
end
98