Class: Moxml::StructPlan

Inherits:
Object
  • Object
show all
Defined in:
lib/moxml/struct_plan.rb

Overview

Struct-compiled materialization — the fully compiled grammar: declare the document shape once as STRUCTS; adapters with a C executor (leptris 1.9.201.1+, the varargs-mint fix) mint the typed with zero Ruby frames per element; everything else falls back to an equivalent Moxml::Plan over the row stream (spec-pinned equal).

plan = Moxml::StructPlan.new do
element "record", Record, attrs: { "id" => :id, "kind" => :kind },
       children: :fields
element "field", Field, attrs: { "name" => :name, "unit" => :unit },
       text: :value
end
records = plan.parse(xml, ctx)   # Array of Record

Slot symbols must be the Struct's member names. Semantics equal Moxml::Plan: unmatched elements are barriers — their matched descendants surface at top level.

Defined Under Namespace

Classes: Element

Constant Summary collapse

TYPE_TAGS =

The compiled spec the C executor consumes: => [klass, attrs, text slot, children slot]. Slots resolve to member INDICES once — the C executor's Integer key path then writes by index, skipping the per-write member name scan.

{ integer: 1, float: 2, boolean: 3 }.freeze

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ StructPlan

Returns a new instance of StructPlan.



25
26
27
28
# File 'lib/moxml/struct_plan.rb', line 25

def initialize(&block)
  @elements = {}
  instance_eval(&block) if block
end

Instance Method Details

#compile ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/moxml/struct_plan.rb', line 53

def compile
  typed = Moxml::Adapter::Leptris::NATIVE_PLAN_TYPED
  @elements.each_with_object({}) do |(name, el), spec|
    members = el.klass.members
    spec[name] = [
      el.klass,
      el.attrs.each_with_object({}) do |(a, slot), h|
        h[a] = compile_slot(slot, members, typed)
      end,
      compile_slot(el.text, members, typed),
      members.index(el.children) || el.children,
    ]
  end
end

#compile_slot(slot, members, typed) ⇒ Object

Resolve a slot to the executor's wire form: the member index (or the raw symbol for unknown slots), wrapped as [slot, tag] when a type is declared and the binding's executor is typed. Without the face, types degrade to strings.



73
74
75
76
77
78
79
80
81
82
# File 'lib/moxml/struct_plan.rb', line 73

def compile_slot(slot, members, typed)
  if slot.is_a?(Array)
    inner, type = slot
    tag = TYPE_TAGS[type] || 0
    resolved = members.index(inner) || inner
    return typed && tag != 0 ? [resolved, tag] : resolved
  end

  members.index(slot) || slot
end

#element(name, klass, attrs: {}, text: nil, children: nil) ⇒ Object

Registers a struct element. attrs maps attribute names to member slots; text and children name the member slots receiving the first text child and the matched children array. Returns self so registrations chain.

Typed scalars (binding with the typed executor — probe Moxml::Adapter::Leptris::NATIVE_PLAN_TYPED): slot values may be [slot, type] pairs — type one of :integer, :float, :boolean (attrs and text). The executor casts in C with no Ruby String materialized; unparseable input degrades to the raw String (lenient). :boolean accepts t/1/y/Y as true.



41
42
43
44
# File 'lib/moxml/struct_plan.rb', line 41

def element(name, klass, attrs: {}, text: nil, children: nil)
  @elements[name] = Element.new(klass, attrs, text, children)
  self
end

#materialize(node) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/moxml/struct_plan.rb', line 88

def materialize(node)
  adapter = node.context.config.adapter
  roots = adapter.plan_structs(node.native, compile)
  return roots if roots

  plan.materialize(node)
end

#parse(xml, context) ⇒ Object



84
85
86
# File 'lib/moxml/struct_plan.rb', line 84

def parse(xml, context)
  materialize(context.parse(xml))
end