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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 48d97a18ea1ad3981e16acd41c03d8abf90c3996
4
- data.tar.gz: ebfda907e73d068f77b6f6bf795207a707040a68
2
+ SHA256:
3
+ metadata.gz: '044582cb43241dc59a65422b8c36a2cc96d1a3120f9648c0950b7ef9fe125be5'
4
+ data.tar.gz: 3a33391e6f5a67f305546173a43725a64bd6ed4df12135434453da9bc3982e98
5
5
  SHA512:
6
- metadata.gz: 8b2328fac7d1ac1672dd8ba64ecfce353681d2e07aba8fe632fb27281ea2c58267764d3e10108993a8f2f0914502b5137293ab1f0fed12442ef9bf8865cef2fe
7
- data.tar.gz: 0e12b9c47b2c6d79ea6e826cee70d538310d9d0203d500a4856fb904794e115528698a7c848d2f5154be8f0bf6fd5d81279b6dafa555a8d6dbd2dfd9040cd52a
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,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`**&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[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`**&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[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`**&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[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`**&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
+
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
- ![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)
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
- node1: '<title>TITLE</title>',
270
- node2: '<title>ANOTHER TITLE</title>',
271
- diff1: 'TITLE',
272
- diff2: 'ANOTHER TITLE',
273
- },
274
- {
275
- node1: '<h1>SOME HEADING</h1>',
276
- node2: '<h1 id="main">SOME HEADING</h1>',
277
- diff1: nil,
278
- diff2: 'id="main"',
279
- },
280
- {
281
- node1: '<a href="/admin" rel="icon">Link</a>',
282
- node2: '<a rel="button" href="/admin">Link</a>',
283
- diff1: '"rel="icon"',
284
- diff2: '"rel="button"',
285
- },
286
- {
287
- node1: '<cite>Author Name</cite>',
288
- node2: nil,
289
- diff1: '<cite>Author Name</cite>',
290
- diff2: nil,
291
- },
292
- {
293
- node1: '<p class="footer">FOOTER</p>',
294
- node2: '<div class="footer">FOOTER</div>',
295
- diff1: 'p',
296
- diff2: 'div',
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).
@@ -1,3 +1,3 @@
1
1
  module CompareXML
2
- VERSION = '0.65'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
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] childopts user-overridden options used for the child nodes
72
- # @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
73
81
  #
74
82
  # @return true if equal, [Array] differences otherwise
75
83
  #
76
- def equivalent?(n1, n2, opts = {}, childopts = {}, diffchildren = false)
84
+ def equivalent?(n1, n2, opts = {}, child_opts = {}, diff_children = false)
77
85
  opts = DEFAULTS_OPTS.merge(opts)
78
- childopts = DEFAULTS_OPTS.merge(childopts)
86
+ child_opts = DEFAULTS_OPTS.merge(child_opts)
79
87
  differences = []
80
- 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)
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::Node] n1 left node
91
- # @param [Nokogiri::XML::Node] n2 right node
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] childopts user-overridden options used for the child nodes
95
- # @param [Bool] diffchildren use different options for the child nodes
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 compareNodes(n1, n2, opts, differences, childopts = {}, diffchildren = false, status = EQUIVALENT)
101
- 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)
102
109
  case n1
103
110
  when Nokogiri::XML::Comment
104
- status = compareCommentNodes(n1, n2, opts, differences)
111
+ status = compare_comment_nodes(n1, n2, opts, differences)
105
112
  when Nokogiri::HTML::Document
106
- 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)
107
114
  when Nokogiri::XML::Element
108
- 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)
109
116
  when Nokogiri::XML::Text
110
- status = compareTextNodes(n1, n2, opts, differences)
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 = compareChildren(n1.children, n2.children, opts, differences)
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
- addDifference(n1, n2, n1, n2, opts, differences)
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
- addDifference(n1.parent, n2, n1, n2, opts, differences)
128
+ add_difference(n1.parent, n2, n1, n2, opts, differences)
122
129
  elsif n2.is_a? Nokogiri::XML::Text
123
- addDifference(n1, n2.parent, n1, n2, opts, differences)
130
+ add_difference(n1, n2.parent, n1, n2, opts, differences)
124
131
  else
125
- addDifference(n1, n2, n1, n2, opts, differences)
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::Comment] n1 left comment
135
- # @param [Nokogiri::XML::Comment] n2 right comment
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 compareCommentNodes(n1, n2, opts, differences, status = EQUIVALENT)
143
- return true if opts[:ignore_comments]
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
- addDifference(n1.parent, n2.parent, t1, t2, opts, differences)
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::Document] n1 left document
161
- # @param [Nokogiri::XML::Document] n2 right document
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] childopts user-overridden options used for the child nodes
165
- # @param [Bool] diffchildren use different options for the child nodes
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 compareDocumentNodes(n1, n2, opts, differences, childopts = {}, diffchildren = false, status = EQUIVALENT)
176
+ def compare_document_nodes(n1, n2, opts, differences, child_opts = {}, diff_children: false)
171
177
  if n1.name == n2.name
172
- 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)
173
179
  else
174
180
  status = UNEQUAL_DOCUMENTS
175
- addDifference(n1, n2, n1, n2, opts, differences)
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] diffchildren use different options for the child nodes
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 compareChildren(n1_set, n2_set, opts, differences, diffchildren = false, status = EQUIVALENT)
193
- i = 0; j = 0
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? && nodeExcluded?(n1_set[i], opts)
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? && nodeExcluded?(n2_set[j], opts)
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 = 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)
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; j += 1
220
+ i += 1
221
+ j += 1
212
222
  end
213
- status
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] childopts user-overridden options used for the child nodes
227
- # @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
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 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)
233
348
  if n1.name == n2.name
234
- 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)
235
350
  return result unless result == EQUIVALENT || opts[:force_children] == true
236
- 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)
237
353
  status = result unless result == EQUIVALENT
238
354
  else
239
355
  status = UNEQUAL_ELEMENTS
240
- addDifference(n1, n2, n1.name, n2.name, opts, differences)
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::Text] n1 left text node
249
- # @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
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 compareTextNodes(n1, n2, opts, differences, status = EQUIVALENT)
257
- 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]
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
- addDifference(n1.parent, n2.parent, t1, t2, opts, differences)
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 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) }
284
402
  return false unless a1_set.length == a2_set.length || opts[:verbose]
285
403
  if opts[:ignore_attr_order]
286
- compareSortedAttributeSets(n1, n2, a1_set, a2_set, opts, differences)
404
+ compare_sorted_attribute_sets(n1, n2, a1_set, a2_set, opts, differences)
287
405
  else
288
- compareUnsortedAttributeSets(n1, n2, a1_set, a2_set, opts, differences)
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 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)
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 = 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
316
435
  elsif a2_set[j].nil?
317
- 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
318
438
  elsif a1_set[i].name < a2_set[j].name
319
- 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
320
441
  elsif a1_set[i].name > a2_set[j].name
321
- 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
322
444
  else
323
- 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
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 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)
349
473
  [a1_set.length, a2_set.length].max.times do |i|
350
- 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)
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 compareAttributes(n1, n2, a1, a2, opts, differences, status = EQUIVALENT)
494
+ def compare_attributes(n1, n2, a1, a2, opts, differences, status = EQUIVALENT)
371
495
  if a1.nil?
372
496
  status = MISSING_ATTRIBUTE
373
- addDifference(n1, n2, nil, "#{a2.name}=\"#{a2.value}\"", opts, differences)
497
+ add_difference(n1, n2, nil, "#{a2.name}=\"#{a2.value}\"", opts, differences)
374
498
  elsif a2.nil?
375
499
  status = MISSING_ATTRIBUTE
376
- addDifference(n1, n2, "#{a1.name}=\"#{a1.value}\"", nil, opts, differences)
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
- 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)
384
505
  end
385
506
  else
386
507
  status = UNEQUAL_ATTRIBUTES
387
- 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)
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 nodeExcluded?(n, opts)
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
- # Checks whether two given attributes should be excluded, based on a user-specified css rule.
416
- # If true, only the specified attributes are ignored; all remaining attributes are still compared.
417
- # The CSS rule is used to locate the node that contains the attributes to be excluded.
418
- # The CSS rule MUST contain the name of the attribute to be ignored.
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
- # @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.
425
543
  #
426
- def attrsExcluded?(a1, a2, opts)
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 attrContentExcluded?(a1, a2, opts)
447
- a1_excluded = false
448
- a2_excluded = false
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::Node] node1 left node
484
- # @param [Nokogiri::XML::Node] node2 right node
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 addDifference(node1, node2, diff1, diff2, opts, differences)
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: '0.65'
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-02-20 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.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
@@ -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,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
@@ -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', __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