Class: Moxml::Plan
- Inherits:
-
Object
- Object
- Moxml::Plan
- Defined in:
- lib/moxml/plan.rb
Overview
Plan-compiled materialization: declare the document shape once, execute it against the adapter's bulk row stream — no wrapper tree, no NodeSet, no per-node contract dispatch. The compiled grammar instead of the interpreted walk.
plan = Moxml::Plan.new do
on("record") { |attrs, _text, fields| Record.new(attrs["id"], attrs["kind"], fields) }
on("field") { |attrs, text, _kids| Field.new(attrs["name"], attrs["unit"], text) }
end
records = plan.parse(xml, ctx)
Handlers run bottom-up as each element completes; children is
the array of handler values for matched child elements whose
parent ALSO matched (values of unmatched parents are dropped —
matching a parent is the consumer's decision to keep a subtree).
attrs is a Hash of the element's attributes (local name =>
value); text is the first text child's content, or nil.
Name matching is global (flat table, not path-relative): element names unique per role — the dominant consumer shape — work as-is; distinct roles sharing a name need distinct documents or a rename upstream.
Adapters with a bulk stream answer plan_rows (leptris: the engine's one-pass C snapshot — pre-order, first-text on the row); the rest run the generic pre-order wrapper walk below, emitting the identical stream — spec-pinned equal.
Instance Method Summary collapse
-
#initialize(&block) ⇒ Plan
constructor
A new instance of Plan.
-
#materialize(node) ⇒ Object
Materializes the plan against a document (or element) wrapper.
-
#on(name, &handler) ⇒ Object
Registers a handler for elements named
name(local name). -
#parse(xml, context) ⇒ Object
Parses
xmland materializes the plan against it.
Constructor Details
#initialize(&block) ⇒ Plan
Returns a new instance of Plan.
32 33 34 35 |
# File 'lib/moxml/plan.rb', line 32 def initialize(&block) @handlers = {} instance_eval(&block) if block end |
Instance Method Details
#materialize(node) ⇒ Object
Materializes the plan against a document (or element) wrapper. Returns the values of top-level matched elements.
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/moxml/plan.rb', line 53 def materialize(node) results = [] frames = [] # per open depth: children values (nil = unmatched) = [] # per open depth: [name, attrs, text] (nil = unmatched) close_top = lambda do m = .pop children = frames.pop next unless m value = @handlers[m[0]].call(m[1], m[2], children) if frames.empty? || frames.last.nil? results << value else frames.last << value end end row = lambda do |name, attrs_pairs, text, depth| close_top.call while frames.size > depth handler = @handlers[name] if handler attrs = {} i = 0 while i < attrs_pairs.length attrs[attrs_pairs[i]] = attrs_pairs[i + 1] i += 2 end frames << [] << [name, attrs, text] else frames << nil << nil end end adapter = node.context.config.adapter ran = adapter.plan_rows(node.native) do |name, pairs, text, depth| row.call(name, pairs, text, depth) end unless ran walk_wrappers(node) do |name, pairs, text, depth| row.call(name, pairs, text, depth) end end close_top.call while .any? results end |
#on(name, &handler) ⇒ Object
Registers a handler for elements named name (local name).
Returns self so registrations chain.
39 40 41 42 43 44 |
# File 'lib/moxml/plan.rb', line 39 def on(name, &handler) raise ArgumentError, "on(#{name.inspect}) requires a block" unless handler @handlers[name] = handler self end |
#parse(xml, context) ⇒ Object
Parses xml and materializes the plan against it.
47 48 49 |
# File 'lib/moxml/plan.rb', line 47 def parse(xml, context) materialize(context.parse(xml)) end |