typed_params 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +42 -1
- data/lib/typed_params/formatters/formatter.rb +1 -1
- data/lib/typed_params/formatters/jsonapi.rb +18 -8
- data/lib/typed_params/mapper.rb +4 -4
- data/lib/typed_params/memoize.rb +2 -2
- data/lib/typed_params/parameter.rb +10 -1
- data/lib/typed_params/parameterizer.rb +3 -3
- data/lib/typed_params/schema.rb +16 -6
- data/lib/typed_params/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 014c42e82286c1ffc175d08aca51e631da96a5b2aee8e9bbf575fc0bea7e1d57
|
4
|
+
data.tar.gz: 3a0ea8bac6b1021143b768767763899a6f5328f16bb6011a87b2f1ed694b6325
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c563758456e6359e37c75926124925141c59f535fc77070be23d46d3f86e0772f2cf2c52d2e57f165e2ba11f93717c6a0b15f90e61fcbe00baa19b3c1985f2a8
|
7
|
+
data.tar.gz: a277c1c13a5d0ac14ef293fbc9611bec82eba5df00686f9afaf8e324e89148aef7c49fa672bf025570a9482ec277e330edc1049a1e59051e41a559a31a4b6584
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
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
|
+
|
8
|
+
## 1.1.1
|
9
|
+
|
10
|
+
- Fix compatibility with Ruby 3.3.0 due to [#20091](https://bugs.ruby-lang.org/issues/20091).
|
11
|
+
|
12
|
+
## 1.1.0
|
13
|
+
|
14
|
+
- Add memoization to `#typed_params` and `#typed_query` methods.
|
15
|
+
|
3
16
|
## 1.0.3
|
4
17
|
|
5
18
|
- Revert 0b0aaa6b66edd3e4c3336e51fa340592e7ef9e86.
|
@@ -18,4 +31,4 @@
|
|
18
31
|
|
19
32
|
## 0.2.0
|
20
33
|
|
21
|
-
- Test release.
|
34
|
+
- Test release.
|
data/README.md
CHANGED
@@ -110,6 +110,7 @@ _We're working on improving the docs._
|
|
110
110
|
- Reuse schemas across controllers by defining named schemas.
|
111
111
|
- Run validations on params, similar to active model validations.
|
112
112
|
- Run transforms on params before they hit your controller.
|
113
|
+
- Support formatters such as JSON:API.
|
113
114
|
|
114
115
|
## Usage
|
115
116
|
|
@@ -435,6 +436,7 @@ Parameters can have validations, transforms, and more.
|
|
435
436
|
- [`:optional`](#optional-parameter)
|
436
437
|
- [`:if` and `:unless`](#conditional-parameter)
|
437
438
|
- [`:as`](#rename-parameter)
|
439
|
+
- [`:alias`](#alias-parameter)
|
438
440
|
- [`:noop`](#noop-parameter)
|
439
441
|
- [`:coerce`](#coerce-parameter)
|
440
442
|
- [`:allow_blank`](#allow-blank)
|
@@ -447,6 +449,7 @@ Parameters can have validations, transforms, and more.
|
|
447
449
|
- [`:length`](#length-validation)
|
448
450
|
- [`:transform`](#transform-parameter)
|
449
451
|
- [`:validate`](#validate-parameter)
|
452
|
+
- [`:polymorphic`](#polymorphic-parameter)
|
450
453
|
|
451
454
|
#### Parameter key
|
452
455
|
|
@@ -519,6 +522,17 @@ typed_params # => { user_id: 1 }
|
|
519
522
|
In this example, the parameter would be accepted as `:user`, but renamed
|
520
523
|
to `:user_id` for use inside of the controller.
|
521
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
|
+
|
522
536
|
#### Noop parameter
|
523
537
|
|
524
538
|
The parameter is accepted but immediately thrown out.
|
@@ -673,6 +687,33 @@ param :invalid, type: :string, validate: -> v {
|
|
673
687
|
}
|
674
688
|
```
|
675
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
|
+
|
676
717
|
### Shared options
|
677
718
|
|
678
719
|
You can define a set of options that will be applied to immediate
|
@@ -747,7 +788,7 @@ which may be a nested schema.
|
|
747
788
|
|
748
789
|
```ruby
|
749
790
|
# array of hashes
|
750
|
-
param :
|
791
|
+
param :endless_array, type: :array do
|
751
792
|
items type: :hash do
|
752
793
|
# ...
|
753
794
|
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
|
-
|
38
|
+
child = schema.children.fetch(:data)
|
39
|
+
|
40
|
+
format_array_data(data, schema: child)
|
39
41
|
in data: Hash => data
|
40
|
-
|
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
|
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
|
|
data/lib/typed_params/mapper.rb
CHANGED
@@ -47,7 +47,7 @@ module TypedParams
|
|
47
47
|
# │ 1 ││ 2 │ │ 5 │
|
48
48
|
# └───┘└───┘ └───┘
|
49
49
|
#
|
50
|
-
def depth_first_map(param, &)
|
50
|
+
def depth_first_map(param, &block)
|
51
51
|
return if param.nil?
|
52
52
|
|
53
53
|
# Postorder DFS, so we'll visit the children first.
|
@@ -55,12 +55,12 @@ module TypedParams
|
|
55
55
|
case param.schema.children
|
56
56
|
in Array if param.array?
|
57
57
|
if param.schema.indexed?
|
58
|
-
param.schema.children.each_with_index { |v, i| self.class.call(param[i], schema: v, controller:, &) }
|
58
|
+
param.schema.children.each_with_index { |v, i| self.class.call(param[i], schema: v, controller:, &block) }
|
59
59
|
else
|
60
|
-
param.value.each { |v| self.class.call(v, schema: param.schema.children.first, controller:, &) }
|
60
|
+
param.value.each { |v| self.class.call(v, schema: param.schema.children.first, controller:, &block) }
|
61
61
|
end
|
62
62
|
in Hash if param.hash?
|
63
|
-
param.schema.children.each { |k, v| self.class.call(param[k], schema: v, controller:, &) }
|
63
|
+
param.schema.children.each { |k, v| self.class.call(param[k], schema: v, controller:, &block) }
|
64
64
|
else
|
65
65
|
end
|
66
66
|
end
|
data/lib/typed_params/memoize.rb
CHANGED
@@ -34,7 +34,7 @@ module TypedParams
|
|
34
34
|
end
|
35
35
|
|
36
36
|
klass.singleton_class.send(:alias_method, :"unmemoized_#{method_name}", method_name)
|
37
|
-
klass.class_eval
|
37
|
+
klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
38
38
|
def self.#{method_name}(*args, **kwargs, &block)
|
39
39
|
key = args.hash ^ kwargs.hash ^ block.hash
|
40
40
|
|
@@ -72,7 +72,7 @@ module TypedParams
|
|
72
72
|
end
|
73
73
|
|
74
74
|
klass.alias_method :"unmemoized_#{method_name}", method_name
|
75
|
-
klass.class_eval
|
75
|
+
klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
76
76
|
def #{method_name}(*args, **kwargs, &block)
|
77
77
|
key = args.hash ^ kwargs.hash ^ block.hash
|
78
78
|
|
@@ -75,7 +75,16 @@ module TypedParams
|
|
75
75
|
if formatter.present?
|
76
76
|
v = case formatter.arity
|
77
77
|
when 2
|
78
|
-
formatter.
|
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.
|
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) {
|
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[
|
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
|
data/lib/typed_params/schema.rb
CHANGED
@@ -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
|
@@ -211,7 +216,7 @@ module TypedParams
|
|
211
216
|
|
212
217
|
##
|
213
218
|
# params defines multiple like-parameters for a hash schema.
|
214
|
-
def params(*keys, **kwargs, &) = keys.each { param(_1, **kwargs, &) }
|
219
|
+
def params(*keys, **kwargs, &block) = keys.each { param(_1, **kwargs, &block) }
|
215
220
|
|
216
221
|
##
|
217
222
|
# item defines an indexed parameter for an array schema.
|
@@ -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? ||
|
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
|
-
|
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
|
269
|
-
def indexed? = !
|
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
|
298
|
+
def endless! = @endless = true
|
289
299
|
end
|
290
300
|
end
|
data/lib/typed_params/version.rb
CHANGED
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.
|
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:
|
11
|
+
date: 2024-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|