compare-xml 0.65 → 1.0.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.
- checksums.yaml +5 -5
- data/README.md +110 -35
- data/lib/compare-xml/version.rb +1 -1
- data/lib/compare-xml.rb +215 -138
- metadata +14 -22
- data/.gitignore +0 -13
- data/.rubocop.yml +0 -42
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/compare-xml.gemspec +0 -22
- data/img/diffing.png +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '044582cb43241dc59a65422b8c36a2cc96d1a3120f9648c0950b7ef9fe125be5'
|
|
4
|
+
data.tar.gz: 3a33391e6f5a67f305546173a43725a64bd6ed4df12135434453da9bc3982e98
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0f3a32d528e4298a96ce59238f7ed556fa110ea7a4d715b651e1e578887f6bde9c6852928d0971c114e6ca9fb77dca8c4e6ca53c9580143cc5d89785ca14d942
|
|
7
|
+
data.tar.gz: fc8371bdce1193d15d42092c24b9b44d2d9c51a685331102ba69fd9290cafdd6faf588feab68871b1c7659c42ed1f1656718b97b72fbf0702bde153e3a2caf30
|
data/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# CompareXML
|
|
2
2
|
|
|
3
|
-
[](https://rubygems.org/gems/compare-xml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://github.com/vkononov/compare-xml/actions/workflows/lint.yml)
|
|
6
|
+
[](https://github.com/vkononov/compare-xml/actions/workflows/test.yml)
|
|
4
7
|
|
|
5
|
-
CompareXML is a fast, lightweight and feature-rich tool that will solve your XML/HTML comparison or diffing needs.
|
|
8
|
+
CompareXML is a fast, lightweight and feature-rich tool that will solve your XML/HTML comparison or diffing needs. Its purpose is to compare two instances of `Nokogiri::XML::Node` or `Nokogiri::XML::NodeSet` for equality or equivalency.
|
|
9
|
+
|
|
10
|
+
[](https://www.buymeacoffee.com/vkononov)
|
|
6
11
|
|
|
7
12
|
**Features**
|
|
8
13
|
|
|
@@ -63,7 +68,7 @@ require 'compare-xml'
|
|
|
63
68
|
CompareXML has a variety of options that can be invoked as an optional argument, e.g.:
|
|
64
69
|
|
|
65
70
|
```ruby
|
|
66
|
-
CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true
|
|
71
|
+
CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true})
|
|
67
72
|
```
|
|
68
73
|
|
|
69
74
|
- `collapse_whitespace: {true|false}` default: **`true`** [show examples ⇨](#collapse_whitespace)
|
|
@@ -78,6 +83,9 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
78
83
|
- `ignore_attrs: [css_selector1, css_selector1, ...]` default: **`[]`** [show examples ⇨](#ignore_attrs)
|
|
79
84
|
- when provided, ignores specific *attributes* using [CSS selectors](http://www.w3schools.com/cssref/css_selectors.asp)
|
|
80
85
|
|
|
86
|
+
- `ignore_attrs_by_name: [matcher1, matcher2, ...]` default: **`[]`** [show examples ⇨](#ignore_attrs_by_name)
|
|
87
|
+
- when provided, ignores *attributes* whose name matches a `String` (exact) or `Regexp` (pattern)
|
|
88
|
+
|
|
81
89
|
- `ignore_comments: {true|false}` default: **`true`** [show examples ⇨](#ignore_comments)
|
|
82
90
|
- when `true`, ignores comments, such as `<!-- comment -->`
|
|
83
91
|
|
|
@@ -90,6 +98,15 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
90
98
|
- `verbose: {true|false}` default: **`false`** [show examples ⇨](#verbose)
|
|
91
99
|
- when `true`, instead of a boolean, `CompareXML.equivalent?` returns an array of discrepancies.
|
|
92
100
|
|
|
101
|
+
- `ignore_children {true|false}` default **`false`** [show examples ⇨](#ignore_children)
|
|
102
|
+
- when `true`, the subnodes of a node in the xml are ignored
|
|
103
|
+
|
|
104
|
+
- `force_children {true|false}` default **`false`** [show examples ⇨](#force_children)
|
|
105
|
+
- when `true`, the subnodes of a node are checked independently of the status of the parent node
|
|
106
|
+
|
|
107
|
+
- `align_children {true|false}` default **`false`** [show examples ⇨](#align_children)
|
|
108
|
+
- when `true` (in `verbose` mode), aligns child nodes so insertions and removals are reported as additions/removals instead of changes
|
|
109
|
+
|
|
93
110
|
|
|
94
111
|
## Options in Depth
|
|
95
112
|
|
|
@@ -185,6 +202,28 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
185
202
|
<a href="http://google.ca" class="primary button">Link</a>
|
|
186
203
|
<a href="https://google.com" class="primary button rounded">Link</a>
|
|
187
204
|
|
|
205
|
+
----------
|
|
206
|
+
|
|
207
|
+
- <a id="ignore_attrs_by_name"></a>`ignore_attrs_by_name: [matcher1, matcher2, ...]` default: **`[]`**
|
|
208
|
+
|
|
209
|
+
When provided, ignores all **attributes** whose name matches one of the given matchers. A `String` matches the attribute name exactly, while a `Regexp` matches it as a pattern.
|
|
210
|
+
|
|
211
|
+
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {ignore_attrs_by_name: ['target', /^data-/]})`
|
|
212
|
+
|
|
213
|
+
**Example:** With `ignore_attrs_by_name: ['target', 'rel']` the following HTML strings are considered equal:
|
|
214
|
+
|
|
215
|
+
<a href="/admin" class="button" target="_blank">Link</a>
|
|
216
|
+
<a href="/admin" class="button" target="_self" rel="nofollow">Link</a>
|
|
217
|
+
|
|
218
|
+
**Example:** With `ignore_attrs_by_name: [/^data-/]` the following HTML strings are considered equal:
|
|
219
|
+
|
|
220
|
+
<div data-id="1" data-role="row">Link</div>
|
|
221
|
+
<div data-id="2" data-role="cell">Link</div>
|
|
222
|
+
|
|
223
|
+
An ignored attribute does not need to be present on both elements. With `ignore_attrs_by_name: ['class']` the following HTML strings are considered equal:
|
|
224
|
+
|
|
225
|
+
<div class="foo"></div>
|
|
226
|
+
<div></div>
|
|
188
227
|
|
|
189
228
|
----------
|
|
190
229
|
|
|
@@ -259,42 +298,42 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
259
298
|
|
|
260
299
|
**Example:** When `true` given the following HTML strings:
|
|
261
300
|
|
|
262
|
-

|
|
263
302
|
|
|
264
303
|
`CompareXML.equivalent?(doc1, doc2, {verbose: true})` will produce an array shown below.
|
|
265
304
|
|
|
266
305
|
```ruby
|
|
267
306
|
[
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
307
|
+
{
|
|
308
|
+
node1: '<title>TITLE</title>',
|
|
309
|
+
node2: '<title>ANOTHER TITLE</title>',
|
|
310
|
+
diff1: 'TITLE',
|
|
311
|
+
diff2: 'ANOTHER TITLE'
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
node1: '<h1>SOME HEADING</h1>',
|
|
315
|
+
node2: '<h1 id="main">SOME HEADING</h1>',
|
|
316
|
+
diff1: nil,
|
|
317
|
+
diff2: 'id="main"'
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
node1: '<a href="/admin" rel="icon">Link</a>',
|
|
321
|
+
node2: '<a rel="button" href="/admin">Link</a>',
|
|
322
|
+
diff1: '"rel="icon"',
|
|
323
|
+
diff2: '"rel="button"'
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
node1: '<cite>Author Name</cite>',
|
|
327
|
+
node2: nil,
|
|
328
|
+
diff1: '<cite>Author Name</cite>',
|
|
329
|
+
diff2: nil
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
node1: '<p class="footer">FOOTER</p>',
|
|
333
|
+
node2: '<div class="footer">FOOTER</div>',
|
|
334
|
+
diff1: 'p',
|
|
335
|
+
diff2: 'div'
|
|
336
|
+
}
|
|
298
337
|
]
|
|
299
338
|
```
|
|
300
339
|
|
|
@@ -305,7 +344,43 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
305
344
|
diff1: [Nokogiri::XML::Node|String] left difference
|
|
306
345
|
diff2: [Nokogiri::XML::Node|String] right difference
|
|
307
346
|
|
|
347
|
+
----------
|
|
348
|
+
|
|
349
|
+
- <a id="ignore_children"></a>`ignore_children: {true|false}` default: **`false`**
|
|
308
350
|
|
|
351
|
+
When provided, ignores all **subnodes** of any node.
|
|
352
|
+
|
|
353
|
+
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {ignore_children: true})`
|
|
354
|
+
|
|
355
|
+
**Example:** With `ignore_children: true` the following HTML strings are considered equal:
|
|
356
|
+
|
|
357
|
+
<body><a href="/admin" class="icon" target="_blank">Link 1</a></body>
|
|
358
|
+
<body><a href="/index" class="button" target="_self" rel="nofollow">Link 2</a></body>
|
|
359
|
+
|
|
360
|
+
----------
|
|
361
|
+
|
|
362
|
+
- <a id="force_children"></a>`force_children: {true|false}` default: **`false`**
|
|
363
|
+
|
|
364
|
+
When provided, compares all **subnodes** of any node.
|
|
365
|
+
|
|
366
|
+
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {force_children: true})`
|
|
367
|
+
|
|
368
|
+
----------
|
|
369
|
+
|
|
370
|
+
- <a id="align_children"></a>`align_children: {true|false}` default: **`false`**
|
|
371
|
+
|
|
372
|
+
Only takes effect in `verbose` mode. When `true`, sibling nodes are aligned before comparison so that an inserted or removed node is reported as an addition or removal (with one side `nil`) rather than causing the remaining siblings to be reported as a series of changes. Identical subtrees are matched as anchors, and the unmatched nodes between them are compared in order.
|
|
373
|
+
|
|
374
|
+
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {verbose: true, align_children: true})`
|
|
375
|
+
|
|
376
|
+
**Example:** Given a left document with three children and a right document with only the third:
|
|
377
|
+
|
|
378
|
+
<properties><a/><b/><c/></properties>
|
|
379
|
+
<properties><c/></properties>
|
|
380
|
+
|
|
381
|
+
With `align_children: true`, `<a>` and `<b>` are reported as removals (`node2` is `nil`); without it, they collapse into a single positional change.
|
|
382
|
+
|
|
383
|
+
----------
|
|
309
384
|
|
|
310
385
|
## Contributing
|
|
311
386
|
|
|
@@ -325,4 +400,4 @@ This gem was inspired by [Michael B. Klein](https://github.com/mbklein)'s gem [`
|
|
|
325
400
|
|
|
326
401
|
## License
|
|
327
402
|
|
|
328
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
403
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/compare-xml/version.rb
CHANGED
data/lib/compare-xml.rb
CHANGED
|
@@ -16,6 +16,14 @@ module CompareXML
|
|
|
16
16
|
# when false, children of elements are not compared if the root is different
|
|
17
17
|
force_children: false,
|
|
18
18
|
|
|
19
|
+
# when true (verbose mode only), child nodes are aligned before being compared so that
|
|
20
|
+
# inserted or removed nodes are reported as additions/removals instead of as changes
|
|
21
|
+
align_children: false,
|
|
22
|
+
|
|
23
|
+
# when true, children of elements are never compared
|
|
24
|
+
# when false, children of elements are compared if root is different or see force_children
|
|
25
|
+
ignore_children: false,
|
|
26
|
+
|
|
19
27
|
# contains an array of user specified strings that is used to ignore any attributes
|
|
20
28
|
# whose content contains a string from this array (e.g. "good automobile" contains "mobile")
|
|
21
29
|
ignore_attr_content: [],
|
|
@@ -68,16 +76,16 @@ module CompareXML
|
|
|
68
76
|
# @param [Nokogiri::XML::Element] n1 left node element
|
|
69
77
|
# @param [Nokogiri::XML::Element] n2 right node element
|
|
70
78
|
# @param [Hash] opts user-overridden options
|
|
71
|
-
# @param [Hash]
|
|
72
|
-
# @param [Bool]
|
|
79
|
+
# @param [Hash] child_opts user-overridden options used for the child nodes
|
|
80
|
+
# @param [Bool] diff_children use different options for the child nodes
|
|
73
81
|
#
|
|
74
82
|
# @return true if equal, [Array] differences otherwise
|
|
75
83
|
#
|
|
76
|
-
def equivalent?(n1, n2, opts = {},
|
|
84
|
+
def equivalent?(n1, n2, opts = {}, child_opts = {}, diff_children = false)
|
|
77
85
|
opts = DEFAULTS_OPTS.merge(opts)
|
|
78
|
-
|
|
86
|
+
child_opts = DEFAULTS_OPTS.merge(child_opts)
|
|
79
87
|
differences = []
|
|
80
|
-
result =
|
|
88
|
+
result = diff_children ? compare_nodes(n1, n2, opts, differences, child_opts, diff_children: diff_children) : compare_nodes(n1, n2, opts, differences)
|
|
81
89
|
opts[:verbose] ? differences : result == EQUIVALENT
|
|
82
90
|
end
|
|
83
91
|
|
|
@@ -87,42 +95,41 @@ module CompareXML
|
|
|
87
95
|
# Compares two nodes for equivalence. The nodes could be any subclass
|
|
88
96
|
# of Nokogiri::XML::Node including node sets and document fragments.
|
|
89
97
|
#
|
|
90
|
-
# @param [Nokogiri::XML::
|
|
91
|
-
# @param [Nokogiri::XML::
|
|
98
|
+
# @param [Nokogiri::XML::Element] n1 left node
|
|
99
|
+
# @param [Nokogiri::XML::Element] n2 right node
|
|
92
100
|
# @param [Hash] opts user-overridden options
|
|
93
101
|
# @param [Array] differences inequivalence messages
|
|
94
|
-
# @param [Hash]
|
|
95
|
-
# @param [Bool]
|
|
96
|
-
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
102
|
+
# @param [Hash] child_opts user-overridden options used for the child nodes
|
|
103
|
+
# @param [Bool] diff_children use different options for the child nodes
|
|
97
104
|
#
|
|
98
105
|
# @return type of equivalence (from equivalence constants)
|
|
99
106
|
#
|
|
100
|
-
def
|
|
101
|
-
if n1.
|
|
107
|
+
def compare_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false)
|
|
108
|
+
if n1.instance_of?(n2.class)
|
|
102
109
|
case n1
|
|
103
110
|
when Nokogiri::XML::Comment
|
|
104
|
-
status =
|
|
111
|
+
status = compare_comment_nodes(n1, n2, opts, differences)
|
|
105
112
|
when Nokogiri::HTML::Document
|
|
106
|
-
status =
|
|
113
|
+
status = diff_children ? compare_document_nodes(n1, n2, opts, differences, child_opts, diff_children: diff_children) : compare_document_nodes(n1, n2, opts, differences)
|
|
107
114
|
when Nokogiri::XML::Element
|
|
108
|
-
status =
|
|
115
|
+
status = diff_children ? compare_element_nodes(n1, n2, opts, differences, child_opts, diff_children: diff_children) : compare_element_nodes(n1, n2, opts, differences)
|
|
109
116
|
when Nokogiri::XML::Text
|
|
110
|
-
status =
|
|
117
|
+
status = compare_text_nodes(n1, n2, opts, differences)
|
|
111
118
|
else
|
|
112
119
|
raise 'Comparison only allowed between objects of type Nokogiri::XML::Node and Nokogiri::XML::NodeSet.' unless n1.is_a?(Nokogiri::XML::Node) || n1.is_a?(Nokogiri::XML::NodeSet)
|
|
113
|
-
status =
|
|
120
|
+
status = compare_children(n1.children, n2.children, opts, differences)
|
|
114
121
|
end
|
|
115
122
|
elsif n1.nil? || n2.nil?
|
|
116
123
|
status = MISSING_NODE
|
|
117
|
-
|
|
124
|
+
add_difference(n1, n2, n1, n2, opts, differences)
|
|
118
125
|
else
|
|
119
126
|
status = UNEQUAL_NODES_TYPES
|
|
120
127
|
if n1.is_a? Nokogiri::XML::Text
|
|
121
|
-
|
|
128
|
+
add_difference(n1.parent, n2, n1, n2, opts, differences)
|
|
122
129
|
elsif n2.is_a? Nokogiri::XML::Text
|
|
123
|
-
|
|
130
|
+
add_difference(n1, n2.parent, n1, n2, opts, differences)
|
|
124
131
|
else
|
|
125
|
-
|
|
132
|
+
add_difference(n1, n2, n1, n2, opts, differences)
|
|
126
133
|
end
|
|
127
134
|
end
|
|
128
135
|
status
|
|
@@ -131,16 +138,16 @@ module CompareXML
|
|
|
131
138
|
##
|
|
132
139
|
# Compares two nodes of type Nokogiri::HTML::Comment.
|
|
133
140
|
#
|
|
134
|
-
# @param [Nokogiri::XML::
|
|
135
|
-
# @param [Nokogiri::XML::
|
|
141
|
+
# @param [Nokogiri::XML::Element] n1 left comment
|
|
142
|
+
# @param [Nokogiri::XML::Element] n2 right comment
|
|
136
143
|
# @param [Hash] opts user-overridden options
|
|
137
144
|
# @param [Array] differences inequivalence messages
|
|
138
145
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
139
146
|
#
|
|
140
147
|
# @return type of equivalence (from equivalence constants)
|
|
141
148
|
#
|
|
142
|
-
def
|
|
143
|
-
return
|
|
149
|
+
def compare_comment_nodes(n1, n2, opts, differences, status: EQUIVALENT)
|
|
150
|
+
return status if opts[:ignore_comments]
|
|
144
151
|
t1 = n1.content
|
|
145
152
|
t2 = n2.content
|
|
146
153
|
if opts[:collapse_whitespace]
|
|
@@ -149,7 +156,7 @@ module CompareXML
|
|
|
149
156
|
end
|
|
150
157
|
unless t1 == t2
|
|
151
158
|
status = UNEQUAL_COMMENTS
|
|
152
|
-
|
|
159
|
+
add_difference(n1.parent, n2.parent, t1, t2, opts, differences)
|
|
153
160
|
end
|
|
154
161
|
status
|
|
155
162
|
end
|
|
@@ -157,22 +164,21 @@ module CompareXML
|
|
|
157
164
|
##
|
|
158
165
|
# Compares two nodes of type Nokogiri::HTML::Document.
|
|
159
166
|
#
|
|
160
|
-
# @param [Nokogiri::XML::
|
|
161
|
-
# @param [Nokogiri::XML::
|
|
167
|
+
# @param [Nokogiri::XML::Element] n1 left document
|
|
168
|
+
# @param [Nokogiri::XML::Element] n2 right document
|
|
162
169
|
# @param [Hash] opts user-overridden options
|
|
163
170
|
# @param [Array] differences inequivalence messages
|
|
164
|
-
# @param [Hash]
|
|
165
|
-
# @param [Bool]
|
|
166
|
-
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
171
|
+
# @param [Hash] child_opts user-overridden options used for the child nodes
|
|
172
|
+
# @param [Bool] diff_children use different options for the child nodes
|
|
167
173
|
#
|
|
168
174
|
# @return type of equivalence (from equivalence constants)
|
|
169
175
|
#
|
|
170
|
-
def
|
|
176
|
+
def compare_document_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false)
|
|
171
177
|
if n1.name == n2.name
|
|
172
|
-
status =
|
|
178
|
+
status = diff_children ? compare_children(n1.children, n2.children, child_opts, differences, diff_children: diff_children) : compare_children(n1.children, n2.children, opts, differences)
|
|
173
179
|
else
|
|
174
180
|
status = UNEQUAL_DOCUMENTS
|
|
175
|
-
|
|
181
|
+
add_difference(n1, n2, n1, n2, opts, differences)
|
|
176
182
|
end
|
|
177
183
|
status
|
|
178
184
|
end
|
|
@@ -184,20 +190,23 @@ module CompareXML
|
|
|
184
190
|
# @param [Nokogiri::XML::NodeSet] n2_set right set of Nokogiri::XML::Node elements
|
|
185
191
|
# @param [Hash] opts user-overridden options
|
|
186
192
|
# @param [Array] differences inequivalence messages
|
|
187
|
-
# @param [Bool]
|
|
193
|
+
# @param [Bool] diff_children use different options for the child nodes
|
|
188
194
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
189
195
|
#
|
|
190
196
|
# @return type of equivalence (from equivalence constants)
|
|
191
197
|
#
|
|
192
|
-
def
|
|
193
|
-
|
|
198
|
+
def compare_children(n1_set, n2_set, opts, differences, diff_children: false, status: EQUIVALENT)
|
|
199
|
+
return status if opts[:ignore_children]
|
|
200
|
+
return compare_aligned_children(n1_set, n2_set, opts, differences, diff_children: diff_children, status: status) if opts[:verbose] && opts[:align_children]
|
|
201
|
+
i = 0
|
|
202
|
+
j = 0
|
|
194
203
|
while i < n1_set.length || j < n2_set.length
|
|
195
|
-
if !n1_set[i].nil? &&
|
|
204
|
+
if !n1_set[i].nil? && node_excluded?(n1_set[i], opts)
|
|
196
205
|
i += 1 # increment counter if left node is excluded
|
|
197
|
-
elsif !n2_set[j].nil? &&
|
|
206
|
+
elsif !n2_set[j].nil? && node_excluded?(n2_set[j], opts)
|
|
198
207
|
j += 1 # increment counter if right node is excluded
|
|
199
208
|
else
|
|
200
|
-
result =
|
|
209
|
+
result = diff_children ? compare_nodes(n1_set[i], n2_set[j], opts, differences, opts, diff_children: diff_children) : compare_nodes(n1_set[i], n2_set[j], opts, differences)
|
|
201
210
|
status = result unless result == EQUIVALENT
|
|
202
211
|
|
|
203
212
|
# return false so that this subtree could halt comparison on error
|
|
@@ -208,12 +217,118 @@ module CompareXML
|
|
|
208
217
|
break unless status == EQUIVALENT || opts[:verbose]
|
|
209
218
|
|
|
210
219
|
# increment both counters when both nodes have been compared
|
|
211
|
-
i += 1
|
|
220
|
+
i += 1
|
|
221
|
+
j += 1
|
|
212
222
|
end
|
|
213
|
-
|
|
223
|
+
end
|
|
224
|
+
status
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
##
|
|
228
|
+
# Compares two sets of child nodes by first aligning them, so that inserted or
|
|
229
|
+
# removed nodes are reported as additions/removals rather than as changes.
|
|
230
|
+
# Identical subtrees are matched as anchors (via a longest common subsequence
|
|
231
|
+
# of their fingerprints); the unmatched nodes between anchors are compared
|
|
232
|
+
# positionally, and any leftovers become additions or removals.
|
|
233
|
+
#
|
|
234
|
+
def compare_aligned_children(n1_set, n2_set, opts, differences, diff_children: false, status: EQUIVALENT)
|
|
235
|
+
left = n1_set.reject { |n| node_excluded?(n, opts) }
|
|
236
|
+
right = n2_set.reject { |n| node_excluded?(n, opts) }
|
|
237
|
+
cache = {}.compare_by_identity
|
|
238
|
+
fingerprints_left = left.map { |n| node_fingerprint(n, opts, cache) }
|
|
239
|
+
fingerprints_right = right.map { |n| node_fingerprint(n, opts, cache) }
|
|
240
|
+
|
|
241
|
+
prev_i = 0
|
|
242
|
+
prev_j = 0
|
|
243
|
+
anchors = longest_common_subsequence(fingerprints_left, fingerprints_right)
|
|
244
|
+
(anchors + [[left.length, right.length]]).each do |anchor_i, anchor_j|
|
|
245
|
+
result = compare_child_gap(left[prev_i...anchor_i], right[prev_j...anchor_j], opts, differences, diff_children)
|
|
246
|
+
status = result unless result == EQUIVALENT
|
|
247
|
+
prev_i = anchor_i + 1
|
|
248
|
+
prev_j = anchor_j + 1
|
|
249
|
+
end
|
|
250
|
+
status
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
##
|
|
254
|
+
# Compares a run of unmatched left and right nodes that lies between two anchors.
|
|
255
|
+
# Overlapping nodes are compared positionally; remaining left nodes are reported
|
|
256
|
+
# as removals and remaining right nodes as additions.
|
|
257
|
+
#
|
|
258
|
+
def compare_child_gap(left, right, opts, differences, diff_children, status: EQUIVALENT)
|
|
259
|
+
common = [left.length, right.length].min
|
|
260
|
+
common.times do |k|
|
|
261
|
+
result = diff_children ? compare_nodes(left[k], right[k], opts, differences, opts, diff_children: diff_children) : compare_nodes(left[k], right[k], opts, differences)
|
|
262
|
+
status = result unless result == EQUIVALENT
|
|
263
|
+
end
|
|
264
|
+
left.drop(common).each do |n|
|
|
265
|
+
status = MISSING_NODE
|
|
266
|
+
add_difference(n, nil, n, nil, opts, differences)
|
|
267
|
+
end
|
|
268
|
+
right.drop(common).each do |n|
|
|
269
|
+
status = MISSING_NODE
|
|
270
|
+
add_difference(nil, n, nil, n, opts, differences)
|
|
271
|
+
end
|
|
272
|
+
status
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
##
|
|
276
|
+
# Builds a canonical, option-aware string for a node so that two nodes with equal
|
|
277
|
+
# fingerprints are guaranteed to be equivalent under the current options. Used to
|
|
278
|
+
# anchor identical subtrees during alignment. Results are memoized per comparison.
|
|
279
|
+
#
|
|
280
|
+
def node_fingerprint(node, opts, cache)
|
|
281
|
+
cache[node] ||= build_node_fingerprint(node, opts, cache)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def build_node_fingerprint(node, opts, cache)
|
|
285
|
+
case node
|
|
286
|
+
when Nokogiri::XML::Comment
|
|
287
|
+
"C:#{opts[:collapse_whitespace] ? collapse(node.content) : node.content}"
|
|
288
|
+
when Nokogiri::XML::Text
|
|
289
|
+
"T:#{opts[:collapse_whitespace] ? collapse(node.content) : node.content}"
|
|
290
|
+
when Nokogiri::XML::Element
|
|
291
|
+
attrs = node.attribute_nodes
|
|
292
|
+
attrs = attrs.sort_by(&:name) if opts[:ignore_attr_order]
|
|
293
|
+
attr_str = attrs.map { |a| "#{a.name}=#{a.value}" }.join(' ')
|
|
294
|
+
children = node.children.reject { |c| node_excluded?(c, opts) }
|
|
295
|
+
child_str = children.map { |c| node_fingerprint(c, opts, cache) }.join
|
|
296
|
+
"E:#{node.name}[#{attr_str}](#{child_str})"
|
|
297
|
+
else
|
|
298
|
+
"O:#{node.class}:#{node}"
|
|
214
299
|
end
|
|
215
300
|
end
|
|
216
301
|
|
|
302
|
+
##
|
|
303
|
+
# Returns the index pairs of a longest common subsequence of the two arrays.
|
|
304
|
+
#
|
|
305
|
+
def longest_common_subsequence(a, b)
|
|
306
|
+
m = a.length
|
|
307
|
+
n = b.length
|
|
308
|
+
lengths = Array.new(m + 1) { Array.new(n + 1, 0) }
|
|
309
|
+
(m - 1).downto(0) do |i|
|
|
310
|
+
(n - 1).downto(0) do |j|
|
|
311
|
+
lengths[i][j] = a[i] == b[j] ? lengths[i + 1][j + 1] + 1 : [lengths[i + 1][j], lengths[i][j + 1]].max
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
pairs = []
|
|
316
|
+
i = 0
|
|
317
|
+
j = 0
|
|
318
|
+
while i < m && j < n
|
|
319
|
+
if a[i] == b[j]
|
|
320
|
+
pairs << [i, j]
|
|
321
|
+
i += 1
|
|
322
|
+
j += 1
|
|
323
|
+
elsif lengths[i + 1][j] >= lengths[i][j + 1]
|
|
324
|
+
i += 1
|
|
325
|
+
else
|
|
326
|
+
j += 1
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
pairs
|
|
330
|
+
end
|
|
331
|
+
|
|
217
332
|
##
|
|
218
333
|
# Compares two nodes of type Nokogiri::XML::Element.
|
|
219
334
|
# - compares element attributes
|
|
@@ -223,21 +338,22 @@ module CompareXML
|
|
|
223
338
|
# @param [Nokogiri::XML::Element] n2 right node element
|
|
224
339
|
# @param [Hash] opts user-overridden options
|
|
225
340
|
# @param [Array] differences inequivalence messages
|
|
226
|
-
# @param [Hash]
|
|
227
|
-
# @param [Bool]
|
|
341
|
+
# @param [Hash] child_opts user-overridden options used for the child nodes
|
|
342
|
+
# @param [Bool] diff_children use different options for the child nodes
|
|
228
343
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
229
344
|
#
|
|
230
345
|
# @return type of equivalence (from equivalence constants)
|
|
231
346
|
#
|
|
232
|
-
def
|
|
347
|
+
def compare_element_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false, status: EQUIVALENT)
|
|
233
348
|
if n1.name == n2.name
|
|
234
|
-
result =
|
|
349
|
+
result = compare_attribute_sets(n1, n2, n1.attribute_nodes, n2.attribute_nodes, opts, differences)
|
|
235
350
|
return result unless result == EQUIVALENT || opts[:force_children] == true
|
|
236
|
-
|
|
351
|
+
status = result unless result == EQUIVALENT
|
|
352
|
+
result = diff_children ? compare_children(n1.children, n2.children, child_opts, differences, diff_children: diff_children) : compare_children(n1.children, n2.children, opts, differences)
|
|
237
353
|
status = result unless result == EQUIVALENT
|
|
238
354
|
else
|
|
239
355
|
status = UNEQUAL_ELEMENTS
|
|
240
|
-
|
|
356
|
+
add_difference(n1, n2, n1.name, n2.name, opts, differences)
|
|
241
357
|
end
|
|
242
358
|
status
|
|
243
359
|
end
|
|
@@ -245,16 +361,16 @@ module CompareXML
|
|
|
245
361
|
##
|
|
246
362
|
# Compares two nodes of type Nokogiri::XML::Text.
|
|
247
363
|
#
|
|
248
|
-
# @param [Nokogiri::XML::
|
|
249
|
-
# @param [Nokogiri::XML::
|
|
364
|
+
# @param [Nokogiri::XML::Element] n1 left text node
|
|
365
|
+
# @param [Nokogiri::XML::Element] n2 right text node
|
|
250
366
|
# @param [Hash] opts user-overridden options
|
|
251
367
|
# @param [Array] differences inequivalence messages
|
|
252
368
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
253
369
|
#
|
|
254
370
|
# @return type of equivalence (from equivalence constants)
|
|
255
371
|
#
|
|
256
|
-
def
|
|
257
|
-
return
|
|
372
|
+
def compare_text_nodes(n1, n2, opts, differences, status = EQUIVALENT)
|
|
373
|
+
return status if opts[:ignore_text_nodes]
|
|
258
374
|
t1 = n1.content
|
|
259
375
|
t2 = n2.content
|
|
260
376
|
if opts[:collapse_whitespace]
|
|
@@ -263,7 +379,7 @@ module CompareXML
|
|
|
263
379
|
end
|
|
264
380
|
unless t1 == t2
|
|
265
381
|
status = UNEQUAL_TEXT_CONTENTS
|
|
266
|
-
|
|
382
|
+
add_difference(n1.parent, n2.parent, t1, t2, opts, differences)
|
|
267
383
|
end
|
|
268
384
|
status
|
|
269
385
|
end
|
|
@@ -280,12 +396,14 @@ module CompareXML
|
|
|
280
396
|
#
|
|
281
397
|
# @return type of equivalence (from equivalence constants)
|
|
282
398
|
#
|
|
283
|
-
def
|
|
399
|
+
def compare_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
|
|
400
|
+
a1_set = a1_set.reject { |a| attribute_excluded?(a, opts) }
|
|
401
|
+
a2_set = a2_set.reject { |a| attribute_excluded?(a, opts) }
|
|
284
402
|
return false unless a1_set.length == a2_set.length || opts[:verbose]
|
|
285
403
|
if opts[:ignore_attr_order]
|
|
286
|
-
|
|
404
|
+
compare_sorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
|
|
287
405
|
else
|
|
288
|
-
|
|
406
|
+
compare_unsorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
|
|
289
407
|
end
|
|
290
408
|
end
|
|
291
409
|
|
|
@@ -304,7 +422,7 @@ module CompareXML
|
|
|
304
422
|
#
|
|
305
423
|
# @return type of equivalence (from equivalence constants)
|
|
306
424
|
#
|
|
307
|
-
def
|
|
425
|
+
def compare_sorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences, status = EQUIVALENT)
|
|
308
426
|
a1_set = a1_set.sort_by(&:name)
|
|
309
427
|
a2_set = a2_set.sort_by(&:name)
|
|
310
428
|
i = j = 0
|
|
@@ -312,15 +430,21 @@ module CompareXML
|
|
|
312
430
|
while i < a1_set.length || j < a2_set.length
|
|
313
431
|
|
|
314
432
|
if a1_set[i].nil?
|
|
315
|
-
result =
|
|
433
|
+
result = compare_attributes(n1, n2, nil, a2_set[j], opts, differences)
|
|
434
|
+
j += 1
|
|
316
435
|
elsif a2_set[j].nil?
|
|
317
|
-
result =
|
|
436
|
+
result = compare_attributes(n1, n2, a1_set[i], nil, opts, differences)
|
|
437
|
+
i += 1
|
|
318
438
|
elsif a1_set[i].name < a2_set[j].name
|
|
319
|
-
result =
|
|
439
|
+
result = compare_attributes(n1, n2, a1_set[i], nil, opts, differences)
|
|
440
|
+
i += 1
|
|
320
441
|
elsif a1_set[i].name > a2_set[j].name
|
|
321
|
-
result =
|
|
442
|
+
result = compare_attributes(n1, n2, nil, a2_set[j], opts, differences)
|
|
443
|
+
j += 1
|
|
322
444
|
else
|
|
323
|
-
result =
|
|
445
|
+
result = compare_attributes(n1, n2, a1_set[i], a2_set[j], opts, differences)
|
|
446
|
+
i += 1
|
|
447
|
+
j += 1
|
|
324
448
|
end
|
|
325
449
|
|
|
326
450
|
status = result unless result == EQUIVALENT
|
|
@@ -345,11 +469,11 @@ module CompareXML
|
|
|
345
469
|
#
|
|
346
470
|
# @return type of equivalence (from equivalence constants)
|
|
347
471
|
#
|
|
348
|
-
def
|
|
472
|
+
def compare_unsorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences, status = EQUIVALENT)
|
|
349
473
|
[a1_set.length, a2_set.length].max.times do |i|
|
|
350
|
-
result =
|
|
474
|
+
result = compare_attributes(n1, n2, a1_set[i], a2_set[i], opts, differences)
|
|
351
475
|
status = result unless result == EQUIVALENT
|
|
352
|
-
break unless status == EQUIVALENT
|
|
476
|
+
break unless status == EQUIVALENT || opts[:verbose]
|
|
353
477
|
end
|
|
354
478
|
status
|
|
355
479
|
end
|
|
@@ -357,34 +481,31 @@ module CompareXML
|
|
|
357
481
|
##
|
|
358
482
|
# Compares two attributes by name and value.
|
|
359
483
|
#
|
|
360
|
-
# @param [Nokogiri::XML::Element] n1 left node element
|
|
361
|
-
# @param [Nokogiri::XML::Element] n2 right node element
|
|
362
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
363
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
484
|
+
# @param [Nokogiri::XML::Element|NilClass] n1 left node element
|
|
485
|
+
# @param [Nokogiri::XML::Element|NilClass] n2 right node element
|
|
486
|
+
# @param [Nokogiri::XML::Attr|NilClass] a1 left attribute
|
|
487
|
+
# @param [Nokogiri::XML::Attr|NilClass] a2 right attribute
|
|
364
488
|
# @param [Hash] opts user-overridden options
|
|
365
489
|
# @param [Array] differences inequivalence messages
|
|
366
490
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
367
491
|
#
|
|
368
492
|
# @return type of equivalence (from equivalence constants)
|
|
369
493
|
#
|
|
370
|
-
def
|
|
494
|
+
def compare_attributes(n1, n2, a1, a2, opts, differences, status = EQUIVALENT)
|
|
371
495
|
if a1.nil?
|
|
372
496
|
status = MISSING_ATTRIBUTE
|
|
373
|
-
|
|
497
|
+
add_difference(n1, n2, nil, "#{a2.name}=\"#{a2.value}\"", opts, differences)
|
|
374
498
|
elsif a2.nil?
|
|
375
499
|
status = MISSING_ATTRIBUTE
|
|
376
|
-
|
|
500
|
+
add_difference(n1, n2, "#{a1.name}=\"#{a1.value}\"", nil, opts, differences)
|
|
377
501
|
elsif a1.name == a2.name
|
|
378
|
-
return status if attrNameExcluded?(a1.name, a2.name, opts)
|
|
379
|
-
return status if attrsExcluded?(a1, a2, opts)
|
|
380
|
-
return status if attrContentExcluded?(a1, a2, opts)
|
|
381
502
|
if a1.value != a2.value
|
|
382
503
|
status = UNEQUAL_ATTRIBUTES
|
|
383
|
-
|
|
504
|
+
add_difference(n1, n2, "#{a1.name}=\"#{a1.value}\"", "#{a2.name}=\"#{a2.value}\"", opts, differences)
|
|
384
505
|
end
|
|
385
506
|
else
|
|
386
507
|
status = UNEQUAL_ATTRIBUTES
|
|
387
|
-
|
|
508
|
+
add_difference(n1, n2, "#{a1.name}=\"#{a1.value}\"", "#{a2.name}=\"#{a2.value}\"", opts, differences)
|
|
388
509
|
end
|
|
389
510
|
status
|
|
390
511
|
end
|
|
@@ -403,7 +524,7 @@ module CompareXML
|
|
|
403
524
|
#
|
|
404
525
|
# @return true if excluded, false otherwise
|
|
405
526
|
#
|
|
406
|
-
def
|
|
527
|
+
def node_excluded?(n, opts)
|
|
407
528
|
return true if n.is_a?(Nokogiri::XML::DTD)
|
|
408
529
|
return true if n.is_a?(Nokogiri::XML::Comment) && opts[:ignore_comments]
|
|
409
530
|
return true if n.is_a?(Nokogiri::XML::Text) && (opts[:ignore_text_nodes] || collapse(n.content).empty?)
|
|
@@ -412,84 +533,40 @@ module CompareXML
|
|
|
412
533
|
end
|
|
413
534
|
|
|
414
535
|
##
|
|
415
|
-
#
|
|
416
|
-
#
|
|
417
|
-
#
|
|
418
|
-
#
|
|
419
|
-
#
|
|
420
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
421
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
422
|
-
# @param [Hash] opts user-overridden options
|
|
536
|
+
# Determines whether a single attribute should be excluded from comparison, based on
|
|
537
|
+
# the +ignore_attrs_by_name+, +ignore_attr_content+ and +ignore_attrs+ options. The
|
|
538
|
+
# check is one-sided, so an attribute is dropped even when its counterpart is missing
|
|
539
|
+
# on the other element.
|
|
423
540
|
#
|
|
424
|
-
#
|
|
541
|
+
# Name matchers are compared with +===+: a String matches the attribute name exactly,
|
|
542
|
+
# while a Regexp matches it as a pattern.
|
|
425
543
|
#
|
|
426
|
-
|
|
427
|
-
opts[:ignore_attrs].each do |css|
|
|
428
|
-
if css.include?(a1.name) && css.include?(a2.name)
|
|
429
|
-
return true if a1.parent.xpath('../*').css(css).include?(a1.parent) && a2.parent.xpath('../*').css(css).include?(a2.parent)
|
|
430
|
-
end
|
|
431
|
-
end
|
|
432
|
-
false
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
##
|
|
436
|
-
# Checks whether two given attributes should be excluded, based on their content.
|
|
437
|
-
# Checks whether both attributes contain content that should be excluded, and
|
|
438
|
-
# returns true only if an excluded string is contained in both attribute values.
|
|
439
|
-
#
|
|
440
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
441
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
544
|
+
# @param [Nokogiri::XML::Attr] attr the attribute being tested
|
|
442
545
|
# @param [Hash] opts user-overridden options
|
|
443
546
|
#
|
|
444
547
|
# @return true if excluded, false otherwise
|
|
445
548
|
#
|
|
446
|
-
def
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
opts[:ignore_attr_content].each do |content|
|
|
450
|
-
a1_excluded ||= a1.value.include?(content)
|
|
451
|
-
a2_excluded ||= a2.value.include?(content)
|
|
452
|
-
return true if a1_excluded && a2_excluded
|
|
453
|
-
end
|
|
454
|
-
false
|
|
455
|
-
end
|
|
549
|
+
def attribute_excluded?(attr, opts)
|
|
550
|
+
return true if opts[:ignore_attrs_by_name].any? { |matcher| matcher === attr.name }
|
|
551
|
+
return true if opts[:ignore_attr_content].any? { |content| attr.value.include?(content) }
|
|
456
552
|
|
|
457
|
-
|
|
458
|
-
# Checks whether two given attributes should be excluded, based on their content.
|
|
459
|
-
# Checks whether both attributes contain content that should be excluded, and
|
|
460
|
-
# returns true only if an excluded string is contained in both attribute values.
|
|
461
|
-
#
|
|
462
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
463
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
464
|
-
# @param [Hash] opts user-overridden options
|
|
465
|
-
#
|
|
466
|
-
# @return true if excluded, false otherwise
|
|
467
|
-
#
|
|
468
|
-
def attrNameExcluded?(a1, a2, opts)
|
|
469
|
-
a1_excluded = false
|
|
470
|
-
a2_excluded = false
|
|
471
|
-
opts[:ignore_attrs_by_name].each do |name|
|
|
472
|
-
a1_excluded ||= a1.to_s.include?(name)
|
|
473
|
-
a2_excluded ||= a2.to_s.include?(name)
|
|
474
|
-
return true if a1_excluded && a2_excluded
|
|
475
|
-
end
|
|
476
|
-
false
|
|
553
|
+
opts[:ignore_attrs].any? { |css| css.include?(attr.name) && attr.parent.xpath('../*').css(css).include?(attr.parent) }
|
|
477
554
|
end
|
|
478
555
|
|
|
479
556
|
##
|
|
480
557
|
# Strips the whitespace (from beginning and end) and collapses it,
|
|
481
558
|
# i.e. multiple spaces, new lines and tabs are all collapsed to a single space.
|
|
482
559
|
#
|
|
483
|
-
# @param [Nokogiri::XML::
|
|
484
|
-
# @param [Nokogiri::XML::
|
|
485
|
-
# @param [String] diff1 left diffing value
|
|
486
|
-
# @param [String] diff2 right diffing value
|
|
560
|
+
# @param [Nokogiri::XML::Element|NilClass] node1 left node
|
|
561
|
+
# @param [Nokogiri::XML::Element|NilClass] node2 right node
|
|
562
|
+
# @param [String|NilClass] diff1 left diffing value
|
|
563
|
+
# @param [String|NilClass] diff2 right diffing value
|
|
487
564
|
# @param [Hash] opts user-overridden options
|
|
488
565
|
# @param [Array] differences inequivalence messages
|
|
489
566
|
#
|
|
490
567
|
# @return collapsed string
|
|
491
568
|
#
|
|
492
|
-
def
|
|
569
|
+
def add_difference(node1, node2, diff1, diff2, opts, differences)
|
|
493
570
|
differences << { node1: node1, node2: node2, diff1: diff1, diff2: diff2 } if opts[:verbose]
|
|
494
571
|
end
|
|
495
572
|
|
metadata
CHANGED
|
@@ -1,55 +1,49 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: compare-xml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vadim Kononov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: nokogiri
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.
|
|
18
|
+
version: '1.6'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
|
-
- - "
|
|
23
|
+
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.
|
|
25
|
+
version: '1.6'
|
|
27
26
|
description: CompareXML is a fast, lightweight and feature-rich tool that will solve
|
|
28
27
|
your XML/HTML comparison or diffing needs. its purpose is to compare two instances
|
|
29
28
|
of Nokogiri::XML::Node or Nokogiri::XML::NodeSet for equality or equivalency.
|
|
30
29
|
email:
|
|
31
|
-
- vadim@
|
|
30
|
+
- vadim@konoson.com
|
|
32
31
|
executables: []
|
|
33
32
|
extensions: []
|
|
34
33
|
extra_rdoc_files: []
|
|
35
34
|
files:
|
|
36
|
-
- ".gitignore"
|
|
37
|
-
- ".rubocop.yml"
|
|
38
|
-
- Gemfile
|
|
39
35
|
- LICENSE.txt
|
|
40
36
|
- README.md
|
|
41
|
-
- Rakefile
|
|
42
|
-
- bin/console
|
|
43
|
-
- bin/setup
|
|
44
|
-
- compare-xml.gemspec
|
|
45
|
-
- img/diffing.png
|
|
46
37
|
- lib/compare-xml.rb
|
|
47
38
|
- lib/compare-xml/version.rb
|
|
48
39
|
homepage: https://github.com/vkononov/compare-xml
|
|
49
40
|
licenses:
|
|
50
41
|
- MIT
|
|
51
|
-
metadata:
|
|
52
|
-
|
|
42
|
+
metadata:
|
|
43
|
+
homepage_uri: https://github.com/vkononov/compare-xml
|
|
44
|
+
source_code_uri: https://github.com/vkononov/compare-xml
|
|
45
|
+
changelog_uri: https://github.com/vkononov/compare-xml/releases
|
|
46
|
+
rubygems_mfa_required: 'true'
|
|
53
47
|
rdoc_options: []
|
|
54
48
|
require_paths:
|
|
55
49
|
- lib
|
|
@@ -57,16 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
57
51
|
requirements:
|
|
58
52
|
- - ">="
|
|
59
53
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
54
|
+
version: 2.4.0
|
|
61
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
56
|
requirements:
|
|
63
57
|
- - ">="
|
|
64
58
|
- !ruby/object:Gem::Version
|
|
65
59
|
version: '0'
|
|
66
60
|
requirements: []
|
|
67
|
-
|
|
68
|
-
rubygems_version: 2.6.13
|
|
69
|
-
signing_key:
|
|
61
|
+
rubygems_version: 4.0.10
|
|
70
62
|
specification_version: 4
|
|
71
63
|
summary: A customizable tool that compares two instances of Nokogiri::XML::Node for
|
|
72
64
|
equality or equivalency.
|
data/.gitignore
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
# Exclude:
|
|
3
|
-
# - config/routes.rb
|
|
4
|
-
# - db/migrate/*
|
|
5
|
-
# - db/schema.rb
|
|
6
|
-
TargetRubyVersion: 2.5
|
|
7
|
-
|
|
8
|
-
Metrics/AbcSize:
|
|
9
|
-
Enabled: false
|
|
10
|
-
|
|
11
|
-
Metrics/CyclomaticComplexity:
|
|
12
|
-
Enabled: false
|
|
13
|
-
|
|
14
|
-
Metrics/LineLength:
|
|
15
|
-
Enabled: false
|
|
16
|
-
|
|
17
|
-
Metrics/MethodLength:
|
|
18
|
-
Enabled: false
|
|
19
|
-
|
|
20
|
-
Metrics/ModuleLength:
|
|
21
|
-
Enabled: false
|
|
22
|
-
|
|
23
|
-
Metrics/ParameterLists:
|
|
24
|
-
Enabled: false
|
|
25
|
-
|
|
26
|
-
Metrics/PerceivedComplexity:
|
|
27
|
-
Enabled: false
|
|
28
|
-
|
|
29
|
-
Naming/FileName:
|
|
30
|
-
Enabled: false
|
|
31
|
-
|
|
32
|
-
Naming/MethodName:
|
|
33
|
-
Enabled: false
|
|
34
|
-
|
|
35
|
-
Style/Documentation:
|
|
36
|
-
Enabled: false
|
|
37
|
-
|
|
38
|
-
Style/FrozenStringLiteralComment:
|
|
39
|
-
Enabled: false
|
|
40
|
-
|
|
41
|
-
Style/Semicolon:
|
|
42
|
-
Enabled: false
|
data/Gemfile
DELETED
data/Rakefile
DELETED
data/bin/console
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'bundler/setup'
|
|
4
|
-
require 'compare-xml/xml'
|
|
5
|
-
|
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
-
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
-
# require 'pry'
|
|
11
|
-
# Pry.start
|
|
12
|
-
|
|
13
|
-
require 'irb'
|
|
14
|
-
IRB.start
|
data/bin/setup
DELETED
data/compare-xml.gemspec
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
lib = File.expand_path('../lib', __FILE__)
|
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
-
require 'compare-xml/version'
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = 'compare-xml'
|
|
7
|
-
spec.version = CompareXML::VERSION
|
|
8
|
-
spec.authors = ['Vadim Kononov']
|
|
9
|
-
spec.email = ['vadim@poetic.com']
|
|
10
|
-
|
|
11
|
-
spec.summary = 'A customizable tool that compares two instances of Nokogiri::XML::Node for equality or equivalency.'
|
|
12
|
-
spec.description = 'CompareXML is a fast, lightweight and feature-rich tool that will solve your XML/HTML comparison or diffing needs. its purpose is to compare two instances of Nokogiri::XML::Node or Nokogiri::XML::NodeSet for equality or equivalency.'
|
|
13
|
-
spec.homepage = 'https://github.com/vkononov/compare-xml'
|
|
14
|
-
spec.license = 'MIT'
|
|
15
|
-
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
-
spec.bindir = 'exe'
|
|
18
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
|
-
spec.require_paths = ['lib']
|
|
20
|
-
|
|
21
|
-
spec.add_runtime_dependency 'nokogiri', '~> 1.8'
|
|
22
|
-
end
|
data/img/diffing.png
DELETED
|
Binary file
|