Completed
Push — dev ( 6cb2fc...405831 )
by Fike
58s
created

Attribute.defaults()   A

Complexity

Conditions 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
rs 9.4285
cc 1
1
# frozen_string_literal: true
2
3
require_relative '../api/default/attribute_validator'
4
require_relative '../mixin/errors'
5
6
module AMA
7
  module Entity
8
    class Mapper
9
      class Type
10
        # Stores data about single type attribute
11
        class Attribute
12
          include Mixin::Errors
13
14
          # @!attribute
15
          #   @return [AMA::Entity::Mapper::Type::Concrete]
16
          attr_accessor :owner
17
          # @!attribute
18
          #   @return [Symbol]
19
          attr_accessor :name
20
          # @!attribute types List of possible types attribute may take
21
          #   @return [Array<AMA::Entity::Mapper::Type>]
22
          attr_accessor :types
23
          # If attribute is declared as virtual, it is omitted from all
24
          # automatic actions, such enumeration, normalization and
25
          # denormalization. Main motivation behind virtual attributes was
26
          # collections problem: collection can't be represented as hash of
27
          # attributes, however, virtual attribute may describe collection
28
          # content.
29
          #
30
          # @!attribute virtual
31
          #   @return [TrueClass, FalseClass]
32
          attr_accessor :virtual
33
          # If set to true, this attribute will be omitted during normalization
34
          # and won't be present in resulting structure.
35
          #
36
          # @!attribute sensitive
37
          #   @return [TrueClass, FalseClass]
38
          attr_accessor :sensitive
39
          # Default value that is set on automatic object creation.
40
          #
41
          # @!attribute default
42
          #   @return [Object]
43
          attr_accessor :default
44
          # Whether or not this attribute may be represented by null.
45
          #
46
          # @!attribute nullable
47
          #   @return [TrueClass, FalseClass]
48
          attr_accessor :nullable
49
          # List of values this attribute acceptable to take. Part of automatic
50
          # validation.
51
          #
52
          # @!attribute values
53
          #   @return [Array<Object>]
54
          attr_accessor :values
55
          # Custom attribute validator
56
          #
57
          # @!attribute validator
58
          #   @return [API::AttributeValidator]
59
          attr_accessor :validator
60
61
          def self.defaults
62
            {
63
              virtual: false,
64
              sensitive: false,
65
              default: nil,
66
              nullable: false,
67
              values: [],
68
              validator: API::Default::AttributeValidator::INSTANCE
69
            }
70
          end
71
72
          # @param [Mapper::Type::Concrete] owner
73
          # @param [Symbol] name
74 View Code Duplication
          # @param [Array<Mapper::Type>] types
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
75
          # @param [Hash<Symbol, Object] options
76
          def initialize(owner, name, *types, **options)
77
            @owner = validate_owner!(owner)
78
            @name = validate_name!(name)
79
            @types = validate_types!(types)
80
            self.class.defaults.each do |key, value|
81
              instance_variable_set("@#{key}", options.fetch(key, value))
82
            end
83
          end
84 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
85
          def satisfied_by?(value)
86
            @types.any? { |type| type.satisfied_by?(value) }
87
          end
88
89
          def resolved?
90
            types.all?(&:resolved?)
91 View Code Duplication
          end
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
92
93
          def resolved!(context = nil)
94
            types.each do |type|
95
              type.resolved!(context)
96
            end
97
          end
98
99
          # @param [AMA::Entity::Mapper::Type] parameter
100
          # @param [AMA::Entity::Mapper::Type] substitution
101
          # @return [AMA::Entity::Mapper::Type::Attribute]
102
          def resolve_parameter(parameter, substitution)
103
            clone.tap do |clone|
104
              clone.types = types.each_with_object([]) do |type, carrier|
105
                if type == parameter
106
                  buffer = substitution
107
                  buffer = [buffer] unless buffer.is_a?(Enumerable)
108
                  next carrier.push(*buffer)
109
                end
110
                carrier.push(type.resolve_parameter(parameter, substitution))
111
              end
112
            end
113
          end
114
115
          def hash
116
            @owner.hash ^ @name.hash
117
          end
118
119
          def eql?(other)
120
            return false unless other.is_a?(self.class)
121
            @owner == other.owner && @name == other.name
122
          end
123
124
          def ==(other)
125
            eql?(other)
126
          end
127
128
          def to_s
129
            message = "Attribute #{owner.type}.#{name}"
130
            return message unless virtual
131
            "#{message} (virtual)"
132
          end
133
134
          private
135
136
          def validate_owner!(owner)
137
            return owner if owner.is_a?(Type)
138
            message = 'Provided owner has to be a Type instance,' \
139
              " #{owner.class} received"
140
            compliance_error(message)
141
          end
142
143
          def validate_name!(name)
144
            return name if name.is_a?(Symbol)
145
            message = "Provided name has to be Symbol, #{name.class} received"
146
            compliance_error(message)
147
          end
148
149
          def validate_types!(types)
150
            compliance_error("No types provided for #{self}") if types.empty?
151
            types.each do |type|
152
              next if type.is_a?(Type)
153
              message = 'Provided type has to be a Type instance, ' \
154
                "#{type.class} received"
155
              compliance_error(message)
156
            end
157
          end
158
        end
159
      end
160
    end
161
  end
162
end
163