Class: Apiwork::Representation::Attribute

Inherits:
Object
  • Object
show all
Defined in:
lib/apiwork/representation/attribute.rb

Overview

Represents an attribute defined on a representation.

Attributes map to model columns and define serialization behavior. Used by adapters to build contracts and serialize records.

Examples:

attribute = InvoiceRepresentation.attributes[:title]
attribute.name # => :title
attribute.type # => :string
attribute.filterable? # => true

Constant Summary collapse

ALLOWED_FORMATS =
{
  decimal: %i[float double],
  integer: %i[int32 int64],
  number: %i[float double],
  string: %i[date datetime email hostname ipv4 ipv6 password text url uuid],
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, owner_representation_class, decode: nil, default: UNSET, deprecated: false, description: nil, empty: false, encode: nil, enum: nil, example: nil, filterable: false, format: nil, max: nil, min: nil, nullable: nil, optional: nil, preload: nil, sortable: false, type: nil, writable: false, write_only: false, &block) ⇒ Attribute

Returns a new instance of Attribute.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/apiwork/representation/attribute.rb', line 98

def initialize(
  name,
  owner_representation_class,
  decode: nil,
  default: UNSET,
  deprecated: false,
  description: nil,
  empty: false,
  encode: nil,
  enum: nil,
  example: nil,
  filterable: false,
  format: nil,
  max: nil,
  min: nil,
  nullable: nil,
  optional: nil,
  preload: nil,
  sortable: false,
  type: nil,
  writable: false,
  write_only: false,
  &block
)
  @name = name
  @owner_representation_class = owner_representation_class
  @of = nil

  if block
    element = Element.new
    block.arity.positive? ? yield(element) : element.instance_eval(&block)
    element.validate!
    @element = element
    type = element.type
    @of = element.inner&.type if [:array, :record].include?(element.type)
  end

  if owner_representation_class.model_class.present?
    @model_class = owner_representation_class.model_class

    begin
      @db_column = @model_class.column_names.include?(name.to_s)

      detected_enum = detect_enum_values(name)
      enum ||= detected_enum
      type ||= detect_type(name) if @db_column
      type = :string if detected_enum && type == :integer
      optional = detect_optional(name) if optional.nil?
      nullable = detect_nullable(name) if nullable.nil?
      default = detect_default(name, empty:, nullable:, optional:) if UNSET.equal?(default)

      if @db_column && type == :string
        detected_max = detect_string_max_length(name)
        max = [max, detected_max].compact.min
      end

      if @db_column && type == :decimal
        detected_min, detected_max = detect_decimal_bounds(name)
        min = [min, detected_min].compact.max
        max = [max, detected_max].compact.min
      end

      if @db_column && type == :integer
        detected_min, detected_max = detect_integer_bounds(name)
        min = [min, detected_min].compact.max
        max = [max, detected_max].compact.min
      end
    rescue ActiveRecord::StatementInvalid, ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished
      @db_column = false
    end
  end

  optional = false if optional.nil?
  nullable = false if nullable.nil?
  default = '' if UNSET.equal?(default) && empty

  @filterable = filterable
  @preload = preload
  @sortable = sortable
  @writable = writable
  @encode = encode
  @decode = decode
  @empty = empty
  @nullable = nullable
  @optional = optional
  @type = type || :unknown
  @enum = enum
  @min = min
  @max = max
  @description = description
  @example = example
  @format = format
  @deprecated = deprecated
  @write_only = write_only
  @default_set = !UNSET.equal?(default)
  @default = @default_set ? default : nil

  validate_min_max_range!
  validate_format!
  validate_empty!
end

Instance Attribute Details

#defaultObject? (readonly)

The default for this attribute.

Returns ‘nil` for both “no default” and “default is explicitly `nil`”. Use #default? to distinguish these cases.

Returns:



82
83
84
# File 'lib/apiwork/representation/attribute.rb', line 82

def default
  @default
end

#descriptionString? (readonly)

The description for this attribute.

Returns:

  • (String, nil)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#elementObject (readonly)



82
83
84
# File 'lib/apiwork/representation/attribute.rb', line 82

def element
  @element
end

#emptyObject (readonly)



82
83
84
# File 'lib/apiwork/representation/attribute.rb', line 82

def empty
  @empty
end

#enumArray<Object>? (readonly)

The enum for this attribute.

Returns:



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#exampleObject? (readonly)

The example for this attribute.

Returns:



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#formatSymbol? (readonly)

The format for this attribute.

Returns:

  • (Symbol, nil)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#maxInteger? (readonly)

The maximum for this attribute.

Returns:

  • (Integer, nil)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#minInteger? (readonly)

The minimum for this attribute.

Returns:

  • (Integer, nil)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#nameSymbol (readonly)

The name for this attribute.

Returns:

  • (Symbol)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#ofSymbol? (readonly)

The of for this attribute.

Returns:

  • (Symbol, nil)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#optionalObject (readonly)



82
83
84
# File 'lib/apiwork/representation/attribute.rb', line 82

def optional
  @optional
end

#preloadSymbol, ... (readonly)

The preload for this attribute.

Returns:

  • (Symbol, Array, Hash, nil)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#typeSymbol (readonly)

The type for this attribute.

Returns:

  • (Symbol)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/apiwork/representation/attribute.rb', line 82

attr_reader :default,
:description,
:element,
:empty,
:enum,
:example,
:format,
:max,
:min,
:name,
:of,
:optional,
:preload,
:type,
:write_only

#write_onlyObject (readonly)



82
83
84
# File 'lib/apiwork/representation/attribute.rb', line 82

def write_only
  @write_only
end

Instance Method Details

#decode(value) ⇒ Object



286
287
288
289
# File 'lib/apiwork/representation/attribute.rb', line 286

def decode(value)
  result = @decode ? @decode.call(value) : value
  @empty ? result.presence : result
end

#default?Boolean

Whether this attribute has a default value.

Use this to distinguish “no default” from “default is explicitly ‘nil`”. The #default accessor returns `nil` in both cases.

Returns:

  • (Boolean)


207
208
209
# File 'lib/apiwork/representation/attribute.rb', line 207

def default?
  @default_set
end

#deprecated?Boolean

Whether this attribute is deprecated.

Returns:

  • (Boolean)


215
216
217
# File 'lib/apiwork/representation/attribute.rb', line 215

def deprecated?
  @deprecated
end

#encode(value) ⇒ Object



281
282
283
284
# File 'lib/apiwork/representation/attribute.rb', line 281

def encode(value)
  result = @empty && value.nil? ? '' : value
  @encode ? @encode.call(result) : result
end

#filterable?Boolean

Whether this attribute is filterable.

Returns:

  • (Boolean)


223
224
225
# File 'lib/apiwork/representation/attribute.rb', line 223

def filterable?
  @filterable
end

#nullable?Boolean

Whether this attribute is nullable.

Returns:

  • (Boolean)


247
248
249
250
251
# File 'lib/apiwork/representation/attribute.rb', line 247

def nullable?
  return false if @empty

  @nullable
end

#optional?Boolean

Whether this attribute is optional.

Returns:

  • (Boolean)


239
240
241
# File 'lib/apiwork/representation/attribute.rb', line 239

def optional?
  @optional
end

#representation_class_nameObject



291
292
293
294
295
296
297
# File 'lib/apiwork/representation/attribute.rb', line 291

def representation_class_name
  @representation_class_name ||= @owner_representation_class
    .name
    .demodulize
    .delete_suffix('Representation')
    .underscore
end

#sortable?Boolean

Whether this attribute is sortable.

Returns:

  • (Boolean)


231
232
233
# File 'lib/apiwork/representation/attribute.rb', line 231

def sortable?
  @sortable
end

#writable?Boolean

Whether this attribute is writable.

Returns:

  • (Boolean)

See Also:



258
259
260
# File 'lib/apiwork/representation/attribute.rb', line 258

def writable?
  [true, :create, :update].include?(@writable)
end

#writable_for?(action) ⇒ Boolean

Whether this attribute is writable for the given action.

Parameters:

  • action (Symbol)
    :create, :update

    The action.

Returns:

  • (Boolean)

See Also:



269
270
271
# File 'lib/apiwork/representation/attribute.rb', line 269

def writable_for?(action)
  [true, action].include?(@writable)
end

#write_only?Boolean

Whether this attribute is write-only.

Returns:

  • (Boolean)


277
278
279
# File 'lib/apiwork/representation/attribute.rb', line 277

def write_only?
  @write_only
end