media_types 0.6.2 → 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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/debian.yml +42 -0
  3. data/.github/workflows/ruby.yml +22 -0
  4. data/.gitignore +10 -10
  5. data/CHANGELOG.md +76 -41
  6. data/Gemfile +6 -6
  7. data/Gemfile.lock +17 -83
  8. data/LICENSE +21 -0
  9. data/README.md +364 -91
  10. data/Rakefile +12 -12
  11. data/lib/media_types.rb +58 -2
  12. data/lib/media_types/constructable.rb +36 -10
  13. data/lib/media_types/dsl.rb +110 -29
  14. data/lib/media_types/dsl/errors.rb +18 -0
  15. data/lib/media_types/errors.rb +19 -0
  16. data/lib/media_types/scheme.rb +153 -2
  17. data/lib/media_types/scheme/errors.rb +66 -0
  18. data/lib/media_types/scheme/links.rb +15 -0
  19. data/lib/media_types/scheme/missing_validation.rb +12 -4
  20. data/lib/media_types/scheme/output_empty_guard.rb +5 -4
  21. data/lib/media_types/scheme/output_iterator_with_predicate.rb +13 -2
  22. data/lib/media_types/scheme/output_type_guard.rb +1 -1
  23. data/lib/media_types/scheme/rules.rb +53 -1
  24. data/lib/media_types/scheme/rules_exhausted_guard.rb +15 -4
  25. data/lib/media_types/scheme/validation_options.rb +17 -5
  26. data/lib/media_types/testing/assertions.rb +20 -0
  27. data/lib/media_types/validations.rb +15 -5
  28. data/lib/media_types/version.rb +1 -1
  29. data/media_types.gemspec +4 -7
  30. metadata +20 -62
  31. data/.travis.yml +0 -19
  32. data/lib/media_types/defaults.rb +0 -31
  33. data/lib/media_types/integrations.rb +0 -32
  34. data/lib/media_types/integrations/actionpack.rb +0 -21
  35. data/lib/media_types/integrations/http.rb +0 -47
  36. data/lib/media_types/minitest/assert_media_type_format.rb +0 -10
  37. data/lib/media_types/minitest/assert_media_types_registered.rb +0 -166
  38. data/lib/media_types/registrar.rb +0 -148
data/Rakefile CHANGED
@@ -1,12 +1,12 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rake/testtask'
5
-
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << 'test'
8
- t.libs << 'lib'
9
- t.test_files = FileList['test/**/*_test.rb']
10
- end
11
-
12
- task default: :test
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ task default: :test
data/lib/media_types.rb CHANGED
@@ -7,11 +7,67 @@ require 'media_types/hash'
7
7
  require 'media_types/object'
8
8
  require 'media_types/scheme'
9
9
  require 'media_types/dsl'
10
+ require 'media_types/errors'
10
11
 
11
12
  require 'media_types/views'
12
- require 'media_types/integrations'
13
13
 
14
14
  module MediaTypes
15
- end
15
+ def self.set_organisation(mod, organisation)
16
+ @organisation_prefixes ||= {}
17
+ @organisation_prefixes[mod.name] = organisation
18
+ end
19
+
20
+ def self.expect_string_keys(mod)
21
+ set_key_expectation(mod, false)
22
+ end
23
+
24
+ def self.expect_symbol_keys(mod)
25
+ set_key_expectation(mod, true)
26
+ end
27
+
28
+ # Keep track of modules setting their key expectations
29
+ def self.set_key_expectation(mod, expect_symbol_keys)
30
+ @key_expectations ||= {}
31
+ @key_expectations_used ||= {}
32
+
33
+ raise KeyExpectationSetError.new(mod: mod) unless @key_expectations[mod.name].nil?
34
+ raise KeyExpectationUsedError.new(mod: mod) if @key_expectations_used[mod.name]
35
+
36
+ @key_expectations[mod.name] = expect_symbol_keys
37
+ end
38
+
39
+ SYMBOL_KEYS_DEFAULT = true
40
+
41
+ def self.get_key_expectation(mod)
42
+ @key_expectations ||= {}
43
+ @key_expectations_used ||= {}
16
44
 
45
+ expect_symbol = find_key_expectation(mod)
17
46
 
47
+ expect_symbol.nil? ? SYMBOL_KEYS_DEFAULT : expect_symbol
48
+ end
49
+
50
+ def self.find_key_expectation(mod)
51
+ modules = mod.name.split('::')
52
+ expect_symbol = nil
53
+
54
+ while modules.any? && expect_symbol.nil?
55
+ current_module = modules.join('::')
56
+ expect_symbol = @key_expectations[current_module]
57
+ @key_expectations_used[current_module] = true
58
+ modules.pop
59
+ end
60
+
61
+ expect_symbol
62
+ end
63
+
64
+ def self.get_organisation(mod)
65
+ name = mod.name
66
+ prefixes = @organisation_prefixes.keys.select { |p| name.start_with? p }
67
+ return nil unless prefixes.any?
68
+
69
+ best = prefixes.max_by { |p| p.length }
70
+
71
+ @organisation_prefixes[best]
72
+ end
73
+ end
@@ -28,11 +28,6 @@ module MediaTypes
28
28
  with(view: view)
29
29
  end
30
30
 
31
- def suffix(suffix = NO_ARG)
32
- return opts[:suffix] if suffix == NO_ARG
33
- with(suffix: suffix)
34
- end
35
-
36
31
  def collection
37
32
  view(COLLECTION_VIEW)
38
33
  end
@@ -73,20 +68,46 @@ module MediaTypes
73
68
  to_str.split(pattern, *limit)
74
69
  end
75
70
 
71
+ def as_key
72
+ [type, view&.to_s, version]
73
+ end
74
+
76
75
  def hash
77
- to_str.hash
76
+ as_key.hash
77
+ end
78
+
79
+ def override_suffix(suffix)
80
+ with(suffix: suffix)
81
+ end
82
+
83
+ def suffix
84
+ return opts[:suffix] if opts.key?(:suffix)
85
+
86
+ schema = schema_for(self)
87
+ schema.type_attributes.fetch(:suffix, 'json')
78
88
  end
79
89
 
80
90
  def to_str(qualifier = nil)
81
- # TODO: remove warning by slicing out these arguments if they don't appear in the format
82
91
  qualified(
83
92
  qualifier,
84
- Formatter.call(opts)
93
+ __getobj__.media_type_name_for.call(
94
+ type: opts[:type],
95
+ view: opts[:view],
96
+ version: opts[:version],
97
+ suffix: suffix
98
+ )
85
99
  )
86
100
  end
87
101
 
102
+ def available_validations
103
+ return [] if !validatable?
104
+ [self]
105
+ end
106
+
88
107
  def valid?(output, **validation_opts)
89
- __getobj__.valid?(
108
+ raise ArgumentError, "Unable to validate #{to_s} type without a corresponding validation. Please mark objects that should be empty with 'empty'." unless validatable?
109
+
110
+ __getobj__.valid_unsafe?(
90
111
  output,
91
112
  self,
92
113
  **validation_opts
@@ -94,7 +115,9 @@ module MediaTypes
94
115
  end
95
116
 
96
117
  def validate!(output, **validation_opts)
97
- __getobj__.validate!(
118
+ raise ArgumentError, "Unable to validate #{to_s} type without a corresponding validation. Please mark objects that should be empty with 'empty'." unless validatable?
119
+
120
+ __getobj__.validate_unsafe!(
98
121
  output,
99
122
  self,
100
123
  **validation_opts
@@ -102,11 +125,14 @@ module MediaTypes
102
125
  end
103
126
 
104
127
  def validatable?
128
+ return false unless media_type_combinations.include? as_key
129
+
105
130
  __getobj__.validatable?(self)
106
131
  end
107
132
 
108
133
  alias inspect to_str
109
134
  alias to_s to_str
135
+ alias identifier to_str
110
136
 
111
137
  private
112
138
 
@@ -1,43 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'media_types/constructable'
4
- require 'media_types/defaults'
5
- require 'media_types/registrar'
6
4
  require 'media_types/validations'
7
5
 
6
+ require 'media_types/dsl/errors'
7
+
8
8
  module MediaTypes
9
9
  module Dsl
10
10
  def self.included(base)
11
11
  base.extend ClassMethods
12
12
  base.class_eval do
13
13
  class << self
14
+ attr_accessor :media_type_name_for, :media_type_combinations, :media_type_validations, :symbol_keys
15
+
14
16
  private
15
17
 
16
- attr_accessor :media_type_constructable, :symbol_base, :media_type_registrar, :media_type_validations
18
+ attr_accessor :media_type_constructable, :symbol_base, :media_type_registrar
17
19
  end
20
+ base.media_type_combinations = Set.new
18
21
  end
19
22
  end
20
23
 
21
24
  module ClassMethods
22
-
23
25
  def to_constructable
26
+ raise UninitializedConstructable if media_type_constructable.nil?
27
+
24
28
  media_type_constructable.dup.tap do |constructable|
25
29
  constructable.__setobj__(self)
26
30
  end
27
31
  end
28
32
 
29
- def valid?(output, media_type = to_constructable, **opts)
30
- validations.find(String(media_type)).valid?(output, backtrace: ['.'], **opts)
33
+ def symbol_keys?
34
+ if symbol_keys.nil?
35
+ MediaTypes.get_key_expectation(self)
36
+ else
37
+ symbol_keys
38
+ end
39
+ end
40
+
41
+ def string_keys?
42
+ !symbol_keys?
43
+ end
44
+
45
+ def valid?(output, **opts)
46
+ to_constructable.valid?(output, **opts)
47
+ end
48
+
49
+ def valid_unsafe?(output, media_type = to_constructable, **opts)
50
+ opts[:expected_key_type] = string_keys? ? String : Symbol
51
+ validations.find(media_type).valid?(output, backtrace: ['.'], **opts)
52
+ end
53
+
54
+ def validate!(output, **opts)
55
+ assert_sane!
56
+ to_constructable.validate!(output, **opts)
31
57
  end
32
58
 
33
- def validate!(output, media_type = to_constructable, **opts)
34
- validations.find(String(media_type)).validate(output, backtrace: ['.'], **opts)
59
+ def validate_unsafe!(output, media_type = to_constructable, **opts)
60
+ opts[:expected_key_type] = string_keys? ? String : Symbol
61
+ validations.find(media_type).validate(output, backtrace: ['.'], **opts)
35
62
  end
36
63
 
37
64
  def validatable?(media_type = to_constructable)
38
65
  return false unless validations
39
66
 
40
- validations.find(String(media_type), -> { nil })
67
+ resolved = validations.find(media_type, -> { nil })
68
+
69
+ !resolved.nil?
41
70
  end
42
71
 
43
72
  def register
@@ -47,43 +76,95 @@ module MediaTypes
47
76
  end
48
77
  end
49
78
 
50
- private
79
+ def view(v)
80
+ to_constructable.view(v)
81
+ end
51
82
 
52
- def media_type(name, defaults: {})
83
+ def version(v)
84
+ to_constructable.version(v)
85
+ end
53
86
 
54
- unless defined?(:base_format)
55
- define_method(:base_format) do
56
- raise format('Implement the class method "base_format" in %<klass>s', klass: self)
57
- end
87
+ def identifier_format
88
+ self.media_type_name_for = proc do |type:, view:, version:, suffix:|
89
+ yield(type: type, view: view, version: version, suffix: suffix)
58
90
  end
91
+ end
59
92
 
60
- self.media_type_constructable = Constructable.new(self, format: base_format, type: name)
61
- .version(defaults.fetch(:version) { nil })
62
- .suffix(defaults.fetch(:suffix) { nil })
63
- .view(defaults.fetch(:view) { nil })
64
- self
93
+ def identifier
94
+ to_constructable.to_s
65
95
  end
66
96
 
67
- def defaults(&block)
68
- return media_type_constructable unless block_given?
69
- self.media_type_constructable = Defaults.new(to_constructable, &block).to_constructable
97
+ def available_validations
98
+ media_type_combinations.map do |a|
99
+ _, view, version = a
100
+ view(view).version(version)
101
+ end
102
+ end
70
103
 
71
- self
104
+ def schema_for(constructable)
105
+ validations.find(constructable)
72
106
  end
73
107
 
74
- def registrations(symbol = nil, &block)
75
- return media_type_registrar unless block_given?
76
- self.media_type_registrar = Registrar.new(self, symbol: symbol, &block)
108
+ def assert_sane!
109
+ return if media_type_validations.scheme.asserted_sane?
77
110
 
78
- self
111
+ media_type_validations.run_fixture_validations(symbol_keys?)
112
+ end
113
+
114
+ private
115
+
116
+ def use_name(name)
117
+ if media_type_name_for.nil?
118
+ self.media_type_name_for = proc do |type:, view:, version:, suffix:|
119
+ resolved_org = nil
120
+ if defined?(organisation)
121
+ resolved_org = organisation
122
+ else
123
+ resolved_org = MediaTypes.get_organisation(self)
124
+
125
+ if resolved_org.nil?
126
+ raise OrganisationNotSetError,
127
+ format('Implement the class method "organisation" in %<klass>s or specify a global organisation using MediaTypes::set_organisation', klass: self)
128
+ end
129
+ end
130
+ raise ArgumentError, 'Unable to create a name for a schema with a nil name.' if type.nil?
131
+ raise ArgumentError, 'Unable to create a name for a schema with a nil organisation.' if resolved_org.nil?
132
+
133
+ result = "application/vnd.#{resolved_org}.#{type}"
134
+ result += ".v#{version}" unless version.nil?
135
+ result += ".#{view}" unless view.nil?
136
+ result += "+#{suffix}" unless suffix.nil?
137
+ result
138
+ end
139
+ end
140
+ self.media_type_constructable = Constructable.new(self, type: name)
141
+ end
142
+
143
+ def expect_string_keys
144
+ raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
145
+
146
+ self.symbol_keys = false
147
+ end
148
+
149
+ def expect_symbol_keys
150
+ raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
151
+
152
+ self.symbol_keys = true
79
153
  end
80
154
 
81
155
  def validations(&block)
82
- return media_type_validations unless block_given?
156
+ return lookup_validations unless block_given?
157
+
83
158
  self.media_type_validations = Validations.new(to_constructable, &block)
84
159
 
85
160
  self
86
161
  end
162
+
163
+ def lookup_validations
164
+ raise MissingValidationError, "No validations defined for #{name}" if media_type_validations.nil?
165
+
166
+ media_type_validations
167
+ end
87
168
  end
88
169
  end
89
170
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MediaTypes
4
+ module Dsl
5
+ class UninitializedConstructable < RuntimeError
6
+ def message
7
+ 'Unable to generate constructable without a name, make sure to have called `use_name(name)` before.'
8
+ end
9
+ end
10
+
11
+ # Raised when an error occurs during setting expected key type
12
+ class KeyTypeExpectationError < StandardError; end
13
+
14
+ class MissingValidationError < StandardError; end
15
+
16
+ class OrganisationNotSetError < StandardError; end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MediaTypes
4
+ module Errors
5
+ # Raised when trying to set a module key expectation twice
6
+ class KeyExpectationSetError < StandardError
7
+ def initialize(mod:)
8
+ super(format('%<mod>s already has a key expectation set', mod: mod.name))
9
+ end
10
+ end
11
+
12
+ # Raised when trying to set a module key expectation while default expectation already used
13
+ class KeyExpectationUsedError < StandardError
14
+ def initialize(mod:)
15
+ super(format('Unable to change key type expectation for %<mod>s since its current expectation is already used', mod: mod.name))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  require 'media_types/scheme/validation_options'
4
6
  require 'media_types/scheme/enumeration_context'
5
7
  require 'media_types/scheme/errors'
@@ -18,6 +20,48 @@ require 'media_types/scheme/output_type_guard'
18
20
  require 'media_types/scheme/rules_exhausted_guard'
19
21
 
20
22
  module MediaTypes
23
+ class AssertionError < StandardError
24
+ def initialize(errors)
25
+ @fixture_errors = errors
26
+ end
27
+
28
+ def message
29
+ fixture_errors.map(&:message).join(', ')
30
+ end
31
+
32
+ attr_reader :fixture_errors
33
+ end
34
+
35
+ class UnexpectedValidationResultError < StandardError
36
+ def initialize(fixture_caller, error)
37
+ self.fixture_caller = fixture_caller
38
+ self.error = error
39
+ end
40
+
41
+ def message
42
+ format(
43
+ '%<caller_path>s:%<caller_line>s -> %<error>s',
44
+ caller_path: fixture_caller.path,
45
+ caller_line: fixture_caller.lineno,
46
+ error: error.is_a?(MediaTypes::Scheme::ValidationError) ? "#{error.class}:#{error.message}" : error
47
+ )
48
+ end
49
+
50
+ attr_accessor :fixture_caller, :error
51
+ end
52
+
53
+ class FixtureData
54
+ def initialize(caller:, fixture:, expect_to_pass:)
55
+ self.caller = caller
56
+ self.fixture = fixture
57
+ self.expect_to_pass = expect_to_pass
58
+ end
59
+
60
+ attr_accessor :caller, :fixture, :expect_to_pass
61
+
62
+ alias expect_to_pass? expect_to_pass
63
+ end
64
+
21
65
  ##
22
66
  # Media Type Schemes can validate content to a media type, by itself. Used by the `validations` dsl.
23
67
  #
@@ -51,10 +95,18 @@ module MediaTypes
51
95
  #
52
96
  def initialize(allow_empty: false, expected_type: ::Object, &block)
53
97
  self.rules = Rules.new(allow_empty: allow_empty, expected_type: expected_type)
98
+ self.type_attributes = {}
99
+ self.fixtures = []
100
+ self.asserted_sane = false
54
101
 
55
102
  instance_exec(&block) if block_given?
56
103
  end
57
104
 
105
+ attr_accessor :type_attributes, :fixtures
106
+ attr_reader :rules, :asserted_sane
107
+
108
+ alias asserted_sane? asserted_sane
109
+
58
110
  ##
59
111
  # Checks if the +output+ is valid
60
112
  #
@@ -93,6 +145,7 @@ module MediaTypes
93
145
  #
94
146
  def validate(output, options = nil, **opts)
95
147
  options ||= ValidationOptions.new(**opts)
148
+ options.context = output
96
149
 
97
150
  catch(:end) do
98
151
  validate!(output, options, context: nil)
@@ -149,7 +202,11 @@ module MediaTypes
149
202
  # MyMedia.valid?({ foo: { bar: 'my-string' }})
150
203
  # # => true
151
204
  #
152
- def attribute(key, type = ::Object, optional: false, **opts, &block)
205
+ def attribute(key, type = nil, optional: false, **opts, &block)
206
+ raise ConflictingTypeDefinitionError, 'You cannot apply a block to a non-hash typed attribute, either remove the type or the block' if type != ::Hash && block_given? && !type.nil?
207
+
208
+ type ||= ::Object
209
+
153
210
  if block_given?
154
211
  return collection(key, expected_type: ::Hash, optional: optional, **opts, &block)
155
212
  end
@@ -202,6 +259,8 @@ module MediaTypes
202
259
  # # => true
203
260
  #
204
261
  def any(scheme = nil, expected_type: ::Hash, allow_empty: false, &block)
262
+ raise ConflictingTypeDefinitionError, 'You cannot apply a block to a non-hash typed property, either remove the type or the block' if scheme != ::Hash && block_given? && !scheme.nil?
263
+
205
264
  unless block_given?
206
265
  if scheme.is_a?(Scheme)
207
266
  return rules.default = scheme
@@ -289,6 +348,8 @@ module MediaTypes
289
348
  # # => true
290
349
  #
291
350
  def collection(key, scheme = nil, allow_empty: false, expected_type: ::Array, optional: false, &block)
351
+ raise ConflictingTypeDefinitionError, 'You cannot apply a block to a non-hash typed collection, either remove the type or the block' if scheme != ::Hash && block_given? && !scheme.nil?
352
+
292
353
  unless block_given?
293
354
  return rules.add(
294
355
  key,
@@ -346,6 +407,21 @@ module MediaTypes
346
407
  end.link(*args, **opts, &block)
347
408
  end
348
409
 
410
+ ##
411
+ # Mark object as a valid empty object
412
+ #
413
+ # @example Empty object
414
+ #
415
+ # class MyMedia
416
+ # include MediaTypes::Dsl
417
+ #
418
+ # validations do
419
+ # empty
420
+ # end
421
+ # end
422
+ def empty
423
+ end
424
+
349
425
  def inspect(indentation = 0)
350
426
  tabs = ' ' * indentation
351
427
  [
@@ -355,8 +431,83 @@ module MediaTypes
355
431
  ].join("\n")
356
432
  end
357
433
 
434
+ def assert_pass(fixture)
435
+ reduced_stack = remove_current_dir_from_stack(caller_locations)
436
+ @fixtures << FixtureData.new(caller: reduced_stack.first, fixture: fixture, expect_to_pass: true)
437
+ end
438
+
439
+ def assert_fail(fixture)
440
+ reduced_stack = remove_current_dir_from_stack(caller_locations)
441
+ @fixtures << FixtureData.new(caller: reduced_stack.first, fixture: fixture, expect_to_pass: false)
442
+ end
443
+
444
+ # Removes all calls originating in current dir from given stack
445
+ # We need this so that we find out the caller of an assert_pass/fail in the caller_locations
446
+ # Which gets polluted by Scheme consecutively executing blocks within the validation blocks
447
+ def remove_current_dir_from_stack(stack)
448
+ stack.reject { |location| location.path.include?(__dir__) }
449
+ end
450
+
451
+ def validate_scheme_fixtures(expect_symbol_keys, backtrace)
452
+ @fixtures.map do |fixture_data|
453
+ begin
454
+ validate_fixture(fixture_data, expect_symbol_keys, backtrace)
455
+ nil
456
+ rescue UnexpectedValidationResultError => e
457
+ e
458
+ end
459
+ end.compact
460
+ end
461
+
462
+ def validate_nested_scheme_fixtures(expect_symbol_keys, backtrace)
463
+ @rules.flat_map do |key, rule|
464
+ next unless rule.is_a?(Scheme) || rule.is_a?(Links)
465
+
466
+ begin
467
+ rule.run_fixture_validations(expect_symbol_keys, backtrace.dup.append(key))
468
+ nil
469
+ rescue AssertionError => e
470
+ e.fixture_errors
471
+ end
472
+ end.compact
473
+ end
474
+
475
+ def validate_default_scheme_fixtures(expect_symbol_keys, backtrace)
476
+ return [] unless @rules.default.is_a?(Scheme)
477
+
478
+ @rules.default.run_fixture_validations(expect_symbol_keys, backtrace.dup.append('*'))
479
+ []
480
+ rescue AssertionError => e
481
+ e.fixture_errors
482
+ end
483
+
484
+ def run_fixture_validations(expect_symbol_keys, backtrace = [])
485
+ fixture_errors = validate_scheme_fixtures(expect_symbol_keys, backtrace)
486
+ fixture_errors += validate_nested_scheme_fixtures(expect_symbol_keys, backtrace)
487
+ fixture_errors += validate_default_scheme_fixtures(expect_symbol_keys, backtrace)
488
+
489
+ raise AssertionError, fixture_errors unless fixture_errors.empty?
490
+
491
+ self.asserted_sane = true
492
+ end
493
+
494
+ def validate_fixture(fixture_data, expect_symbol_keys, backtrace = [])
495
+ json = JSON.parse(fixture_data.fixture, { symbolize_names: expect_symbol_keys })
496
+ expected_key_type = expect_symbol_keys ? Symbol : String
497
+
498
+ begin
499
+ validate(json, expected_key_type: expected_key_type, backtrace: backtrace)
500
+ unless fixture_data.expect_to_pass?
501
+ raise UnexpectedValidationResultError.new(fixture_data.caller, 'No error encounterd whilst expecting to')
502
+ end
503
+ rescue MediaTypes::Scheme::ValidationError => e
504
+ raise UnexpectedValidationResultError.new(fixture_data.caller, e) if fixture_data.expect_to_pass?
505
+ end
506
+ end
507
+
358
508
  private
359
509
 
360
- attr_accessor :rules
510
+ attr_writer :rules, :asserted_sane
511
+
361
512
  end
362
513
  end