Class: TypedParams::Schema

Inherits:
Object
  • Object
show all
Defined in:
lib/typed_params/schema.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(controller: nil, source: nil, strict: true, parent: nil, type: :hash, key: nil, optional: false, coerce: false, polymorphic: false, allow_blank: false, allow_nil: false, allow_non_scalars: false, nilify_blanks: false, noop: false, inclusion: nil, exclusion: nil, format: nil, length: nil, depth: nil, transform: nil, validate: nil, if: nil, unless: nil, as: nil, alias: nil, collapse: nil, casing: TypedParams.config.key_transform, &block) ⇒ Schema

Returns a new instance of Schema.

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
# File 'lib/typed_params/schema.rb', line 18

def initialize(
  controller: nil,
  source: nil,
  strict: true,
  parent: nil,
  type: :hash,
  key: nil,
  optional: false,
  coerce: false,
  polymorphic: false,
  allow_blank: false,
  allow_nil: false,
  allow_non_scalars: false,
  nilify_blanks: false,
  noop: false,
  inclusion: nil,
  exclusion: nil,
  format: nil,
  length: nil,
  depth: nil,
  transform: nil,
  validate: nil,
  if: nil,
  unless: nil,
  as: nil,
  alias: nil,
  collapse: nil,
  casing: TypedParams.config.key_transform,
  &block
)
  key ||= ROOT

  raise ArgumentError, 'key is required for child schema' if
    key == ROOT && parent.present?

  raise ArgumentError, 'root cannot be null' if
    key == ROOT && allow_nil

  raise ArgumentError, 'source must be one of: :params or :query' unless
    source.nil? || source == :params || source == :query

  raise ArgumentError, 'inclusion must be a hash with :in key' unless
    inclusion.nil? || inclusion.is_a?(Hash) && inclusion.key?(:in)

  raise ArgumentError, 'exclusion must be a hash with :in key' unless
    exclusion.nil? || exclusion.is_a?(Hash) && exclusion.key?(:in)

  raise ArgumentError, 'format must be a hash with :with or :without keys (but not both)' unless
    format.nil? || format.is_a?(Hash) && (
      format.key?(:with) ^
      format.key?(:without)
    )

  raise ArgumentError, 'length must be a hash with :minimum, :maximum, :within, :in, or :is keys (but not multiple except for :minimum and :maximum)' unless
    length.nil? || length.is_a?(Hash) && (
      length.key?(:minimum) && length.key?(:maximum) && length.size == 2 ||
        length.key?(:minimum) ^
        length.key?(:maximum) ^
        length.key?(:within) ^
        length.key?(:in) ^
        length.key?(:is)
    )

  raise ArgumentError, 'depth must be a hash with :maximum key' unless
    depth.nil? || depth.is_a?(Hash) && depth.key?(:maximum)

  raise ArgumentError, 'collapse must be true, a hash, or a proc' unless
    collapse.nil? || collapse == true || collapse.is_a?(Hash) || collapse.is_a?(Proc)

  @controller        = controller
  @source            = source
  @type              = Types[type]
  @strict            = strict
  @parent            = parent
  @key               = key
  @as                = as
  @alias             = binding.local_variable_get(:alias)
  @optional          = optional
  @coerce            = coerce && @type.coercable?
  @polymorphic       = polymorphic
  @allow_blank       = key == ROOT || allow_blank
  @allow_non_scalars = allow_non_scalars || depth.present?
  @allow_nil         = allow_nil
  @nilify_blanks     = nilify_blanks
  @noop              = noop
  @inclusion         = inclusion
  @exclusion         = exclusion
  @format            = format
  @length            = length
  @depth             = depth
  @casing            = casing
  @transform         = transform
  @validate          = validate
  @collapse          = collapse
  @children          = nil
  @if                = binding.local_variable_get(:if)
  @unless            = binding.local_variable_get(:unless)
  @formatter         = nil
  @options           = {}

  # Validations
  @validations = []

  @validations << Validations::Inclusion.new(inclusion) if
    inclusion.present?

  @validations << Validations::Exclusion.new(exclusion) if
    exclusion.present?

  @validations << Validations::Format.new(format) if
    format.present?

  @validations << Validations::Length.new(length) if
    length.present?

  @validations << Validations::Depth.new(depth) if
    depth.present?

  @validations << Validations::Validation.wrap(validate) if
    validate.present?

  # Transforms
  @transforms = []

  @transforms << Transforms::KeyAlias.new(as) if
    as.present?

  @transforms << Transforms::NilifyBlanks.new if
    nilify_blanks

  @transforms << Transforms::Transform.wrap(transform) if
    transform.present?

  @transforms << Transforms::Collapse.new(collapse) if
    collapse.present?

  @transforms << Transforms::KeyCasing.new(casing) if
    casing.present?

  @transforms << Transforms::Noop.new if
    noop

  raise ArgumentError, "type #{type} is a not registered type" if
    @type.nil?

  if block_given?
    raise ArgumentError, "type #{@type} does not accept a block" unless
      @type.accepts_block?

    raise ArgumentError, "type #{@type} with a block cannot be coerced" if
      @coerce

    @children = case
                when Types.array?(@type)
                  []
                when Types.hash?(@type)
                  {}
                end

    self.instance_exec &block
  end
end

Instance Attribute Details

#aliasObject (readonly)

Returns the value of attribute alias.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def alias
  @alias
end

#asObject (readonly)

Returns the value of attribute as.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def as
  @as
end

#childrenObject (readonly)

Returns the value of attribute children.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def children
  @children
end

#formatterObject (readonly)

Returns the value of attribute formatter.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def formatter
  @formatter
end

#ifObject (readonly)

Returns the value of attribute if.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def if
  @if
end

#keyObject (readonly)

Returns the value of attribute key.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def key
  @key
end

#parentObject (readonly)

Returns the value of attribute parent.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def parent
  @parent
end

#sourceObject (readonly)

Returns the value of attribute source.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def source
  @source
end

#transformsObject (readonly)

Returns the value of attribute transforms.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def transforms
  @transforms
end

#typeObject (readonly)

Returns the value of attribute type.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def type
  @type
end

#unlessObject (readonly)

Returns the value of attribute unless.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def unless
  @unless
end

#validationsObject (readonly)

Returns the value of attribute validations.



5
6
7
# File 'lib/typed_params/schema.rb', line 5

def validations
  @validations
end

Instance Method Details

#aliased?Boolean

Returns:

  • (Boolean)


293
# File 'lib/typed_params/schema.rb', line 293

def aliased?           = !!@alias

#allow_blank?Boolean

Returns:

  • (Boolean)


294
# File 'lib/typed_params/schema.rb', line 294

def allow_blank?       = !!@allow_blank

#allow_nil?Boolean

Returns:

  • (Boolean)


295
# File 'lib/typed_params/schema.rb', line 295

def allow_nil?         = !!@allow_nil

#allow_non_scalars?Boolean

Returns:

  • (Boolean)


296
# File 'lib/typed_params/schema.rb', line 296

def allow_non_scalars? = !!@allow_non_scalars

#any?Boolean

Returns:

  • (Boolean)


305
# File 'lib/typed_params/schema.rb', line 305

def any?               = type.any?

#array?Boolean

Returns:

  • (Boolean)


302
# File 'lib/typed_params/schema.rb', line 302

def array?             = Types.array?(type)

#child?Boolean

Returns:

  • (Boolean)


285
# File 'lib/typed_params/schema.rb', line 285

def child?             = !root?

#children?Boolean

Returns:

  • (Boolean)


286
# File 'lib/typed_params/schema.rb', line 286

def children?          = !children.blank?

#coerce?Boolean

Returns:

  • (Boolean)


291
# File 'lib/typed_params/schema.rb', line 291

def coerce?            = !!@coerce

#collapse?Boolean

Returns:

  • (Boolean)


307
# File 'lib/typed_params/schema.rb', line 307

def collapse?          = !!@collapse

#endless?Boolean

Returns:

  • (Boolean)


298
# File 'lib/typed_params/schema.rb', line 298

def endless?           = !!@endless

#format(format) ⇒ Object

format defines the final output format for the schema, transforming the params from an input format to an output format, e.g. a JSONAPI document to Rails’ standard params format. This also applies the formatter’s decorators onto the controller.

Raises:

  • (NotImplementedError)


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/typed_params/schema.rb', line 186

def format(format)
  raise NotImplementedError, 'cannot define format for child schema' if
    child?

  formatter = Formatters[format]

  raise ArgumentError, "invalid format: #{format.inspect}" if
    formatter.nil?

  # Apply the formatter's decorators onto the controller.
  controller.instance_exec(&formatter.decorator) if
    controller.present? && formatter.decorator?

  @formatter = formatter
end

#formatter?Boolean

Returns:

  • (Boolean)


306
# File 'lib/typed_params/schema.rb', line 306

def formatter?         = !!@formatter

#hash?Boolean

Returns:

  • (Boolean)


303
# File 'lib/typed_params/schema.rb', line 303

def hash?              = Types.hash?(type)

#if?Boolean

Returns:

  • (Boolean)


300
# File 'lib/typed_params/schema.rb', line 300

def if?                = !@if.nil?

#indexed?Boolean

Returns:

  • (Boolean)


299
# File 'lib/typed_params/schema.rb', line 299

def indexed?           = !endless?

#inspectObject



309
310
311
# File 'lib/typed_params/schema.rb', line 309

def inspect
  "#<#{self.class.name} key=#{key.inspect} type=#{type.inspect} children=#{children.inspect}>"
end

#item(key = children&.size || 0, type:, **kwargs, &block) ⇒ Object

item defines an indexed parameter for an array schema.

Raises:

  • (NotImplementedError)


246
247
248
249
250
251
252
253
254
# File 'lib/typed_params/schema.rb', line 246

def item(key = children&.size || 0, type:, **kwargs, &block)
  raise NotImplementedError, "cannot define item for non-array type (got #{self.type})" unless
    Types.array?(children)

  raise ArgumentError, "index #{key} has already been defined" if
    children[key].present? || endless?

  children << Schema.new(**options, **kwargs, key:, type:, strict:, source:, casing:, parent: self, &block)
end

#items(**kwargs) ⇒ Object

items defines a set of like-parameters for an array schema.



258
259
260
261
262
# File 'lib/typed_params/schema.rb', line 258

def items(**kwargs, &)
  item(0, **kwargs, &)

  endless!
end

#keysObject



270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/typed_params/schema.rb', line 270

def keys
  return [] if
    children.blank?

  case children
  when Array
    (0...children.size).to_a
  when Hash
    children.keys
  else
    []
  end
end

#lenient?Boolean

Returns:

  • (Boolean)


288
# File 'lib/typed_params/schema.rb', line 288

def lenient?           = !strict?

#nilify_blanks?Boolean

Returns:

  • (Boolean)


297
# File 'lib/typed_params/schema.rb', line 297

def nilify_blanks?     = !!@nilify_blanks

#optional?Boolean

Returns:

  • (Boolean)


289
# File 'lib/typed_params/schema.rb', line 289

def optional?          = !!@optional

#param(key, type:, **kwargs, &block) ⇒ Object

param defines a keyed parameter for a hash schema.

Raises:

  • (NotImplementedError)


230
231
232
233
234
235
236
237
238
# File 'lib/typed_params/schema.rb', line 230

def param(key, type:, **kwargs, &block)
  raise NotImplementedError, "cannot define param for non-hash type (got #{self.type})" unless
    Types.hash?(children)

  raise ArgumentError, "key #{key} has already been defined" if
    children.key?(key)

  children[key] = Schema.new(**options, **kwargs, key:, type:, strict:, source:, casing:, parent: self, &block)
end

#params(*keys, **kwargs, &block) ⇒ Object

params defines multiple like-parameters for a hash schema.



242
# File 'lib/typed_params/schema.rb', line 242

def params(*keys, **kwargs, &block) = keys.each { param(_1, **kwargs, &block) }

#pathObject



264
265
266
267
268
# File 'lib/typed_params/schema.rb', line 264

def path
  key = @key == ROOT ? nil : @key

  @path ||= Path.new(*parent&.path&.keys, *key)
end

#polymorphic?Boolean

Returns:

  • (Boolean)


292
# File 'lib/typed_params/schema.rb', line 292

def polymorphic?       = !!@polymorphic

#required?Boolean

Returns:

  • (Boolean)


290
# File 'lib/typed_params/schema.rb', line 290

def required?          = !optional?

#root?Boolean

Returns:

  • (Boolean)


284
# File 'lib/typed_params/schema.rb', line 284

def root?              = key == ROOT

#scalar?Boolean

Returns:

  • (Boolean)


304
# File 'lib/typed_params/schema.rb', line 304

def scalar?            = type.scalar?

#strict?Boolean

Returns:

  • (Boolean)


287
# File 'lib/typed_params/schema.rb', line 287

def strict?            = !!strict

#unless?Boolean

Returns:

  • (Boolean)


301
# File 'lib/typed_params/schema.rb', line 301

def unless?            = !@unless.nil?

#with(**kwargs) ⇒ Object

with defines a set of options to use for all direct children of the schema defined within the block.

For example, it can be used to define a common guard:

with if: -> { ... } do
  param :foo, type: :string
  param :bar, type: :string
  param :baz, type: :hash do
    param :qux, type: :string
  end
end

In this example, :foo, :bar, and :baz will inherit the if: guard, but :qux will not, since it is not a direct child.



219
220
221
222
223
224
225
226
# File 'lib/typed_params/schema.rb', line 219

def with(**kwargs, &)
  orig     = @options
  @options = kwargs

  yield

  @options = orig
end