Module: Canon::YamlBackend

Defined in:
lib/canon/yaml_backend.rb

Overview

Selection of the YAML engine: :psych (stdlib) or :yeptris (FFI over libyeptris — the YAML counterpart of the leptris XML stack).

Mirrors XmlBackend's discipline (MECE — this module owns selection, YamlParsing owns the calls). yeptris loads 4.5–5x faster than Psych (0.1.28 Marshal materialization, 2,000-item document) and the Psych-safe_load parity spec runs clean — the former gate-blockers (yeptris-ruby#29/#30/#31) are all fixed upstream. The default therefore follows availability: yeptris when loadable, Psych otherwise. CANON_YAML_BACKEND=psych forces the stdlib engine.

Only the namespaced API (Yeptris::YAML) is ever used — requiring "yeptris/psych" rebinds the global ::Psych constant for the whole process, which a library must never do.

Constant Summary collapse

VALID_BACKENDS =
%i[psych yeptris].freeze

Class Method Summary collapse

Class Method Details

.activeObject



23
24
25
26
27
28
29
30
31
# File 'lib/canon/yaml_backend.rb', line 23

def active
  @active ||= begin
    wanted = forced || (yeptris_available? ? :yeptris : :psych)
    # A forced yeptris without a loadable gem/native lib must
    # degrade to Psych, not NameError in the gateway.
    wanted = :psych if wanted == :yeptris && !yeptris_available?
    wanted
  end
end

.json_yeptris?Boolean

JSON defaults to the strict yeptris surface whenever the native materializer is installed (0.1.13.4 ships platform gems — zero compilation, zero env). JSON parity is spec-pinned to JSON.parse upstream and complete except the #37 duplicate-keys leniency; YAML keeps waiting on #30/#31 and stays behind the env opt-in. CANON_YAML_BACKEND=psych forces both formats back to stdlib.

Returns:

  • (Boolean)


69
70
71
72
73
74
75
76
# File 'lib/canon/yaml_backend.rb', line 69

def json_yeptris?
  return false if RUBY_ENGINE == "opal"
  return false if ENV["CANON_YAML_BACKEND"].to_s.casecmp("psych").zero?

  # Independent of the YAML selection: the strict JSON surface
  # is complete on its own, so JSON does not wait for #30/#31.
  yeptris_available? && !defined?(::Yeptris::Native).nil?
end

.psych?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/canon/yaml_backend.rb', line 33

def psych?
  active == :psych
end

.reset!Object



41
42
43
# File 'lib/canon/yaml_backend.rb', line 41

def reset!
  @active = nil
end

.yeptris?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/canon/yaml_backend.rb', line 37

def yeptris?
  active == :yeptris
end

.yeptris_available?Boolean

Returns:

  • (Boolean)


45
46
47
48
49
50
51
52
# File 'lib/canon/yaml_backend.rb', line 45

def yeptris_available?
  return false if RUBY_ENGINE == "opal"

  require "yeptris"
  Yeptris::YAML.respond_to?(:load)
rescue LoadError, StandardError
  false
end

.yeptris_native?Boolean

The JSON fast path needs the fused C-API materializer (Yeptris::Native — load_json) specifically: the FFI ladder is ~29x slower than the stdlib JSON C extension, so JSON loads fall back to stdlib when only FFI is present.

Returns:

  • (Boolean)


58
59
60
# File 'lib/canon/yaml_backend.rb', line 58

def yeptris_native?
  yeptris? && yeptris_available? && !defined?(::Yeptris::Native).nil?
end