media_types 2.1.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 -23
- data/.gitignore +20 -10
- data/.rubocop.yml +29 -29
- data/CHANGELOG.md +175 -164
- data/Gemfile +6 -6
- data/Gemfile.lock +9 -9
- 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 +14 -12
@@ -1,41 +1,41 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'delegate'
|
4
|
-
|
5
|
-
module MediaTypes
|
6
|
-
class Scheme
|
7
|
-
class CaseEqualityWithList < SimpleDelegator
|
8
|
-
|
9
|
-
# True for Enumerable#any? {Object#===}
|
10
|
-
def ===(other)
|
11
|
-
any? { |it| it === other } # rubocop:disable Style/CaseEquality
|
12
|
-
end
|
13
|
-
|
14
|
-
def inspect
|
15
|
-
"[Scheme::AnyOf(#{__getobj__})]"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class << self
|
20
|
-
# noinspection RubyClassMethodNamingConvention
|
21
|
-
##
|
22
|
-
# Allows +it+ to be any of the wrapped +klazzes+
|
23
|
-
#
|
24
|
-
# @param [Array<Class>] klazzes the classes that are valid for +it+
|
25
|
-
# @return [CaseEqualityWithList]
|
26
|
-
def AnyOf(*klazzes) # rubocop:disable Naming/MethodName
|
27
|
-
CaseEqualityWithList.new(klazzes)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# noinspection RubyInstanceMethodNamingConvention
|
32
|
-
##
|
33
|
-
# Allows +it+ to be any of the wrapped +klazzes+
|
34
|
-
#
|
35
|
-
# @param [Array<Class>] klazzes the classes that are valid for +it+
|
36
|
-
# @return [CaseEqualityWithList]
|
37
|
-
def AnyOf(*klazzes) # rubocop:disable Naming/MethodName
|
38
|
-
self.class.AnyOf(*klazzes)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
class Scheme
|
7
|
+
class CaseEqualityWithList < SimpleDelegator
|
8
|
+
|
9
|
+
# True for Enumerable#any? {Object#===}
|
10
|
+
def ===(other)
|
11
|
+
any? { |it| it === other } # rubocop:disable Style/CaseEquality
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"[Scheme::AnyOf(#{__getobj__})]"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# noinspection RubyClassMethodNamingConvention
|
21
|
+
##
|
22
|
+
# Allows +it+ to be any of the wrapped +klazzes+
|
23
|
+
#
|
24
|
+
# @param [Array<Class>] klazzes the classes that are valid for +it+
|
25
|
+
# @return [CaseEqualityWithList]
|
26
|
+
def AnyOf(*klazzes) # rubocop:disable Naming/MethodName
|
27
|
+
CaseEqualityWithList.new(klazzes)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# noinspection RubyInstanceMethodNamingConvention
|
32
|
+
##
|
33
|
+
# Allows +it+ to be any of the wrapped +klazzes+
|
34
|
+
#
|
35
|
+
# @param [Array<Class>] klazzes the classes that are valid for +it+
|
36
|
+
# @return [CaseEqualityWithList]
|
37
|
+
def AnyOf(*klazzes) # rubocop:disable Naming/MethodName
|
38
|
+
self.class.AnyOf(*klazzes)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,46 +1,46 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/errors'
|
4
|
-
require 'media_types/scheme/allow_nil'
|
5
|
-
|
6
|
-
module MediaTypes
|
7
|
-
class Scheme
|
8
|
-
class Attribute
|
9
|
-
|
10
|
-
##
|
11
|
-
# An attribute that expects a value of type +type+
|
12
|
-
#
|
13
|
-
# @see AllowNil
|
14
|
-
# @see AnyOf
|
15
|
-
#
|
16
|
-
# @param [Class] type the class +it+ must be
|
17
|
-
# @param [TrueClass, FalseClass] allow_nil if true, nil? is allowed
|
18
|
-
#
|
19
|
-
def initialize(type, allow_nil: false)
|
20
|
-
self.type = allow_nil ? Scheme.AllowNil(type) : type
|
21
|
-
|
22
|
-
freeze
|
23
|
-
end
|
24
|
-
|
25
|
-
def validate!(output, options, **_opts)
|
26
|
-
return true if type === output # rubocop:disable Style/CaseEquality
|
27
|
-
raise ValidationError,
|
28
|
-
format(
|
29
|
-
'Expected %<type>s, got %<actual>s at [%<backtrace>s]',
|
30
|
-
type: type,
|
31
|
-
actual: output.inspect,
|
32
|
-
backtrace: options.backtrace.join('->')
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
def inspect
|
37
|
-
"[Scheme::Attribute of #{type.inspect}]"
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
attr_accessor :allow_nil, :type
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/errors'
|
4
|
+
require 'media_types/scheme/allow_nil'
|
5
|
+
|
6
|
+
module MediaTypes
|
7
|
+
class Scheme
|
8
|
+
class Attribute
|
9
|
+
|
10
|
+
##
|
11
|
+
# An attribute that expects a value of type +type+
|
12
|
+
#
|
13
|
+
# @see AllowNil
|
14
|
+
# @see AnyOf
|
15
|
+
#
|
16
|
+
# @param [Class] type the class +it+ must be
|
17
|
+
# @param [TrueClass, FalseClass] allow_nil if true, nil? is allowed
|
18
|
+
#
|
19
|
+
def initialize(type, allow_nil: false)
|
20
|
+
self.type = allow_nil ? Scheme.AllowNil(type) : type
|
21
|
+
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate!(output, options, **_opts)
|
26
|
+
return true if type === output # rubocop:disable Style/CaseEquality
|
27
|
+
raise ValidationError,
|
28
|
+
format(
|
29
|
+
'Expected %<type>s, got %<actual>s at [%<backtrace>s]',
|
30
|
+
type: type,
|
31
|
+
actual: output.inspect,
|
32
|
+
backtrace: options.backtrace.join('->')
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
"[Scheme::Attribute of #{type.inspect}]"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_accessor :allow_nil, :type
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MediaTypes
|
4
|
-
class Scheme
|
5
|
-
class EnumerationContext
|
6
|
-
def initialize(rules:)
|
7
|
-
self.rules = rules
|
8
|
-
end
|
9
|
-
|
10
|
-
def enumerate(val)
|
11
|
-
self.key = val
|
12
|
-
self
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_accessor :rules, :key
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MediaTypes
|
4
|
+
class Scheme
|
5
|
+
class EnumerationContext
|
6
|
+
def initialize(rules:)
|
7
|
+
self.rules = rules
|
8
|
+
end
|
9
|
+
|
10
|
+
def enumerate(val)
|
11
|
+
self.key = val
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :rules, :key
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,80 +1,80 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/errors'
|
4
|
-
|
5
|
-
module MediaTypes
|
6
|
-
class Scheme
|
7
|
-
class EnumerationOfType
|
8
|
-
|
9
|
-
##
|
10
|
-
# An attribute that expects a value of type +enumeration_type+ and each item of type +item_type+
|
11
|
-
#
|
12
|
-
# @param [Class] item_type the type of each item
|
13
|
-
# @param [Class] enumeration_type the type of the enumeration as a whole
|
14
|
-
# @param [TrueClass, FalseClass] allow_empty if true, an empty instance of +enumeration_type+ is valid
|
15
|
-
#
|
16
|
-
def initialize(item_type, enumeration_type: Array, allow_empty: false)
|
17
|
-
self.item_type = item_type
|
18
|
-
self.enumeration_type = enumeration_type
|
19
|
-
self.allow_empty = allow_empty
|
20
|
-
|
21
|
-
freeze
|
22
|
-
end
|
23
|
-
|
24
|
-
def validate!(output, options, **_opts)
|
25
|
-
validate_enumeration!(output, options) &&
|
26
|
-
validate_not_empty!(output, options) &&
|
27
|
-
validate_items!(output, options)
|
28
|
-
end
|
29
|
-
|
30
|
-
def inspect
|
31
|
-
"[Scheme::EnumerationOfType #{item_type} collection=#{enumeration_type} empty=#{allow_empty}]"
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
attr_accessor :allow_empty, :enumeration_type, :item_type
|
37
|
-
|
38
|
-
def validate_enumeration!(output, options)
|
39
|
-
return true if enumeration_type === output # rubocop:disable Style/CaseEquality
|
40
|
-
raise ValidationError,
|
41
|
-
format(
|
42
|
-
'Expected collection as %<type>s, got %<actual>s at [%<backtrace>s]',
|
43
|
-
type: enumeration_type,
|
44
|
-
actual: output.inspect,
|
45
|
-
backtrace: options.backtrace.join('->')
|
46
|
-
)
|
47
|
-
end
|
48
|
-
|
49
|
-
def validate_not_empty!(output, options)
|
50
|
-
return true if allow_empty
|
51
|
-
return true if output.respond_to?(:length) && output.length.positive?
|
52
|
-
return true if output.respond_to?(:empty?) && !output.empty?
|
53
|
-
|
54
|
-
raise EmptyOutputError,
|
55
|
-
format(
|
56
|
-
'Expected collection to be not empty, got empty at [%<backtrace>s]',
|
57
|
-
backtrace: options.backtrace.join('->')
|
58
|
-
)
|
59
|
-
end
|
60
|
-
|
61
|
-
def validate_items!(output, options)
|
62
|
-
output.each_with_index.all? do |item, index|
|
63
|
-
next true if item_type === item # rubocop:disable Style/CaseEquality
|
64
|
-
if item_type.is_a?(Scheme)
|
65
|
-
item_type.validate(item, options.trace("[#{index}]"))
|
66
|
-
next true
|
67
|
-
end
|
68
|
-
raise ValidationError,
|
69
|
-
format(
|
70
|
-
'Expected collection item as %<type>s, got %<actual>s at [%<backtrace>s]',
|
71
|
-
type: item_type.inspect,
|
72
|
-
actual: item.inspect,
|
73
|
-
backtrace: options.backtrace.join('->')
|
74
|
-
)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/errors'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
class Scheme
|
7
|
+
class EnumerationOfType
|
8
|
+
|
9
|
+
##
|
10
|
+
# An attribute that expects a value of type +enumeration_type+ and each item of type +item_type+
|
11
|
+
#
|
12
|
+
# @param [Class] item_type the type of each item
|
13
|
+
# @param [Class] enumeration_type the type of the enumeration as a whole
|
14
|
+
# @param [TrueClass, FalseClass] allow_empty if true, an empty instance of +enumeration_type+ is valid
|
15
|
+
#
|
16
|
+
def initialize(item_type, enumeration_type: Array, allow_empty: false)
|
17
|
+
self.item_type = item_type
|
18
|
+
self.enumeration_type = enumeration_type
|
19
|
+
self.allow_empty = allow_empty
|
20
|
+
|
21
|
+
freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate!(output, options, **_opts)
|
25
|
+
validate_enumeration!(output, options) &&
|
26
|
+
validate_not_empty!(output, options) &&
|
27
|
+
validate_items!(output, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"[Scheme::EnumerationOfType #{item_type} collection=#{enumeration_type} empty=#{allow_empty}]"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_accessor :allow_empty, :enumeration_type, :item_type
|
37
|
+
|
38
|
+
def validate_enumeration!(output, options)
|
39
|
+
return true if enumeration_type === output # rubocop:disable Style/CaseEquality
|
40
|
+
raise ValidationError,
|
41
|
+
format(
|
42
|
+
'Expected collection as %<type>s, got %<actual>s at [%<backtrace>s]',
|
43
|
+
type: enumeration_type,
|
44
|
+
actual: output.inspect,
|
45
|
+
backtrace: options.backtrace.join('->')
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_not_empty!(output, options)
|
50
|
+
return true if allow_empty
|
51
|
+
return true if output.respond_to?(:length) && output.length.positive?
|
52
|
+
return true if output.respond_to?(:empty?) && !output.empty?
|
53
|
+
|
54
|
+
raise EmptyOutputError,
|
55
|
+
format(
|
56
|
+
'Expected collection to be not empty, got empty at [%<backtrace>s]',
|
57
|
+
backtrace: options.backtrace.join('->')
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate_items!(output, options)
|
62
|
+
output.each_with_index.all? do |item, index|
|
63
|
+
next true if item_type === item # rubocop:disable Style/CaseEquality
|
64
|
+
if item_type.is_a?(Scheme)
|
65
|
+
item_type.validate(item, options.trace("[#{index}]"))
|
66
|
+
next true
|
67
|
+
end
|
68
|
+
raise ValidationError,
|
69
|
+
format(
|
70
|
+
'Expected collection item as %<type>s, got %<actual>s at [%<backtrace>s]',
|
71
|
+
type: item_type.inspect,
|
72
|
+
actual: item.inspect,
|
73
|
+
backtrace: options.backtrace.join('->')
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,87 +1,87 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MediaTypes
|
4
|
-
class Scheme
|
5
|
-
class ConflictingTypeDefinitionError < ArgumentError; end
|
6
|
-
|
7
|
-
# Base class for all validations errors
|
8
|
-
class ValidationError < ArgumentError; end
|
9
|
-
|
10
|
-
# Raised when trying to register an attribute with a non-string key
|
11
|
-
class KeyTypeError < ArgumentError; end
|
12
|
-
|
13
|
-
# Raised when trying to register a key twice
|
14
|
-
class DuplicateKeyError < ArgumentError; end
|
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
|
-
|
48
|
-
# Raised when it did not expect more data, but there was more left
|
49
|
-
class StrictValidationError < ValidationError; end
|
50
|
-
|
51
|
-
# Raised when it expected not to be empty, but it was
|
52
|
-
class EmptyOutputError < ValidationError; end
|
53
|
-
|
54
|
-
# Raised when a value did not have the expected type
|
55
|
-
class OutputTypeMismatch < ValidationError; end
|
56
|
-
|
57
|
-
# Raised when it expected more data but there wasn't any left
|
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
|
86
|
-
end
|
87
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MediaTypes
|
4
|
+
class Scheme
|
5
|
+
class ConflictingTypeDefinitionError < ArgumentError; end
|
6
|
+
|
7
|
+
# Base class for all validations errors
|
8
|
+
class ValidationError < ArgumentError; end
|
9
|
+
|
10
|
+
# Raised when trying to register an attribute with a non-string key
|
11
|
+
class KeyTypeError < ArgumentError; end
|
12
|
+
|
13
|
+
# Raised when trying to register a key twice
|
14
|
+
class DuplicateKeyError < ArgumentError; end
|
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
|
+
|
48
|
+
# Raised when it did not expect more data, but there was more left
|
49
|
+
class StrictValidationError < ValidationError; end
|
50
|
+
|
51
|
+
# Raised when it expected not to be empty, but it was
|
52
|
+
class EmptyOutputError < ValidationError; end
|
53
|
+
|
54
|
+
# Raised when a value did not have the expected type
|
55
|
+
class OutputTypeMismatch < ValidationError; end
|
56
|
+
|
57
|
+
# Raised when it expected more data but there wasn't any left
|
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
|
86
|
+
end
|
87
|
+
end
|
@@ -1,54 +1,54 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'media_types/scheme/rules'
|
4
|
-
require 'media_types/scheme/rules_exhausted_guard'
|
5
|
-
|
6
|
-
module MediaTypes
|
7
|
-
class Scheme
|
8
|
-
class Links
|
9
|
-
def initialize
|
10
|
-
self.links = Rules.new(allow_empty: false, expected_type: ::Hash)
|
11
|
-
end
|
12
|
-
|
13
|
-
def link(key, allow_nil: false, optional: false, &block)
|
14
|
-
links.add(
|
15
|
-
key,
|
16
|
-
Scheme.new do
|
17
|
-
attribute :href, String, allow_nil: allow_nil
|
18
|
-
instance_exec(&block) if block_given?
|
19
|
-
end,
|
20
|
-
optional: optional
|
21
|
-
)
|
22
|
-
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
|
-
def validate!(output, options, **_opts)
|
27
|
-
RulesExhaustedGuard.call(output, options, rules: links)
|
28
|
-
end
|
29
|
-
|
30
|
-
def inspect
|
31
|
-
"[Scheme::Links #{links.keys}]"
|
32
|
-
end
|
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
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
attr_accessor :links
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/scheme/rules'
|
4
|
+
require 'media_types/scheme/rules_exhausted_guard'
|
5
|
+
|
6
|
+
module MediaTypes
|
7
|
+
class Scheme
|
8
|
+
class Links
|
9
|
+
def initialize
|
10
|
+
self.links = Rules.new(allow_empty: false, expected_type: ::Hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
def link(key, allow_nil: false, optional: false, &block)
|
14
|
+
links.add(
|
15
|
+
key,
|
16
|
+
Scheme.new do
|
17
|
+
attribute :href, String, allow_nil: allow_nil
|
18
|
+
instance_exec(&block) if block_given?
|
19
|
+
end,
|
20
|
+
optional: optional
|
21
|
+
)
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate!(output, options, **_opts)
|
27
|
+
RulesExhaustedGuard.call(output, options, rules: links)
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"[Scheme::Links #{links.keys}]"
|
32
|
+
end
|
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
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_accessor :links
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|