Class: Lutaml::Model::AdapterResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/model/adapter_resolver.rb

Overview

Single authority for adapter resolution: (format, type_name) → adapter class.

Consolidates adapter metadata, loading, validation, and auto-detection that was previously spread across Config, Configuration, and FormatRegistry.

Resolution chain (in order):

1. Thread-local AdapterScope override
2. Explicitly configured type (from Config.xml_adapter_type =)
3. Lazy auto-detected type (cached after first probe)
4. Default from format metadata
5. Raise FormatAdapterNotSpecifiedError

Examples:

Configure an adapter

AdapterResolver.set_adapter_type(:xml, :nokogiri)

Resolve an adapter

adapter_class = AdapterResolver.adapter_for(:xml)

Per-operation override

adapter_class = AdapterResolver.resolved_adapter_class(:xml, :ox)

Class Method Summary collapse

Class Method Details

.adapter_for(format) ⇒ Class?

Resolve the adapter class for a format using the full resolution chain.

Parameters:

  • format (Symbol)

    the format name (:xml, :json, etc.)

Returns:

  • (Class, nil)

    the adapter class or nil



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/lutaml/model/adapter_resolver.rb', line 38

def adapter_for(format)
  # 1. Thread-local scope override
  scope_override = AdapterScope.override_for(format)
  if scope_override
    return resolved_adapter_class(format, scope_override)
  end

  # 2. Explicitly configured type — a named type is an explicit pin
  # and must never fall through to auto-detection: detection loads
  # optional engines (e.g. yeptris) into the process even when the
  # user pinned a standard adapter, which crashes platforms where
  # the engine's build is broken. FIXED_ADAPTER_TYPE with no
  # resolved class means "unset" (set_adapter_class(format, nil))
  # and keeps the old detect-through behavior.
  configured = configured_type(format)
  if configured
    adapter = resolved[format]
    return adapter if adapter

    return load_and_cache(format, configured) if configured != FIXED_ADAPTER_TYPE
  end

  # 3. Lazy auto-detection (cached)
  detected = detected_types[format]
  if detected.nil? && !detected_formats.key?(format)
    result = detect_adapter_for(format)
    if result
      detected_formats[format] = true
      detected_types[format] = result
      return load_and_cache(format, result)
    end
    detected_formats[format] = true
  elsif detected
    return load_and_cache(format, detected)
  end

  # 4. Default from metadata
  default = .dig(format, :default)
  if default
    return load_and_cache(format, default)
  end

  # 5. No adapter available
  nil
end

.configured_type(format) ⇒ Symbol?

Get the configured type name for a format.

Parameters:

  • format (Symbol)

    the format name

Returns:

  • (Symbol, nil)

    the type name or nil



156
157
158
# File 'lib/lutaml/model/adapter_resolver.rb', line 156

def configured_type(format)
  configured_types[format]
end

.register_fixed(format, adapter_class, adapter_name) ⇒ void

This method returns an undefined value.

Register a fixed adapter class for a format with a single adapter.

Used for key-value formats (json, yaml, hash, etc.) that have exactly one adapter. Creates metadata so validation works.

Parameters:

  • format (Symbol)

    the format name

  • adapter_class (Class)

    the adapter class

  • adapter_name (Symbol)

    the adapter type name (e.g., :standard)



143
144
145
146
147
148
149
150
# File 'lib/lutaml/model/adapter_resolver.rb', line 143

def register_fixed(format, adapter_class, adapter_name)
  [format] = {
    available: [adapter_name],
    default: adapter_name,
  }
  resolved[format] = adapter_class
  configured_types[format] = adapter_name
end

.register_metadata(format, options) ⇒ void

This method returns an undefined value.

Register adapter metadata for a format (available adapters, default).

Called by FormatRegistry.register for formats with selectable adapters.

Parameters:

  • format (Symbol)

    the format name

  • options (Hash)

    { available: [...], default: :name }



130
131
132
# File 'lib/lutaml/model/adapter_resolver.rb', line 130

def (format, options)
  [format] = options
end

.reset!void

This method returns an undefined value.

Clear all state (for testing reset).



165
166
167
168
169
170
171
# File 'lib/lutaml/model/adapter_resolver.rb', line 165

def reset!
  @metadata = nil
  @configured_types = nil
  @resolved = nil
  @detected_types = nil
  @detected_formats = nil
end

.resolved_adapter_class(format, type_name) ⇒ Class

Load and resolve an adapter class by type name. Used for per-operation overrides (from_xml adapter: :ox).

Parameters:

  • format (Symbol)

    the format name

  • type_name (Symbol, String)

    the adapter type name

Returns:

  • (Class)

    the adapter class



115
116
117
118
119
# File 'lib/lutaml/model/adapter_resolver.rb', line 115

def resolved_adapter_class(format, type_name)
  type_name = type_name.to_sym
  validate!(format, type_name)
  load_adapter(format, type_name)
end

.set_adapter_class(format, adapter_class) ⇒ void

This method returns an undefined value.

Store a pre-resolved adapter class for a format.

Used by FormatRegistry.register for key-value formats that have a fixed adapter class (not selectable by type name).

Parameters:

  • format (Symbol)

    the format name

  • adapter_class (Class)

    the adapter class



104
105
106
107
# File 'lib/lutaml/model/adapter_resolver.rb', line 104

def set_adapter_class(format, adapter_class)
  resolved[format] = adapter_class
  configured_types[format] = FIXED_ADAPTER_TYPE
end

.set_adapter_type(format, type_name) ⇒ Class

Set the adapter type for a format (validates and loads).

Parameters:

  • format (Symbol)

    the format name

  • type_name (Symbol)

    the adapter type name (:nokogiri, :ox, etc.)

Returns:

  • (Class)

    the loaded adapter class



89
90
91
92
93
94
# File 'lib/lutaml/model/adapter_resolver.rb', line 89

def set_adapter_type(format, type_name)
  type_name = type_name.to_sym
  validate!(format, type_name)
  configured_types[format] = type_name
  resolved[format] = load_adapter(format, type_name)
end