compare-xml 0.66 → 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 +4 -4
- data/README.md +74 -41
- data/lib/compare-xml/version.rb +1 -1
- data/lib/compare-xml.rb +211 -139
- metadata +14 -22
- data/.gitignore +0 -13
- data/.rubocop.yml +0 -45
- 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
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
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,8 +83,8 @@ 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
|
|
|
81
|
-
- `ignore_attrs_by_name: [
|
|
82
|
-
- when provided, ignores
|
|
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)
|
|
83
88
|
|
|
84
89
|
- `ignore_comments: {true|false}` default: **`true`** [show examples ⇨](#ignore_comments)
|
|
85
90
|
- when `true`, ignores comments, such as `<!-- comment -->`
|
|
@@ -99,6 +104,9 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
99
104
|
- `force_children {true|false}` default **`false`** [show examples ⇨](#force_children)
|
|
100
105
|
- when `true`, the subnodes of a node are checked independently of the status of the parent node
|
|
101
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
|
+
|
|
102
110
|
|
|
103
111
|
## Options in Depth
|
|
104
112
|
|
|
@@ -196,17 +204,27 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
196
204
|
|
|
197
205
|
----------
|
|
198
206
|
|
|
199
|
-
- <a id="ignore_attrs_by_name"></a>`ignore_attrs_by_name: [
|
|
207
|
+
- <a id="ignore_attrs_by_name"></a>`ignore_attrs_by_name: [matcher1, matcher2, ...]` default: **`[]`**
|
|
200
208
|
|
|
201
|
-
When provided, ignores all **attributes**
|
|
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.
|
|
202
210
|
|
|
203
|
-
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {ignore_attrs_by_name: ['target'])`
|
|
211
|
+
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {ignore_attrs_by_name: ['target', /^data-/]})`
|
|
204
212
|
|
|
205
213
|
**Example:** With `ignore_attrs_by_name: ['target', 'rel']` the following HTML strings are considered equal:
|
|
206
214
|
|
|
207
215
|
<a href="/admin" class="button" target="_blank">Link</a>
|
|
208
216
|
<a href="/admin" class="button" target="_self" rel="nofollow">Link</a>
|
|
209
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>
|
|
227
|
+
|
|
210
228
|
----------
|
|
211
229
|
|
|
212
230
|
|
|
@@ -280,42 +298,42 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
280
298
|
|
|
281
299
|
**Example:** When `true` given the following HTML strings:
|
|
282
300
|
|
|
283
|
-

|
|
284
302
|
|
|
285
303
|
`CompareXML.equivalent?(doc1, doc2, {verbose: true})` will produce an array shown below.
|
|
286
304
|
|
|
287
305
|
```ruby
|
|
288
306
|
[
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
+
}
|
|
319
337
|
]
|
|
320
338
|
```
|
|
321
339
|
|
|
@@ -345,7 +363,22 @@ CompareXML.equivalent?(doc1, doc2, {collapse_whitespace: false, verbose: true, .
|
|
|
345
363
|
|
|
346
364
|
When provided, compares all **subnodes** of any node.
|
|
347
365
|
|
|
348
|
-
**Usage Example:** `CompareXML.equivalent?(doc1, doc2, {
|
|
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.
|
|
349
382
|
|
|
350
383
|
----------
|
|
351
384
|
|
|
@@ -367,4 +400,4 @@ This gem was inspired by [Michael B. Klein](https://github.com/mbklein)'s gem [`
|
|
|
367
400
|
|
|
368
401
|
## License
|
|
369
402
|
|
|
370
|
-
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,10 @@ 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
|
+
|
|
19
23
|
# when true, children of elements are never compared
|
|
20
24
|
# when false, children of elements are compared if root is different or see force_children
|
|
21
25
|
ignore_children: false,
|
|
@@ -72,16 +76,16 @@ module CompareXML
|
|
|
72
76
|
# @param [Nokogiri::XML::Element] n1 left node element
|
|
73
77
|
# @param [Nokogiri::XML::Element] n2 right node element
|
|
74
78
|
# @param [Hash] opts user-overridden options
|
|
75
|
-
# @param [Hash]
|
|
76
|
-
# @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
|
|
77
81
|
#
|
|
78
82
|
# @return true if equal, [Array] differences otherwise
|
|
79
83
|
#
|
|
80
|
-
def equivalent?(n1, n2, opts = {},
|
|
84
|
+
def equivalent?(n1, n2, opts = {}, child_opts = {}, diff_children = false)
|
|
81
85
|
opts = DEFAULTS_OPTS.merge(opts)
|
|
82
|
-
|
|
86
|
+
child_opts = DEFAULTS_OPTS.merge(child_opts)
|
|
83
87
|
differences = []
|
|
84
|
-
result =
|
|
88
|
+
result = diff_children ? compare_nodes(n1, n2, opts, differences, child_opts, diff_children: diff_children) : compare_nodes(n1, n2, opts, differences)
|
|
85
89
|
opts[:verbose] ? differences : result == EQUIVALENT
|
|
86
90
|
end
|
|
87
91
|
|
|
@@ -91,42 +95,41 @@ module CompareXML
|
|
|
91
95
|
# Compares two nodes for equivalence. The nodes could be any subclass
|
|
92
96
|
# of Nokogiri::XML::Node including node sets and document fragments.
|
|
93
97
|
#
|
|
94
|
-
# @param [Nokogiri::XML::
|
|
95
|
-
# @param [Nokogiri::XML::
|
|
98
|
+
# @param [Nokogiri::XML::Element] n1 left node
|
|
99
|
+
# @param [Nokogiri::XML::Element] n2 right node
|
|
96
100
|
# @param [Hash] opts user-overridden options
|
|
97
101
|
# @param [Array] differences inequivalence messages
|
|
98
|
-
# @param [Hash]
|
|
99
|
-
# @param [Bool]
|
|
100
|
-
# @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
|
|
101
104
|
#
|
|
102
105
|
# @return type of equivalence (from equivalence constants)
|
|
103
106
|
#
|
|
104
|
-
def
|
|
105
|
-
if n1.
|
|
107
|
+
def compare_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false)
|
|
108
|
+
if n1.instance_of?(n2.class)
|
|
106
109
|
case n1
|
|
107
110
|
when Nokogiri::XML::Comment
|
|
108
|
-
status =
|
|
111
|
+
status = compare_comment_nodes(n1, n2, opts, differences)
|
|
109
112
|
when Nokogiri::HTML::Document
|
|
110
|
-
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)
|
|
111
114
|
when Nokogiri::XML::Element
|
|
112
|
-
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)
|
|
113
116
|
when Nokogiri::XML::Text
|
|
114
|
-
status =
|
|
117
|
+
status = compare_text_nodes(n1, n2, opts, differences)
|
|
115
118
|
else
|
|
116
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)
|
|
117
|
-
status =
|
|
120
|
+
status = compare_children(n1.children, n2.children, opts, differences)
|
|
118
121
|
end
|
|
119
122
|
elsif n1.nil? || n2.nil?
|
|
120
123
|
status = MISSING_NODE
|
|
121
|
-
|
|
124
|
+
add_difference(n1, n2, n1, n2, opts, differences)
|
|
122
125
|
else
|
|
123
126
|
status = UNEQUAL_NODES_TYPES
|
|
124
127
|
if n1.is_a? Nokogiri::XML::Text
|
|
125
|
-
|
|
128
|
+
add_difference(n1.parent, n2, n1, n2, opts, differences)
|
|
126
129
|
elsif n2.is_a? Nokogiri::XML::Text
|
|
127
|
-
|
|
130
|
+
add_difference(n1, n2.parent, n1, n2, opts, differences)
|
|
128
131
|
else
|
|
129
|
-
|
|
132
|
+
add_difference(n1, n2, n1, n2, opts, differences)
|
|
130
133
|
end
|
|
131
134
|
end
|
|
132
135
|
status
|
|
@@ -135,16 +138,16 @@ module CompareXML
|
|
|
135
138
|
##
|
|
136
139
|
# Compares two nodes of type Nokogiri::HTML::Comment.
|
|
137
140
|
#
|
|
138
|
-
# @param [Nokogiri::XML::
|
|
139
|
-
# @param [Nokogiri::XML::
|
|
141
|
+
# @param [Nokogiri::XML::Element] n1 left comment
|
|
142
|
+
# @param [Nokogiri::XML::Element] n2 right comment
|
|
140
143
|
# @param [Hash] opts user-overridden options
|
|
141
144
|
# @param [Array] differences inequivalence messages
|
|
142
145
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
143
146
|
#
|
|
144
147
|
# @return type of equivalence (from equivalence constants)
|
|
145
148
|
#
|
|
146
|
-
def
|
|
147
|
-
return
|
|
149
|
+
def compare_comment_nodes(n1, n2, opts, differences, status: EQUIVALENT)
|
|
150
|
+
return status if opts[:ignore_comments]
|
|
148
151
|
t1 = n1.content
|
|
149
152
|
t2 = n2.content
|
|
150
153
|
if opts[:collapse_whitespace]
|
|
@@ -153,7 +156,7 @@ module CompareXML
|
|
|
153
156
|
end
|
|
154
157
|
unless t1 == t2
|
|
155
158
|
status = UNEQUAL_COMMENTS
|
|
156
|
-
|
|
159
|
+
add_difference(n1.parent, n2.parent, t1, t2, opts, differences)
|
|
157
160
|
end
|
|
158
161
|
status
|
|
159
162
|
end
|
|
@@ -161,22 +164,21 @@ module CompareXML
|
|
|
161
164
|
##
|
|
162
165
|
# Compares two nodes of type Nokogiri::HTML::Document.
|
|
163
166
|
#
|
|
164
|
-
# @param [Nokogiri::XML::
|
|
165
|
-
# @param [Nokogiri::XML::
|
|
167
|
+
# @param [Nokogiri::XML::Element] n1 left document
|
|
168
|
+
# @param [Nokogiri::XML::Element] n2 right document
|
|
166
169
|
# @param [Hash] opts user-overridden options
|
|
167
170
|
# @param [Array] differences inequivalence messages
|
|
168
|
-
# @param [Hash]
|
|
169
|
-
# @param [Bool]
|
|
170
|
-
# @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
|
|
171
173
|
#
|
|
172
174
|
# @return type of equivalence (from equivalence constants)
|
|
173
175
|
#
|
|
174
|
-
def
|
|
176
|
+
def compare_document_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false)
|
|
175
177
|
if n1.name == n2.name
|
|
176
|
-
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)
|
|
177
179
|
else
|
|
178
180
|
status = UNEQUAL_DOCUMENTS
|
|
179
|
-
|
|
181
|
+
add_difference(n1, n2, n1, n2, opts, differences)
|
|
180
182
|
end
|
|
181
183
|
status
|
|
182
184
|
end
|
|
@@ -188,21 +190,23 @@ module CompareXML
|
|
|
188
190
|
# @param [Nokogiri::XML::NodeSet] n2_set right set of Nokogiri::XML::Node elements
|
|
189
191
|
# @param [Hash] opts user-overridden options
|
|
190
192
|
# @param [Array] differences inequivalence messages
|
|
191
|
-
# @param [Bool]
|
|
193
|
+
# @param [Bool] diff_children use different options for the child nodes
|
|
192
194
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
193
195
|
#
|
|
194
196
|
# @return type of equivalence (from equivalence constants)
|
|
195
197
|
#
|
|
196
|
-
def
|
|
197
|
-
|
|
198
|
-
return if opts[:
|
|
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
|
|
199
203
|
while i < n1_set.length || j < n2_set.length
|
|
200
|
-
if !n1_set[i].nil? &&
|
|
204
|
+
if !n1_set[i].nil? && node_excluded?(n1_set[i], opts)
|
|
201
205
|
i += 1 # increment counter if left node is excluded
|
|
202
|
-
elsif !n2_set[j].nil? &&
|
|
206
|
+
elsif !n2_set[j].nil? && node_excluded?(n2_set[j], opts)
|
|
203
207
|
j += 1 # increment counter if right node is excluded
|
|
204
208
|
else
|
|
205
|
-
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)
|
|
206
210
|
status = result unless result == EQUIVALENT
|
|
207
211
|
|
|
208
212
|
# return false so that this subtree could halt comparison on error
|
|
@@ -213,10 +217,116 @@ module CompareXML
|
|
|
213
217
|
break unless status == EQUIVALENT || opts[:verbose]
|
|
214
218
|
|
|
215
219
|
# increment both counters when both nodes have been compared
|
|
216
|
-
i += 1
|
|
220
|
+
i += 1
|
|
221
|
+
j += 1
|
|
222
|
+
end
|
|
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}"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
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
|
|
217
327
|
end
|
|
218
|
-
status
|
|
219
328
|
end
|
|
329
|
+
pairs
|
|
220
330
|
end
|
|
221
331
|
|
|
222
332
|
##
|
|
@@ -228,21 +338,22 @@ module CompareXML
|
|
|
228
338
|
# @param [Nokogiri::XML::Element] n2 right node element
|
|
229
339
|
# @param [Hash] opts user-overridden options
|
|
230
340
|
# @param [Array] differences inequivalence messages
|
|
231
|
-
# @param [Hash]
|
|
232
|
-
# @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
|
|
233
343
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
234
344
|
#
|
|
235
345
|
# @return type of equivalence (from equivalence constants)
|
|
236
346
|
#
|
|
237
|
-
def
|
|
347
|
+
def compare_element_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false, status: EQUIVALENT)
|
|
238
348
|
if n1.name == n2.name
|
|
239
|
-
result =
|
|
349
|
+
result = compare_attribute_sets(n1, n2, n1.attribute_nodes, n2.attribute_nodes, opts, differences)
|
|
240
350
|
return result unless result == EQUIVALENT || opts[:force_children] == true
|
|
241
|
-
|
|
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)
|
|
242
353
|
status = result unless result == EQUIVALENT
|
|
243
354
|
else
|
|
244
355
|
status = UNEQUAL_ELEMENTS
|
|
245
|
-
|
|
356
|
+
add_difference(n1, n2, n1.name, n2.name, opts, differences)
|
|
246
357
|
end
|
|
247
358
|
status
|
|
248
359
|
end
|
|
@@ -250,16 +361,16 @@ module CompareXML
|
|
|
250
361
|
##
|
|
251
362
|
# Compares two nodes of type Nokogiri::XML::Text.
|
|
252
363
|
#
|
|
253
|
-
# @param [Nokogiri::XML::
|
|
254
|
-
# @param [Nokogiri::XML::
|
|
364
|
+
# @param [Nokogiri::XML::Element] n1 left text node
|
|
365
|
+
# @param [Nokogiri::XML::Element] n2 right text node
|
|
255
366
|
# @param [Hash] opts user-overridden options
|
|
256
367
|
# @param [Array] differences inequivalence messages
|
|
257
368
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
258
369
|
#
|
|
259
370
|
# @return type of equivalence (from equivalence constants)
|
|
260
371
|
#
|
|
261
|
-
def
|
|
262
|
-
return
|
|
372
|
+
def compare_text_nodes(n1, n2, opts, differences, status = EQUIVALENT)
|
|
373
|
+
return status if opts[:ignore_text_nodes]
|
|
263
374
|
t1 = n1.content
|
|
264
375
|
t2 = n2.content
|
|
265
376
|
if opts[:collapse_whitespace]
|
|
@@ -268,7 +379,7 @@ module CompareXML
|
|
|
268
379
|
end
|
|
269
380
|
unless t1 == t2
|
|
270
381
|
status = UNEQUAL_TEXT_CONTENTS
|
|
271
|
-
|
|
382
|
+
add_difference(n1.parent, n2.parent, t1, t2, opts, differences)
|
|
272
383
|
end
|
|
273
384
|
status
|
|
274
385
|
end
|
|
@@ -285,12 +396,14 @@ module CompareXML
|
|
|
285
396
|
#
|
|
286
397
|
# @return type of equivalence (from equivalence constants)
|
|
287
398
|
#
|
|
288
|
-
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) }
|
|
289
402
|
return false unless a1_set.length == a2_set.length || opts[:verbose]
|
|
290
403
|
if opts[:ignore_attr_order]
|
|
291
|
-
|
|
404
|
+
compare_sorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
|
|
292
405
|
else
|
|
293
|
-
|
|
406
|
+
compare_unsorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
|
|
294
407
|
end
|
|
295
408
|
end
|
|
296
409
|
|
|
@@ -309,7 +422,7 @@ module CompareXML
|
|
|
309
422
|
#
|
|
310
423
|
# @return type of equivalence (from equivalence constants)
|
|
311
424
|
#
|
|
312
|
-
def
|
|
425
|
+
def compare_sorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences, status = EQUIVALENT)
|
|
313
426
|
a1_set = a1_set.sort_by(&:name)
|
|
314
427
|
a2_set = a2_set.sort_by(&:name)
|
|
315
428
|
i = j = 0
|
|
@@ -317,15 +430,21 @@ module CompareXML
|
|
|
317
430
|
while i < a1_set.length || j < a2_set.length
|
|
318
431
|
|
|
319
432
|
if a1_set[i].nil?
|
|
320
|
-
result =
|
|
433
|
+
result = compare_attributes(n1, n2, nil, a2_set[j], opts, differences)
|
|
434
|
+
j += 1
|
|
321
435
|
elsif a2_set[j].nil?
|
|
322
|
-
result =
|
|
436
|
+
result = compare_attributes(n1, n2, a1_set[i], nil, opts, differences)
|
|
437
|
+
i += 1
|
|
323
438
|
elsif a1_set[i].name < a2_set[j].name
|
|
324
|
-
result =
|
|
439
|
+
result = compare_attributes(n1, n2, a1_set[i], nil, opts, differences)
|
|
440
|
+
i += 1
|
|
325
441
|
elsif a1_set[i].name > a2_set[j].name
|
|
326
|
-
result =
|
|
442
|
+
result = compare_attributes(n1, n2, nil, a2_set[j], opts, differences)
|
|
443
|
+
j += 1
|
|
327
444
|
else
|
|
328
|
-
result =
|
|
445
|
+
result = compare_attributes(n1, n2, a1_set[i], a2_set[j], opts, differences)
|
|
446
|
+
i += 1
|
|
447
|
+
j += 1
|
|
329
448
|
end
|
|
330
449
|
|
|
331
450
|
status = result unless result == EQUIVALENT
|
|
@@ -350,11 +469,11 @@ module CompareXML
|
|
|
350
469
|
#
|
|
351
470
|
# @return type of equivalence (from equivalence constants)
|
|
352
471
|
#
|
|
353
|
-
def
|
|
472
|
+
def compare_unsorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences, status = EQUIVALENT)
|
|
354
473
|
[a1_set.length, a2_set.length].max.times do |i|
|
|
355
|
-
result =
|
|
474
|
+
result = compare_attributes(n1, n2, a1_set[i], a2_set[i], opts, differences)
|
|
356
475
|
status = result unless result == EQUIVALENT
|
|
357
|
-
break unless status == EQUIVALENT
|
|
476
|
+
break unless status == EQUIVALENT || opts[:verbose]
|
|
358
477
|
end
|
|
359
478
|
status
|
|
360
479
|
end
|
|
@@ -362,34 +481,31 @@ module CompareXML
|
|
|
362
481
|
##
|
|
363
482
|
# Compares two attributes by name and value.
|
|
364
483
|
#
|
|
365
|
-
# @param [Nokogiri::XML::Element] n1 left node element
|
|
366
|
-
# @param [Nokogiri::XML::Element] n2 right node element
|
|
367
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
368
|
-
# @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
|
|
369
488
|
# @param [Hash] opts user-overridden options
|
|
370
489
|
# @param [Array] differences inequivalence messages
|
|
371
490
|
# @param [int] status comparison status code (EQUIVALENT by default)
|
|
372
491
|
#
|
|
373
492
|
# @return type of equivalence (from equivalence constants)
|
|
374
493
|
#
|
|
375
|
-
def
|
|
494
|
+
def compare_attributes(n1, n2, a1, a2, opts, differences, status = EQUIVALENT)
|
|
376
495
|
if a1.nil?
|
|
377
496
|
status = MISSING_ATTRIBUTE
|
|
378
|
-
|
|
497
|
+
add_difference(n1, n2, nil, "#{a2.name}=\"#{a2.value}\"", opts, differences)
|
|
379
498
|
elsif a2.nil?
|
|
380
499
|
status = MISSING_ATTRIBUTE
|
|
381
|
-
|
|
500
|
+
add_difference(n1, n2, "#{a1.name}=\"#{a1.value}\"", nil, opts, differences)
|
|
382
501
|
elsif a1.name == a2.name
|
|
383
|
-
return status if attrNameExcluded?(a1.name, a2.name, opts)
|
|
384
|
-
return status if attrsExcluded?(a1, a2, opts)
|
|
385
|
-
return status if attrContentExcluded?(a1, a2, opts)
|
|
386
502
|
if a1.value != a2.value
|
|
387
503
|
status = UNEQUAL_ATTRIBUTES
|
|
388
|
-
|
|
504
|
+
add_difference(n1, n2, "#{a1.name}=\"#{a1.value}\"", "#{a2.name}=\"#{a2.value}\"", opts, differences)
|
|
389
505
|
end
|
|
390
506
|
else
|
|
391
507
|
status = UNEQUAL_ATTRIBUTES
|
|
392
|
-
|
|
508
|
+
add_difference(n1, n2, "#{a1.name}=\"#{a1.value}\"", "#{a2.name}=\"#{a2.value}\"", opts, differences)
|
|
393
509
|
end
|
|
394
510
|
status
|
|
395
511
|
end
|
|
@@ -408,7 +524,7 @@ module CompareXML
|
|
|
408
524
|
#
|
|
409
525
|
# @return true if excluded, false otherwise
|
|
410
526
|
#
|
|
411
|
-
def
|
|
527
|
+
def node_excluded?(n, opts)
|
|
412
528
|
return true if n.is_a?(Nokogiri::XML::DTD)
|
|
413
529
|
return true if n.is_a?(Nokogiri::XML::Comment) && opts[:ignore_comments]
|
|
414
530
|
return true if n.is_a?(Nokogiri::XML::Text) && (opts[:ignore_text_nodes] || collapse(n.content).empty?)
|
|
@@ -417,84 +533,40 @@ module CompareXML
|
|
|
417
533
|
end
|
|
418
534
|
|
|
419
535
|
##
|
|
420
|
-
#
|
|
421
|
-
#
|
|
422
|
-
#
|
|
423
|
-
#
|
|
424
|
-
#
|
|
425
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
426
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
427
|
-
# @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.
|
|
428
540
|
#
|
|
429
|
-
#
|
|
541
|
+
# Name matchers are compared with +===+: a String matches the attribute name exactly,
|
|
542
|
+
# while a Regexp matches it as a pattern.
|
|
430
543
|
#
|
|
431
|
-
|
|
432
|
-
opts[:ignore_attrs].each do |css|
|
|
433
|
-
if css.include?(a1.name) && css.include?(a2.name)
|
|
434
|
-
return true if a1.parent.xpath('../*').css(css).include?(a1.parent) && a2.parent.xpath('../*').css(css).include?(a2.parent)
|
|
435
|
-
end
|
|
436
|
-
end
|
|
437
|
-
false
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
##
|
|
441
|
-
# Checks whether two given attributes should be excluded, based on their content.
|
|
442
|
-
# Checks whether both attributes contain content that should be excluded, and
|
|
443
|
-
# returns true only if an excluded string is contained in both attribute values.
|
|
444
|
-
#
|
|
445
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
446
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
544
|
+
# @param [Nokogiri::XML::Attr] attr the attribute being tested
|
|
447
545
|
# @param [Hash] opts user-overridden options
|
|
448
546
|
#
|
|
449
547
|
# @return true if excluded, false otherwise
|
|
450
548
|
#
|
|
451
|
-
def
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
opts[:ignore_attr_content].each do |content|
|
|
455
|
-
a1_excluded ||= a1.value.include?(content)
|
|
456
|
-
a2_excluded ||= a2.value.include?(content)
|
|
457
|
-
return true if a1_excluded && a2_excluded
|
|
458
|
-
end
|
|
459
|
-
false
|
|
460
|
-
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) }
|
|
461
552
|
|
|
462
|
-
|
|
463
|
-
# Checks whether two given attributes should be excluded, based on their content.
|
|
464
|
-
# Checks whether both attributes contain content that should be excluded, and
|
|
465
|
-
# returns true only if an excluded string is contained in both attribute values.
|
|
466
|
-
#
|
|
467
|
-
# @param [Nokogiri::XML::Attr] a1 left attribute
|
|
468
|
-
# @param [Nokogiri::XML::Attr] a2 right attribute
|
|
469
|
-
# @param [Hash] opts user-overridden options
|
|
470
|
-
#
|
|
471
|
-
# @return true if excluded, false otherwise
|
|
472
|
-
#
|
|
473
|
-
def attrNameExcluded?(a1, a2, opts)
|
|
474
|
-
a1_excluded = false
|
|
475
|
-
a2_excluded = false
|
|
476
|
-
opts[:ignore_attrs_by_name].each do |name|
|
|
477
|
-
a1_excluded ||= a1.to_s.include?(name)
|
|
478
|
-
a2_excluded ||= a2.to_s.include?(name)
|
|
479
|
-
return true if a1_excluded && a2_excluded
|
|
480
|
-
end
|
|
481
|
-
false
|
|
553
|
+
opts[:ignore_attrs].any? { |css| css.include?(attr.name) && attr.parent.xpath('../*').css(css).include?(attr.parent) }
|
|
482
554
|
end
|
|
483
555
|
|
|
484
556
|
##
|
|
485
557
|
# Strips the whitespace (from beginning and end) and collapses it,
|
|
486
558
|
# i.e. multiple spaces, new lines and tabs are all collapsed to a single space.
|
|
487
559
|
#
|
|
488
|
-
# @param [Nokogiri::XML::
|
|
489
|
-
# @param [Nokogiri::XML::
|
|
490
|
-
# @param [String] diff1 left diffing value
|
|
491
|
-
# @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
|
|
492
564
|
# @param [Hash] opts user-overridden options
|
|
493
565
|
# @param [Array] differences inequivalence messages
|
|
494
566
|
#
|
|
495
567
|
# @return collapsed string
|
|
496
568
|
#
|
|
497
|
-
def
|
|
569
|
+
def add_difference(node1, node2, diff1, diff2, opts, differences)
|
|
498
570
|
differences << { node1: node1, node2: node2, diff1: diff1, diff2: diff2 } if opts[:verbose]
|
|
499
571
|
end
|
|
500
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.7.3
|
|
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,45 +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
|
-
Naming/UncommunicativeMethodParamName:
|
|
36
|
-
Enabled: false
|
|
37
|
-
|
|
38
|
-
Style/Documentation:
|
|
39
|
-
Enabled: false
|
|
40
|
-
|
|
41
|
-
Style/FrozenStringLiteralComment:
|
|
42
|
-
Enabled: false
|
|
43
|
-
|
|
44
|
-
Style/Semicolon:
|
|
45
|
-
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', __dir__)
|
|
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
|