canon 0.3.22 → 0.3.24
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 +4 -4
- data/CLAUDE.md +4 -0
- data/lib/canon/comparison/json_comparator.rb +1 -1
- data/lib/canon/comparison/yaml_comparator.rb +1 -1
- data/lib/canon/formatters/json_formatter.rb +1 -1
- data/lib/canon/formatters/yaml_formatter.rb +1 -1
- data/lib/canon/json_parsing.rb +27 -0
- data/lib/canon/validators/json_validator.rb +1 -1
- data/lib/canon/validators/yaml_validator.rb +1 -1
- data/lib/canon/version.rb +1 -1
- data/lib/canon/yaml_backend.rb +72 -0
- data/lib/canon/yaml_parsing.rb +36 -0
- data/lib/canon.rb +3 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1d317692d756e058f2a4fb6865fffa4240744ac1b7cf8c83cb026552acb3f16
|
|
4
|
+
data.tar.gz: b993bde4273af1fdfe30d05b43bc0e642b9c3b80d857dc437196d2e7ea20567f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ee303aab423e1bfb48b754b441c8e70b307af2a03956bd1cb7c5998dcb575cfc57c9c6ba1d02a22af74717cfbd8aaa1f05b88a52cc698da6e8547e3a0a25ce8
|
|
7
|
+
data.tar.gz: a2de4ba755d59ecd72bfe50e53f6fea6c1136cbc98fda6886c22f5240098a84235d304040dcefb8e1d882a9bab27228f86030bf784b297cbb29df1c745a622af
|
data/CLAUDE.md
CHANGED
|
@@ -143,6 +143,10 @@ Engine parity is complete through libleptris 1.9.8 / leptris-ruby 1.9.33 / moxml
|
|
|
143
143
|
|
|
144
144
|
Engine A/B testing: `CANON_XML_BACKEND=nokogiri bundle exec rspec` (or `=moxml` to force leptris when it isn't the resolved default). The default suite must stay green under BOTH values; the only expected pendings are the upstream-tracked ones. The benchmark header (`rake performance:quick`) reports the active engine.
|
|
145
145
|
|
|
146
|
+
### YAML Engines
|
|
147
|
+
|
|
148
|
+
`Canon::YamlBackend` selects the YAML engine: `:psych` (default) or `:yeptris` (FFI over libyeptris, the YAML counterpart of the leptris XML stack — `CANON_YAML_BACKEND=yeptris` opts in; the optional `yeptris` Gemfile group must be enabled). `Canon::YamlParsing` is the single gateway for string loads; `Canon::JsonParsing` mirrors it for JSON (the same yeptris engine serves both — `Yeptris::YAML.load` auto-detects JSON and routes to the native C-API materializer; JSON falls back to stdlib unless `YamlBackend.yeptris_native?`, since the FFI ladder is ~29x slower than the stdlib C extension). `YAML.dump` stays on Psych everywhere — canonical output bytes are canon's product and the writers differ. The default stays `:psych` until the yeptris Psych-parity gaps close (yeptris-ruby#30 sexagesimal scalars, #31 >64-bit integers — #29 empty documents was fixed in 0.1.12); `spec/canon/yaml_engine_parity_spec.rb` is the executable gate, with upstream-tracked cases pending. Never `require "yeptris/psych"` — it rebinds the global `::Psych` constant for the whole process; only the namespaced `Yeptris::YAML` API is used.
|
|
149
|
+
|
|
146
150
|
### Format Detection
|
|
147
151
|
|
|
148
152
|
`Canon::Comparison::FormatDetector` auto-detects format from string content or object type (Moxml::Node → XML, Nokogiri::HTML → HTML, Hash → JSON, etc.). HTML4 vs HTML5 is determined by DOCTYPE.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Canon
|
|
6
|
+
# The only place canon talks to a JSON engine. The yeptris fast path
|
|
7
|
+
# (fused C-API materializer, on par with the stdlib C extension)
|
|
8
|
+
# engages through Yeptris::YAML.load's JSON auto-detection; without
|
|
9
|
+
# the native materializer the stdlib parser serves, unchanged.
|
|
10
|
+
# Errors normalize to JSON::ParserError so canon's rescues hold on
|
|
11
|
+
# both engines.
|
|
12
|
+
module JsonParsing
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def parse(json)
|
|
16
|
+
if Canon::YamlBackend.yeptris_native?
|
|
17
|
+
begin
|
|
18
|
+
::Yeptris::YAML.load(json)
|
|
19
|
+
rescue ::Yeptris::ParseError, ::FFI::NullPointerError => e
|
|
20
|
+
raise JSON::ParserError, e.message
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
JSON.parse(json)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -20,7 +20,7 @@ module Canon
|
|
|
20
20
|
return if input.is_a?(Hash) || input.is_a?(Array) # Already parsed
|
|
21
21
|
return if input.strip.empty?
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Canon::JsonParsing.parse(input)
|
|
24
24
|
rescue JSON::ParserError => e
|
|
25
25
|
# Extract position from error message
|
|
26
26
|
position = extract_position(e.message)
|
|
@@ -22,7 +22,7 @@ module Canon
|
|
|
22
22
|
return if input.is_a?(Hash) || input.is_a?(Array) # Already parsed
|
|
23
23
|
return if input.strip.empty?
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Canon::YamlParsing.safe_load(input)
|
|
26
26
|
rescue Psych::SyntaxError => e
|
|
27
27
|
location = extract_location(e)
|
|
28
28
|
|
data/lib/canon/version.rb
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Canon
|
|
4
|
+
# Selection of the YAML engine: :psych (stdlib) or :yeptris
|
|
5
|
+
# (FFI over libyeptris — the YAML counterpart of the leptris XML
|
|
6
|
+
# stack).
|
|
7
|
+
#
|
|
8
|
+
# Mirrors XmlBackend's discipline (MECE — this module owns selection,
|
|
9
|
+
# YamlParsing owns the calls). The default stays :psych until the
|
|
10
|
+
# yeptris Psych-safe_load parity gaps close (yeptris-ruby#29 empty
|
|
11
|
+
# documents crash, #30 sexagesimal scalars, #31 >64-bit integers);
|
|
12
|
+
# yeptris is 3.1x faster at loading (measured, 2,000-item document),
|
|
13
|
+
# so CANON_YAML_BACKEND=yeptris opts in early and the default flips
|
|
14
|
+
# once the parity spec runs clean.
|
|
15
|
+
#
|
|
16
|
+
# Only the namespaced API (Yeptris::YAML) is ever used — requiring
|
|
17
|
+
# "yeptris/psych" rebinds the global ::Psych constant for the whole
|
|
18
|
+
# process, which a library must never do.
|
|
19
|
+
module YamlBackend
|
|
20
|
+
VALID_BACKENDS = %i[psych yeptris].freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def active
|
|
24
|
+
@active ||= begin
|
|
25
|
+
wanted = forced || :psych
|
|
26
|
+
# A forced yeptris without a loadable gem/native lib must
|
|
27
|
+
# degrade to Psych, not NameError in the gateway.
|
|
28
|
+
wanted = :psych if wanted == :yeptris && !yeptris_available?
|
|
29
|
+
wanted
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def psych?
|
|
34
|
+
active == :psych
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def yeptris?
|
|
38
|
+
active == :yeptris
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def reset!
|
|
42
|
+
@active = nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def yeptris_available?
|
|
46
|
+
return false if RUBY_ENGINE == "opal"
|
|
47
|
+
|
|
48
|
+
require "yeptris"
|
|
49
|
+
Yeptris::YAML.respond_to?(:load)
|
|
50
|
+
rescue LoadError, StandardError
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The JSON fast path needs the fused C-API materializer
|
|
55
|
+
# (Yeptris::Native — load_json) specifically: the FFI ladder is
|
|
56
|
+
# ~29x slower than the stdlib JSON C extension, so JSON loads
|
|
57
|
+
# fall back to stdlib when only FFI is present.
|
|
58
|
+
def yeptris_native?
|
|
59
|
+
yeptris? && yeptris_available? && !defined?(::Yeptris::Native).nil?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def forced
|
|
65
|
+
value = ENV["CANON_YAML_BACKEND"].to_s.downcase
|
|
66
|
+
return nil unless VALID_BACKENDS.include?(value.to_sym)
|
|
67
|
+
|
|
68
|
+
value.to_sym
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Canon
|
|
6
|
+
# The only place canon talks to a YAML engine. All string loads go
|
|
7
|
+
# through safe_load here (see XmlParsing for the XML equivalent);
|
|
8
|
+
# YAML.dump stays on Psych everywhere — canonical output bytes are
|
|
9
|
+
# canon's product and the writers differ.
|
|
10
|
+
module YamlParsing
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Psych::safe_load semantics. yeptris is safe-by-default plain
|
|
14
|
+
# data with Symbol/Date/Time built in (aliases resolve), so the
|
|
15
|
+
# permitted-class list is inherently satisfied; parse failures are
|
|
16
|
+
# normalized to Psych::SyntaxError so canon's rescues hold on both
|
|
17
|
+
# engines.
|
|
18
|
+
def safe_load(yaml, permitted_classes: [Symbol, Date, Time],
|
|
19
|
+
aliases: false)
|
|
20
|
+
if YamlBackend.yeptris?
|
|
21
|
+
begin
|
|
22
|
+
::Yeptris::YAML.load(yaml)
|
|
23
|
+
rescue ::Yeptris::ParseError => e
|
|
24
|
+
raise Psych::SyntaxError.new(nil, 0, 0, 0, e.message, "")
|
|
25
|
+
rescue ::FFI::NullPointerError
|
|
26
|
+
# yeptris-ruby#29: empty/comment-only input. Psych returns
|
|
27
|
+
# nil; mirror that until the upstream fix lands.
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
YAML.safe_load(yaml, permitted_classes: permitted_classes,
|
|
32
|
+
aliases: aliases)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/canon.rb
CHANGED
|
@@ -5,6 +5,9 @@ require "canon/errors"
|
|
|
5
5
|
require "nokogiri" unless RUBY_ENGINE == "opal"
|
|
6
6
|
require "canon/xml_backend"
|
|
7
7
|
require "canon/xml_parsing"
|
|
8
|
+
require "canon/yaml_backend"
|
|
9
|
+
require "canon/yaml_parsing"
|
|
10
|
+
require "canon/json_parsing"
|
|
8
11
|
require "canon/config"
|
|
9
12
|
require "canon/data_model"
|
|
10
13
|
require "canon/xml"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: canon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.24
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -332,6 +332,7 @@ files:
|
|
|
332
332
|
- lib/canon/html.rb
|
|
333
333
|
- lib/canon/html/data_model.rb
|
|
334
334
|
- lib/canon/html/nokogiri_support.rb
|
|
335
|
+
- lib/canon/json_parsing.rb
|
|
335
336
|
- lib/canon/options.rb
|
|
336
337
|
- lib/canon/options/cli_generator.rb
|
|
337
338
|
- lib/canon/options/registry.rb
|
|
@@ -407,6 +408,8 @@ files:
|
|
|
407
408
|
- lib/canon/xml/xpath_engine.rb
|
|
408
409
|
- lib/canon/xml_backend.rb
|
|
409
410
|
- lib/canon/xml_parsing.rb
|
|
411
|
+
- lib/canon/yaml_backend.rb
|
|
412
|
+
- lib/canon/yaml_parsing.rb
|
|
410
413
|
- lib/tasks/benchmark_runner.rb
|
|
411
414
|
- lib/tasks/performance.rake
|
|
412
415
|
- lib/tasks/performance_comparator.rb
|