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.
@@ -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 a %<expected>s, got a %<actual>s at %<backtrace>s',
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
- # Because default ruby hashes don't treat symbols and string equal we can't normalize them here.
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, :scheme
66
+ attr_accessor :media_type, :registry
67
+ attr_writer :scheme
65
68
 
66
69
  ##
67
70
  # Switches the inner block to a specific version
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MediaTypes
4
- VERSION = '2.0.1'
4
+ VERSION = '2.1.0'
5
5
  end
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', '>= 1.16.1'
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.1
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: 2020-04-15 00:00:00.000000000 Z
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: 1.16.1
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: 1.16.1
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.0.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: []
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MediaTypes
4
- module Assertions
5
- def assert_media_type_format(media_type, output, **opts)
6
- return pass unless media_type.validatable?
7
- assert media_type.validate!(output, **opts)
8
- end
9
- end
10
- end