schemurai 2.2.2 → 2.3.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
  require "base64"
4
4
  require "json"
5
+ require "set" # rubocop:disable Lint/RedundantRequireStatement -- Required on Ruby 3.4.
5
6
  require_relative "../evaluation"
6
7
  require_relative "../error_message"
7
8
  require_relative "../unique_items"
@@ -13,8 +14,10 @@ module Schemurai
13
14
  attr_reader :evaluated_properties, :evaluated_items
14
15
 
15
16
  def initialize
16
- @evaluated_properties = []
17
- @evaluated_items = []
17
+ # Applicators can overlap. Array membership makes collecting and merging
18
+ # their annotations quadratic in the number of instance locations.
19
+ @evaluated_properties = Set.new
20
+ @evaluated_items = Set.new
18
21
  end
19
22
 
20
23
  def valid? = true
@@ -26,26 +29,24 @@ module Schemurai
26
29
  end
27
30
 
28
31
  def record_property(name)
29
- @evaluated_properties << name unless @evaluated_properties.include?(name)
32
+ @evaluated_properties.add(name)
30
33
  self
31
34
  end
32
35
 
33
36
  def record_item(index)
34
- @evaluated_items << index unless @evaluated_items.include?(index)
37
+ @evaluated_items.add(index)
35
38
  self
36
39
  end
37
40
 
38
41
  def merge(other)
39
42
  return Evaluation.invalid unless other.valid?
40
43
 
41
- merge_locations(@evaluated_properties, other.evaluated_properties)
42
- merge_locations(@evaluated_items, other.evaluated_items)
44
+ properties = other.evaluated_properties
45
+ items = other.evaluated_items
46
+ @evaluated_properties.merge(properties) unless properties.empty?
47
+ @evaluated_items.merge(items) unless items.empty?
43
48
  self
44
49
  end
45
-
46
- private def merge_locations(target, locations)
47
- locations.each { |location| target << location unless target.include?(location) }
48
- end
49
50
  end
50
51
  private_constant :EvaluationBuffer
51
52
 
@@ -652,7 +653,7 @@ module Schemurai
652
653
  matched = nil
653
654
  index = 0
654
655
  while index < value.length
655
- (matched ||= []) << index if trial_at(contains, value[index], index, "contains").valid?
656
+ (matched ||= []) << index if trial(contains, value[index]).valid?
656
657
  index += 1
657
658
  end
658
659
  matched_count = matched ? matched.length : 0
@@ -873,7 +874,7 @@ module Schemurai
873
874
  matches = 0
874
875
  index = 0
875
876
  while index < operand.length
876
- result = trial_at(operand[index], value, MISSING_SEGMENT, "anyOf", index)
877
+ result = trial(operand[index], value)
877
878
  if result.valid?
878
879
  matches += 1
879
880
  evaluation = evaluation.merge(result)
@@ -886,7 +887,7 @@ module Schemurai
886
887
  matched_evaluation = nil
887
888
  index = 0
888
889
  while index < operand.length
889
- result = trial_at(operand[index], value, MISSING_SEGMENT, "oneOf", index)
890
+ result = trial(operand[index], value)
890
891
  if result.valid?
891
892
  matches += 1
892
893
  matched_evaluation = result
@@ -899,9 +900,9 @@ module Schemurai
899
900
  add_error("oneOf") { Internal::ErrorMessage.one_of(matches) }
900
901
  end
901
902
  when :not
902
- add_error("not") { Internal::ErrorMessage.not } if trial_at(operand, value, MISSING_SEGMENT, "not").valid?
903
+ add_error("not") { Internal::ErrorMessage.not } if trial(operand, value).valid?
903
904
  when :conditional
904
- condition = trial_at(operand.condition, value, MISSING_SEGMENT, "if")
905
+ condition = trial(operand.condition, value)
905
906
  condition_valid = condition.valid?
906
907
  evaluation = evaluation.merge(condition) if condition_valid
907
908
  if condition_valid && operand.then_branch
@@ -916,12 +917,24 @@ module Schemurai
916
917
  private def trial(program, value)
917
918
  saved_errors = @errors
918
919
  saved_count = @error_count
920
+ saved_instance_path = @instance_path
919
921
  @errors = nil
920
922
  @error_count = 0
923
+ # Trials retain annotations, but discard errors. Descendants therefore
924
+ # need neither instance nor schema paths until the trial has returned.
925
+ if saved_instance_path
926
+ saved_schema_path = @schema_path
927
+ @instance_path = nil
928
+ @schema_path = nil
929
+ end
921
930
  evaluate(program, value)
922
931
  ensure
923
932
  @errors = saved_errors
924
933
  @error_count = saved_count
934
+ if saved_instance_path
935
+ @instance_path = saved_instance_path
936
+ @schema_path = saved_schema_path
937
+ end
925
938
  end
926
939
 
927
940
  private def evaluate_at(program, instance, instance_segment, schema_segment, child_segment = MISSING_SEGMENT)
@@ -949,23 +962,6 @@ module Schemurai
949
962
  @instance_path.pop unless instance_segment.equal?(MISSING_SEGMENT)
950
963
  end
951
964
 
952
- private def trial_at(program, instance, instance_segment, schema_segment, child_segment = MISSING_SEGMENT)
953
- return trial(program, instance) unless @instance_path
954
-
955
- trial_at_with_path(program, instance, instance_segment, schema_segment, child_segment)
956
- end
957
-
958
- private def trial_at_with_path(program, instance, instance_segment, schema_segment, child_segment)
959
- @instance_path << instance_segment unless instance_segment.equal?(MISSING_SEGMENT)
960
- @schema_path << schema_segment
961
- @schema_path << child_segment unless child_segment.equal?(MISSING_SEGMENT)
962
- trial(program, instance)
963
- ensure
964
- @schema_path.pop unless child_segment.equal?(MISSING_SEGMENT)
965
- @schema_path.pop
966
- @instance_path.pop unless instance_segment.equal?(MISSING_SEGMENT)
967
- end
968
-
969
965
  private def add_error(keyword, message = nil, append_keyword: true)
970
966
  @error_count += 1
971
967
  return false unless @errors
data/lib/schemurai.rb CHANGED
@@ -72,19 +72,19 @@ module Schemurai
72
72
  # Register every external schema with +schemas:+ before compiling. A registry
73
73
  # can be made shareable once registration and compilation are complete.
74
74
  class SchemaRegistry
75
- # The selected backend, either +:ruby+ or +:vm+.
75
+ # The selected backend, +:ruby+, +:vm+, or +:native+.
76
76
  attr_reader :backend
77
77
 
78
78
  # Creates a registry.
79
79
  #
80
80
  # +schemas+ maps absolute URI strings to JSON-shaped Ruby schemas. Pass
81
81
  # +validate_schema: true+ to validate every registered and compiled schema
82
- # against its meta-schema. +backend+ accepts +:ruby+, +:vm+, or +:default+.
82
+ # against its meta-schema. +backend+ accepts +:ruby+, +:vm+, +:native+, or +:default+.
83
83
  def initialize(schemas: {}, validate_schema: false, backend: Backend.requested)
84
84
  @backend = Backend.resolve(backend)
85
85
  @validate_schema = validate_schema
86
86
  @graph = Internal::SchemaGraph.new(schemas: schemas)
87
- @compiler = VM::Compiler.new(@graph) if @backend == :vm
87
+ @compiler = VM::Compiler.new(@graph) if %i[vm native].include?(@backend)
88
88
  schemas.each_value { |schema| validate_schema!(schema) } if validate_schema?
89
89
  end
90
90
 
@@ -150,7 +150,8 @@ module Schemurai
150
150
  private def build_validator(root, content:, format:)
151
151
  evaluator = if @compiler
152
152
  program = @compiler.compile(root)
153
- VM::Evaluator.new(@graph, @compiler, program, content: content, format: format)
153
+ evaluator_class = (@backend == :native) ? Native::Evaluator : VM::Evaluator
154
+ evaluator_class.new(@graph, @compiler, program, content: content, format: format)
154
155
  else
155
156
  Internal::Evaluator.new(@graph, root, content: content, format: format)
156
157
  end
@@ -177,7 +178,7 @@ module Schemurai
177
178
  @evaluator = evaluator
178
179
  end
179
180
 
180
- # The backend reported by the evaluator, either +:ruby+ or +:vm+.
181
+ # The backend reported by the evaluator, +:ruby+, +:vm+, or +:native+.
181
182
  def backend
182
183
  @evaluator.backend
183
184
  end
@@ -193,7 +194,7 @@ module Schemurai
193
194
  end
194
195
  end
195
196
 
196
- # Returns the backend selected by SCHEMURAI_BACKEND, either +:ruby+ or +:vm+.
197
+ # Returns the backend selected by SCHEMURAI_BACKEND, +:ruby+, +:vm+, or +:native+.
197
198
  module_function def backend
198
199
  Backend.resolve
199
200
  end
@@ -202,7 +203,7 @@ module Schemurai
202
203
  #
203
204
  # +schemas+ maps external URI strings to schemas. +base_uri+ supplies the
204
205
  # schema's base URI. Optional content and format assertions are disabled by
205
- # default. +backend+ accepts +:ruby+, +:vm+, or +:default+.
206
+ # default. +backend+ accepts +:ruby+, +:vm+, +:native+, or +:default+.
206
207
  module_function def compile(schema, schemas: {}, base_uri: nil, content: false, format: false, validate_schema: false, backend: Backend.requested)
207
208
  SchemaRegistry.new(schemas: schemas, validate_schema: validate_schema, backend: backend).compile(
208
209
  schema,
data/sig/schemurai.rbs CHANGED
@@ -9,7 +9,7 @@ module Schemurai
9
9
  type schemas = Hash[String, schema]
10
10
 
11
11
  # A resolved evaluator backend.
12
- type backend = :ruby | :vm
12
+ type backend = :ruby | :vm | :native
13
13
 
14
14
  # A backend accepted when configuring a validator.
15
15
  type backend_selection = backend | :default
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schemurai
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuya KOHARA (oakcask)
@@ -28,13 +28,29 @@ description: Validates Ruby values against JSON Schema Draft 7, 2019-09, and 202
28
28
  email:
29
29
  - kuya.kohara@gmail.com
30
30
  executables: []
31
- extensions: []
31
+ extensions:
32
+ - ext/schemurai_native/extconf.rb
32
33
  extra_rdoc_files:
33
34
  - LICENSE
34
35
  - README.md
35
36
  files:
36
37
  - LICENSE
37
38
  - README.md
39
+ - ext/schemurai_native/Cargo.lock
40
+ - ext/schemurai_native/Cargo.toml
41
+ - ext/schemurai_native/build.rs
42
+ - ext/schemurai_native/extconf.rb
43
+ - ext/schemurai_native/glue.c
44
+ - ext/schemurai_native/protocol.def
45
+ - ext/schemurai_native/protocol.h
46
+ - ext/schemurai_native/src/content.rs
47
+ - ext/schemurai_native/src/diagnostics.rs
48
+ - ext/schemurai_native/src/format.rs
49
+ - ext/schemurai_native/src/lib.rs
50
+ - ext/schemurai_native/src/number.rs
51
+ - ext/schemurai_native/src/pattern.rs
52
+ - ext/schemurai_native/src/scope.rs
53
+ - ext/schemurai_native/src/tracking.rs
38
54
  - lib/schemurai.rb
39
55
  - lib/schemurai/backend.rb
40
56
  - lib/schemurai/dialect.rb
@@ -50,6 +66,8 @@ files:
50
66
  - lib/schemurai/meta_schemas/draft2019_09.rb
51
67
  - lib/schemurai/meta_schemas/draft2020_12.rb
52
68
  - lib/schemurai/meta_schemas/draft7.rb
69
+ - lib/schemurai/native/diagnostics.rb
70
+ - lib/schemurai/native/evaluator.rb
53
71
  - lib/schemurai/schema_domain.rb
54
72
  - lib/schemurai/schema_graph.rb
55
73
  - lib/schemurai/schema_node.rb
@@ -64,7 +82,7 @@ licenses:
64
82
  metadata:
65
83
  homepage_uri: https://github.com/oakcask/schemurai
66
84
  source_code_uri: https://github.com/oakcask/schemurai/tree/main
67
- documentation_uri: https://rubydoc.info/gems/schemurai/2.2.2
85
+ documentation_uri: https://rubydoc.info/gems/schemurai/2.3.0
68
86
  rubygems_mfa_required: 'true'
69
87
  rdoc_options:
70
88
  - "--main"
@@ -90,5 +108,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
108
  requirements: []
91
109
  rubygems_version: 4.0.20
92
110
  specification_version: 4
93
- summary: A fast, Ractor-safe, pure Ruby JSON Schema Draft 7+ validator
111
+ summary: A fast, Ractor-safe JSON Schema Draft 7+ validator
94
112
  test_files: []