moxml 0.2.0 → 0.4.0

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.
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -22,6 +22,7 @@ module Moxml
22
22
  class Compiler
23
23
  # Shared context for compiled Procs
24
24
  CONTEXT = Context.new
25
+ private_constant :CONTEXT
25
26
 
26
27
  # Expression cache
27
28
  CACHE = Cache.new
@@ -294,6 +295,12 @@ module Moxml
294
295
 
295
296
  handler = axis_name.gsub("-", "_")
296
297
 
298
+ unless respond_to?(:"on_axis_#{handler}", true)
299
+ raise XPath::Error.new(
300
+ "XPath axis #{axis_name.inspect} is not supported by this engine",
301
+ )
302
+ end
303
+
297
304
  send(:"on_axis_#{handler}", test, input, &block)
298
305
  end
299
306
 
@@ -362,6 +369,78 @@ module Moxml
362
369
  end
363
370
  end
364
371
 
372
+ # AXIS: ancestor - all ancestors up to the document
373
+ def on_axis_ancestor(ast, input)
374
+ ancestor = unique_literal(:ancestor)
375
+
376
+ document_or_node(input).if_true do
377
+ input.ancestors.each.add_block(ancestor) do
378
+ condition = process(ast, ancestor)
379
+ if block_given?
380
+ condition.if_true { yield ancestor }
381
+ else
382
+ condition.if_true { ancestor }
383
+ end
384
+ end
385
+ end
386
+ end
387
+
388
+ # AXIS: ancestor-or-self - the context node and its ancestors
389
+ def on_axis_ancestor_or_self(ast, input)
390
+ ancestor = unique_literal(:ancestor)
391
+
392
+ document_or_node(input).if_true do
393
+ self_result = process(ast, input)
394
+ self_collection = if block_given?
395
+ self_result.if_true { yield input }
396
+ else
397
+ self_result.if_true { input }
398
+ end
399
+ self_collection.followed_by do
400
+ input.ancestors.each.add_block(ancestor) do
401
+ condition = process(ast, ancestor)
402
+ if block_given?
403
+ condition.if_true { yield ancestor }
404
+ else
405
+ condition.if_true { ancestor }
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ # AXIS: following-sibling - siblings after the context node
413
+ def on_axis_following_sibling(ast, input)
414
+ sibling = unique_literal(:sibling)
415
+
416
+ document_or_node(input).if_true do
417
+ input.following_siblings.each.add_block(sibling) do
418
+ condition = process(ast, sibling)
419
+ if block_given?
420
+ condition.if_true { yield sibling }
421
+ else
422
+ condition.if_true { sibling }
423
+ end
424
+ end
425
+ end
426
+ end
427
+
428
+ # AXIS: preceding-sibling - siblings before the context node
429
+ def on_axis_preceding_sibling(ast, input)
430
+ sibling = unique_literal(:sibling)
431
+
432
+ document_or_node(input).if_true do
433
+ input.preceding_siblings.each.add_block(sibling) do
434
+ condition = process(ast, sibling)
435
+ if block_given?
436
+ condition.if_true { yield sibling }
437
+ else
438
+ condition.if_true { sibling }
439
+ end
440
+ end
441
+ end
442
+ end
443
+
365
444
  # AXIS: descendant-or-self - Enables // operator
366
445
  def on_axis_descendant_or_self(ast, input)
367
446
  node = unique_literal(:descendant)
@@ -623,7 +702,11 @@ module Moxml
623
702
 
624
703
  # Parent node (..)
625
704
  def on_parent(_ast, input)
626
- input.parent
705
+ if block_given?
706
+ yield input.parent
707
+ else
708
+ input.parent
709
+ end
627
710
  end
628
711
 
629
712
  # ===== OPERATORS =====
@@ -1752,7 +1835,7 @@ module Moxml
1752
1835
 
1753
1836
  # Helper: Check if AST node is a number
1754
1837
  def number?(ast)
1755
- %i[int float number].include?(ast.type)
1838
+ ast.type == :number
1756
1839
  end
1757
1840
 
1758
1841
  # Helper: Check if AST contains a call node with given name
data/lib/moxml.rb CHANGED
@@ -26,7 +26,6 @@ module Moxml
26
26
  default_indent: original.default_indent,
27
27
  default_line_ending: original.default_line_ending,
28
28
  entity_load_mode: original.entity_load_mode,
29
- entity_encoding: original.entity_encoding,
30
29
  restore_entities: original.restore_entities,
31
30
  namespace_validation_mode: original.namespace_validation_mode,
32
31
  entity_restoration_mode: original.entity_restoration_mode,
@@ -48,7 +47,6 @@ module Moxml
48
47
  config.default_indent = saved_values[:default_indent]
49
48
  config.default_line_ending = saved_values[:default_line_ending]
50
49
  config.entity_load_mode = saved_values[:entity_load_mode]
51
- config.entity_encoding = saved_values[:entity_encoding]
52
50
  config.restore_entities = saved_values[:restore_entities]
53
51
  config.namespace_validation_mode = saved_values[:namespace_validation_mode]
54
52
  config.entity_restoration_mode = saved_values[:entity_restoration_mode]
@@ -82,7 +80,6 @@ module Moxml
82
80
  autoload :Namespace, "moxml/namespace"
83
81
  autoload :Doctype, "moxml/doctype"
84
82
  autoload :EntityReference, "moxml/entity_reference"
85
- autoload :DocumentBuilder, "moxml/document_builder"
86
83
  autoload :Builder, "moxml/builder"
87
84
  autoload :Entity, "moxml/entity"
88
85
  autoload :EntityRegistry, "moxml/entity_registry"
@@ -31,7 +31,7 @@ RSpec.describe "Cross-adapter integration" do
31
31
  "Moxml::EntityReference",
32
32
  "Moxml::Context",
33
33
  "Moxml::Builder",
34
- "Moxml::DocumentBuilder",
34
+ "Moxml::Parse Behavior",
35
35
  "Moxml Integration",
36
36
  "Moxml Edge Cases",
37
37
  "Attribute Examples",
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.shared_examples "Moxml::DocumentBuilder" do
3
+ RSpec.shared_examples "Moxml::Parse Behavior" do
4
4
  let(:context) { Moxml.new }
5
- let(:builder) { Moxml::DocumentBuilder.new(context) }
6
5
 
7
6
  describe "#build" do
8
7
  it "builds a document model from native document" do
@@ -106,5 +106,20 @@ RSpec.shared_examples "Moxml::Node" do
106
106
  expect(node.text).to eq("1")
107
107
  end
108
108
  end
109
+
110
+ describe "duplication" do
111
+ it "returns an independent deep copy" do
112
+ doc = context.parse("<root><a id='1'>text</a></root>")
113
+ original = doc.at_xpath("//a")
114
+ copy = original.dup
115
+
116
+ expect(copy).not_to equal(original)
117
+ expect(copy.name).to eq("a")
118
+ expect(copy["id"]).to eq("1")
119
+ expect(copy.text).to eq("text")
120
+ copy["id"] = "changed"
121
+ expect(original["id"]).to eq("1")
122
+ end
123
+ end
109
124
  end
110
125
  end
@@ -58,8 +58,16 @@ RSpec.describe Moxml::Adapter::Leptris do
58
58
  expect(attrs.map(&:value)).to eq(%w[1 2])
59
59
  end
60
60
 
61
- it "falls back to the Ruby engine for element-context queries" do
61
+ it "evaluates element-context queries on the native engine" do
62
62
  expect(doc.root.xpath(".//item").size).to eq(2)
63
+ first_item = doc.root.children.find(&:element?)
64
+ expect(first_item.xpath("following-sibling::*").map(&:name)).to eq(%w[item])
65
+ end
66
+
67
+ it "falls back to the Ruby engine for parent-axis queries from the root" do
68
+ # The binding reports the root element parentless; moxml roots
69
+ # it at the document
70
+ expect(doc.root.xpath("../*").size).to eq(1)
63
71
  end
64
72
 
65
73
  it "falls back to the Ruby engine for the xmlns: reserved prefix" do
@@ -154,7 +154,7 @@ RSpec.describe Moxml::Adapter::Libxml do
154
154
  end
155
155
  end
156
156
 
157
- describe Moxml::Adapter::Libxml::EntityRestorer do
157
+ describe Moxml::Entity::Restorer do
158
158
  let(:context) { Moxml.new(:libxml) }
159
159
 
160
160
  it "restores entities through its public entry point" do
@@ -11,7 +11,6 @@ RSpec.describe Moxml::Config do
11
11
  expect(config.default_encoding).to eq("UTF-8")
12
12
  expect(config.default_indent).to eq(2)
13
13
  expect(config.default_line_ending).to eq("\n")
14
- expect(config.entity_encoding).to eq(:basic)
15
14
  end
16
15
 
17
16
  it "sets default entity_load_mode to :required" do
@@ -39,4 +39,26 @@ RSpec.describe Moxml::Context do
39
39
  expect(doc.root.name).to eq("root")
40
40
  end
41
41
  end
42
+
43
+ describe "wrapper identity map" do
44
+ it "hands back the same wrapper for the same native" do
45
+ doc = context.parse("<root><item id='1'/></root>")
46
+ first = doc.xpath("//item").first
47
+ second = doc.xpath("//item").first
48
+ expect(first).to equal(second)
49
+
50
+ again = doc.root.children.last
51
+ expect(again).to equal(first)
52
+ end
53
+
54
+ it "re-keys when refresh_native! swaps the native" do
55
+ doc = context.create_document
56
+ element = doc.create_element("x")
57
+ doc.add_child(element)
58
+
59
+ refreshed = doc.root
60
+ expect(refreshed.name).to eq("x")
61
+ expect(context.wrapper_for(refreshed.native)).to equal(refreshed)
62
+ end
63
+ end
42
64
  end
@@ -150,31 +150,6 @@ RSpec.describe "Moxml errors" do
150
150
  end
151
151
  end
152
152
 
153
- describe Moxml::SerializationError do
154
- it "includes node, adapter, and format information" do
155
- error = described_class.new(
156
- "Failed to serialize",
157
- adapter: "LibXML",
158
- format: "xml",
159
- )
160
- expect(error.adapter).to eq("LibXML")
161
- expect(error.format).to eq("xml")
162
- end
163
-
164
- it "provides helpful error message" do
165
- error = described_class.new(
166
- "Failed to serialize",
167
- adapter: "Ox",
168
- format: "xml",
169
- )
170
- message = error.to_s
171
- expect(message).to include("Failed to serialize")
172
- expect(message).to include("Adapter: Ox")
173
- expect(message).to include("Format: xml")
174
- expect(message).to include("Hint:")
175
- end
176
- end
177
-
178
153
  describe Moxml::DocumentStructureError do
179
154
  it "includes attempted operation and state" do
180
155
  error = described_class.new(
@@ -200,31 +175,6 @@ RSpec.describe "Moxml errors" do
200
175
  end
201
176
  end
202
177
 
203
- describe Moxml::AttributeError do
204
- it "includes attribute name, element, and value" do
205
- error = described_class.new(
206
- "Invalid attribute",
207
- name: "id",
208
- value: 123,
209
- )
210
- expect(error.attribute_name).to eq("id")
211
- expect(error.value).to eq(123)
212
- end
213
-
214
- it "provides helpful error message" do
215
- error = described_class.new(
216
- "Invalid attribute name",
217
- name: "123invalid",
218
- value: "test",
219
- )
220
- message = error.to_s
221
- expect(message).to include("Invalid attribute name")
222
- expect(message).to include("Attribute: 123invalid")
223
- expect(message).to include("Value: \"test\"")
224
- expect(message).to include("Hint:")
225
- end
226
- end
227
-
228
178
  describe Moxml::NotImplementedError do
229
179
  it "includes feature and adapter information" do
230
180
  error = described_class.new(
@@ -205,6 +205,46 @@ RSpec.describe "XPath Axes" do
205
205
  end
206
206
  end
207
207
 
208
+ describe "ancestor and sibling axes" do
209
+ let(:family) do
210
+ context.parse("<root><parent id='p1'><b id='1'/><b id='2'/><c/><d/></parent></root>")
211
+ end
212
+
213
+ def select(expression, node = family)
214
+ ast = Moxml::XPath::Parser.parse(expression)
215
+ proc = Moxml::XPath::Compiler.compile_with_cache(ast)
216
+ proc.call(node).map(&:name)
217
+ end
218
+
219
+ it "selects ancestors with ancestor::" do
220
+ expect(select("//c/ancestor::*")).to eq(%w[parent root])
221
+ end
222
+
223
+ it "includes the context node with ancestor-or-self::" do
224
+ expect(select("//c/ancestor-or-self::*")).to eq(%w[c parent root])
225
+ end
226
+
227
+ it "selects following siblings" do
228
+ expect(select("//c/following-sibling::*")).to eq(%w[d])
229
+ expect(select("//b[@id='1']/following-sibling::b")).to eq(%w[b])
230
+ expect(select("//b[@id='1']/following-sibling::*").size).to eq(3)
231
+ end
232
+
233
+ it "selects preceding siblings" do
234
+ expect(select("//c/preceding-sibling::*")).to eq(%w[b b])
235
+ end
236
+
237
+ it "resolves the bare parent step and steps after it" do
238
+ expect(select("//c/..")).to eq(%w[parent])
239
+ expect(select("//c/../*").size).to eq(4)
240
+ end
241
+
242
+ it "raises a clear error for unimplemented axes" do
243
+ expect { select("//c/following::*") }
244
+ .to raise_error(Moxml::XPath::Error, /not supported/)
245
+ end
246
+ end
247
+
208
248
  describe "descendant-or-self shorthand from an element" do
209
249
  it "selects descendants with .//step" do
210
250
  ast = Moxml::XPath::Parser.parse(".//grandchild")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-25 00:00:00.000000000 Z
11
+ date: 2026-08-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Moxml is a unified XML manipulation library that provides a common API
@@ -151,7 +151,6 @@ files:
151
151
  - lib/moxml/adapter/leptris.rb
152
152
  - lib/moxml/adapter/libxml.rb
153
153
  - lib/moxml/adapter/libxml/entity_ref_registry.rb
154
- - lib/moxml/adapter/libxml/entity_restorer.rb
155
154
  - lib/moxml/adapter/nokogiri.rb
156
155
  - lib/moxml/adapter/oga.rb
157
156
  - lib/moxml/adapter/ox.rb
@@ -187,10 +186,10 @@ files:
187
186
  - lib/moxml/declaration.rb
188
187
  - lib/moxml/doctype.rb
189
188
  - lib/moxml/document.rb
190
- - lib/moxml/document_builder.rb
191
189
  - lib/moxml/element.rb
192
190
  - lib/moxml/entity.rb
193
191
  - lib/moxml/entity/reference.rb
192
+ - lib/moxml/entity/restorer.rb
194
193
  - lib/moxml/entity_reference.rb
195
194
  - lib/moxml/entity_registry.rb
196
195
  - lib/moxml/entity_registry_opal_data.rb
@@ -390,7 +389,7 @@ files:
390
389
  - spec/integration/shared_examples/high_level/.gitkeep
391
390
  - spec/integration/shared_examples/high_level/builder_behavior.rb
392
391
  - spec/integration/shared_examples/high_level/context_behavior.rb
393
- - spec/integration/shared_examples/high_level/document_builder_behavior.rb
392
+ - spec/integration/shared_examples/high_level/parse_behavior.rb
394
393
  - spec/integration/shared_examples/integration_workflows.rb
395
394
  - spec/integration/shared_examples/line_ending_behavior.rb
396
395
  - spec/integration/shared_examples/node_wrappers/.gitkeep
@@ -444,7 +443,6 @@ files:
444
443
  - spec/moxml/declaration_preservation_spec.rb
445
444
  - spec/moxml/declaration_spec.rb
446
445
  - spec/moxml/doctype_spec.rb
447
- - spec/moxml/document_builder_spec.rb
448
446
  - spec/moxml/document_spec.rb
449
447
  - spec/moxml/element_spec.rb
450
448
  - spec/moxml/entity_preservation_spec.rb
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Moxml
4
- module Adapter
5
- class Libxml < Base
6
- # Restores configured character entities into explicit Moxml
7
- # EntityReference nodes after LibXML has parsed the native tree.
8
- class EntityRestorer
9
- def initialize(doc, adapter: Libxml)
10
- @doc = doc
11
- @ctx = doc.context
12
- @registry = @ctx.entity_registry
13
- @config = @ctx.config
14
- @adapter = adapter
15
- end
16
-
17
- def run
18
- return unless @registry && @doc.root
19
-
20
- walk(@doc.root)
21
- end
22
-
23
- private
24
-
25
- def walk(element)
26
- # Snapshot because we may add/remove siblings during the walk.
27
- element.children.to_a.each do |child|
28
- if child.is_a?(::Moxml::Text)
29
- restore_text_node(child)
30
- elsif child.is_a?(::Moxml::Element)
31
- walk(child)
32
- end
33
- end
34
- end
35
-
36
- # Matches DocumentBuilder's previous behavior, including the libxml
37
- # limitation that adjacent native text nodes get merged.
38
- def restore_text_node(text_node)
39
- content = text_node.content
40
- return unless content
41
-
42
- chunks = chunk_text(content)
43
- return if chunks.size == 1 && chunks.first.first == :text
44
-
45
- parent = text_node.parent
46
- return unless parent
47
-
48
- text_node.remove
49
- chunks.each { |type, payload| append_chunk(parent, type, payload) }
50
- end
51
-
52
- def chunk_text(content)
53
- chunks = []
54
- buffer = +""
55
- restorable = @registry.restorable_codepoints
56
-
57
- content.each_char do |char|
58
- cp = char.ord
59
- if restorable.include?(cp) &&
60
- (name = @registry.primary_name_for_codepoint(cp)) &&
61
- @registry.should_restore?(cp, config: @config)
62
- unless buffer.empty?
63
- chunks << [:text, buffer.dup]
64
- buffer.clear
65
- end
66
- chunks << [:eref, name]
67
- else
68
- buffer << char
69
- end
70
- end
71
-
72
- chunks << [:text, buffer.dup] unless buffer.empty?
73
- chunks
74
- end
75
-
76
- def append_chunk(parent, type, payload)
77
- case type
78
- when :text
79
- parent.add_child(::Moxml::Text.new(
80
- @adapter.create_native_text(payload), @ctx
81
- ))
82
- when :eref
83
- parent.add_child(
84
- ::Moxml::EntityReference.new(
85
- @adapter.create_native_entity_reference(payload),
86
- @ctx,
87
- ),
88
- )
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,167 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Moxml
4
- class DocumentBuilder
5
- attr_reader :context
6
-
7
- def initialize(context)
8
- @context = context
9
- @node_stack = []
10
- end
11
-
12
- def build(native_doc)
13
- @current_doc = context.create_document(native_doc)
14
-
15
- # Transfer has_declaration flag if present in attachments
16
- if adapter.attachments.key?(native_doc, :has_declaration)
17
- has_declaration = adapter.attachments.get(native_doc, :has_declaration)
18
- @current_doc.has_xml_declaration = has_declaration
19
- end
20
-
21
- # Transfer DOCTYPE from parsed document if it exists in attachments
22
- if adapter.attachments.key?(native_doc, :doctype)
23
- doctype = adapter.attachments.get(native_doc, :doctype)
24
- if doctype
25
- adapter.attachments.set(@current_doc.native, :doctype, doctype)
26
- end
27
- end
28
-
29
- visit_node(native_doc)
30
- @current_doc
31
- end
32
-
33
- private
34
-
35
- def visit_node(node)
36
- case node_type(node)
37
- when :document then visit_document(node)
38
- when :element then visit_element(node)
39
- when :text then visit_text(node)
40
- when :cdata then visit_cdata(node)
41
- when :comment then visit_comment(node)
42
- when :processing_instruction then visit_processing_instruction(node)
43
- when :doctype then visit_doctype(node)
44
- when :entity_reference then visit_entity_reference(node)
45
- end
46
- end
47
-
48
- def visit_document(doc)
49
- @node_stack.push(@current_doc)
50
- visit_children(doc)
51
- @node_stack.clear
52
- end
53
-
54
- def visit_element(node)
55
- childless_node = adapter.duplicate_node(node)
56
- adapter.replace_children(childless_node, [])
57
- # Prepare node for new document (LibXML needs this)
58
- childless_node = adapter.prepare_for_new_document(
59
- childless_node,
60
- @current_doc.native,
61
- )
62
- element = Element.new(childless_node, context)
63
- @node_stack.last.add_child(element)
64
-
65
- @node_stack.push(element) # add a parent for its children
66
- visit_children(node)
67
- @node_stack.pop # remove the parent
68
- end
69
-
70
- def visit_text(node)
71
- # Prepare node for new document before wrapping
72
- prepared = adapter.prepare_for_new_document(node, @current_doc.native)
73
- content = adapter.text_content(node)
74
-
75
- # Check if we should restore entity references for this text
76
- if context.config.restore_entities && text_has_restorable_entities?(content)
77
- restore_entities_in_text(content)
78
- else
79
- @node_stack.last&.add_child(Text.new(prepared, context))
80
- end
81
- end
82
-
83
- def text_has_restorable_entities?(content)
84
- return false unless content
85
-
86
- registry = context.entity_registry
87
- codepoints = registry.restorable_codepoints
88
- content.each_char do |char|
89
- return true if codepoints.include?(char.ord)
90
- end
91
- false
92
- end
93
-
94
- def restore_entities_in_text(content)
95
- parent = @node_stack.last
96
- return unless parent
97
-
98
- registry = context.entity_registry
99
- config = context.config
100
- buffer = +""
101
-
102
- content.to_s.each_char do |char|
103
- codepoint = char.ord
104
- name = registry.primary_name_for_codepoint(codepoint)
105
-
106
- if name && registry.should_restore?(codepoint, config: config)
107
- # Flush buffered text before the entity
108
- unless buffer.empty?
109
- parent.add_child(Text.new(adapter.create_text(buffer), context))
110
- buffer.clear
111
- end
112
- parent.add_child(
113
- EntityReference.new(adapter.create_entity_reference(name), context),
114
- )
115
- else
116
- buffer << char
117
- end
118
- end
119
-
120
- # Flush remaining buffer
121
- unless buffer.empty?
122
- parent.add_child(Text.new(adapter.create_text(buffer), context))
123
- end
124
- end
125
-
126
- def visit_cdata(node)
127
- prepared = adapter.prepare_for_new_document(node, @current_doc.native)
128
- @node_stack.last&.add_child(Cdata.new(prepared, context))
129
- end
130
-
131
- def visit_comment(node)
132
- prepared = adapter.prepare_for_new_document(node, @current_doc.native)
133
- @node_stack.last&.add_child(Comment.new(prepared, context))
134
- end
135
-
136
- def visit_processing_instruction(node)
137
- prepared = adapter.prepare_for_new_document(node, @current_doc.native)
138
- @node_stack.last&.add_child(ProcessingInstruction.new(prepared, context))
139
- end
140
-
141
- def visit_doctype(node)
142
- prepared = adapter.prepare_for_new_document(node, @current_doc.native)
143
- @node_stack.last&.add_child(Doctype.new(prepared, context))
144
- end
145
-
146
- def visit_entity_reference(node)
147
- prepared = adapter.prepare_for_new_document(node, @current_doc.native)
148
- @node_stack.last&.add_child(EntityReference.new(prepared, context))
149
- end
150
-
151
- def visit_children(node)
152
- children(node).each { |child| visit_node(child) }
153
- end
154
-
155
- def node_type(node)
156
- context.config.adapter.node_type(node)
157
- end
158
-
159
- def children(node)
160
- context.config.adapter.children(node)
161
- end
162
-
163
- def adapter
164
- context.config.adapter
165
- end
166
- end
167
- end