media_types 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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