media_types 2.0.1 → 2.1.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 +70 -63
- data/Gemfile.lock +43 -41
- data/LICENSE +21 -0
- data/README.md +276 -32
- data/lib/media_types.rb +46 -2
- data/lib/media_types/constructable.rb +8 -2
- data/lib/media_types/dsl.rb +60 -17
- data/lib/media_types/dsl/errors.rb +18 -0
- data/lib/media_types/errors.rb +19 -0
- data/lib/media_types/scheme.rb +123 -18
- data/lib/media_types/scheme/errors.rb +60 -0
- data/lib/media_types/scheme/links.rb +15 -0
- data/lib/media_types/scheme/output_type_guard.rb +1 -3
- data/lib/media_types/scheme/rules.rb +54 -3
- data/lib/media_types/scheme/rules_exhausted_guard.rb +10 -0
- data/lib/media_types/scheme/validation_options.rb +5 -4
- data/lib/media_types/testing/assertions.rb +20 -0
- data/lib/media_types/validations.rb +7 -4
- data/lib/media_types/version.rb +1 -1
- data/media_types.gemspec +1 -1
- metadata +12 -9
- data/lib/media_types/minitest/assert_media_type_format.rb +0 -10
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module MediaTypes
|
4
4
|
class Scheme
|
5
|
+
class ConflictingTypeDefinitionError < ArgumentError; end
|
5
6
|
|
6
7
|
# Base class for all validations errors
|
7
8
|
class ValidationError < ArgumentError; end
|
@@ -12,6 +13,38 @@ module MediaTypes
|
|
12
13
|
# Raised when trying to register a key twice
|
13
14
|
class DuplicateKeyError < ArgumentError; end
|
14
15
|
|
16
|
+
class DuplicateSymbolKeyError < DuplicateKeyError
|
17
|
+
MESSAGE_TEMPLATE = '%<rule_type>s rule with key :%<key>s has already been defined. Please remove one of the two.'
|
18
|
+
|
19
|
+
def initialize(rule_type, key)
|
20
|
+
super(format(MESSAGE_TEMPLATE, rule_type: rule_type, key: key))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class DuplicateStringKeyError < DuplicateKeyError
|
25
|
+
MESSAGE_TEMPLATE = '%<rule_type>s rule with key %<key>s has already been defined. Please remove one of the two.'
|
26
|
+
|
27
|
+
def initialize(rule_type, key)
|
28
|
+
super(format(MESSAGE_TEMPLATE, { rule_type: rule_type, key: key }))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class StringOverwritingSymbolError < DuplicateKeyError
|
33
|
+
MESSAGE_TEMPLATE = 'Trying to add %<rule_type>s rule String key %<key>s while a Symbol with the same name already exists. Please remove one of the two.'
|
34
|
+
|
35
|
+
def initialize(rule_type, key)
|
36
|
+
super(format(MESSAGE_TEMPLATE, { rule_type: rule_type, key: key }))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class SymbolOverwritingStringError < DuplicateKeyError
|
41
|
+
MESSAGE_TEMPLATE = 'Trying to add %<rule_type>s rule with Symbol key :%<key>s while a String key with the same name already exists. Please remove one of the two.'
|
42
|
+
|
43
|
+
def initialize(rule_type, key)
|
44
|
+
super(format(MESSAGE_TEMPLATE, { rule_type: rule_type, key: key }))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
15
48
|
# Raised when it did not expect more data, but there was more left
|
16
49
|
class StrictValidationError < ValidationError; end
|
17
50
|
|
@@ -23,5 +56,32 @@ module MediaTypes
|
|
23
56
|
|
24
57
|
# Raised when it expected more data but there wasn't any left
|
25
58
|
class ExhaustedOutputError < ValidationError; end
|
59
|
+
|
60
|
+
# Raised when trying to override a non default rule scheme in the Rules Hash's default object method
|
61
|
+
class OverwritingRuleError < ArgumentError; end
|
62
|
+
|
63
|
+
class DuplicateAnyRuleError < OverwritingRuleError
|
64
|
+
def message
|
65
|
+
"An 'any' rule has already been defined. Please remove one of the two."
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class DuplicateNotStrictRuleError < OverwritingRuleError
|
70
|
+
def message
|
71
|
+
"The 'not_strict' rule has already been defined. Please remove one of the two."
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class NotStrictOverwritingAnyError < OverwritingRuleError
|
76
|
+
def message
|
77
|
+
"An 'any' rule has already been defined. Setting 'not_strict' will override that rule. Please remove one of the two."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class AnyOverwritingNotStrictError < OverwritingRuleError
|
82
|
+
def message
|
83
|
+
"The 'not_strict' rule has already been defined. Setting 'any' will override that rule. Please remove one of the two."
|
84
|
+
end
|
85
|
+
end
|
26
86
|
end
|
27
87
|
end
|
@@ -31,6 +31,21 @@ module MediaTypes
|
|
31
31
|
"[Scheme::Links #{links.keys}]"
|
32
32
|
end
|
33
33
|
|
34
|
+
def run_fixture_validations(expect_symbol_keys, backtrace = [])
|
35
|
+
fixture_errors = @links.flat_map {|key, rule|
|
36
|
+
if rule.is_a?(Scheme)
|
37
|
+
begin
|
38
|
+
rule.run_fixture_validations(expect_symbol_keys, backtrace.dup.append(key))
|
39
|
+
nil
|
40
|
+
rescue AssertionError => e
|
41
|
+
e.fixture_errors
|
42
|
+
end
|
43
|
+
end
|
44
|
+
}.compact
|
45
|
+
|
46
|
+
raise AssertionError.new(fixture_errors) unless fixture_errors.empty?
|
47
|
+
end
|
48
|
+
|
34
49
|
private
|
35
50
|
|
36
51
|
attr_accessor :links
|
@@ -18,8 +18,6 @@ module MediaTypes
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def call
|
21
|
-
#puts @rules
|
22
|
-
#puts "input: <#{output}>, type: <#{expected_type.name}>"
|
23
21
|
return unless expected_type && !(expected_type === output) # rubocop:disable Style/CaseEquality
|
24
22
|
raise_type_error!(type: output.class, backtrace: options.backtrace)
|
25
23
|
end
|
@@ -30,7 +28,7 @@ module MediaTypes
|
|
30
28
|
|
31
29
|
def raise_type_error!(type:, backtrace:)
|
32
30
|
raise OutputTypeMismatch, format(
|
33
|
-
'Expected
|
31
|
+
'Expected %<expected>s, got %<actual>s at %<backtrace>s',
|
34
32
|
expected: expected_type,
|
35
33
|
actual: type,
|
36
34
|
backtrace: backtrace.join('->')
|
@@ -14,6 +14,7 @@ module MediaTypes
|
|
14
14
|
self.allow_empty = allow_empty
|
15
15
|
self.expected_type = expected_type
|
16
16
|
self.optional_keys = []
|
17
|
+
self.original_key_type = {}
|
17
18
|
|
18
19
|
self.default = MissingValidation.new
|
19
20
|
end
|
@@ -27,13 +28,44 @@ module MediaTypes
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def add(key, val, optional: false)
|
31
|
+
validate_input(key, val)
|
32
|
+
|
30
33
|
normalized_key = normalize_key(key)
|
31
34
|
__getobj__[normalized_key] = val
|
32
35
|
optional_keys << normalized_key if optional
|
36
|
+
original_key_type[normalized_key] = key.class
|
33
37
|
|
34
38
|
self
|
35
39
|
end
|
36
40
|
|
41
|
+
def validate_input(key, val)
|
42
|
+
raise KeyTypeError, "Unexpected key type #{key.class.name}, please use either a symbol or string." unless key.is_a?(String) || key.is_a?(Symbol)
|
43
|
+
|
44
|
+
validate_key_name(key, val)
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_key_name(key, val)
|
48
|
+
return unless has_key?(key)
|
49
|
+
|
50
|
+
if key.is_a?(Symbol)
|
51
|
+
duplicate_symbol_key_name(key, val)
|
52
|
+
else
|
53
|
+
duplicate_string_key_name(key, val)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def duplicate_symbol_key_name(key, val)
|
58
|
+
raise DuplicateSymbolKeyError.new(val.class.name.split('::').last, key) if get_original_key_type(key) == Symbol
|
59
|
+
|
60
|
+
raise SymbolOverwritingStringError.new(val.class.name.split('::').last, key)
|
61
|
+
end
|
62
|
+
|
63
|
+
def duplicate_string_key_name(key, val)
|
64
|
+
raise DuplicateStringKeyError.new(val.class.name.split('::').last, key) if get_original_key_type(key) == String
|
65
|
+
|
66
|
+
raise StringOverwritingSymbolError.new(val.class.name.split('::').last, key)
|
67
|
+
end
|
68
|
+
|
37
69
|
def []=(key, val)
|
38
70
|
add(key, val, optional: false)
|
39
71
|
end
|
@@ -105,17 +137,36 @@ module MediaTypes
|
|
105
137
|
].join(': ')
|
106
138
|
end
|
107
139
|
|
140
|
+
def has_key?(key)
|
141
|
+
__getobj__.key?(normalize_key(key))
|
142
|
+
end
|
143
|
+
|
144
|
+
def get_original_key_type(key)
|
145
|
+
raise format('Key %<key>s does not exist', key: key) unless has_key?(key)
|
146
|
+
|
147
|
+
original_key_type[normalize_key(key)]
|
148
|
+
end
|
149
|
+
|
150
|
+
def default=(input_default)
|
151
|
+
unless default.nil?
|
152
|
+
raise DuplicateAnyRuleError if !(default.is_a?(MissingValidation) || default.is_a?(NotStrict)) && !(input_default.is_a?(MissingValidation) || input_default.is_a?(NotStrict))
|
153
|
+
raise DuplicateNotStrictRuleError if default.is_a?(NotStrict) && input_default.is_a?(NotStrict)
|
154
|
+
raise NotStrictOverwritingAnyError if !(default.is_a?(MissingValidation) || default.is_a?(NotStrict)) && input_default.is_a?(NotStrict)
|
155
|
+
raise AnyOverwritingNotStrictError if default.is_a?(NotStrict) && !(input_default.is_a?(MissingValidation) || input_default.is_a?(NotStrict))
|
156
|
+
end
|
157
|
+
super(input_default)
|
158
|
+
end
|
159
|
+
|
108
160
|
alias get []
|
109
161
|
alias remove delete
|
110
162
|
|
111
163
|
private
|
112
164
|
|
113
|
-
attr_accessor :allow_empty, :optional_keys
|
165
|
+
attr_accessor :allow_empty, :optional_keys, :original_key_type
|
114
166
|
attr_writer :expected_type
|
115
167
|
|
116
168
|
def normalize_key(key)
|
117
|
-
|
118
|
-
key
|
169
|
+
String(key).to_sym
|
119
170
|
end
|
120
171
|
end
|
121
172
|
end
|
@@ -36,6 +36,16 @@ module MediaTypes
|
|
36
36
|
|
37
37
|
def iterate(mark)
|
38
38
|
OutputIteratorWithPredicate.call(output, options, rules: rules) do |key, value, options:, context:|
|
39
|
+
unless key.class == options.expected_key_type || rules.get(key).class == NotStrict
|
40
|
+
raise ValidationError,
|
41
|
+
format(
|
42
|
+
'Expected key as %<type>s, got %<actual>s at [%<backtrace>s]',
|
43
|
+
type: options.expected_key_type,
|
44
|
+
actual: key.class,
|
45
|
+
backtrace: options.trace(key).backtrace.join('->')
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
39
49
|
mark.call(key)
|
40
50
|
|
41
51
|
rules.get(key).validate!(
|
@@ -3,13 +3,14 @@
|
|
3
3
|
module MediaTypes
|
4
4
|
class Scheme
|
5
5
|
class ValidationOptions
|
6
|
-
attr_accessor :exhaustive, :strict, :backtrace, :context
|
6
|
+
attr_accessor :exhaustive, :strict, :backtrace, :context, :expected_key_type
|
7
7
|
|
8
|
-
def initialize(context = {}, exhaustive: true, strict: true, backtrace: [])
|
8
|
+
def initialize(context = {}, exhaustive: true, strict: true, backtrace: [], expected_key_type:)
|
9
9
|
self.exhaustive = exhaustive
|
10
10
|
self.strict = strict
|
11
11
|
self.backtrace = backtrace
|
12
12
|
self.context = context
|
13
|
+
self.expected_key_type = expected_key_type
|
13
14
|
end
|
14
15
|
|
15
16
|
def inspect
|
@@ -27,7 +28,7 @@ module MediaTypes
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def with_backtrace(backtrace)
|
30
|
-
ValidationOptions.new(context, exhaustive: exhaustive, strict: strict, backtrace: backtrace)
|
31
|
+
ValidationOptions.new(context, exhaustive: exhaustive, strict: strict, backtrace: backtrace, expected_key_type: expected_key_type)
|
31
32
|
end
|
32
33
|
|
33
34
|
def trace(*traces)
|
@@ -35,7 +36,7 @@ module MediaTypes
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def exhaustive!
|
38
|
-
ValidationOptions.new(context, exhaustive: true, strict: strict, backtrace: backtrace)
|
39
|
+
ValidationOptions.new(context, exhaustive: true, strict: strict, backtrace: backtrace, expected_key_type: expected_key_type)
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MediaTypes
|
4
|
+
module Testing
|
5
|
+
module Assertions
|
6
|
+
def assert_media_type_format(media_type, output, **opts)
|
7
|
+
return pass unless media_type.validatable?
|
8
|
+
|
9
|
+
assert media_type.validate!(output, **opts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_mediatype(mediatype)
|
13
|
+
mediatype.assert_sane!
|
14
|
+
assert mediatype.media_type_validations.scheme.asserted_sane?
|
15
|
+
rescue MediaTypes::AssertionError => e
|
16
|
+
flunk e.message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -12,6 +12,8 @@ module MediaTypes
|
|
12
12
|
#
|
13
13
|
class Validations
|
14
14
|
|
15
|
+
attr_reader :scheme
|
16
|
+
|
15
17
|
##
|
16
18
|
# Creates a new stack of validations
|
17
19
|
#
|
@@ -44,12 +46,12 @@ module MediaTypes
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
def method_missing(method_name, *arguments, &block)
|
49
|
+
def method_missing(method_name, *arguments, **kwargs, &block)
|
48
50
|
if scheme.respond_to?(method_name)
|
49
51
|
media_type.__getobj__.media_type_combinations ||= Set.new
|
50
52
|
media_type.__getobj__.media_type_combinations.add(media_type.as_key)
|
51
|
-
|
52
|
-
return scheme.send(method_name, *arguments, &block)
|
53
|
+
|
54
|
+
return scheme.send(method_name, *arguments, **kwargs, &block)
|
53
55
|
end
|
54
56
|
|
55
57
|
super
|
@@ -61,7 +63,8 @@ module MediaTypes
|
|
61
63
|
|
62
64
|
private
|
63
65
|
|
64
|
-
attr_accessor :media_type, :registry
|
66
|
+
attr_accessor :media_type, :registry
|
67
|
+
attr_writer :scheme
|
65
68
|
|
66
69
|
##
|
67
70
|
# Switches the inner block to a specific version
|
data/lib/media_types/version.rb
CHANGED
data/media_types.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
26
|
spec.add_development_dependency 'awesome_print'
|
27
|
-
spec.add_development_dependency 'bundler', '>=
|
27
|
+
spec.add_development_dependency 'bundler', '>= 2'
|
28
28
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
29
29
|
spec.add_development_dependency 'minitest-reporters'
|
30
30
|
spec.add_development_dependency 'oj'
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: media_types
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derk-Jan Karrenbeld
|
8
8
|
- Max Maton
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-08-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: awesome_print
|
@@ -31,14 +31,14 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
34
|
+
version: '2'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
41
|
+
version: '2'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: minitest
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- CHANGELOG.md
|
126
126
|
- Gemfile
|
127
127
|
- Gemfile.lock
|
128
|
+
- LICENSE
|
128
129
|
- README.md
|
129
130
|
- Rakefile
|
130
131
|
- bin/console
|
@@ -132,9 +133,10 @@ files:
|
|
132
133
|
- lib/media_types.rb
|
133
134
|
- lib/media_types/constructable.rb
|
134
135
|
- lib/media_types/dsl.rb
|
136
|
+
- lib/media_types/dsl/errors.rb
|
137
|
+
- lib/media_types/errors.rb
|
135
138
|
- lib/media_types/formatter.rb
|
136
139
|
- lib/media_types/hash.rb
|
137
|
-
- lib/media_types/minitest/assert_media_type_format.rb
|
138
140
|
- lib/media_types/object.rb
|
139
141
|
- lib/media_types/scheme.rb
|
140
142
|
- lib/media_types/scheme/allow_nil.rb
|
@@ -152,6 +154,7 @@ files:
|
|
152
154
|
- lib/media_types/scheme/rules.rb
|
153
155
|
- lib/media_types/scheme/rules_exhausted_guard.rb
|
154
156
|
- lib/media_types/scheme/validation_options.rb
|
157
|
+
- lib/media_types/testing/assertions.rb
|
155
158
|
- lib/media_types/validations.rb
|
156
159
|
- lib/media_types/version.rb
|
157
160
|
- lib/media_types/views.rb
|
@@ -159,7 +162,7 @@ files:
|
|
159
162
|
homepage: https://github.com/SleeplessByte/media-types-ruby
|
160
163
|
licenses: []
|
161
164
|
metadata: {}
|
162
|
-
post_install_message:
|
165
|
+
post_install_message:
|
163
166
|
rdoc_options: []
|
164
167
|
require_paths:
|
165
168
|
- lib
|
@@ -174,8 +177,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
174
177
|
- !ruby/object:Gem::Version
|
175
178
|
version: '0'
|
176
179
|
requirements: []
|
177
|
-
rubygems_version: 3.
|
178
|
-
signing_key:
|
180
|
+
rubygems_version: 3.1.6
|
181
|
+
signing_key:
|
179
182
|
specification_version: 4
|
180
183
|
summary: Library to create media type definitions, schemes and validations
|
181
184
|
test_files: []
|