moxml 0.1.18 → 0.1.20
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +181 -11
- data/README.adoc +24 -1
- data/docs/_guides/node-api-consistency.adoc +4 -0
- data/lib/moxml/adapter/base.rb +6 -1
- data/lib/moxml/adapter/customized_ox/text.rb +15 -2
- data/lib/moxml/adapter/customized_rexml/formatter.rb +1 -0
- data/lib/moxml/adapter/libxml.rb +7 -2
- data/lib/moxml/adapter/oga.rb +6 -2
- data/lib/moxml/adapter/ox.rb +15 -8
- data/lib/moxml/builder.rb +12 -5
- data/lib/moxml/config.rb +1 -1
- data/lib/moxml/entity_registry.rb +1 -0
- data/lib/moxml/native_attachment/native.rb +69 -0
- data/lib/moxml/native_attachment/opal.rb +34 -0
- data/lib/moxml/native_attachment.rb +16 -46
- data/lib/moxml/text.rb +4 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xpath/compiler.rb +2 -1
- data/spec/integration/shared_examples/edge_cases.rb +4 -2
- data/spec/integration/shared_examples/entity_reference_whitespace.rb +1 -1
- data/spec/integration/shared_examples/high_level/document_builder_behavior.rb +3 -1
- data/spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb +10 -4
- data/spec/integration/shared_examples/node_wrappers/node_set_behavior.rb +1 -1
- data/spec/moxml/adapter/headed_ox_spec.rb +1 -1
- data/spec/moxml/lazy_parse_spec.rb +1 -1
- data/spec/moxml/moxml_spec.rb +6 -40
- data/spec/moxml/native_attachment/native_spec.rb +57 -0
- data/spec/moxml/native_attachment/opal_spec.rb +29 -0
- data/spec/moxml/native_attachment/shared_examples.rb +43 -0
- data/spec/moxml/native_attachment_spec.rb +184 -0
- data/spec/moxml/text_spec.rb +23 -0
- data/spec/moxml/xpath/functions/node_functions_spec.rb +3 -2
- data/spec/performance/benchmark_spec.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0633c51783a25d02190769345f0c6a4af79c29665b180e5b46fc28f0b6eeee31
|
|
4
|
+
data.tar.gz: b3d9d706cc185c2d045ed8d3c9a62bb884196cc2cc520cd51fd17cecb07cd897
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6626a13b9dda295113caa1e1d99085afd20776b1d3d4f156bbdf63efcefccef59cd1ea37d14439aa7487f3f617ed65ac1003ec775b22e77614a4b0082c832307
|
|
7
|
+
data.tar.gz: 68b26d50fa35b206835f633dfd7b26f6c389fc3507a524a84784929b089e0c699d1361af8838d86e5fcec0e8031f81ca82e2e799b544737cc1616a3da5d15707
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,11 +1,65 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2026-
|
|
3
|
+
# on 2026-05-03 12:53:32 UTC using RuboCop version 1.86.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
+
# Offense count: 5
|
|
10
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
11
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
12
|
+
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
13
|
+
Layout/ArgumentAlignment:
|
|
14
|
+
Exclude:
|
|
15
|
+
- 'lib/moxml/adapter/base.rb'
|
|
16
|
+
- 'lib/moxml/adapter/libxml.rb'
|
|
17
|
+
- 'lib/moxml/builder.rb'
|
|
18
|
+
|
|
19
|
+
# Offense count: 2
|
|
20
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
21
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
22
|
+
# SupportedStyles: with_first_element, with_fixed_indentation
|
|
23
|
+
Layout/ArrayAlignment:
|
|
24
|
+
Exclude:
|
|
25
|
+
- 'lib/moxml/xpath/compiler.rb'
|
|
26
|
+
|
|
27
|
+
# Offense count: 9
|
|
28
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
29
|
+
# Configuration parameters: EnforcedStyleAlignWith.
|
|
30
|
+
# SupportedStylesAlignWith: either, start_of_block, start_of_line
|
|
31
|
+
Layout/BlockAlignment:
|
|
32
|
+
Exclude:
|
|
33
|
+
- 'lib/moxml/adapter/libxml.rb'
|
|
34
|
+
- 'lib/moxml/adapter/ox.rb'
|
|
35
|
+
- 'spec/integration/shared_examples/edge_cases.rb'
|
|
36
|
+
- 'spec/integration/shared_examples/high_level/document_builder_behavior.rb'
|
|
37
|
+
- 'spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb'
|
|
38
|
+
- 'spec/moxml/xpath/functions/node_functions_spec.rb'
|
|
39
|
+
|
|
40
|
+
# Offense count: 9
|
|
41
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
42
|
+
Layout/BlockEndNewline:
|
|
43
|
+
Exclude:
|
|
44
|
+
- 'lib/moxml/adapter/libxml.rb'
|
|
45
|
+
- 'lib/moxml/adapter/ox.rb'
|
|
46
|
+
- 'spec/integration/shared_examples/edge_cases.rb'
|
|
47
|
+
- 'spec/integration/shared_examples/high_level/document_builder_behavior.rb'
|
|
48
|
+
- 'spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb'
|
|
49
|
+
- 'spec/moxml/xpath/functions/node_functions_spec.rb'
|
|
50
|
+
|
|
51
|
+
# Offense count: 2
|
|
52
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
53
|
+
Layout/ClosingParenthesisIndentation:
|
|
54
|
+
Exclude:
|
|
55
|
+
- 'lib/moxml/adapter/oga.rb'
|
|
56
|
+
|
|
57
|
+
# Offense count: 1
|
|
58
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
59
|
+
Layout/ElseAlignment:
|
|
60
|
+
Exclude:
|
|
61
|
+
- 'lib/moxml/adapter/base.rb'
|
|
62
|
+
|
|
9
63
|
# Offense count: 4
|
|
10
64
|
# This cop supports safe autocorrection (--autocorrect).
|
|
11
65
|
Layout/EmptyLineAfterGuardClause:
|
|
@@ -27,13 +81,67 @@ Layout/EmptyLines:
|
|
|
27
81
|
Exclude:
|
|
28
82
|
- 'lib/moxml/adapter/ox.rb'
|
|
29
83
|
|
|
30
|
-
# Offense count:
|
|
84
|
+
# Offense count: 2
|
|
85
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
86
|
+
Layout/EmptyLinesAroundMethodBody:
|
|
87
|
+
Exclude:
|
|
88
|
+
- 'lib/moxml/adapter/ox.rb'
|
|
89
|
+
|
|
90
|
+
# Offense count: 1
|
|
91
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
92
|
+
# Configuration parameters: EnforcedStyleAlignWith.
|
|
93
|
+
# SupportedStylesAlignWith: keyword, variable, start_of_line
|
|
94
|
+
Layout/EndAlignment:
|
|
95
|
+
Exclude:
|
|
96
|
+
- 'lib/moxml/adapter/base.rb'
|
|
97
|
+
|
|
98
|
+
# Offense count: 2
|
|
99
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
100
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
101
|
+
# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses
|
|
102
|
+
Layout/FirstArgumentIndentation:
|
|
103
|
+
Exclude:
|
|
104
|
+
- 'lib/moxml/adapter/oga.rb'
|
|
105
|
+
|
|
106
|
+
# Offense count: 2
|
|
107
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
108
|
+
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
|
109
|
+
# SupportedHashRocketStyles: key, separator, table
|
|
110
|
+
# SupportedColonStyles: key, separator, table
|
|
111
|
+
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
|
112
|
+
Layout/HashAlignment:
|
|
113
|
+
Exclude:
|
|
114
|
+
- 'lib/moxml/builder.rb'
|
|
115
|
+
|
|
116
|
+
# Offense count: 20
|
|
117
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
118
|
+
# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
|
|
119
|
+
# SupportedStylesAlignWith: start_of_line, relative_to_receiver
|
|
120
|
+
Layout/IndentationWidth:
|
|
121
|
+
Exclude:
|
|
122
|
+
- 'lib/moxml/adapter/base.rb'
|
|
123
|
+
- 'lib/moxml/adapter/libxml.rb'
|
|
124
|
+
- 'lib/moxml/adapter/ox.rb'
|
|
125
|
+
- 'spec/integration/shared_examples/edge_cases.rb'
|
|
126
|
+
- 'spec/integration/shared_examples/high_level/document_builder_behavior.rb'
|
|
127
|
+
- 'spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb'
|
|
128
|
+
- 'spec/moxml/xpath/functions/node_functions_spec.rb'
|
|
129
|
+
|
|
130
|
+
# Offense count: 344
|
|
31
131
|
# This cop supports safe autocorrection (--autocorrect).
|
|
32
132
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
33
133
|
# URISchemes: http, https
|
|
34
134
|
Layout/LineLength:
|
|
35
135
|
Enabled: false
|
|
36
136
|
|
|
137
|
+
# Offense count: 2
|
|
138
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
139
|
+
# Configuration parameters: EnforcedStyle.
|
|
140
|
+
# SupportedStyles: symmetrical, new_line, same_line
|
|
141
|
+
Layout/MultilineMethodCallBraceLayout:
|
|
142
|
+
Exclude:
|
|
143
|
+
- 'lib/moxml/adapter/oga.rb'
|
|
144
|
+
|
|
37
145
|
# Offense count: 1
|
|
38
146
|
# This cop supports safe autocorrection (--autocorrect).
|
|
39
147
|
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
@@ -42,6 +150,17 @@ Layout/MultilineOperationIndentation:
|
|
|
42
150
|
Exclude:
|
|
43
151
|
- 'lib/moxml/adapter/ox.rb'
|
|
44
152
|
|
|
153
|
+
# Offense count: 10
|
|
154
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
155
|
+
# Configuration parameters: AllowInHeredoc.
|
|
156
|
+
Layout/TrailingWhitespace:
|
|
157
|
+
Exclude:
|
|
158
|
+
- 'lib/moxml/adapter/base.rb'
|
|
159
|
+
- 'lib/moxml/adapter/libxml.rb'
|
|
160
|
+
- 'lib/moxml/adapter/ox.rb'
|
|
161
|
+
- 'lib/moxml/builder.rb'
|
|
162
|
+
- 'lib/moxml/xpath/compiler.rb'
|
|
163
|
+
|
|
45
164
|
# Offense count: 7
|
|
46
165
|
# Configuration parameters: AllowedMethods.
|
|
47
166
|
# AllowedMethods: enums
|
|
@@ -61,11 +180,10 @@ Lint/DuplicateBranch:
|
|
|
61
180
|
- 'lib/moxml/document.rb'
|
|
62
181
|
- 'lib/moxml/entity_registry.rb'
|
|
63
182
|
|
|
64
|
-
# Offense count:
|
|
183
|
+
# Offense count: 4
|
|
65
184
|
Lint/DuplicateMethods:
|
|
66
185
|
Exclude:
|
|
67
186
|
- 'lib/moxml/config.rb'
|
|
68
|
-
- 'lib/moxml/element.rb'
|
|
69
187
|
- 'lib/moxml/node.rb'
|
|
70
188
|
|
|
71
189
|
# Offense count: 4
|
|
@@ -91,7 +209,7 @@ Lint/EmptyWhen:
|
|
|
91
209
|
# Offense count: 3
|
|
92
210
|
Lint/HashCompareByIdentity:
|
|
93
211
|
Exclude:
|
|
94
|
-
- 'lib/moxml/native_attachment.rb'
|
|
212
|
+
- 'lib/moxml/native_attachment/native.rb'
|
|
95
213
|
|
|
96
214
|
# Offense count: 1
|
|
97
215
|
Lint/IneffectiveAccessModifier:
|
|
@@ -127,12 +245,12 @@ Metrics/BlockLength:
|
|
|
127
245
|
Metrics/BlockNesting:
|
|
128
246
|
Max: 4
|
|
129
247
|
|
|
130
|
-
# Offense count:
|
|
248
|
+
# Offense count: 75
|
|
131
249
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
132
250
|
Metrics/CyclomaticComplexity:
|
|
133
251
|
Enabled: false
|
|
134
252
|
|
|
135
|
-
# Offense count:
|
|
253
|
+
# Offense count: 188
|
|
136
254
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
137
255
|
Metrics/MethodLength:
|
|
138
256
|
Max: 110
|
|
@@ -180,6 +298,12 @@ Naming/VariableNumber:
|
|
|
180
298
|
- 'spec/moxml/allocation_guard_spec.rb'
|
|
181
299
|
- 'spec/support/allocation_helper.rb'
|
|
182
300
|
|
|
301
|
+
# Offense count: 1
|
|
302
|
+
# Configuration parameters: MinSize.
|
|
303
|
+
Performance/CollectionLiteralInLoop:
|
|
304
|
+
Exclude:
|
|
305
|
+
- 'lib/moxml/xpath/compiler.rb'
|
|
306
|
+
|
|
183
307
|
# Offense count: 5
|
|
184
308
|
RSpec/BeforeAfterAll:
|
|
185
309
|
Exclude:
|
|
@@ -205,12 +329,12 @@ RSpec/ContextWording:
|
|
|
205
329
|
- 'spec/moxml/xpath/parser_spec.rb'
|
|
206
330
|
- 'spec/performance/benchmark_spec.rb'
|
|
207
331
|
|
|
208
|
-
# Offense count:
|
|
332
|
+
# Offense count: 25
|
|
209
333
|
# Configuration parameters: IgnoredMetadata.
|
|
210
334
|
RSpec/DescribeClass:
|
|
211
335
|
Enabled: false
|
|
212
336
|
|
|
213
|
-
# Offense count:
|
|
337
|
+
# Offense count: 328
|
|
214
338
|
# Configuration parameters: CountAsOne.
|
|
215
339
|
RSpec/ExampleLength:
|
|
216
340
|
Max: 64
|
|
@@ -240,13 +364,13 @@ RSpec/LeakyConstantDeclaration:
|
|
|
240
364
|
- 'spec/moxml/declaration_preservation_spec.rb'
|
|
241
365
|
- 'spec/moxml/sax_spec.rb'
|
|
242
366
|
|
|
243
|
-
# Offense count:
|
|
367
|
+
# Offense count: 4
|
|
244
368
|
# Configuration parameters: .
|
|
245
369
|
# SupportedStyles: have_received, receive
|
|
246
370
|
RSpec/MessageSpies:
|
|
247
371
|
EnforcedStyle: receive
|
|
248
372
|
|
|
249
|
-
# Offense count:
|
|
373
|
+
# Offense count: 414
|
|
250
374
|
RSpec/MultipleExpectations:
|
|
251
375
|
Max: 10
|
|
252
376
|
|
|
@@ -306,6 +430,22 @@ Security/Eval:
|
|
|
306
430
|
Exclude:
|
|
307
431
|
- 'spec/moxml/xpath/ruby/generator_spec.rb'
|
|
308
432
|
|
|
433
|
+
# Offense count: 12
|
|
434
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
435
|
+
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
|
|
436
|
+
# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
|
|
437
|
+
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
|
|
438
|
+
# FunctionalMethods: let, let!, subject, watch
|
|
439
|
+
# AllowedMethods: lambda, proc, it
|
|
440
|
+
Style/BlockDelimiters:
|
|
441
|
+
Exclude:
|
|
442
|
+
- 'lib/moxml/adapter/libxml.rb'
|
|
443
|
+
- 'lib/moxml/adapter/ox.rb'
|
|
444
|
+
- 'spec/integration/shared_examples/edge_cases.rb'
|
|
445
|
+
- 'spec/integration/shared_examples/high_level/document_builder_behavior.rb'
|
|
446
|
+
- 'spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb'
|
|
447
|
+
- 'spec/moxml/xpath/functions/node_functions_spec.rb'
|
|
448
|
+
|
|
309
449
|
# Offense count: 1
|
|
310
450
|
Style/DocumentDynamicEvalDefinition:
|
|
311
451
|
Exclude:
|
|
@@ -329,6 +469,18 @@ Style/MissingRespondToMissing:
|
|
|
329
469
|
Exclude:
|
|
330
470
|
- 'lib/moxml/xpath/ruby/node.rb'
|
|
331
471
|
|
|
472
|
+
# Offense count: 1
|
|
473
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
474
|
+
Style/MultilineIfModifier:
|
|
475
|
+
Exclude:
|
|
476
|
+
- 'lib/moxml/builder.rb'
|
|
477
|
+
|
|
478
|
+
# Offense count: 1
|
|
479
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
480
|
+
Style/MultilineTernaryOperator:
|
|
481
|
+
Exclude:
|
|
482
|
+
- 'lib/moxml/adapter/base.rb'
|
|
483
|
+
|
|
332
484
|
# Offense count: 1
|
|
333
485
|
# This cop supports safe autocorrection (--autocorrect).
|
|
334
486
|
# Configuration parameters: AllowMethodComparison, ComparisonsThreshold.
|
|
@@ -365,7 +517,25 @@ Style/RedundantConstantBase:
|
|
|
365
517
|
- 'spec/moxml/adapter/headed_ox_spec.rb'
|
|
366
518
|
|
|
367
519
|
# Offense count: 1
|
|
520
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
521
|
+
Style/RedundantParentheses:
|
|
522
|
+
Exclude:
|
|
523
|
+
- 'lib/moxml/xpath/compiler.rb'
|
|
524
|
+
|
|
525
|
+
# Offense count: 8
|
|
368
526
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
369
527
|
Style/SelectByKind:
|
|
370
528
|
Exclude:
|
|
529
|
+
- 'spec/integration/shared_examples/edge_cases.rb'
|
|
530
|
+
- 'spec/integration/shared_examples/entity_reference_whitespace.rb'
|
|
371
531
|
- 'spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb'
|
|
532
|
+
- 'spec/integration/shared_examples/node_wrappers/node_set_behavior.rb'
|
|
533
|
+
- 'spec/moxml/xpath/functions/node_functions_spec.rb'
|
|
534
|
+
|
|
535
|
+
# Offense count: 2
|
|
536
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
537
|
+
# Configuration parameters: EnforcedStyle, MinSize.
|
|
538
|
+
# SupportedStyles: percent, brackets
|
|
539
|
+
Style/SymbolArray:
|
|
540
|
+
Exclude:
|
|
541
|
+
- 'lib/moxml/xpath/compiler.rb'
|
data/README.adoc
CHANGED
|
@@ -33,7 +33,7 @@ Moxml supports the following XML libraries:
|
|
|
33
33
|
REXML:: https://github.com/ruby/rexml[REXML], a pure Ruby XML parser
|
|
34
34
|
distributed with standard Ruby. Not the fastest, but always available.
|
|
35
35
|
|
|
36
|
-
Nokogiri::
|
|
36
|
+
Nokogiri:: https://github.com/sparklemotion/nokogiri[Nokogiri], a
|
|
37
37
|
widely used implementation which wraps around the performant
|
|
38
38
|
https://github.com/GNOME/libxml2[libxml2] C library.
|
|
39
39
|
|
|
@@ -47,6 +47,29 @@ LibXML:: https://github.com/xml4r/libxml-ruby[libxml-ruby], Ruby bindings
|
|
|
47
47
|
for the performant https://github.com/GNOME/libxml2[libxml2] C library.
|
|
48
48
|
Alternative to Nokogiri with similar performance characteristics.
|
|
49
49
|
|
|
50
|
+
==== Default adapter selection
|
|
51
|
+
|
|
52
|
+
When no adapter is explicitly specified, Moxml selects one automatically based on
|
|
53
|
+
the runtime environment:
|
|
54
|
+
|
|
55
|
+
. If running on Opal (`RUBY_ENGINE == "opal"`), Oga is used (pure Ruby, no C extensions)
|
|
56
|
+
. Otherwise, Moxml detects already-loaded XML libraries in this order:
|
|
57
|
+
.. Nokogiri
|
|
58
|
+
.. Ox
|
|
59
|
+
.. Oga
|
|
60
|
+
. If none of the above are loaded, Nokogiri is the fallback default
|
|
61
|
+
|
|
62
|
+
[source,ruby]
|
|
63
|
+
----
|
|
64
|
+
# Automatic detection — picks the best available adapter
|
|
65
|
+
ctx = Moxml.new
|
|
66
|
+
ctx.config.adapter_name # => :nokogiri (or :oga on Opal)
|
|
67
|
+
|
|
68
|
+
# Explicit override — always takes precedence
|
|
69
|
+
ctx = Moxml.new(:ox)
|
|
70
|
+
ctx.config.adapter_name # => :ox
|
|
71
|
+
----
|
|
72
|
+
|
|
50
73
|
=== Feature table
|
|
51
74
|
|
|
52
75
|
Moxml exercises its best effort to provide a consistent interface across basic
|
data/lib/moxml/adapter/base.rb
CHANGED
|
@@ -35,7 +35,12 @@ module Moxml
|
|
|
35
35
|
# not valid UTF-8, fall back to encoding as UTF-8 with
|
|
36
36
|
# replacement to avoid raising on gsub.
|
|
37
37
|
dup = xml.dup.force_encoding("UTF-8")
|
|
38
|
-
dup.valid_encoding?
|
|
38
|
+
if dup.valid_encoding?
|
|
39
|
+
dup
|
|
40
|
+
else
|
|
41
|
+
xml.dup.encode("UTF-8",
|
|
42
|
+
"ASCII-8BIT", invalid: :replace, undef: :replace)
|
|
43
|
+
end
|
|
39
44
|
elsif xml.encoding == Encoding::UTF_8
|
|
40
45
|
xml
|
|
41
46
|
else
|
|
@@ -3,8 +3,21 @@
|
|
|
3
3
|
module Moxml
|
|
4
4
|
module Adapter
|
|
5
5
|
module CustomizedOx
|
|
6
|
-
# Ox uses Strings, but a
|
|
7
|
-
|
|
6
|
+
# Ox uses Strings for text content, but a String cannot carry a @parent
|
|
7
|
+
# back-reference. We subclass ::Ox::Node so a Text wrapper can hold one.
|
|
8
|
+
#
|
|
9
|
+
# ::Ox::Node subclasses that are neither ::Ox::Element nor ::Ox::Document
|
|
10
|
+
# are unknown to Ox.dump's standard XML emitter, so they fall through to
|
|
11
|
+
# Ox's generic object-marshalling format. The serializer in
|
|
12
|
+
# Moxml::Adapter::Ox#serialize special-cases this class to emit the value
|
|
13
|
+
# with proper XML escaping. The #to_s override ensures string
|
|
14
|
+
# interpolation (`"#{text}"`) produces the text content rather than the
|
|
15
|
+
# default Object representation.
|
|
16
|
+
class Text < ::Ox::Node
|
|
17
|
+
def to_s
|
|
18
|
+
value.to_s
|
|
19
|
+
end
|
|
20
|
+
end
|
|
8
21
|
end
|
|
9
22
|
end
|
|
10
23
|
end
|
data/lib/moxml/adapter/libxml.rb
CHANGED
|
@@ -565,7 +565,8 @@ module Moxml
|
|
|
565
565
|
# Set as root element
|
|
566
566
|
native_elem.root = native_child
|
|
567
567
|
# Flag for actual_native to refresh the wrapper's native reference
|
|
568
|
-
attachments.set(native_elem, :_pending_root_refresh,
|
|
568
|
+
attachments.set(native_elem, :_pending_root_refresh,
|
|
569
|
+
native_child.object_id)
|
|
569
570
|
elsif native_elem.root
|
|
570
571
|
# Document has root, add to it instead
|
|
571
572
|
import_and_add(native_elem.doc, native_elem.root, native_child)
|
|
@@ -594,6 +595,7 @@ module Moxml
|
|
|
594
595
|
def lookup_entity_refs(doc, element)
|
|
595
596
|
pairs = attachments.get(doc, :_entity_ref_pairs)
|
|
596
597
|
return nil unless pairs
|
|
598
|
+
|
|
597
599
|
pair = pairs.find { |elem, _| elem == element }
|
|
598
600
|
pair&.last
|
|
599
601
|
end
|
|
@@ -614,6 +616,7 @@ module Moxml
|
|
|
614
616
|
def lookup_child_sequence(doc, element)
|
|
615
617
|
pairs = attachments.get(doc, :_child_seq_pairs)
|
|
616
618
|
return nil unless pairs
|
|
619
|
+
|
|
617
620
|
pair = pairs.find { |elem, _| elem == element }
|
|
618
621
|
pair&.last
|
|
619
622
|
end
|
|
@@ -1481,7 +1484,9 @@ module Moxml
|
|
|
1481
1484
|
# Interleave native children with entity refs using tracked sequence
|
|
1482
1485
|
native_children = []
|
|
1483
1486
|
if elem.children?
|
|
1484
|
-
elem.each_child
|
|
1487
|
+
elem.each_child do |c|
|
|
1488
|
+
native_children << c unless c.text? && c.content.to_s.strip.empty?
|
|
1489
|
+
end
|
|
1485
1490
|
end
|
|
1486
1491
|
|
|
1487
1492
|
eref_idx = 0
|
data/lib/moxml/adapter/oga.rb
CHANGED
|
@@ -470,7 +470,9 @@ module Moxml
|
|
|
470
470
|
if node.is_a?(::Oga::XML::Document)
|
|
471
471
|
# Check if we should include declaration
|
|
472
472
|
# Priority: explicit option > existence of xml_declaration (native or attachment)
|
|
473
|
-
effective_xml_declaration = node.xml_declaration || attachments.get(
|
|
473
|
+
effective_xml_declaration = node.xml_declaration || attachments.get(
|
|
474
|
+
node, :xml_declaration
|
|
475
|
+
)
|
|
474
476
|
should_include_decl = if options.key?(:no_declaration)
|
|
475
477
|
!options[:no_declaration]
|
|
476
478
|
elsif options.key?(:declaration)
|
|
@@ -519,7 +521,9 @@ module Moxml
|
|
|
519
521
|
|
|
520
522
|
# Default: use XmlGenerator
|
|
521
523
|
# But first check if we need to handle declaration specially
|
|
522
|
-
effective_xml_declaration = node.is_a?(::Oga::XML::Document) && (node.xml_declaration || attachments.get(
|
|
524
|
+
effective_xml_declaration = node.is_a?(::Oga::XML::Document) && (node.xml_declaration || attachments.get(
|
|
525
|
+
node, :xml_declaration
|
|
526
|
+
))
|
|
523
527
|
if node.is_a?(::Oga::XML::Document) && effective_xml_declaration
|
|
524
528
|
# Document has declaration - use custom handling to avoid duplicates
|
|
525
529
|
output = []
|
data/lib/moxml/adapter/ox.rb
CHANGED
|
@@ -74,8 +74,7 @@ module Moxml
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def create_native_element(name, _owner_doc = nil)
|
|
77
|
-
|
|
78
|
-
element
|
|
77
|
+
::Ox::Element.new(name)
|
|
79
78
|
end
|
|
80
79
|
|
|
81
80
|
def create_native_text(content, _owner_doc = nil)
|
|
@@ -622,6 +621,12 @@ module Moxml
|
|
|
622
621
|
end
|
|
623
622
|
|
|
624
623
|
def serialize(node, options = {})
|
|
624
|
+
# CustomizedOx::Text subclasses ::Ox::Node so it can carry a @parent
|
|
625
|
+
# back-reference, but that makes it unknown to Ox.dump's XML emitter,
|
|
626
|
+
# which then falls back to generic object marshalling. Short-circuit
|
|
627
|
+
# here with proper XML escaping.
|
|
628
|
+
return escape_xml_text(node.value) if node.is_a?(CustomizedOx::Text)
|
|
629
|
+
|
|
625
630
|
needs_custom = needs_custom_serialize?(node)
|
|
626
631
|
|
|
627
632
|
unless needs_custom
|
|
@@ -643,7 +648,7 @@ module Moxml
|
|
|
643
648
|
return true if attachments.get(node, :has_entity_refs)
|
|
644
649
|
return true if attachments.get(node, :has_cdata_end_markers)
|
|
645
650
|
return false if attachments.key?(node, :has_entity_refs) &&
|
|
646
|
-
|
|
651
|
+
attachments.key?(node, :has_cdata_end_markers)
|
|
647
652
|
end
|
|
648
653
|
|
|
649
654
|
# Only scan tree on first call — short-circuit on first hit
|
|
@@ -694,9 +699,8 @@ module Moxml
|
|
|
694
699
|
encoding: options[:encoding],
|
|
695
700
|
no_empty: options[:expand_empty],
|
|
696
701
|
}
|
|
697
|
-
|
|
702
|
+
output + ::Ox.dump(node, ox_options)
|
|
698
703
|
# Fix CDATA ]]> end markers that Ox doesn't escape
|
|
699
|
-
result
|
|
700
704
|
end
|
|
701
705
|
|
|
702
706
|
def tree_has_entity_references?(node)
|
|
@@ -721,9 +725,13 @@ module Moxml
|
|
|
721
725
|
when ::Ox::CData
|
|
722
726
|
node.value&.include?("]]>") || false
|
|
723
727
|
when ::Ox::Element
|
|
724
|
-
node.nodes&.any?
|
|
728
|
+
node.nodes&.any? do |child|
|
|
729
|
+
tree_has_cdata_end_markers?(child)
|
|
730
|
+
end || false
|
|
725
731
|
when ::Ox::Document
|
|
726
|
-
node.nodes&.any?
|
|
732
|
+
node.nodes&.any? do |child|
|
|
733
|
+
tree_has_cdata_end_markers?(child)
|
|
734
|
+
end || false
|
|
727
735
|
else
|
|
728
736
|
false
|
|
729
737
|
end
|
|
@@ -814,7 +822,6 @@ module Moxml
|
|
|
814
822
|
end
|
|
815
823
|
end
|
|
816
824
|
|
|
817
|
-
|
|
818
825
|
# Translate a subset of XPath to Ox locate() syntax
|
|
819
826
|
# Supports: //element, /path/to/element, .//element, element[@attr]
|
|
820
827
|
# Note: Ox locate() doesn't support namespace prefixes in the path
|
data/lib/moxml/builder.rb
CHANGED
|
@@ -30,12 +30,14 @@ module Moxml
|
|
|
30
30
|
# and a valid XML tag name (XSD/RelaxNG).
|
|
31
31
|
def element(name_or_attrs = nil, attributes = {}, &block)
|
|
32
32
|
if name_or_attrs.is_a?(Hash)
|
|
33
|
-
return create_element_node("element", name_or_attrs, block: block,
|
|
33
|
+
return create_element_node("element", name_or_attrs, block: block,
|
|
34
|
+
eval_block: false)
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
raise ArgumentError, "element requires a tag name" if name_or_attrs.nil?
|
|
37
38
|
|
|
38
|
-
create_element_node(name_or_attrs, attributes, block: block,
|
|
39
|
+
create_element_node(name_or_attrs, attributes, block: block,
|
|
40
|
+
eval_block: true)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
def text(content)
|
|
@@ -101,10 +103,14 @@ module Moxml
|
|
|
101
103
|
text_content = args.first.is_a?(String) ? args.shift : nil
|
|
102
104
|
attrs = args.first.is_a?(Hash) ? args.shift : {}
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
unless args.empty?
|
|
107
|
+
raise ArgumentError,
|
|
108
|
+
"unexpected arguments for #{method_name}: #{args.inspect}"
|
|
109
|
+
end
|
|
105
110
|
|
|
106
111
|
if text_content && block
|
|
107
|
-
raise ArgumentError,
|
|
112
|
+
raise ArgumentError,
|
|
113
|
+
"#{method_name}: cannot combine text content with a block"
|
|
108
114
|
end
|
|
109
115
|
|
|
110
116
|
# Strip trailing underscore to allow reserved Ruby method names as tags
|
|
@@ -126,7 +132,8 @@ module Moxml
|
|
|
126
132
|
# Single method for all element creation.
|
|
127
133
|
# eval_block: true → instance_eval (build DSL context)
|
|
128
134
|
# eval_block: false → yield (preserves caller's self)
|
|
129
|
-
def create_element_node(tag_name, attrs = {}, text_content: nil,
|
|
135
|
+
def create_element_node(tag_name, attrs = {}, text_content: nil,
|
|
136
|
+
block: nil, eval_block: true)
|
|
130
137
|
el = @document.create_element(tag_name)
|
|
131
138
|
|
|
132
139
|
attrs.each do |key, value|
|
data/lib/moxml/config.rb
CHANGED
|
@@ -156,6 +156,7 @@ module Moxml
|
|
|
156
156
|
def primary_name_for_codepoint(codepoint)
|
|
157
157
|
names = @by_codepoint[codepoint]
|
|
158
158
|
return nil unless names&.any?
|
|
159
|
+
|
|
159
160
|
# Prefer lowercase names (e.g., "amp" over "AMP") for XML compatibility
|
|
160
161
|
names.find { |n| n == n.downcase } || names.first
|
|
161
162
|
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "monitor"
|
|
4
|
+
|
|
5
|
+
module Moxml
|
|
6
|
+
class NativeAttachment
|
|
7
|
+
# Stores Moxml-specific state associated with native adapter objects
|
|
8
|
+
# without polluting their internals.
|
|
9
|
+
#
|
|
10
|
+
# Uses object_id as key with GC finalizer cleanup to prevent memory leaks.
|
|
11
|
+
# Thread-safe via Monitor (reentrant-safe).
|
|
12
|
+
#
|
|
13
|
+
# Replaces the anti-pattern of using instance_variable_set/get on
|
|
14
|
+
# foreign library objects (Nokogiri, REXML, Oga, Ox, LibXML nodes).
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# attachments = NativeAttachment.new
|
|
18
|
+
# attachments.set(native_element, :entity_refs, [])
|
|
19
|
+
# refs = attachments.get(native_element, :entity_refs)
|
|
20
|
+
# attachments.key?(native_element, :doctype) #=> false
|
|
21
|
+
class Native
|
|
22
|
+
def initialize
|
|
23
|
+
@data = {}
|
|
24
|
+
@finalizer_registered = {}
|
|
25
|
+
@monitor = Monitor.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get(native, key)
|
|
29
|
+
@monitor.synchronize { @data[native.object_id]&.[](key) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def set(native, key, value)
|
|
33
|
+
id = native.object_id
|
|
34
|
+
@monitor.synchronize do
|
|
35
|
+
@data[id] ||= {}
|
|
36
|
+
@data[id][key] = value
|
|
37
|
+
register_finalizer(native, id) unless @finalizer_registered[id]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def key?(native, key)
|
|
42
|
+
@monitor.synchronize { @data[native.object_id]&.key?(key) || false }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def delete(native, key)
|
|
46
|
+
@monitor.synchronize { @data[native.object_id]&.delete(key) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def register_finalizer(native, id)
|
|
52
|
+
@finalizer_registered[id] = true
|
|
53
|
+
ObjectSpace.define_finalizer(native, finalizer_for(id))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def finalizer_for(id)
|
|
57
|
+
data = @data
|
|
58
|
+
registered = @finalizer_registered
|
|
59
|
+
# Finalizers must NOT use Mutex/Monitor (can't be called from trap context).
|
|
60
|
+
# Direct Hash operations are safe here since finalizers run sequentially
|
|
61
|
+
# and the GC'd object's id won't be accessed by any other thread.
|
|
62
|
+
proc do
|
|
63
|
+
data.delete(id)
|
|
64
|
+
registered.delete(id)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|