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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb6c3fb3e704a49642e05b7c2ff26442ad63a77aa22c4a2dc461ffce22c5dc59
4
- data.tar.gz: 8fa4604c0f7216f077fa48807a68c4b222fefca2dd6beedfc0d745cb3f5074ee
3
+ metadata.gz: '044582cb43241dc59a65422b8c36a2cc96d1a3120f9648c0950b7ef9fe125be5'
4
+ data.tar.gz: 3a33391e6f5a67f305546173a43725a64bd6ed4df12135434453da9bc3982e98
5
5
  SHA512:
6
- metadata.gz: 7be5a2a949660aff92a2d4fea1fa6820a960ceb7b177479ee6a7b3464cc0f9b12ce7a81f08fe4567243a658987ab48c34362cf09b9a2f11767724d669cc0f142
7
- data.tar.gz: 7d17c1fd43511146021eadc83037cf27d7c259fad86658ecba24f6c021ec4095b984d1e5cb9becaa1cd2e78b4338db95f0b437e00ab60aed0f54f6733087dde9
6
+ metadata.gz: 0f3a32d528e4298a96ce59238f7ed556fa110ea7a4d715b651e1e578887f6bde9c6852928d0971c114e6ca9fb77dca8c4e6ca53c9580143cc5d89785ca14d942
7
+ data.tar.gz: fc8371bdce1193d15d42092c24b9b44d2d9c51a685331102ba69fd9290cafdd6faf588feab68871b1c7659c42ed1f1656718b97b72fbf0702bde153e3a2caf30
data/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # CompareXML
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/compare-xml.svg)](https://rubygems.org/gems/compare-xml)
3
+ [![Gem Version](https://img.shields.io/gem/v/compare-xml.svg)](https://rubygems.org/gems/compare-xml)
4
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![Lint Status](https://github.com/vkononov/compare-xml/actions/workflows/lint.yml/badge.svg)](https://github.com/vkononov/compare-xml/actions/workflows/lint.yml)
6
+ [![Test Status](https://github.com/vkononov/compare-xml/actions/workflows/test.yml/badge.svg)](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. its purpose is to compare two instances of `Nokogiri::XML::Node` or `Nokogiri::XML::NodeSet` for equality or equivalency.
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
+ [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](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: [string1, string2, ...]` default: **`[]`**     [show examples ⇨](#ignore_attrs_by_name)
82
- - when provided, ignores specific *attributes* using [String]
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`**&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[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`**&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[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: [string1, string2, ...]` default: **`false`**
207
+ - <a id="ignore_attrs_by_name"></a>`ignore_attrs_by_name: [matcher1, matcher2, ...]` default: **`[]`**
200
208
 
201
- When provided, ignores all **attributes** which name is specified in the string array.
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
- ![diffing](https://github.com/vkononov/compare-xml/raw/master/img/diffing.png)
301
+ ![diffing](https://github.com/vkononov/compare-xml/raw/main/img/diffing.png)
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
- node1: '<title>TITLE</title>',
291
- node2: '<title>ANOTHER TITLE</title>',
292
- diff1: 'TITLE',
293
- diff2: 'ANOTHER TITLE',
294
- },
295
- {
296
- node1: '<h1>SOME HEADING</h1>',
297
- node2: '<h1 id="main">SOME HEADING</h1>',
298
- diff1: nil,
299
- diff2: 'id="main"',
300
- },
301
- {
302
- node1: '<a href="/admin" rel="icon">Link</a>',
303
- node2: '<a rel="button" href="/admin">Link</a>',
304
- diff1: '"rel="icon"',
305
- diff2: '"rel="button"',
306
- },
307
- {
308
- node1: '<cite>Author Name</cite>',
309
- node2: nil,
310
- diff1: '<cite>Author Name</cite>',
311
- diff2: nil,
312
- },
313
- {
314
- node1: '<p class="footer">FOOTER</p>',
315
- node2: '<div class="footer">FOOTER</div>',
316
- diff1: 'p',
317
- diff2: 'div',
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, {ignore_children: true})`
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).
@@ -1,3 +1,3 @@
1
1
  module CompareXML
2
- VERSION = '0.66'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
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] childopts user-overridden options used for the child nodes
76
- # @param [Bool] diffchildren use different options for the child nodes
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 = {}, childopts = {}, diffchildren = false)
84
+ def equivalent?(n1, n2, opts = {}, child_opts = {}, diff_children = false)
81
85
  opts = DEFAULTS_OPTS.merge(opts)
82
- childopts = DEFAULTS_OPTS.merge(childopts)
86
+ child_opts = DEFAULTS_OPTS.merge(child_opts)
83
87
  differences = []
84
- result = diffchildren ? compareNodes(n1, n2, opts, differences, childopts, diffchildren) : compareNodes(n1, n2, opts, differences)
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::Node] n1 left node
95
- # @param [Nokogiri::XML::Node] n2 right node
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] childopts user-overridden options used for the child nodes
99
- # @param [Bool] diffchildren use different options for the child nodes
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 compareNodes(n1, n2, opts, differences, childopts = {}, diffchildren = false, status = EQUIVALENT)
105
- if n1.class == n2.class
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 = compareCommentNodes(n1, n2, opts, differences)
111
+ status = compare_comment_nodes(n1, n2, opts, differences)
109
112
  when Nokogiri::HTML::Document
110
- status = diffchildren ? compareDocumentNodes(n1, n2, opts, differences, childopts, diffchildren) : compareDocumentNodes(n1, n2, opts, differences)
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 = diffchildren ? compareElementNodes(n1, n2, opts, differences, childopts, diffchildren) : compareElementNodes(n1, n2, opts, differences)
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 = compareTextNodes(n1, n2, opts, differences)
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 = compareChildren(n1.children, n2.children, opts, differences)
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
- addDifference(n1, n2, n1, n2, opts, differences)
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
- addDifference(n1.parent, n2, n1, n2, opts, differences)
128
+ add_difference(n1.parent, n2, n1, n2, opts, differences)
126
129
  elsif n2.is_a? Nokogiri::XML::Text
127
- addDifference(n1, n2.parent, n1, n2, opts, differences)
130
+ add_difference(n1, n2.parent, n1, n2, opts, differences)
128
131
  else
129
- addDifference(n1, n2, n1, n2, opts, differences)
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::Comment] n1 left comment
139
- # @param [Nokogiri::XML::Comment] n2 right comment
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 compareCommentNodes(n1, n2, opts, differences, status = EQUIVALENT)
147
- return true if opts[:ignore_comments]
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
- addDifference(n1.parent, n2.parent, t1, t2, opts, differences)
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::Document] n1 left document
165
- # @param [Nokogiri::XML::Document] n2 right document
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] childopts user-overridden options used for the child nodes
169
- # @param [Bool] diffchildren use different options for the child nodes
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 compareDocumentNodes(n1, n2, opts, differences, childopts = {}, diffchildren = false, status = EQUIVALENT)
176
+ def compare_document_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false)
175
177
  if n1.name == n2.name
176
- status = diffchildren ? compareChildren(n1.children, n2.children, childopts, differences, diffchildren) : compareChildren(n1.children, n2.children, opts, differences)
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
- addDifference(n1, n2, n1, n2, opts, differences)
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] diffchildren use different options for the child nodes
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 compareChildren(n1_set, n2_set, opts, differences, diffchildren = false, status = EQUIVALENT)
197
- i = 0; j = 0
198
- return if opts[:ignore_children]
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? && nodeExcluded?(n1_set[i], opts)
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? && nodeExcluded?(n2_set[j], opts)
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 = diffchildren ? compareNodes(n1_set[i], n2_set[j], opts, differences, opts, diffchildren) : compareNodes(n1_set[i], n2_set[j], opts, differences)
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; j += 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] childopts user-overridden options used for the child nodes
232
- # @param [Bool] diffchildren use different options for the child nodes
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 compareElementNodes(n1, n2, opts, differences, childopts = {}, diffchildren = false, status = EQUIVALENT)
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 = compareAttributeSets(n1, n2, n1.attribute_nodes, n2.attribute_nodes, opts, differences)
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
- result = diffchildren ? compareChildren(n1.children, n2.children, childopts, differences, diffchildren) : compareChildren(n1.children, n2.children, opts, differences)
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
- addDifference(n1, n2, n1.name, n2.name, opts, differences)
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::Text] n1 left text node
254
- # @param [Nokogiri::XML::Text] n2 right text node
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 compareTextNodes(n1, n2, opts, differences, status = EQUIVALENT)
262
- return true if opts[:ignore_text_nodes]
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
- addDifference(n1.parent, n2.parent, t1, t2, opts, differences)
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 compareAttributeSets(n1, n2, a1_set, a2_set, opts, differences)
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
- compareSortedAttributeSets(n1, n2, a1_set, a2_set, opts, differences)
404
+ compare_sorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
292
405
  else
293
- compareUnsortedAttributeSets(n1, n2, a1_set, a2_set, opts, differences)
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 compareSortedAttributeSets(n1, n2, a1_set, a2_set, opts, differences, status = EQUIVALENT)
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 = compareAttributes(n1, n2, nil, a2_set[j], opts, differences); j += 1
433
+ result = compare_attributes(n1, n2, nil, a2_set[j], opts, differences)
434
+ j += 1
321
435
  elsif a2_set[j].nil?
322
- result = compareAttributes(n1, n2, a1_set[i], nil, opts, differences); i += 1
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 = compareAttributes(n1, n2, a1_set[i], nil, opts, differences); i += 1
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 = compareAttributes(n1, n2, nil, a2_set[j], opts, differences); j += 1
442
+ result = compare_attributes(n1, n2, nil, a2_set[j], opts, differences)
443
+ j += 1
327
444
  else
328
- result = compareAttributes(n1, n2, a1_set[i], a2_set[j], opts, differences); i += 1; j += 1
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 compareUnsortedAttributeSets(n1, n2, a1_set, a2_set, opts, differences, status = EQUIVALENT)
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 = compareAttributes(n1, n2, a1_set[i], a2_set[i], opts, differences)
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 compareAttributes(n1, n2, a1, a2, opts, differences, status = EQUIVALENT)
494
+ def compare_attributes(n1, n2, a1, a2, opts, differences, status = EQUIVALENT)
376
495
  if a1.nil?
377
496
  status = MISSING_ATTRIBUTE
378
- addDifference(n1, n2, nil, "#{a2.name}=\"#{a2.value}\"", opts, differences)
497
+ add_difference(n1, n2, nil, "#{a2.name}=\"#{a2.value}\"", opts, differences)
379
498
  elsif a2.nil?
380
499
  status = MISSING_ATTRIBUTE
381
- addDifference(n1, n2, "#{a1.name}=\"#{a1.value}\"", nil, opts, differences)
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
- addDifference(n1, n2, "#{a1.name}=\"#{a1.value}\"", "#{a2.name}=\"#{a2.value}\"", opts, differences)
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
- addDifference(n1, n2, "#{a1.name}=\"#{a1.value}\"", "#{a2.name}=\"#{a2.value}\"", opts, differences)
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 nodeExcluded?(n, opts)
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
- # Checks whether two given attributes should be excluded, based on a user-specified css rule.
421
- # If true, only the specified attributes are ignored; all remaining attributes are still compared.
422
- # The CSS rule is used to locate the node that contains the attributes to be excluded.
423
- # The CSS rule MUST contain the name of the attribute to be ignored.
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
- # @return true if excluded, false otherwise
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
- def attrsExcluded?(a1, a2, opts)
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 attrContentExcluded?(a1, a2, opts)
452
- a1_excluded = false
453
- a2_excluded = false
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::Node] node1 left node
489
- # @param [Nokogiri::XML::Node] node2 right node
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 addDifference(node1, node2, diff1, diff2, opts, differences)
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: '0.66'
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: 2018-04-24 00:00:00.000000000 Z
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.8'
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.8'
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@poetic.com
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
- post_install_message:
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: '0'
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
- rubyforge_project:
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
@@ -1,13 +0,0 @@
1
- *.DS_Store
2
- *thumbs.db
3
- /*.gem
4
- /.bundle/
5
- /.idea/
6
- /.yardoc
7
- /_yardoc/
8
- /coverage/
9
- /doc/
10
- /Gemfile.lock
11
- /pkg/
12
- /spec/reports/
13
- /tmp/
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
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in compare-xml-xml.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- require 'bundler/gem_tasks'
2
- task default: :spec
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
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
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