media_types 2.2.0 → 2.3.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/.github/workflows/debian.yml +43 -43
- data/.github/workflows/publish-bookworm.yml +33 -0
- data/.github/workflows/publish-sid.yml +33 -0
- data/.github/workflows/ruby.yml +22 -22
- data/.gitignore +20 -10
- data/.rubocop.yml +29 -29
- data/CHANGELOG.md +175 -164
- data/Gemfile +6 -6
- data/LICENSE +21 -21
- data/README.md +666 -664
- data/Rakefile +12 -12
- data/bin/console +15 -15
- data/bin/setup +8 -8
- data/lib/media_types/constructable.rb +161 -160
- data/lib/media_types/dsl/errors.rb +18 -18
- data/lib/media_types/dsl.rb +172 -172
- data/lib/media_types/errors.rb +25 -19
- data/lib/media_types/formatter.rb +56 -56
- data/lib/media_types/hash.rb +21 -21
- data/lib/media_types/object.rb +35 -35
- data/lib/media_types/scheme/allow_nil.rb +30 -30
- data/lib/media_types/scheme/any_of.rb +41 -41
- data/lib/media_types/scheme/attribute.rb +46 -46
- data/lib/media_types/scheme/enumeration_context.rb +18 -18
- data/lib/media_types/scheme/enumeration_of_type.rb +80 -80
- data/lib/media_types/scheme/errors.rb +87 -87
- data/lib/media_types/scheme/links.rb +54 -54
- data/lib/media_types/scheme/missing_validation.rb +41 -41
- data/lib/media_types/scheme/not_strict.rb +15 -15
- data/lib/media_types/scheme/output_empty_guard.rb +45 -45
- data/lib/media_types/scheme/output_iterator_with_predicate.rb +66 -66
- data/lib/media_types/scheme/output_type_guard.rb +39 -39
- data/lib/media_types/scheme/rules.rb +186 -173
- data/lib/media_types/scheme/rules_exhausted_guard.rb +73 -73
- data/lib/media_types/scheme/validation_options.rb +44 -43
- data/lib/media_types/scheme.rb +535 -513
- data/lib/media_types/testing/assertions.rb +20 -20
- data/lib/media_types/validations.rb +118 -105
- data/lib/media_types/version.rb +5 -5
- data/lib/media_types/views.rb +12 -12
- data/lib/media_types.rb +73 -73
- data/media_types.gemspec +33 -33
- metadata +8 -6
@@ -1,41 +1,41 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/errors'
|
4
|
-
|
5
|
-
module MediaTypes
|
6
|
-
class Scheme
|
7
|
-
class MissingValidation
|
8
|
-
|
9
|
-
def validate!(_output, options, context:, **_opts)
|
10
|
-
# Check that no unknown keys are present
|
11
|
-
return true unless options.strict
|
12
|
-
raise_strict!(key: context.key, strict_keys: context.rules, backtrace: options.backtrace, found: options.scoped_output)
|
13
|
-
end
|
14
|
-
|
15
|
-
def raise_strict!(key:, backtrace:, strict_keys:, found:)
|
16
|
-
raise StrictValidationError, format(
|
17
|
-
"Unknown key %<key>s in data.\n" \
|
18
|
-
"\tFound at: %<backtrace>s\n" \
|
19
|
-
"\tExpected:\n\n" \
|
20
|
-
"%<strict_keys>s\n\n" \
|
21
|
-
"\tBut I Found:\n\n" \
|
22
|
-
'%<found>s',
|
23
|
-
key: key.inspect,
|
24
|
-
backtrace: backtrace.join('->'),
|
25
|
-
strict_keys: keys_to_str(strict_keys.keys),
|
26
|
-
found: (found.respond_to? :keys) ? keys_to_str(found.keys) : found.class.name
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
30
|
-
def inspect
|
31
|
-
'((raise when strict))'
|
32
|
-
end
|
33
|
-
|
34
|
-
def keys_to_str(keys)
|
35
|
-
converted = keys.map { |k| k.is_a?(Symbol) ? ":#{k}" : "'#{k}'" }
|
36
|
-
"[#{converted.join ', '}]"
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/errors'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
class Scheme
|
7
|
+
class MissingValidation
|
8
|
+
|
9
|
+
def validate!(_output, options, context:, **_opts)
|
10
|
+
# Check that no unknown keys are present
|
11
|
+
return true unless options.strict
|
12
|
+
raise_strict!(key: context.key, strict_keys: context.rules, backtrace: options.backtrace, found: options.scoped_output)
|
13
|
+
end
|
14
|
+
|
15
|
+
def raise_strict!(key:, backtrace:, strict_keys:, found:)
|
16
|
+
raise StrictValidationError, format(
|
17
|
+
"Unknown key %<key>s in data.\n" \
|
18
|
+
"\tFound at: %<backtrace>s\n" \
|
19
|
+
"\tExpected:\n\n" \
|
20
|
+
"%<strict_keys>s\n\n" \
|
21
|
+
"\tBut I Found:\n\n" \
|
22
|
+
'%<found>s',
|
23
|
+
key: key.inspect,
|
24
|
+
backtrace: backtrace.join('->'),
|
25
|
+
strict_keys: keys_to_str(strict_keys.keys),
|
26
|
+
found: (found.respond_to? :keys) ? keys_to_str(found.keys) : found.class.name
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
'((raise when strict))'
|
32
|
+
end
|
33
|
+
|
34
|
+
def keys_to_str(keys)
|
35
|
+
converted = keys.map { |k| k.is_a?(Symbol) ? ":#{k}" : "'#{k}'" }
|
36
|
+
"[#{converted.join ', '}]"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MediaTypes
|
4
|
-
class Scheme
|
5
|
-
class NotStrict
|
6
|
-
def validate!(*_args, **_opts)
|
7
|
-
true
|
8
|
-
end
|
9
|
-
|
10
|
-
def inspect
|
11
|
-
'((noop: not strict))'
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MediaTypes
|
4
|
+
class Scheme
|
5
|
+
class NotStrict
|
6
|
+
def validate!(*_args, **_opts)
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
'((noop: not strict))'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,45 +1,45 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/errors'
|
4
|
-
require 'media_types/object'
|
5
|
-
|
6
|
-
module MediaTypes
|
7
|
-
class Scheme
|
8
|
-
class OutputEmptyGuard
|
9
|
-
class << self
|
10
|
-
def call(*args, **opts, &block)
|
11
|
-
new(*args, **opts).call(&block)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(output, options, rules:)
|
16
|
-
self.output = output
|
17
|
-
self.options = options
|
18
|
-
self.rules = rules
|
19
|
-
end
|
20
|
-
|
21
|
-
def call
|
22
|
-
return unless MediaTypes::Object.new(output).empty?
|
23
|
-
throw(:end, true) if allow_empty?
|
24
|
-
raise_empty!(backtrace: options.backtrace, found: options.scoped_output)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
attr_accessor :output, :options, :rules
|
30
|
-
|
31
|
-
def allow_empty?
|
32
|
-
rules.allow_empty? || rules.required.empty?
|
33
|
-
end
|
34
|
-
|
35
|
-
def raise_empty!(backtrace:, found:)
|
36
|
-
raise EmptyOutputError, format(
|
37
|
-
'
|
38
|
-
backtrace: backtrace.join('->'),
|
39
|
-
required: rules.required.keys,
|
40
|
-
found: (found.
|
41
|
-
)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/errors'
|
4
|
+
require 'media_types/object'
|
5
|
+
|
6
|
+
module MediaTypes
|
7
|
+
class Scheme
|
8
|
+
class OutputEmptyGuard
|
9
|
+
class << self
|
10
|
+
def call(*args, **opts, &block)
|
11
|
+
new(*args, **opts).call(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(output, options, rules:)
|
16
|
+
self.output = output
|
17
|
+
self.options = options
|
18
|
+
self.rules = rules
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
return unless MediaTypes::Object.new(output).empty?
|
23
|
+
throw(:end, true) if allow_empty?
|
24
|
+
raise_empty!(backtrace: options.backtrace, found: options.scoped_output)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_accessor :output, :options, :rules
|
30
|
+
|
31
|
+
def allow_empty?
|
32
|
+
rules.allow_empty? || rules.required(loose: options.loose).empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def raise_empty!(backtrace:, found:)
|
36
|
+
raise EmptyOutputError, format(
|
37
|
+
'The object at %<backtrace>s was empty but I expected contents. Required keys are: %<required>s.',
|
38
|
+
backtrace: backtrace.join('->'),
|
39
|
+
required: rules.required(loose: options.loose).keys,
|
40
|
+
found: (found.respond_to? :keys) ? found.keys : found.class.name,
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,66 +1,66 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/enumeration_context'
|
4
|
-
|
5
|
-
module MediaTypes
|
6
|
-
class Scheme
|
7
|
-
class OutputIteratorWithPredicate
|
8
|
-
|
9
|
-
class << self
|
10
|
-
def call(*args, **opts, &block)
|
11
|
-
new(*args, **opts).call(&block)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(enumerable, options, rules:)
|
16
|
-
self.enumerable = enumerable
|
17
|
-
self.options = options
|
18
|
-
self.rules = rules
|
19
|
-
end
|
20
|
-
|
21
|
-
##
|
22
|
-
# Mimics Enumerable#all? with mandatory +&block+
|
23
|
-
#
|
24
|
-
def call
|
25
|
-
if hash?
|
26
|
-
return iterate_hash { |*args, **opts| yield(*args, **opts) }
|
27
|
-
end
|
28
|
-
|
29
|
-
if array?
|
30
|
-
return iterate { |*args, **opts| yield(*args, **opts) }
|
31
|
-
end
|
32
|
-
|
33
|
-
raise "Internal consistency error, unexpected: #{enumerable.class}"
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
attr_accessor :enumerable, :options, :rules
|
39
|
-
|
40
|
-
def hash?
|
41
|
-
enumerable.is_a?(::Hash) || enumerable.respond_to?(:key)
|
42
|
-
end
|
43
|
-
|
44
|
-
def array?
|
45
|
-
enumerable.is_a?(::Array)
|
46
|
-
end
|
47
|
-
|
48
|
-
def iterate_hash
|
49
|
-
context = EnumerationContext.new(rules: rules)
|
50
|
-
|
51
|
-
enumerable.all? do |key, value|
|
52
|
-
yield key, value, options: options, context: context.enumerate(key)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def iterate(&block)
|
57
|
-
hash_rule = Rules.new(allow_empty: false, expected_type: ::Hash)
|
58
|
-
|
59
|
-
enumerable.each_with_index.all? do |array_like_element, i|
|
60
|
-
OutputTypeGuard.call(array_like_element, options.trace(1), rules: hash_rule)
|
61
|
-
OutputIteratorWithPredicate.call(array_like_element, options.trace(i), rules: rules, &block)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/enumeration_context'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
class Scheme
|
7
|
+
class OutputIteratorWithPredicate
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def call(*args, **opts, &block)
|
11
|
+
new(*args, **opts).call(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(enumerable, options, rules:)
|
16
|
+
self.enumerable = enumerable
|
17
|
+
self.options = options
|
18
|
+
self.rules = rules
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Mimics Enumerable#all? with mandatory +&block+
|
23
|
+
#
|
24
|
+
def call
|
25
|
+
if hash?
|
26
|
+
return iterate_hash { |*args, **opts| yield(*args, **opts) }
|
27
|
+
end
|
28
|
+
|
29
|
+
if array?
|
30
|
+
return iterate { |*args, **opts| yield(*args, **opts) }
|
31
|
+
end
|
32
|
+
|
33
|
+
raise "Internal consistency error, unexpected: #{enumerable.class}"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_accessor :enumerable, :options, :rules
|
39
|
+
|
40
|
+
def hash?
|
41
|
+
enumerable.is_a?(::Hash) || enumerable.respond_to?(:key)
|
42
|
+
end
|
43
|
+
|
44
|
+
def array?
|
45
|
+
enumerable.is_a?(::Array)
|
46
|
+
end
|
47
|
+
|
48
|
+
def iterate_hash
|
49
|
+
context = EnumerationContext.new(rules: rules)
|
50
|
+
|
51
|
+
enumerable.all? do |key, value|
|
52
|
+
yield key, value, options: options, context: context.enumerate(key)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def iterate(&block)
|
57
|
+
hash_rule = Rules.new(allow_empty: false, expected_type: ::Hash)
|
58
|
+
|
59
|
+
enumerable.each_with_index.all? do |array_like_element, i|
|
60
|
+
OutputTypeGuard.call(array_like_element, options.trace(1), rules: hash_rule)
|
61
|
+
OutputIteratorWithPredicate.call(array_like_element, options.trace(i), rules: rules, &block)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,39 +1,39 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/errors'
|
4
|
-
|
5
|
-
module MediaTypes
|
6
|
-
class Scheme
|
7
|
-
class OutputTypeGuard
|
8
|
-
class << self
|
9
|
-
def call(*args, **opts, &block)
|
10
|
-
new(*args, **opts).call(&block)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize(output, options, rules:)
|
15
|
-
self.output = output
|
16
|
-
self.options = options
|
17
|
-
self.expected_type = rules.expected_type
|
18
|
-
end
|
19
|
-
|
20
|
-
def call
|
21
|
-
return unless expected_type && !(expected_type === output) # rubocop:disable Style/CaseEquality
|
22
|
-
raise_type_error!(type: output.class, backtrace: options.backtrace)
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
attr_accessor :output, :options, :expected_type
|
28
|
-
|
29
|
-
def raise_type_error!(type:, backtrace:)
|
30
|
-
raise OutputTypeMismatch, format(
|
31
|
-
'Expected %<expected>s, got %<actual>s at %<backtrace>s',
|
32
|
-
expected: expected_type,
|
33
|
-
actual: type,
|
34
|
-
backtrace: backtrace.join('->')
|
35
|
-
)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/errors'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
class Scheme
|
7
|
+
class OutputTypeGuard
|
8
|
+
class << self
|
9
|
+
def call(*args, **opts, &block)
|
10
|
+
new(*args, **opts).call(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(output, options, rules:)
|
15
|
+
self.output = output
|
16
|
+
self.options = options
|
17
|
+
self.expected_type = rules.expected_type
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
return unless expected_type && !(expected_type === output) # rubocop:disable Style/CaseEquality
|
22
|
+
raise_type_error!(type: output.class, backtrace: options.backtrace)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_accessor :output, :options, :expected_type
|
28
|
+
|
29
|
+
def raise_type_error!(type:, backtrace:)
|
30
|
+
raise OutputTypeMismatch, format(
|
31
|
+
'Expected %<expected>s, got %<actual>s at %<backtrace>s',
|
32
|
+
expected: expected_type,
|
33
|
+
actual: type,
|
34
|
+
backtrace: backtrace.join('->')
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|