typed_params 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab9149f0354b3110bc3a5610b06785055ad0e3ec00cc5e352b2daef7be902178
4
- data.tar.gz: a48c899da8bc5d9fa9ee1041a2e35ff8f2f942b9ff083f7bf42e18e34e7813a2
3
+ metadata.gz: 014c42e82286c1ffc175d08aca51e631da96a5b2aee8e9bbf575fc0bea7e1d57
4
+ data.tar.gz: 3a0ea8bac6b1021143b768767763899a6f5328f16bb6011a87b2f1ed694b6325
5
5
  SHA512:
6
- metadata.gz: 4dabec975049677e23ab38f91c121f8f83a2dab36b753c9ad58f0693ad31ce780122b503b4b7554f6afe36568e3a85ef8c4d559591a8205bbadc9740e5cfd4b9
7
- data.tar.gz: a4c8be23f0472002c9e317924091b624ac7cd640f08bfc515f61db119b72d20d6312919a4f45b31b14560d8bde08dad55467be058422acd4f39e225b927fd539
6
+ metadata.gz: c563758456e6359e37c75926124925141c59f535fc77070be23d46d3f86e0772f2cf2c52d2e57f165e2ba11f93717c6a0b15f90e61fcbe00baa19b3c1985f2a8
7
+ data.tar.gz: a277c1c13a5d0ac14ef293fbc9611bec82eba5df00686f9afaf8e324e89148aef7c49fa672bf025570a9482ec277e330edc1049a1e59051e41a559a31a4b6584
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ - Add `aliases:` keyword to `#param` to allow the schema to be accessible by different names.
6
+ - Add `polymorphic:` keyword to `#param` to define a polymorphic schema.
7
+
3
8
  ## 1.1.1
4
9
 
5
10
  - Fix compatibility with Ruby 3.3.0 due to [#20091](https://bugs.ruby-lang.org/issues/20091).
data/README.md CHANGED
@@ -436,6 +436,7 @@ Parameters can have validations, transforms, and more.
436
436
  - [`:optional`](#optional-parameter)
437
437
  - [`:if` and `:unless`](#conditional-parameter)
438
438
  - [`:as`](#rename-parameter)
439
+ - [`:alias`](#alias-parameter)
439
440
  - [`:noop`](#noop-parameter)
440
441
  - [`:coerce`](#coerce-parameter)
441
442
  - [`:allow_blank`](#allow-blank)
@@ -448,6 +449,7 @@ Parameters can have validations, transforms, and more.
448
449
  - [`:length`](#length-validation)
449
450
  - [`:transform`](#transform-parameter)
450
451
  - [`:validate`](#validate-parameter)
452
+ - [`:polymorphic`](#polymorphic-parameter)
451
453
 
452
454
  #### Parameter key
453
455
 
@@ -520,6 +522,17 @@ typed_params # => { user_id: 1 }
520
522
  In this example, the parameter would be accepted as `:user`, but renamed
521
523
  to `:user_id` for use inside of the controller.
522
524
 
525
+ #### Alias parameter
526
+
527
+ Allow a parameter to be provided via an alias.
528
+
529
+ ```ruby
530
+ param :owner_id, type: :integer, alias: :user_id
531
+ ```
532
+
533
+ In this example, the parameter would be accepted as both `:owner_id` and
534
+ `:user_id`, but accessible as `:owner_id` inside the controller.
535
+
523
536
  #### Noop parameter
524
537
 
525
538
  The parameter is accepted but immediately thrown out.
@@ -674,6 +687,33 @@ param :invalid, type: :string, validate: -> v {
674
687
  }
675
688
  ```
676
689
 
690
+ #### Polymorphic parameter
691
+
692
+ ***Currently, this option is only utilized by the JSONAPI formatter. Define
693
+ a polymorphic parameter, used when formatting JSONAPI relationships.***
694
+
695
+ ```ruby
696
+ format :jsonapi
697
+
698
+ param :data, type: :hash do
699
+ param :relationships, type: :hash do
700
+ param :owner, type: :hash, polymorphic: true do
701
+ param :data, type: :hash do
702
+ param :type, type: :string, inclusion: { in: %w[users user] }
703
+ param :id, type: :integer
704
+ end
705
+ end
706
+ end
707
+ end
708
+
709
+ typed_params # => { owner_type: 'User', owner_id: 1 }
710
+ ```
711
+
712
+ In this example, a polymorphic `:owner` relationship is defined. When run
713
+ through the JSONAPI formatter, instead of formatting the relationship
714
+ into the `:owner_id` key, it also includes the `:owner_type` key
715
+ for a polymorphic association.
716
+
677
717
  ### Shared options
678
718
 
679
719
  You can define a set of options that will be applied to immediate
@@ -748,7 +788,7 @@ which may be a nested schema.
748
788
 
749
789
  ```ruby
750
790
  # array of hashes
751
- param :boundless_array, type: :array do
791
+ param :endless_array, type: :array do
752
792
  items type: :hash do
753
793
  # ...
754
794
  end
@@ -14,7 +14,7 @@ module TypedParams
14
14
 
15
15
  def decorator? = decorator.present?
16
16
 
17
- delegate :arity, :call, to: :@transform
17
+ delegate :arity, :parameters, :call, to: :@transform
18
18
  end
19
19
  end
20
20
  end
@@ -32,12 +32,16 @@ module TypedParams
32
32
  # }
33
33
  #
34
34
  module JSONAPI
35
- def self.call(params)
35
+ def self.call(params, schema:)
36
36
  case params
37
37
  in data: Array => data
38
- format_array_data(data)
38
+ child = schema.children.fetch(:data)
39
+
40
+ format_array_data(data, schema: child)
39
41
  in data: Hash => data
40
- format_hash_data(data)
42
+ child = schema.children.fetch(:data)
43
+
44
+ format_hash_data(data, schema: child)
41
45
  else
42
46
  params
43
47
  end
@@ -45,11 +49,15 @@ module TypedParams
45
49
 
46
50
  private
47
51
 
48
- def self.format_array_data(data)
49
- data.map { format_hash_data(_1) }
52
+ def self.format_array_data(data, schema:)
53
+ data.each_with_index.map do |value, i|
54
+ child = schema.children.fetch(i) { schema.endless? ? schema.children.first : nil }
55
+
56
+ format_hash_data(value, schema: child)
57
+ end
50
58
  end
51
59
 
52
- def self.format_hash_data(data)
60
+ def self.format_hash_data(data, schema:)
53
61
  rels = data[:relationships]
54
62
  attrs = data[:attributes]
55
63
  res = data.except(
@@ -69,6 +77,8 @@ module TypedParams
69
77
  # relationship data only contains :type and :id, otherwise it
70
78
  # will use the x_attributes key.
71
79
  rels&.each do |key, rel|
80
+ child = schema.children.dig(:relationships, key)
81
+
72
82
  case rel
73
83
  # FIXME(ezekg) We need https://bugs.ruby-lang.org/issues/18961 to
74
84
  # clean this up (i.e. remove the if guard).
@@ -77,7 +87,7 @@ module TypedParams
77
87
  in data: []
78
88
  res[:"#{key.to_s.singularize}_ids"] = []
79
89
  # FIXME(ezekg) Not sure how to make this cleaner, but this handles polymorphic relationships.
80
- in data: { type:, id:, **nil } if key.to_s.underscore.classify != type.underscore.classify
90
+ in data: { type:, id:, **nil } if key.to_s.underscore.classify != type.underscore.classify && child.polymorphic?
81
91
  res[:"#{key}_type"], res[:"#{key}_id"] = type.underscore.classify, id
82
92
  in data: { type:, id:, **nil }
83
93
  res[:"#{key}_id"] = id
@@ -86,7 +96,7 @@ module TypedParams
86
96
  else
87
97
  # NOTE(ezekg) Embedded relationships are non-standard as per the
88
98
  # JSONAPI spec, but I don't really care. :)
89
- res[:"#{key}_attributes"] = call(rel)
99
+ res[:"#{key}_attributes"] = call(rel, schema: child)
90
100
  end
91
101
  end
92
102
 
@@ -75,7 +75,16 @@ module TypedParams
75
75
  if formatter.present?
76
76
  v = case formatter.arity
77
77
  when 2
78
- formatter.call(v, controller:)
78
+ case formatter.parameters
79
+ in [[:req, *], [:keyreq | :key, :controller], [:keyreq | :key, :schema]]
80
+ formatter.call(v, controller:, schema:)
81
+ in [[:req, *], [:keyreq | :key, :schema], [:keyreq | :key, :controller]]
82
+ formatter.call(v, schema:, controller:)
83
+ in [[:req, *], [:keyreq | :key, :controller]]
84
+ formatter.call(v, controller:)
85
+ in [[:req, *], [:keyreq | :key, :schema]]
86
+ formatter.call(v, schema:)
87
+ end
79
88
  when 1
80
89
  formatter.call(v)
81
90
  end
@@ -38,7 +38,7 @@ module TypedParams
38
38
 
39
39
  value.each_with_index do |v, i|
40
40
  unless schema.children.nil?
41
- child = schema.children.fetch(i) { schema.boundless? ? schema.children.first : nil }
41
+ child = schema.children.fetch(i) { schema.endless? ? schema.children.first : nil }
42
42
  if child.nil?
43
43
  raise UnpermittedParameterError.new('unpermitted parameter', path: Path.new(*param.path.keys, i), source: schema.source) if
44
44
  schema.strict?
@@ -63,7 +63,7 @@ module TypedParams
63
63
 
64
64
  value.each do |k, v|
65
65
  unless schema.children.nil?
66
- child = schema.children.fetch(k) { nil }
66
+ child = schema.children.fetch(k) { schema.children.values.find { _1.alias == k } }
67
67
  if child.nil?
68
68
  raise UnpermittedParameterError.new('unpermitted parameter', path: Path.new(*param.path.keys, k), source: schema.source) if
69
69
  schema.strict?
@@ -71,7 +71,7 @@ module TypedParams
71
71
  next
72
72
  end
73
73
 
74
- param[k] = Parameterizer.new(schema: child, parent: param).call(key: k, value: v)
74
+ param[child.key] = Parameterizer.new(schema: child, parent: param).call(key: child.key, value: v)
75
75
  else
76
76
  param[k] = Parameter.new(key: k, value: v, schema:, parent: param)
77
77
  end
@@ -10,6 +10,7 @@ module TypedParams
10
10
  :source,
11
11
  :type,
12
12
  :key,
13
+ :alias,
13
14
  :if,
14
15
  :unless
15
16
 
@@ -22,6 +23,7 @@ module TypedParams
22
23
  key: nil,
23
24
  optional: false,
24
25
  coerce: false,
26
+ polymorphic: false,
25
27
  allow_blank: false,
26
28
  allow_nil: false,
27
29
  allow_non_scalars: false,
@@ -36,6 +38,7 @@ module TypedParams
36
38
  if: nil,
37
39
  unless: nil,
38
40
  as: nil,
41
+ alias: nil,
39
42
  casing: TypedParams.config.key_transform,
40
43
  &block
41
44
  )
@@ -77,8 +80,10 @@ module TypedParams
77
80
  @strict = strict
78
81
  @parent = parent
79
82
  @key = key
83
+ @alias = binding.local_variable_get(:alias)
80
84
  @optional = optional
81
85
  @coerce = coerce && @type.coercable?
86
+ @polymorphic = polymorphic
82
87
  @allow_blank = key == ROOT || allow_blank
83
88
  @allow_nil = allow_nil
84
89
  @allow_non_scalars = allow_non_scalars
@@ -220,7 +225,7 @@ module TypedParams
220
225
  Types.array?(children)
221
226
 
222
227
  raise ArgumentError, "index #{key} has already been defined" if
223
- children[key].present? || boundless?
228
+ children[key].present? || endless?
224
229
 
225
230
  children << Schema.new(**options, **kwargs, key:, type:, strict:, source:, casing:, parent: self, &block)
226
231
  end
@@ -230,7 +235,7 @@ module TypedParams
230
235
  def items(**kwargs, &)
231
236
  item(0, **kwargs, &)
232
237
 
233
- boundless!
238
+ endless!
234
239
  end
235
240
 
236
241
  def path
@@ -261,12 +266,14 @@ module TypedParams
261
266
  def optional? = !!@optional
262
267
  def required? = !optional?
263
268
  def coerce? = !!@coerce
269
+ def polymorphic? = !!@polymorphic
270
+ def aliased? = !!@alias
264
271
  def allow_blank? = !!@allow_blank
265
272
  def allow_nil? = !!@allow_nil
266
273
  def allow_non_scalars? = !!@allow_non_scalars
267
274
  def nilify_blanks? = !!@nilify_blanks
268
- def boundless? = !!@boundless
269
- def indexed? = !boundless?
275
+ def endless? = !!@endless
276
+ def indexed? = !endless?
270
277
  def if? = !@if.nil?
271
278
  def unless? = !@unless.nil?
272
279
  def array? = Types.array?(type)
@@ -278,6 +285,9 @@ module TypedParams
278
285
  "#<#{self.class.name} key=#{key.inspect} type=#{type.inspect} children=#{children.inspect}>"
279
286
  end
280
287
 
288
+ # @private
289
+ def dig(*keys) = children.dig(*keys)
290
+
281
291
  private
282
292
 
283
293
  attr_reader :controller,
@@ -285,6 +295,6 @@ module TypedParams
285
295
  :strict,
286
296
  :casing
287
297
 
288
- def boundless! = @boundless = true
298
+ def endless! = @endless = true
289
299
  end
290
300
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TypedParams
4
- VERSION = '1.1.1'
4
+ VERSION = '1.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typed_params
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zeke Gabrielse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-01 00:00:00.000000000 Z
11
+ date: 2024-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails