typed_params 1.3.0 → 1.4.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 +4 -4
- data/CHANGELOG.md +5 -1
- data/README.md +34 -3
- data/lib/typed_params/path.rb +12 -4
- data/lib/typed_params/schema.rb +10 -1
- data/lib/typed_params/validations/depth.rb +51 -0
- data/lib/typed_params/validator.rb +1 -1
- data/lib/typed_params/version.rb +1 -1
- data/lib/typed_params.rb +15 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aedcf15896c7a9b3975e1d8fcd5ec05752120f813ff92707fa9c7684fdb77f64
|
4
|
+
data.tar.gz: 1593452e1eef49a1ec497f9118ff3f08f3902ed117d9785f4c0f54b3bcf417b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fad33f1bac6f33d40487e88fab1bba065223e9986c3cf96fc1dc2654d87b2cbc2b59d82af073ca833a71e5d953eab6793f43f2b8b4bc698e21d21e50c544b96b
|
7
|
+
data.tar.gz: 2b0b082f223b1a7b6fd31d1f2a8b4f86364a117cf4efb1893d31a3c395a276ceaf0797481b9958679780c3b1814d62c842b3e696ff605a9b4bd89d4e52ed2649
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.4.0
|
4
|
+
|
5
|
+
- Add `depth:` validator for asserting maximum depth of `hash` and `array` params.
|
6
|
+
|
3
7
|
## 1.3.0
|
4
8
|
|
5
|
-
- Add
|
9
|
+
- Add `any` type to skip type validation for a given param.
|
6
10
|
|
7
11
|
## 1.2.7
|
8
12
|
|
data/README.md
CHANGED
@@ -448,6 +448,7 @@ Parameters can have validations, transforms, and more.
|
|
448
448
|
- [`:exclusion`](#exclusion-validation)
|
449
449
|
- [`:format`](#format-validation)
|
450
450
|
- [`:length`](#length-validation)
|
451
|
+
- [`:depth`](#depth-validation)
|
451
452
|
- [`:transform`](#transform-parameter)
|
452
453
|
- [`:validate`](#validate-parameter)
|
453
454
|
- [`:polymorphic`](#polymorphic-parameter)
|
@@ -580,15 +581,21 @@ error.
|
|
580
581
|
|
581
582
|
#### Allow non-scalars
|
582
583
|
|
583
|
-
Only applicable to the `:hash`
|
584
|
-
a `:hash`
|
584
|
+
Only applicable to the `:hash` and `:array` types, and their subtypes. Allow
|
585
|
+
non-scalar values in a `:hash` or `:array` parameter.
|
585
586
|
|
586
587
|
```ruby
|
588
|
+
# Allow unlimited nesting
|
587
589
|
param :metadata, type: :hash, allow_non_scalars: true
|
590
|
+
|
591
|
+
# Disallow non-scalars
|
592
|
+
param :data, type: :hash, allow_non_scalars: false
|
588
593
|
```
|
589
594
|
|
590
595
|
By default, non-scalar parameters are rejected with a `TypedParams::InvalidParameterError`
|
591
|
-
error.
|
596
|
+
error. Use [`:depth`](#depth-validation) to control maximum depth.
|
597
|
+
|
598
|
+
Scalar types can be found under [Types](#scalar-types).
|
592
599
|
|
593
600
|
#### Nilify blanks
|
594
601
|
|
@@ -639,6 +646,20 @@ param :odd, type: :string, length: { in: [2, 4, 6, 8] }
|
|
639
646
|
param :ten, type: :string, length: { is: 10 }
|
640
647
|
```
|
641
648
|
|
649
|
+
#### Depth validation
|
650
|
+
|
651
|
+
The parameter must be a certain depth.
|
652
|
+
|
653
|
+
```ruby
|
654
|
+
# Allow nested objects up to 2 levels deep
|
655
|
+
param :config, type: :hash, depth: { maximum: 2 }
|
656
|
+
|
657
|
+
# Allow nested arrays up to 3 levels deep
|
658
|
+
param :data, type: :array, depth: { maximum: 3 }
|
659
|
+
```
|
660
|
+
|
661
|
+
Only applicable for `:hash` and `:array` types. Automatically sets `allow_non_scalars: true`.
|
662
|
+
|
642
663
|
#### Transform parameter
|
643
664
|
|
644
665
|
Transform the parameter using a lambda. This is commonly used to transform a
|
@@ -781,6 +802,7 @@ Type `:date`. Defines a date parameter. Must be a `Date`.
|
|
781
802
|
|
782
803
|
- [`:array`](#array-type)
|
783
804
|
- [`:hash`](#hash-type)
|
805
|
+
- [`:any`](#any-type)
|
784
806
|
|
785
807
|
#### Array type
|
786
808
|
|
@@ -820,6 +842,15 @@ end
|
|
820
842
|
# non-schema hash
|
821
843
|
param :only_scalars, type: :hash
|
822
844
|
param :non_scalars_too, type: :hash, allow_non_scalars: true
|
845
|
+
param :limited_nesting, type: :hash, depth: { maximum: 2 }
|
846
|
+
```
|
847
|
+
|
848
|
+
#### Any type
|
849
|
+
|
850
|
+
Type `:any`. Defines a parameter that is not type checked.
|
851
|
+
|
852
|
+
```ruby
|
853
|
+
param :anything_goes, type: :any
|
823
854
|
```
|
824
855
|
|
825
856
|
### Custom types
|
data/lib/typed_params/path.rb
CHANGED
@@ -12,21 +12,29 @@ module TypedParams
|
|
12
12
|
def to_json_pointer = '/' + keys.map { transform_key(_1) }.join('/')
|
13
13
|
def to_dot_notation = keys.map { transform_key(_1) }.join('.')
|
14
14
|
|
15
|
+
def +(other)
|
16
|
+
raise ArgumentError, 'must be a Path object or nil' unless other in Path | nil
|
17
|
+
|
18
|
+
return self if
|
19
|
+
other.nil?
|
20
|
+
|
21
|
+
Path.new(*keys, *other.keys, casing:)
|
22
|
+
end
|
23
|
+
|
15
24
|
def to_s
|
16
25
|
keys.map { transform_key(_1) }.reduce(+'') do |s, key|
|
17
|
-
next s << key if s.blank?
|
18
|
-
|
19
26
|
case key
|
20
27
|
when Integer
|
21
28
|
s << "[#{key}]"
|
22
29
|
else
|
23
|
-
s <<
|
30
|
+
s << '.' unless s.blank?
|
31
|
+
s << key.to_s
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
28
36
|
def inspect
|
29
|
-
"#<#{self.class.name}: #{to_s.inspect}>"
|
37
|
+
"#<#{self.class.name}: #{to_s.inspect} keys=#{keys.inspect} casing=#{casing.inspect}>"
|
30
38
|
end
|
31
39
|
|
32
40
|
private
|
data/lib/typed_params/schema.rb
CHANGED
@@ -34,6 +34,7 @@ module TypedParams
|
|
34
34
|
exclusion: nil,
|
35
35
|
format: nil,
|
36
36
|
length: nil,
|
37
|
+
depth: nil,
|
37
38
|
transform: nil,
|
38
39
|
validate: nil,
|
39
40
|
if: nil,
|
@@ -76,6 +77,9 @@ module TypedParams
|
|
76
77
|
length.key?(:is)
|
77
78
|
)
|
78
79
|
|
80
|
+
raise ArgumentError, 'depth must be a hash with :maximum key' unless
|
81
|
+
depth.nil? || depth.is_a?(Hash) && depth.key?(:maximum)
|
82
|
+
|
79
83
|
@controller = controller
|
80
84
|
@source = source
|
81
85
|
@type = Types[type]
|
@@ -88,14 +92,15 @@ module TypedParams
|
|
88
92
|
@coerce = coerce && @type.coercable?
|
89
93
|
@polymorphic = polymorphic
|
90
94
|
@allow_blank = key == ROOT || allow_blank
|
95
|
+
@allow_non_scalars = allow_non_scalars || depth.present?
|
91
96
|
@allow_nil = allow_nil
|
92
|
-
@allow_non_scalars = allow_non_scalars
|
93
97
|
@nilify_blanks = nilify_blanks
|
94
98
|
@noop = noop
|
95
99
|
@inclusion = inclusion
|
96
100
|
@exclusion = exclusion
|
97
101
|
@format = format
|
98
102
|
@length = length
|
103
|
+
@depth = depth
|
99
104
|
@casing = casing
|
100
105
|
@transform = transform
|
101
106
|
@children = nil
|
@@ -119,6 +124,9 @@ module TypedParams
|
|
119
124
|
@validations << Validations::Length.new(length) if
|
120
125
|
length.present?
|
121
126
|
|
127
|
+
@validations << Validations::Depth.new(depth) if
|
128
|
+
depth.present?
|
129
|
+
|
122
130
|
@validations << Validations::Validation.wrap(validate) if
|
123
131
|
validate.present?
|
124
132
|
|
@@ -261,6 +269,7 @@ module TypedParams
|
|
261
269
|
end
|
262
270
|
end
|
263
271
|
|
272
|
+
|
264
273
|
def root? = key == ROOT
|
265
274
|
def child? = !root?
|
266
275
|
def children? = !children.blank?
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'typed_params/validations/validation'
|
4
|
+
require 'typed_params/path'
|
5
|
+
|
6
|
+
module TypedParams
|
7
|
+
module Validations
|
8
|
+
class Depth < Validation
|
9
|
+
def call(value)
|
10
|
+
case options
|
11
|
+
in maximum: Integer => maximum_depth
|
12
|
+
return if
|
13
|
+
maximum_depth.nil? || maximum_depth == Float::INFINITY
|
14
|
+
|
15
|
+
validate_depth!(value, maximum_depth:)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_depth!(current_value, maximum_depth:, current_depth: 0, current_path: [])
|
22
|
+
case current_value
|
23
|
+
in Hash => hash
|
24
|
+
hash.each do |key, value|
|
25
|
+
path = current_path + [key]
|
26
|
+
next if
|
27
|
+
Types.scalar?(value)
|
28
|
+
|
29
|
+
raise ValidationError.new("maximum depth of #{maximum_depth} exceeded", path: Path.new(*path)) if
|
30
|
+
current_depth >= maximum_depth
|
31
|
+
|
32
|
+
validate_depth!(value, maximum_depth:, current_depth: current_depth + 1, current_path: path)
|
33
|
+
end
|
34
|
+
in Array => arr
|
35
|
+
arr.each_with_index do |value, index|
|
36
|
+
path = current_path + [index]
|
37
|
+
next if
|
38
|
+
Types.scalar?(value)
|
39
|
+
|
40
|
+
raise ValidationError.new("maximum depth of #{maximum_depth} exceeded", path: Path.new(*path)) if
|
41
|
+
current_depth >= maximum_depth
|
42
|
+
|
43
|
+
validate_depth!(value, maximum_depth:, current_depth: current_depth + 1, current_path: path)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# TODO(ezekg) add support for custom types e.g. enumerables?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -67,7 +67,7 @@ module TypedParams
|
|
67
67
|
schema.validations.each do |validation|
|
68
68
|
validation.call(param.value)
|
69
69
|
rescue ValidationError => e
|
70
|
-
raise InvalidParameterError.new(e.message, path: param.path, source: schema.source)
|
70
|
+
raise InvalidParameterError.new(e.message, path: param.path + e.path, source: schema.source)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
data/lib/typed_params/version.rb
CHANGED
data/lib/typed_params.rb
CHANGED
@@ -40,6 +40,7 @@ require 'typed_params/types/string'
|
|
40
40
|
require 'typed_params/types/symbol'
|
41
41
|
require 'typed_params/types/time'
|
42
42
|
require 'typed_params/types/type'
|
43
|
+
require 'typed_params/validations/depth'
|
43
44
|
require 'typed_params/validations/exclusion'
|
44
45
|
require 'typed_params/validations/format'
|
45
46
|
require 'typed_params/validations/inclusion'
|
@@ -59,9 +60,22 @@ module TypedParams
|
|
59
60
|
|
60
61
|
class UndefinedActionError < StandardError; end
|
61
62
|
class InvalidMethodError < StandardError; end
|
62
|
-
class ValidationError < StandardError; end
|
63
63
|
class CoercionError < StandardError; end
|
64
64
|
|
65
|
+
class ValidationError < StandardError
|
66
|
+
attr_reader :path
|
67
|
+
|
68
|
+
def initialize(message, path: nil)
|
69
|
+
@path = path
|
70
|
+
|
71
|
+
super(message)
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
"#<#{self.class.name} message=#{message.inspect} path=#{path.inspect}>"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
65
79
|
class InvalidParameterError < StandardError
|
66
80
|
attr_reader :source,
|
67
81
|
:path
|
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.4.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: 2025-
|
11
|
+
date: 2025-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- lib/typed_params/types/symbol.rb
|
94
94
|
- lib/typed_params/types/time.rb
|
95
95
|
- lib/typed_params/types/type.rb
|
96
|
+
- lib/typed_params/validations/depth.rb
|
96
97
|
- lib/typed_params/validations/exclusion.rb
|
97
98
|
- lib/typed_params/validations/format.rb
|
98
99
|
- lib/typed_params/validations/inclusion.rb
|