json_schemer 2.1.1 → 2.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1981782f2342c7123beb0374680037bc11f3be72a8e4be0a213cfb7691643415
4
- data.tar.gz: e8b6c605ba1ce2a2751ac90167f53c27fad975140ab9a0e8328c2b04ce8fe732
3
+ metadata.gz: 4013d2026a3ac04ca50649b4cf6437ea2ee477ed1e9d350942cfa7e56ead0828
4
+ data.tar.gz: 810f08388b21cc68d849181a87a820982c2fc7a577c402d5e05699b0c8d07507
5
5
  SHA512:
6
- metadata.gz: 4d4c6e6c3660560a602b5610559efe37b5839f1142af6486e350bd207cad49e37a00f397d555878957b14951da95745092240bd14417e2ffd02826f053099952
7
- data.tar.gz: 8dc561414463ceeacf7bbd0be95be9c441a50919cdb839ff8366fa0268d96d91e4947985bd7bab406f3c163e913bf7ca923a38e22d777d2549af3329f91c14f2
6
+ metadata.gz: f4c5bb6fe5e6d4e0ccfd8abd28a0c12ae23254e74c807ba94278680bf7b706970276cef14f26eaf488f9c46ebd8d418b5aef90bf4abd886b9fd1d7513bb6d27a
7
+ data.tar.gz: 19a0c839189ba44d6688c389fa38b3f0b1ccc9986643471e2df4d50c347c5ce22ec3fa170002fc000fc877e05080842b57cc01351f46ff1e876ba972880e6584
@@ -6,7 +6,7 @@ jobs:
6
6
  fail-fast: false
7
7
  matrix:
8
8
  os: [ubuntu-latest, windows-latest, macos-latest]
9
- ruby: [2.5, 2.6, 2.7, 3.0, 3.1, 3.2, head, jruby, jruby-head, truffleruby, truffleruby-head]
9
+ ruby: [2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, head, jruby, jruby-head, truffleruby, truffleruby-head]
10
10
  exclude:
11
11
  - os: windows-latest
12
12
  ruby: truffleruby
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.0] - XXXX-XX-XX
4
+
5
+ ## Bug Fixes
6
+
7
+ - Support symbol keys when accessing original instance: https://github.com/davishmcclurg/json_schemer/commit/d52c130e9967919c6cf1c9dbc3f0babfb8b01cf8
8
+ - Support custom keywords in nested schemas: https://github.com/davishmcclurg/json_schemer/commit/93c85a5006981347c7e9a4c11b73c6bdb65d8ba2
9
+ - Stringify instance location for custom keywords: https://github.com/davishmcclurg/json_schemer/commit/513c99130b9e7986b09881e7efd3fb7143744754
10
+ - Reduce unhelpful error output in `unevaluated` keywords: https://github.com/davishmcclurg/json_schemer/pull/164
11
+ - Handle parse errors during schema validation: https://github.com/davishmcclurg/json_schemer/pull/171
12
+ - Follow refs when finding default property values: https://github.com/davishmcclurg/json_schemer/pull/175
13
+
14
+ ## Features
15
+
16
+ - Global configuration with `Configuration` object: https://github.com/davishmcclurg/json_schemer/pull/170
17
+ - Symbol key property defaults with `insert_property_defaults: :symbol`: https://github.com/davishmcclurg/json_schemer/commit/a72473dc84199107ddedc8998950e5b82273232a
18
+ - Consistent schema type support for schema validation methods: https://github.com/davishmcclurg/json_schemer/commit/bbcd0cea20cbaa61cf2bdae5f53840861cae54b8
19
+ - Validation option support for schema validation methods: https://github.com/davishmcclurg/json_schemer/commit/2eeef77de522f127619b7d0faa51e0d7e40977ad
20
+
21
+ [2.2.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.2.0
22
+
3
23
  ## [2.1.1] - 2023-11-28
4
24
 
5
25
  ### Bug Fixes
data/Gemfile.lock CHANGED
@@ -1,7 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- json_schemer (2.1.1)
4
+ json_schemer (2.2.0)
5
+ base64
6
+ bigdecimal
5
7
  hana (~> 1.3)
6
8
  regexp_parser (~> 2.0)
7
9
  simpleidn (~> 0.2)
@@ -9,7 +11,11 @@ PATH
9
11
  GEM
10
12
  remote: https://rubygems.org/
11
13
  specs:
14
+ base64 (0.2.0)
15
+ bigdecimal (3.1.6)
16
+ bigdecimal (3.1.6-java)
12
17
  concurrent-ruby (1.2.2)
18
+ csv (3.2.8)
13
19
  docile (1.4.0)
14
20
  hana (1.3.7)
15
21
  i18n (1.14.1)
@@ -17,8 +23,8 @@ GEM
17
23
  i18n-debug (1.2.0)
18
24
  i18n (< 2)
19
25
  minitest (5.15.0)
20
- rake (13.0.6)
21
- regexp_parser (2.8.2)
26
+ rake (13.1.0)
27
+ regexp_parser (2.9.0)
22
28
  simplecov (0.22.0)
23
29
  docile (~> 1.1)
24
30
  simplecov-html (~> 0.11)
@@ -38,6 +44,7 @@ PLATFORMS
38
44
 
39
45
  DEPENDENCIES
40
46
  bundler (~> 2.0)
47
+ csv
41
48
  i18n
42
49
  i18n-debug
43
50
  json_schemer!
@@ -46,4 +53,4 @@ DEPENDENCIES
46
53
  simplecov (~> 0.22)
47
54
 
48
55
  BUNDLED WITH
49
- 2.3.25
56
+ 2.3.27
data/README.md CHANGED
@@ -212,7 +212,8 @@ JSONSchemer.schema(
212
212
  },
213
213
 
214
214
  # insert default property values during validation
215
- # true/false
215
+ # string keys by default (use `:symbol` to insert symbol keys)
216
+ # true/false/:symbol
216
217
  # default: false
217
218
  insert_property_defaults: true,
218
219
 
@@ -256,6 +257,20 @@ JSONSchemer.schema(
256
257
  )
257
258
  ```
258
259
 
260
+ ## Global Configuration
261
+
262
+ Configuration options can be set globally by modifying `JSONSchemer.configuration`. Global options are applied to any new schemas at creation time (global configuration changes are not reflected in existing schemas). They can be overridden with the regular keyword arguments described [above](#options).
263
+
264
+ ```ruby
265
+ # configuration block
266
+ JSONSchemer.configure do |config|
267
+ config.regexp_resolver = 'ecma'
268
+ end
269
+
270
+ # configuration accessors
271
+ JSONSchemer.configuration.insert_property_defaults = true
272
+ ```
273
+
259
274
  ## Custom Error Messages
260
275
 
261
276
  Error messages can be customized using the `x-error` keyword and/or [I18n](https://github.com/ruby-i18n/i18n) translations. `x-error` takes precedence if both are defined.
data/json_schemer.gemspec CHANGED
@@ -26,9 +26,12 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "rake", "~> 13.0"
27
27
  spec.add_development_dependency "minitest", "~> 5.0"
28
28
  spec.add_development_dependency "simplecov", "~> 0.22"
29
+ spec.add_development_dependency "csv"
29
30
  spec.add_development_dependency "i18n"
30
31
  spec.add_development_dependency "i18n-debug"
31
32
 
33
+ spec.add_runtime_dependency "base64"
34
+ spec.add_runtime_dependency "bigdecimal"
32
35
  spec.add_runtime_dependency "hana", "~> 1.3"
33
36
  spec.add_runtime_dependency "regexp_parser", "~> 2.0"
34
37
  spec.add_runtime_dependency "simpleidn", "~> 0.2"
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSchemer
4
+ Configuration = Struct.new(
5
+ :base_uri, :meta_schema, :vocabulary, :format, :formats, :content_encodings, :content_media_types, :keywords,
6
+ :before_property_validation, :after_property_validation, :insert_property_defaults, :property_default_resolver,
7
+ :ref_resolver, :regexp_resolver, :output_format, :resolve_enumerators, :access_mode,
8
+ keyword_init: true
9
+ ) do
10
+ def initialize(
11
+ base_uri: URI('json-schemer://schema'),
12
+ meta_schema: Draft202012::BASE_URI.to_s,
13
+ vocabulary: nil,
14
+ format: true,
15
+ formats: {},
16
+ content_encodings: {},
17
+ content_media_types: {},
18
+ keywords: {},
19
+ before_property_validation: [],
20
+ after_property_validation: [],
21
+ insert_property_defaults: false,
22
+ property_default_resolver: nil,
23
+ ref_resolver: proc { |uri| raise UnknownRef, uri.to_s },
24
+ regexp_resolver: 'ruby',
25
+ output_format: 'classic',
26
+ resolve_enumerators: false,
27
+ access_mode: nil
28
+ )
29
+ super
30
+ end
31
+ end
32
+ end
@@ -300,7 +300,7 @@ module JSONSchemer
300
300
  end
301
301
 
302
302
  def false_schema_error(formatted_instance_location:, **)
303
- "object property at #{formatted_instance_location} is not defined and schema does not allow additional properties"
303
+ "object property at #{formatted_instance_location} is a disallowed additional property"
304
304
  end
305
305
 
306
306
  def parse
@@ -8,6 +8,10 @@ module JSONSchemer
8
8
  "array items at #{formatted_instance_location} do not match `unevaluatedItems` schema"
9
9
  end
10
10
 
11
+ def false_schema_error(formatted_instance_location:, **)
12
+ "array item at #{formatted_instance_location} is a disallowed unevaluated item"
13
+ end
14
+
11
15
  def parse
12
16
  subschema(value)
13
17
  end
@@ -18,7 +22,7 @@ module JSONSchemer
18
22
  unevaluated_items = instance.size.times.to_set
19
23
 
20
24
  context.adjacent_results.each_value do |adjacent_result|
21
- collect_unevaluated_items(adjacent_result, instance_location, unevaluated_items)
25
+ collect_unevaluated_items(adjacent_result, unevaluated_items)
22
26
  end
23
27
 
24
28
  nested = unevaluated_items.map do |index|
@@ -30,8 +34,7 @@ module JSONSchemer
30
34
 
31
35
  private
32
36
 
33
- def collect_unevaluated_items(result, instance_location, unevaluated_items)
34
- return unless result.valid && result.instance_location == instance_location
37
+ def collect_unevaluated_items(result, unevaluated_items)
35
38
  case result.source
36
39
  when Applicator::PrefixItems
37
40
  unevaluated_items.subtract(0..result.annotation)
@@ -41,7 +44,9 @@ module JSONSchemer
41
44
  unevaluated_items.subtract(result.annotation)
42
45
  end
43
46
  result.nested&.each do |subresult|
44
- collect_unevaluated_items(subresult, instance_location, unevaluated_items)
47
+ if subresult.valid && subresult.instance_location == result.instance_location
48
+ collect_unevaluated_items(subresult, unevaluated_items)
49
+ end
45
50
  end
46
51
  end
47
52
  end
@@ -51,6 +56,10 @@ module JSONSchemer
51
56
  "object properties at #{formatted_instance_location} do not match `unevaluatedProperties` schema"
52
57
  end
53
58
 
59
+ def false_schema_error(formatted_instance_location:, **)
60
+ "object property at #{formatted_instance_location} is a disallowed unevaluated property"
61
+ end
62
+
54
63
  def parse
55
64
  subschema(value)
56
65
  end
@@ -61,7 +70,7 @@ module JSONSchemer
61
70
  evaluated_keys = Set[]
62
71
 
63
72
  context.adjacent_results.each_value do |adjacent_result|
64
- collect_evaluated_keys(adjacent_result, instance_location, evaluated_keys)
73
+ collect_evaluated_keys(adjacent_result, evaluated_keys)
65
74
  end
66
75
 
67
76
  evaluated = instance.reject do |key, _value|
@@ -77,14 +86,15 @@ module JSONSchemer
77
86
 
78
87
  private
79
88
 
80
- def collect_evaluated_keys(result, instance_location, evaluated_keys)
81
- return unless result.valid && result.instance_location == instance_location
89
+ def collect_evaluated_keys(result, evaluated_keys)
82
90
  case result.source
83
91
  when Applicator::Properties, Applicator::PatternProperties, Applicator::AdditionalProperties, UnevaluatedProperties
84
92
  evaluated_keys.merge(result.annotation)
85
93
  end
86
94
  result.nested&.each do |subresult|
87
- collect_evaluated_keys(subresult, instance_location, evaluated_keys)
95
+ if subresult.valid && subresult.instance_location == result.instance_location
96
+ collect_evaluated_keys(subresult, evaluated_keys)
97
+ end
88
98
  end
89
99
  end
90
100
  end
@@ -76,8 +76,8 @@ module JSONSchemer
76
76
  IRI_ESCAPE_REGEX = /[^[:ascii:]]/
77
77
  UUID_REGEX = /\A\h{8}-\h{4}-\h{4}-[89AB]\h{3}-\h{12}\z/i
78
78
  NIL_UUID = '00000000-0000-0000-0000-000000000000'
79
- ASCII_8BIT_TO_PERCENT_ENCODED = 256.times.each_with_object({}) do |byte, out|
80
- out[-byte.chr] = -sprintf('%%%02X', byte)
79
+ BINARY_TO_PERCENT_ENCODED = 256.times.each_with_object({}) do |byte, out|
80
+ out[-byte.chr(Encoding::BINARY)] = -sprintf('%%%02X', byte)
81
81
  end.freeze
82
82
 
83
83
  class << self
@@ -88,10 +88,9 @@ module JSONSchemer
88
88
  include URITemplate
89
89
 
90
90
  def percent_encode(data, regexp)
91
- data = data.dup
92
- data.force_encoding(Encoding::ASCII_8BIT)
93
- data.gsub!(regexp, ASCII_8BIT_TO_PERCENT_ENCODED)
94
- data.force_encoding(Encoding::US_ASCII)
91
+ binary = data.b
92
+ binary.gsub!(regexp, BINARY_TO_PERCENT_ENCODED)
93
+ binary.force_encoding(data.encoding)
95
94
  end
96
95
 
97
96
  def valid_date_time?(data)
@@ -45,8 +45,11 @@ module JSONSchemer
45
45
  end
46
46
 
47
47
  def subschema(value, keyword = nil, **options)
48
+ options[:configuration] ||= schema.configuration
48
49
  options[:base_uri] ||= schema.base_uri
49
50
  options[:meta_schema] ||= schema.meta_schema
51
+ options[:ref_resolver] ||= schema.ref_resolver
52
+ options[:regexp_resolver] ||= schema.regexp_resolver
50
53
  Schema.new(value, self, root, keyword, **options)
51
54
  end
52
55
  end
@@ -8,16 +8,14 @@ module JSONSchemer
8
8
  case version
9
9
  when /\A3\.1\.\d+\z/
10
10
  @document_schema = JSONSchemer.openapi31_document
11
- json_schema_dialect = document.fetch('jsonSchemaDialect') { OpenAPI31::BASE_URI.to_s }
11
+ meta_schema = document.fetch('jsonSchemaDialect') { OpenAPI31::BASE_URI.to_s }
12
12
  when /\A3\.0\.\d+\z/
13
13
  @document_schema = JSONSchemer.openapi30_document
14
- json_schema_dialect = OpenAPI30::BASE_URI.to_s
14
+ meta_schema = OpenAPI30::BASE_URI.to_s
15
15
  else
16
16
  raise UnsupportedOpenAPIVersion, version
17
17
  end
18
18
 
19
- meta_schema = META_SCHEMAS_BY_BASE_URI_STR[json_schema_dialect] || raise(UnsupportedMetaSchema, json_schema_dialect)
20
-
21
19
  @schema = JSONSchemer.schema(@document, :meta_schema => meta_schema, **options)
22
20
  end
23
21
 
@@ -5,7 +5,7 @@ module JSONSchemer
5
5
  # https://spec.openapis.org/oas/v3.0.3#data-types
6
6
  FORMATS = OpenAPI31::FORMATS.merge(
7
7
  'byte' => proc { |instance, _value| ContentEncoding::BASE64.call(instance).first },
8
- 'binary' => proc { |instance, _value| instance.is_a?(String) && instance.encoding == Encoding::ASCII_8BIT },
8
+ 'binary' => proc { |instance, _value| instance.is_a?(String) && instance.encoding == Encoding::BINARY },
9
9
  'date' => Format::DATE
10
10
  )
11
11
  SCHEMA = {
@@ -201,8 +201,8 @@ module JSONSchemer
201
201
 
202
202
  if result.source.is_a?(Schema::PROPERTIES_KEYWORD_CLASS) && result.instance.is_a?(Hash)
203
203
  result.source.parsed.each do |property, schema|
204
- next if result.instance.key?(property) || !schema.parsed.key?('default')
205
- default = schema.parsed.fetch('default')
204
+ next if result.instance.key?(property)
205
+ next unless default = default_keyword_instance(schema)
206
206
  instance_location = Location.join(result.instance_location, property)
207
207
  keyword_location = Location.join(Location.join(result.keyword_location, property), default.keyword)
208
208
  default_result = default.validate(nil, instance_location, keyword_location, nil)
@@ -225,5 +225,17 @@ module JSONSchemer
225
225
 
226
226
  inserted
227
227
  end
228
+
229
+ private
230
+
231
+ def default_keyword_instance(schema)
232
+ schema.parsed.fetch('default') do
233
+ schema.parsed.find do |_keyword, keyword_instance|
234
+ next unless keyword_instance.respond_to?(:ref_schema)
235
+ next unless default = default_keyword_instance(keyword_instance.ref_schema)
236
+ break default
237
+ end
238
+ end
239
+ end
228
240
  end
229
241
  end
@@ -4,28 +4,26 @@ module JSONSchemer
4
4
  Context = Struct.new(:instance, :dynamic_scope, :adjacent_results, :short_circuit, :access_mode) do
5
5
  def original_instance(instance_location)
6
6
  Hana::Pointer.parse(Location.resolve(instance_location)).reduce(instance) do |obj, token|
7
- obj.fetch(obj.is_a?(Array) ? token.to_i : token)
7
+ if obj.is_a?(Array)
8
+ obj.fetch(token.to_i)
9
+ elsif !obj.key?(token) && obj.key?(token.to_sym)
10
+ obj.fetch(token.to_sym)
11
+ else
12
+ obj.fetch(token)
13
+ end
8
14
  end
9
15
  end
10
16
  end
11
17
 
12
18
  include Output
13
19
 
14
- DEFAULT_SCHEMA = Draft202012::BASE_URI.to_s.freeze
15
20
  SCHEMA_KEYWORD_CLASS = Draft202012::Vocab::Core::Schema
16
21
  VOCABULARY_KEYWORD_CLASS = Draft202012::Vocab::Core::Vocabulary
17
22
  ID_KEYWORD_CLASS = Draft202012::Vocab::Core::Id
18
23
  UNKNOWN_KEYWORD_CLASS = Draft202012::Vocab::Core::UnknownKeyword
19
24
  NOT_KEYWORD_CLASS = Draft202012::Vocab::Applicator::Not
20
25
  PROPERTIES_KEYWORD_CLASS = Draft202012::Vocab::Applicator::Properties
21
- DEFAULT_BASE_URI = URI('json-schemer://schema').freeze
22
- DEFAULT_FORMATS = {}.freeze
23
- DEFAULT_CONTENT_ENCODINGS = {}.freeze
24
- DEFAULT_CONTENT_MEDIA_TYPES = {}.freeze
25
- DEFAULT_KEYWORDS = {}.freeze
26
- DEFAULT_BEFORE_PROPERTY_VALIDATION = [].freeze
27
- DEFAULT_AFTER_PROPERTY_VALIDATION = [].freeze
28
- DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }
26
+
29
27
  NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
30
28
  RUBY_REGEXP_RESOLVER = proc { |pattern| Regexp.new(pattern) }
31
29
  ECMA_REGEXP_RESOLVER = proc { |pattern| Regexp.new(EcmaRegexp.ruby_equivalent(pattern)) }
@@ -40,39 +38,44 @@ module JSONSchemer
40
38
  false
41
39
  end
42
40
  end
41
+ SYMBOL_PROPERTY_DEFAULT_RESOLVER = proc do |instance, property, results_with_tree_validity|
42
+ DEFAULT_PROPERTY_DEFAULT_RESOLVER.call(instance, property.to_sym, results_with_tree_validity)
43
+ end
43
44
 
44
45
  attr_accessor :base_uri, :meta_schema, :keywords, :keyword_order
45
- attr_reader :value, :parent, :root, :parsed
46
- attr_reader :vocabulary, :format, :formats, :content_encodings, :content_media_types, :custom_keywords, :before_property_validation, :after_property_validation, :insert_property_defaults, :property_default_resolver
46
+ attr_reader :value, :parent, :root, :configuration, :parsed
47
+ attr_reader :vocabulary, :format, :formats, :content_encodings, :content_media_types, :custom_keywords, :before_property_validation, :after_property_validation, :insert_property_defaults
47
48
 
48
49
  def initialize(
49
50
  value,
50
51
  parent = nil,
51
52
  root = self,
52
53
  keyword = nil,
53
- base_uri: DEFAULT_BASE_URI,
54
- meta_schema: nil,
55
- vocabulary: nil,
56
- format: true,
57
- formats: DEFAULT_FORMATS,
58
- content_encodings: DEFAULT_CONTENT_ENCODINGS,
59
- content_media_types: DEFAULT_CONTENT_MEDIA_TYPES,
60
- keywords: DEFAULT_KEYWORDS,
61
- before_property_validation: DEFAULT_BEFORE_PROPERTY_VALIDATION,
62
- after_property_validation: DEFAULT_AFTER_PROPERTY_VALIDATION,
63
- insert_property_defaults: false,
64
- property_default_resolver: DEFAULT_PROPERTY_DEFAULT_RESOLVER,
65
- ref_resolver: DEFAULT_REF_RESOLVER,
66
- regexp_resolver: 'ruby',
67
- output_format: 'classic',
68
- resolve_enumerators: false,
69
- access_mode: nil
54
+ configuration: JSONSchemer.configuration,
55
+ base_uri: configuration.base_uri,
56
+ meta_schema: configuration.meta_schema,
57
+ vocabulary: configuration.vocabulary,
58
+ format: configuration.format,
59
+ formats: configuration.formats,
60
+ content_encodings: configuration.content_encodings,
61
+ content_media_types: configuration.content_media_types,
62
+ keywords: configuration.keywords,
63
+ before_property_validation: configuration.before_property_validation,
64
+ after_property_validation: configuration.after_property_validation,
65
+ insert_property_defaults: configuration.insert_property_defaults,
66
+ property_default_resolver: configuration.property_default_resolver,
67
+ ref_resolver: configuration.ref_resolver,
68
+ regexp_resolver: configuration.regexp_resolver,
69
+ output_format: configuration.output_format,
70
+ resolve_enumerators: configuration.resolve_enumerators,
71
+ access_mode: configuration.access_mode
70
72
  )
71
73
  @value = deep_stringify_keys(value)
72
74
  @parent = parent
73
75
  @root = root
74
76
  @keyword = keyword
75
77
  @schema = self
78
+ @configuration = configuration
76
79
  @base_uri = base_uri
77
80
  @meta_schema = meta_schema
78
81
  @vocabulary = vocabulary
@@ -109,12 +112,12 @@ module JSONSchemer
109
112
  output
110
113
  end
111
114
 
112
- def valid_schema?
113
- meta_schema.valid?(value)
115
+ def valid_schema?(**options)
116
+ meta_schema.valid?(value, **options)
114
117
  end
115
118
 
116
- def validate_schema
117
- meta_schema.validate(value)
119
+ def validate_schema(**options)
120
+ meta_schema.validate(value, **options)
118
121
  end
119
122
 
120
123
  def ref(value)
@@ -142,10 +145,11 @@ module JSONSchemer
142
145
  adjacent_results[keyword_instance.class] = keyword_result
143
146
  end
144
147
 
145
- if custom_keywords.any?
146
- custom_keywords.each do |custom_keyword, callable|
148
+ if root.custom_keywords.any?
149
+ resolved_instance_location = Location.resolve(instance_location)
150
+ root.custom_keywords.each do |custom_keyword, callable|
147
151
  if value.key?(custom_keyword)
148
- [*callable.call(instance, value, instance_location)].each do |custom_keyword_result|
152
+ [*callable.call(instance, value, resolved_instance_location)].each do |custom_keyword_result|
149
153
  custom_keyword_valid = custom_keyword_result == true
150
154
  valid &&= custom_keyword_valid
151
155
  type = custom_keyword_result.is_a?(String) ? custom_keyword_result : custom_keyword
@@ -184,16 +188,9 @@ module JSONSchemer
184
188
  uri.fragment = nil
185
189
  remote_schema = JSONSchemer.schema(
186
190
  ref_resolver.call(uri) || raise(InvalidRefResolution, uri.to_s),
191
+ :configuration => configuration,
187
192
  :base_uri => uri,
188
193
  :meta_schema => meta_schema,
189
- :format => format,
190
- :formats => formats,
191
- :content_encodings => content_encodings,
192
- :content_media_types => content_media_types,
193
- :keywords => custom_keywords,
194
- :before_property_validation => before_property_validation,
195
- :after_property_validation => after_property_validation,
196
- :property_default_resolver => property_default_resolver,
197
194
  :ref_resolver => ref_resolver,
198
195
  :regexp_resolver => regexp_resolver
199
196
  )
@@ -342,6 +339,21 @@ module JSONSchemer
342
339
  end
343
340
  end
344
341
 
342
+ def ref_resolver
343
+ @ref_resolver ||= @original_ref_resolver == 'net/http' ? CachedResolver.new(&NET_HTTP_REF_RESOLVER) : @original_ref_resolver
344
+ end
345
+
346
+ def regexp_resolver
347
+ @regexp_resolver ||= case @original_regexp_resolver
348
+ when 'ecma'
349
+ CachedResolver.new(&ECMA_REGEXP_RESOLVER)
350
+ when 'ruby'
351
+ CachedResolver.new(&RUBY_REGEXP_RESOLVER)
352
+ else
353
+ @original_regexp_resolver
354
+ end
355
+ end
356
+
345
357
  def inspect
346
358
  "#<#{self.class.name} @value=#{@value.inspect} @parent=#{@parent.inspect} @keyword=#{@keyword.inspect}>"
347
359
  end
@@ -353,8 +365,8 @@ module JSONSchemer
353
365
 
354
366
  if value.is_a?(Hash) && value.key?('$schema')
355
367
  @parsed['$schema'] = SCHEMA_KEYWORD_CLASS.new(value.fetch('$schema'), self, '$schema')
356
- elsif root == self && !meta_schema
357
- SCHEMA_KEYWORD_CLASS.new(DEFAULT_SCHEMA, self, '$schema')
368
+ elsif meta_schema.is_a?(String)
369
+ SCHEMA_KEYWORD_CLASS.new(meta_schema, self, '$schema')
358
370
  end
359
371
 
360
372
  if value.is_a?(Hash) && value.key?('$vocabulary')
@@ -394,19 +406,8 @@ module JSONSchemer
394
406
  @root_keyword_location ||= Location.root
395
407
  end
396
408
 
397
- def ref_resolver
398
- @ref_resolver ||= @original_ref_resolver == 'net/http' ? CachedResolver.new(&NET_HTTP_REF_RESOLVER) : @original_ref_resolver
399
- end
400
-
401
- def regexp_resolver
402
- @regexp_resolver ||= case @original_regexp_resolver
403
- when 'ecma'
404
- CachedResolver.new(&ECMA_REGEXP_RESOLVER)
405
- when 'ruby'
406
- CachedResolver.new(&RUBY_REGEXP_RESOLVER)
407
- else
408
- @original_regexp_resolver
409
- end
409
+ def property_default_resolver
410
+ @property_default_resolver ||= insert_property_defaults == :symbol ? SYMBOL_PROPERTY_DEFAULT_RESOLVER : DEFAULT_PROPERTY_DEFAULT_RESOLVER
410
411
  end
411
412
 
412
413
  def resolve_enumerators!(output)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module JSONSchemer
3
- VERSION = '2.1.1'
3
+ VERSION = '2.2.0'
4
4
  end
data/lib/json_schemer.rb CHANGED
@@ -59,10 +59,10 @@ require 'json_schemer/openapi30/meta'
59
59
  require 'json_schemer/openapi30/vocab/base'
60
60
  require 'json_schemer/openapi30/vocab'
61
61
  require 'json_schemer/openapi'
62
+ require 'json_schemer/configuration'
62
63
  require 'json_schemer/schema'
63
64
 
64
65
  module JSONSchemer
65
- class UnsupportedMetaSchema < StandardError; end
66
66
  class UnsupportedOpenAPIVersion < StandardError; end
67
67
  class UnknownRef < StandardError; end
68
68
  class UnknownFormat < StandardError; end
@@ -113,33 +113,19 @@ module JSONSchemer
113
113
  end
114
114
 
115
115
  class << self
116
- def schema(schema, meta_schema: draft202012, **options)
117
- case schema
118
- when String
119
- schema = JSON.parse(schema)
120
- when Pathname
121
- base_uri = URI.parse(File.join('file:', URI::DEFAULT_PARSER.escape(schema.realpath.to_s)))
122
- options[:base_uri] = base_uri
123
- schema = if options.key?(:ref_resolver)
124
- FILE_URI_REF_RESOLVER.call(base_uri)
125
- else
126
- ref_resolver = CachedResolver.new(&FILE_URI_REF_RESOLVER)
127
- options[:ref_resolver] = ref_resolver
128
- ref_resolver.call(base_uri)
129
- end
130
- end
131
- unless meta_schema.is_a?(Schema)
132
- meta_schema = META_SCHEMAS_BY_BASE_URI_STR[meta_schema] || raise(UnsupportedMetaSchema, meta_schema)
133
- end
134
- Schema.new(schema, :meta_schema => meta_schema, **options)
116
+ def schema(schema, **options)
117
+ schema = resolve(schema, options)
118
+ Schema.new(schema, **options)
135
119
  end
136
120
 
137
121
  def valid_schema?(schema, **options)
138
- schema(schema, **options).valid_schema?
122
+ schema = resolve(schema, options)
123
+ meta_schema(schema, options).valid?(schema, **options.slice(:output_format, :resolve_enumerators, :access_mode))
139
124
  end
140
125
 
141
126
  def validate_schema(schema, **options)
142
- schema(schema, **options).validate_schema
127
+ schema = resolve(schema, options)
128
+ meta_schema(schema, options).validate(schema, **options.slice(:output_format, :resolve_enumerators, :access_mode))
143
129
  end
144
130
 
145
131
  def draft202012
@@ -245,6 +231,44 @@ module JSONSchemer
245
231
  def openapi(document, **options)
246
232
  OpenAPI.new(document, **options)
247
233
  end
234
+
235
+ def configuration
236
+ @configuration ||= Configuration.new
237
+ end
238
+
239
+ def configure
240
+ yield configuration
241
+ end
242
+
243
+ private
244
+
245
+ def resolve(schema, options)
246
+ case schema
247
+ when String
248
+ JSON.parse(schema)
249
+ when Pathname
250
+ base_uri = URI.parse(File.join('file:', URI::DEFAULT_PARSER.escape(schema.realpath.to_s)))
251
+ options[:base_uri] = base_uri
252
+ if options.key?(:ref_resolver)
253
+ FILE_URI_REF_RESOLVER.call(base_uri)
254
+ else
255
+ ref_resolver = CachedResolver.new(&FILE_URI_REF_RESOLVER)
256
+ options[:ref_resolver] = ref_resolver
257
+ ref_resolver.call(base_uri)
258
+ end
259
+ else
260
+ schema
261
+ end
262
+ end
263
+
264
+ def meta_schema(schema, options)
265
+ parseable_schema = {}
266
+ if schema.is_a?(Hash)
267
+ meta_schema = schema['$schema'] || schema[:'$schema']
268
+ parseable_schema['$schema'] = meta_schema if meta_schema.is_a?(String)
269
+ end
270
+ schema(parseable_schema, **options).meta_schema
271
+ end
248
272
  end
249
273
 
250
274
  META_SCHEMA_CALLABLES_BY_BASE_URI_STR = {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_schemer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Harsha
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-28 00:00:00.000000000 Z
11
+ date: 2024-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.22'
69
+ - !ruby/object:Gem::Dependency
70
+ name: csv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: i18n
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,34 @@ dependencies:
94
108
  - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: base64
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: bigdecimal
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  - !ruby/object:Gem::Dependency
98
140
  name: hana
99
141
  requirement: !ruby/object:Gem::Requirement
@@ -160,6 +202,7 @@ files:
160
202
  - json_schemer.gemspec
161
203
  - lib/json_schemer.rb
162
204
  - lib/json_schemer/cached_resolver.rb
205
+ - lib/json_schemer/configuration.rb
163
206
  - lib/json_schemer/content.rb
164
207
  - lib/json_schemer/draft201909/meta.rb
165
208
  - lib/json_schemer/draft201909/vocab.rb
@@ -225,7 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
268
  - !ruby/object:Gem::Version
226
269
  version: '0'
227
270
  requirements: []
228
- rubygems_version: 3.4.10
271
+ rubygems_version: 3.5.3
229
272
  signing_key:
230
273
  specification_version: 4
231
274
  summary: JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI
OSZAR »