xqsr3-xml 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1995665f234ee13f105bf6316a5ff88c91d49a22
4
+ data.tar.gz: f75620b304904ff142f92ef8576cc32b7ce1bba1
5
+ SHA512:
6
+ metadata.gz: 9719a1c7e803abf401646f4934d06231ff06cb0f371921d1cae3e442fd7408d7197e0ea3bafa03524f230a71638ff7f2ded0beabdfb449befe7e31c3aff0ed76
7
+ data.tar.gz: 9fad44e02ea4784441a2e170f476e7f593799339354ab05d7cb861b89c355b8d1a376c7b3b345457a1c69f408e6c10b146da60e90107140abbad9fc39d82e01f
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ xqsr3-xml
2
+
3
+ Copyright (c) 2005-2016, Matthew Wilson and Synesis Software
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the names of xqsr3 and xqsr3-xml nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # xqsr3-xml
2
+ e**X**tensions by fine **Q**uantum for **S**tandard **R**uby and **3**rd-party libraries, for **XML**
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/xqsr3-xml.svg)](https://badge.fury.io/rb/xqsr3-xml)
5
+
6
+ ## Introduction
7
+
8
+ [**xqsr3**](https://github.com/synesissoftware/xqsr3/) is a lightweight, low-coupling library of assorted extensions to standard ruby and 3rd-party libraries. As of version **0.31** of **xqsr3**, the **XML Utilites** components were moved out into **xqsr3-xml** in order that the core **xqsr** can be independent of any non-standard libraries.
9
+
10
+ ## Table of Contents
11
+
12
+ 1. [Introduction](#introduction)
13
+ 2. [Installation](#installation)
14
+ 3. [Components](#components)
15
+ 4. [Project Information](#project-information)
16
+
17
+ ## Installation
18
+
19
+ Install using `gem install xqsr3-xml` or add it to your `Gemfile`.
20
+
21
+ ## Components
22
+
23
+ **xqsr3** provides components in the following categories:
24
+
25
+ * Array Utilities
26
+ * Command-line Utilities
27
+ * Containers
28
+ * Conversion
29
+ * Diagnostics
30
+ * Hash Utilities
31
+ * IO
32
+ * Quality
33
+ * String Utilities
34
+
35
+ **xqsr3-xml** provides components in the following categories:
36
+
37
+ * XML Utilities
38
+
39
+ ## Project Information
40
+
41
+ ### Where to get help
42
+
43
+ [GitHub Page](https://github.com/synesissoftware/xqsr3-xml "GitHub Page")
44
+
45
+ ### Contribution guidelines
46
+
47
+ Defect reports, feature requests, and pull requests are welcome on https://github.com/synesissoftware/xqsr3-xml.
48
+
49
+ ### Dependencies
50
+
51
+ * [**Nokogiri**](http://nokogiri.org/)
52
+ * [**xqsr3**](https://github.com/synesissoftware/xqsr3/)
53
+
54
+ ### Related projects
55
+
56
+ * [**xqsr3**](https://github.com/synesissoftware/xqsr3/)
57
+
58
+ ### License
59
+
60
+ **xqsr3-xml** is released under the 3-clause BSD license. See [LICENSE](./LICENSE) for details.
@@ -0,0 +1,544 @@
1
+
2
+ # ######################################################################## #
3
+ # File: lib/xqsr3/xml/_utilities/compare.rb
4
+ #
5
+ # Purpose: Definition of the ::Xqsr3::XML::Utilities::Compare
6
+ # module
7
+ #
8
+ # Created: 30th July 2017
9
+ # Updated: 16th August 2018
10
+ #
11
+ # Home: http://github.com/synesissoftware/xqsr3
12
+ #
13
+ # Author: Matthew Wilson
14
+ #
15
+ # Copyright (c) 2017-2018, Matthew Wilson and Synesis Software
16
+ # All rights reserved.
17
+ #
18
+ # Redistribution and use in source and binary forms, with or without
19
+ # modification, are permitted provided that the following conditions are
20
+ # met:
21
+ #
22
+ # * Redistributions of source code must retain the above copyright notice,
23
+ # this list of conditions and the following disclaimer.
24
+ #
25
+ # * Redistributions in binary form must reproduce the above copyright
26
+ # notice, this list of conditions and the following disclaimer in the
27
+ # documentation and/or other materials provided with the distribution.
28
+ #
29
+ # * Neither the names of the copyright holder nor the names of its
30
+ # contributors may be used to endorse or promote products derived from
31
+ # this software without specific prior written permission.
32
+ #
33
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
34
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
37
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
+ #
45
+ # ######################################################################## #
46
+
47
+
48
+ # ##########################################################
49
+ # ::Xqsr3::XML::Utilities::Compare
50
+
51
+ =begin
52
+ =end
53
+
54
+ require 'xqsr3/xml/utilities/navigation'
55
+
56
+ require 'xqsr3/quality/parameter_checking'
57
+
58
+ require 'nokogiri'
59
+
60
+ module Xqsr3
61
+ module XML
62
+ module Utilities
63
+
64
+ module Compare
65
+
66
+ # Class that represents the result of an XML comparison
67
+ #
68
+ # NOTE: Sadly, we cannot create instances of +FalseClass+/+TrueClass+,
69
+ # to which we could then add a +reason+ attribute, so instead we must
70
+ # have a results class
71
+ class Result
72
+
73
+ include ::Xqsr3::Quality::ParameterChecking
74
+
75
+ protected :check_parameter
76
+
77
+ #
78
+ # Options:
79
+ #
80
+ # +:different_attributes+
81
+ # +:different_attribute_count+
82
+ # +:different_attribute_order+
83
+ # +:different_child_node_count+
84
+ # +:different_child_node_order+
85
+ # +:different_child_nodes+
86
+ # +:different_node_names+
87
+ # +:different_node_contents+
88
+ # +:parameter_is_empty+
89
+ # +:parameter_is_nil+
90
+ # +:+
91
+
92
+ def initialize status, reason, **options
93
+
94
+ @call_stack = caller(2)
95
+
96
+ check_parameter status, 'status', types: [ ::FalseClass, ::TrueClass ]
97
+ check_parameter reason, 'reason', type: ::Symbol, allow_nil: true
98
+
99
+ @status = status
100
+ @reason = reason
101
+
102
+ @lhs_node = options[:lhs_node]
103
+ @rhs_node = options[:rhs_node]
104
+ end
105
+
106
+ def self.return status, reason, **options
107
+
108
+ return self.new status, reason, **options
109
+ end
110
+
111
+ def self.same reason = nil, **options
112
+
113
+ return self.new true, reason, **options
114
+ end
115
+
116
+ def self.different reason, **options
117
+
118
+ return self.new false, reason, **options
119
+ end
120
+
121
+ attr_reader :call_stack
122
+ attr_reader :status
123
+ attr_reader :reason
124
+
125
+ def different?
126
+
127
+ !status
128
+ end
129
+
130
+ def same?
131
+
132
+ status
133
+ end
134
+
135
+ def details
136
+
137
+ r = reason.to_s.gsub(/_/, ' ')
138
+
139
+ qualifying = ''
140
+
141
+ if @lhs_node
142
+
143
+ qualifying += '; ' unless qualifying.empty?
144
+ qualifying += "lhs-node=#{@lhs_node}"
145
+ end
146
+
147
+ if @rhs_node
148
+
149
+ qualifying += '; ' unless qualifying.empty?
150
+ qualifying += "rhs-node=#{@rhs_node}"
151
+ end
152
+
153
+ r = "#{r}: #{qualifying}" unless qualifying.empty?
154
+
155
+ r
156
+ end
157
+
158
+ def to_s
159
+
160
+ return 'same' if same?
161
+
162
+ "different, because: #{details}"
163
+ end
164
+ end
165
+
166
+ module Internal_Compare_
167
+
168
+ include ::Xqsr3::XML::Utilities::Navigation
169
+
170
+ extend ::Xqsr3::Quality::ParameterChecking
171
+
172
+ DEFAULT_OPTIONS = {
173
+
174
+ debug: false,
175
+ # element_order: false,
176
+ equate_nil_and_empty: false,
177
+ ignore_attributes: false,
178
+ ignore_attribute_order: true,
179
+ ignore_child_node_order: true,
180
+ ignore_content: false,
181
+ ignore_content_case: false,
182
+ ignore_xml_declarations: true,
183
+ normalise_whitespace: true,
184
+ # normalize_whitespace: true,
185
+ validate_params: true,
186
+ }
187
+
188
+ ORDER_OPTIONS_SYMBOLS = [
189
+
190
+ :element_order,
191
+ :ignore_attribute_order,
192
+ :ignore_child_node_order,
193
+ :ignore_content,
194
+ :ignore_content_case,
195
+ ]
196
+
197
+ WHITESPACE_OPTIONS_SYMBOLS = [
198
+
199
+ :normalise_whitespace,
200
+ :normalize_whitespace,
201
+ ]
202
+
203
+ def self.derive_options_ given_options
204
+
205
+ default_options = DEFAULT_OPTIONS
206
+ derived_options = {}.merge given_options
207
+
208
+
209
+ # sort whitespace
210
+
211
+ if WHITESPACE_OPTIONS_SYMBOLS.any? { |sym| given_options.has_key? sym }
212
+
213
+ default_options = default_options.reject { |k, v| WHITESPACE_OPTIONS_SYMBOLS.include? k }
214
+ end
215
+
216
+ if given_options.has_key? :normalise_whitespace
217
+
218
+ derived_options.delete :normalize_whitespace
219
+ elsif given_options.has_key? :normalize_whitespace
220
+
221
+ derived_options[:normalise_whitespace] = given_options[:normalize_whitespace]
222
+
223
+ derived_options.delete :normalize_whitespace
224
+ end
225
+
226
+
227
+ # sort element-order
228
+
229
+ if ORDER_OPTIONS_SYMBOLS.any? { |sym| given_options.has_key? sym }
230
+
231
+ default_options = default_options.reject { |k, v| ORDER_OPTIONS_SYMBOLS.include? k }
232
+ end
233
+
234
+ if given_options.has_key? :element_order
235
+
236
+ element_order = given_options[:element_order]
237
+
238
+ derived_options[:ignore_attribute_order] = !element_order
239
+ derived_options[:ignore_child_node_order] = !element_order
240
+ end
241
+
242
+ derived_options[:ignore_attribute_order] = given_options[:ignore_attribute_order] if given_options.has_key? :ignore_attribute_order
243
+ derived_options[:ignore_child_node_order] = given_options[:ignore_child_node_order] if given_options.has_key? :ignore_child_node_order
244
+
245
+ default_options.merge derived_options
246
+ end
247
+
248
+ def self.one_line_ s
249
+
250
+ s = s.to_s.gsub(/\s+/, ' ')
251
+ end
252
+
253
+ #
254
+ # +:debug+
255
+ # +:element_order+
256
+ # +:equate_nil_and_empty+
257
+ # +:ignore_attributes+
258
+ # +:ignore_attribute_order+
259
+ # +:ignore_xml_declarations+
260
+ # +:normalise_whitespace+
261
+ # +:normalize_whitespace+
262
+ # +:validate_params+
263
+ #
264
+
265
+ def self.xml_compare_ lhs, rhs, options
266
+
267
+ $stderr.puts "#{self}#{__method__}(lhs (#{lhs.class})=#{self.one_line_ lhs}, rhs (#{rhs.class})=#{self.one_line_ rhs}, options (#{options.class})=#{options})" if $DEBUG
268
+
269
+ # validate parameter(s)
270
+
271
+ check_parameter options, 'options', type: ::Hash if $DEBUG
272
+
273
+ validate_params = $DEBUG || options[:debug] || options[:validate_params]
274
+
275
+ check_parameter lhs, 'lhs', types: [ ::String, ::Nokogiri::XML::Node ], allow_nil: true if validate_params
276
+ check_parameter rhs, 'rhs', types: [ ::String, ::Nokogiri::XML::Node ], allow_nil: true if validate_params
277
+
278
+ options = self.derive_options_ options
279
+
280
+ # deal with nil(s)
281
+
282
+ return Result.same if lhs.nil? && rhs.nil?
283
+
284
+ if lhs.nil?
285
+
286
+ return Result.same if options[:equate_nil_and_empty] && ::String === rhs && rhs.empty?
287
+
288
+ return Result.different :parameter_is_nil
289
+ end
290
+
291
+ if rhs.nil?
292
+
293
+ return Result.same if options[:equate_nil_and_empty] && ::String === lhs && lhs.empty?
294
+
295
+ return Result.different :parameter_is_nil
296
+ end
297
+
298
+
299
+ # deal with string(s)
300
+
301
+ lhs = Nokogiri::XML(lhs) if ::String === lhs
302
+ rhs = Nokogiri::XML(rhs) if ::String === rhs
303
+
304
+
305
+
306
+ # deal with XML Declaration(s)
307
+
308
+ if options[:ignore_xml_declarations]
309
+
310
+ if ::Nokogiri::XML::Document === lhs
311
+
312
+ lhs_root = lhs.root
313
+ lhs = lhs_root if lhs_root
314
+ end
315
+
316
+ if ::Nokogiri::XML::Document === rhs
317
+
318
+ rhs_root = rhs.root
319
+ rhs = rhs_root if rhs_root
320
+ end
321
+ end
322
+
323
+
324
+ self.xml_compare_nodes_ lhs, rhs, options
325
+ end
326
+
327
+ def self.xml_compare_nodes_ lhs, rhs, options
328
+
329
+ $stderr.puts "#{self}#{__method__}(lhs (#{lhs.class})=#{self.one_line_ lhs}, rhs (#{rhs.class})=#{self.one_line_ rhs}, options (#{options.class})=#{options})" if $DEBUG
330
+
331
+
332
+ # Compare:
333
+ #
334
+ # - name
335
+ # - attributes
336
+ # - content
337
+ # - children
338
+ # -
339
+
340
+
341
+ # ##########################
342
+ # name
343
+
344
+ lhs_name = lhs.name
345
+ rhs_name = rhs.name
346
+
347
+ return Result.different :different_node_names, lhs_node: lhs, rhs_node: rhs if lhs_name != rhs_name
348
+
349
+
350
+ # ##########################
351
+ # attributes
352
+
353
+ unless options[:ignore_attributes]
354
+
355
+ lhs_attributes = lhs.attribute_nodes
356
+ rhs_attributes = rhs.attribute_nodes
357
+
358
+ return Result.different :different_attribute_count, lhs_node: lhs, rhs_node: rhs if lhs_attributes.count != rhs_attributes.count
359
+
360
+
361
+ lhs_attr_list = lhs_attributes.map { |attr| [ attr.name, attr.content ] }
362
+ rhs_attr_list = rhs_attributes.map { |attr| [ attr.name, attr.content ] }
363
+
364
+ if lhs_attr_list != rhs_attr_list
365
+
366
+ # do the sort first
367
+
368
+ lhs_attr_list.sort! { |l, r| l[0] <=> r[0] }
369
+ rhs_attr_list.sort! { |l, r| l[0] <=> r[0] }
370
+
371
+ # Now there are four possibiliies:
372
+ #
373
+ # 1. Different attributes
374
+ # 2. Different attribute order
375
+ # 3. Same (when reordered)
376
+
377
+ if lhs_attr_list == rhs_attr_list
378
+
379
+ if options[:ignore_attribute_order]
380
+
381
+ # 3
382
+ else
383
+
384
+ # 2
385
+
386
+ return Result.different :different_attribute_order, lhs_node: lhs, rhs_node: rhs
387
+ end
388
+ else
389
+
390
+ return Result.different :different_attributes, lhs_node: lhs, rhs_node: rhs
391
+ end
392
+ end
393
+ end
394
+
395
+ # ##########################
396
+ # content
397
+
398
+ unless options[:ignore_content]
399
+
400
+ lhs_texts = self.get_descendants(lhs).select { |el| el.text? }.map { |el| el.content }
401
+ rhs_texts = self.get_descendants(rhs).select { |el| el.text? }.map { |el| el.content }
402
+
403
+ content_same = lhs_texts == rhs_texts
404
+
405
+ unless content_same
406
+
407
+ if options[:normalise_whitespace]
408
+
409
+ lhs_texts = lhs_texts.reject { |s| s.strip.empty? }
410
+ rhs_texts = rhs_texts.reject { |s| s.strip.empty? }
411
+
412
+ content_same = lhs_texts == rhs_texts
413
+ end
414
+ end
415
+
416
+ unless content_same
417
+
418
+ if options[:ignore_content_case]
419
+
420
+ lhs_texts = lhs_texts.reject { |s| s.downcase }
421
+ rhs_texts = rhs_texts.reject { |s| s.downcase }
422
+
423
+ content_same = lhs_texts == rhs_texts
424
+ end
425
+ end
426
+
427
+ unless content_same
428
+
429
+ if options[:ignore_child_node_order]
430
+
431
+ lhs_texts = lhs_texts.sort
432
+ rhs_texts = rhs_texts.sort
433
+
434
+ content_same = lhs_texts == rhs_texts
435
+ end
436
+ end
437
+
438
+ return Result.different :different_node_contents, lhs_node: lhs, rhs_node: rhs unless content_same
439
+ end
440
+
441
+
442
+ # ##########################
443
+ # children (preparation)
444
+
445
+ lhs_children = lhs.children.to_a
446
+ rhs_children = rhs.children.to_a
447
+
448
+ lhs_children.reject! { |child| child.text? && child.content.strip.empty? }
449
+ rhs_children.reject! { |child| child.text? && child.content.strip.empty? }
450
+
451
+
452
+ # ##########################
453
+ # children - count
454
+
455
+ lhs_children_count = lhs_children.count
456
+ rhs_children_count = rhs_children.count
457
+
458
+ return Result.different :different_child_node_count, lhs_node: lhs, rhs_node: rhs if lhs_children_count != rhs_children_count
459
+
460
+
461
+ # ##########################
462
+ # children - names
463
+
464
+ lhs_children_names = lhs_children.map { |ch| ch.name }
465
+ rhs_children_names = rhs_children.map { |ch| ch.name }
466
+
467
+ if lhs_children_names != rhs_children_names
468
+
469
+ # At this point, the lists of names of child elements are
470
+ # different. This may be because there are different
471
+ # elements or because they are in a different order. Either
472
+ # way, in order to provide detailed reasons for
473
+ # inequivalency, we must do an order-independent comparison
474
+
475
+ children_sorted_lhs = lhs_children.sort { |x, y| x.name <=> y.name }
476
+ children_sorted_rhs = rhs_children.sort { |x, y| x.name <=> y.name }
477
+
478
+ ch_names_sorted_lhs = children_sorted_lhs.map { |ch| ch.name }
479
+ ch_names_sorted_rhs = children_sorted_rhs.map { |ch| ch.name }
480
+
481
+ ignore_order = options[:ignore_child_node_order]
482
+
483
+ if ignore_order
484
+
485
+ return Result.different :different_child_nodes, lhs_node: lhs, rhs_node: rhs if ch_names_sorted_lhs != ch_names_sorted_rhs
486
+
487
+ # Since they are the same (when reordered), we need to
488
+ # adopt the ordered sequences so that the comparison of
489
+ # the children are meaningful
490
+
491
+ lhs_children = children_sorted_lhs
492
+ rhs_children = children_sorted_rhs
493
+ else
494
+
495
+ # failed, so need to determine whether it's due to
496
+ # different nodes or different order
497
+
498
+ if ch_names_sorted_lhs == ch_names_sorted_rhs
499
+
500
+ return Result.different :different_child_node_order, lhs_node: lhs, rhs_node: rhs
501
+ else
502
+
503
+ return Result.different :different_child_nodes, lhs_node: lhs, rhs_node: rhs
504
+ end
505
+ end
506
+ end
507
+
508
+ (0 ... lhs_children.count).each do |index|
509
+
510
+ ch_lhs = lhs_children[index]
511
+ ch_rhs = rhs_children[index]
512
+
513
+ r = self.xml_compare_nodes_ ch_lhs, ch_rhs, options
514
+
515
+ return r unless r.status
516
+ end
517
+
518
+ return Result.same
519
+ end
520
+ end
521
+
522
+ def self.xml_compare lhs, rhs, **options
523
+
524
+ $stderr.puts "#{self}#{__method__}(lhs (#{lhs.class})=#{Internal_Compare_.one_line_ lhs}, rhs (#{rhs.class})=#{Internal_Compare_.one_line_ rhs}, options (#{options.class})=#{options})" if $DEBUG
525
+
526
+ Internal_Compare_.xml_compare_ lhs, rhs, options
527
+ end
528
+
529
+ def xml_compare lhs, rhs, **options
530
+
531
+ $stderr.puts "#{self}#{__method__}(lhs (#{lhs.class})=#{Internal_Compare_.one_line_ lhs}, rhs (#{rhs.class})=#{Internal_Compare_.one_line_ rhs}, options (#{options.class})=#{options})" if $DEBUG
532
+
533
+ Internal_Compare_.xml_compare_ lhs, rhs, options
534
+ end
535
+
536
+ end # module Compare
537
+
538
+ end # module Utilities
539
+ end # module XML
540
+ end # module Xqsr3
541
+
542
+ # ############################## end of file ############################# #
543
+
544
+
@@ -0,0 +1,108 @@
1
+
2
+ # ######################################################################## #
3
+ # File: lib/xqsr3/xml/_utilities/navigation.rb
4
+ #
5
+ # Purpose: Definition of the ::Xqsr3::XML::Utilities::Navigation
6
+ # module
7
+ #
8
+ # Created: 7th August 2018
9
+ # Updated: 7th August 2018
10
+ #
11
+ # Home: http://github.com/synesissoftware/xqsr3
12
+ #
13
+ # Author: Matthew Wilson
14
+ #
15
+ # Copyright (c) 2018, Matthew Wilson and Synesis Software
16
+ # All rights reserved.
17
+ #
18
+ # Redistribution and use in source and binary forms, with or without
19
+ # modification, are permitted provided that the following conditions are
20
+ # met:
21
+ #
22
+ # * Redistributions of source code must retain the above copyright notice,
23
+ # this list of conditions and the following disclaimer.
24
+ #
25
+ # * Redistributions in binary form must reproduce the above copyright
26
+ # notice, this list of conditions and the following disclaimer in the
27
+ # documentation and/or other materials provided with the distribution.
28
+ #
29
+ # * Neither the names of the copyright holder nor the names of its
30
+ # contributors may be used to endorse or promote products derived from
31
+ # this software without specific prior written permission.
32
+ #
33
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
34
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
37
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
+ #
45
+ # ######################################################################## #
46
+
47
+
48
+ # ##########################################################
49
+ # ::Xqsr3::XML::Utilities::Navigation
50
+
51
+ =begin
52
+ =end
53
+
54
+ require 'xqsr3/quality/parameter_checking'
55
+
56
+ require 'nokogiri'
57
+
58
+ module Xqsr3
59
+ module XML
60
+ module Utilities
61
+
62
+ module Navigation
63
+
64
+ module Internal_Compare_
65
+
66
+ extend ::Xqsr3::Quality::ParameterChecking
67
+
68
+ def self.get_descendants node
69
+
70
+ descendants = []
71
+
72
+ node.children.each do |child|
73
+
74
+ descendants << child
75
+
76
+ descendants += self.get_descendants child
77
+ end
78
+
79
+ descendants
80
+ end
81
+ end # module Internal_Compare_
82
+
83
+ def self.included receiver
84
+
85
+ def receiver.get_descendants node
86
+
87
+ Internal_Compare_.get_descendants node
88
+ end
89
+ end
90
+
91
+ def self.get_descendants node
92
+
93
+ Internal_Compare_.get_descendants node
94
+ end
95
+
96
+ def get_descendants
97
+
98
+ Internal_Compare_.get_descendants self
99
+ end
100
+ end # module Navigation
101
+
102
+ end # module Utilities
103
+ end # module XML
104
+ end # module Xqsr3
105
+
106
+ # ############################## end of file ############################# #
107
+
108
+
@@ -0,0 +1,71 @@
1
+
2
+ # ######################################################################## #
3
+ # File: lib/xqsr3/xml/version.rb
4
+ #
5
+ # Purpose: Version for xqsr3-xml library
6
+ #
7
+ # Created: 1st March 2019
8
+ # Updated: 2nd March 2019
9
+ #
10
+ # Home: http://github.com/synesissoftware/xqsr3
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2019, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+ =begin
48
+ =end
49
+
50
+ module Xqsr3
51
+ module XML
52
+
53
+ # Current version of the Xqsr3 library
54
+ VERSION = '0.1.0.1'
55
+
56
+ private
57
+ VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
58
+ public
59
+ # Major version of the Xqsr3 library
60
+ VERSION_MAJOR = VERSION_PARTS_[0] # :nodoc:
61
+ # Minor version of the Xqsr3 library
62
+ VERSION_MINOR = VERSION_PARTS_[1] # :nodoc:
63
+ # Revision version of the Xqsr3 library
64
+ VERSION_REVISION = VERSION_PARTS_[2] # :nodoc:
65
+
66
+ end # module XML
67
+ end # module Xqsr3
68
+
69
+ # ############################## end of file ############################# #
70
+
71
+
@@ -0,0 +1,36 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '../../lib')
4
+
5
+ require 'xqsr3/xml/version'
6
+
7
+ require 'test/unit'
8
+
9
+ class Test_version < Test::Unit::TestCase
10
+
11
+ def test_has_VERSION
12
+
13
+ assert defined? Xqsr3::XML::VERSION
14
+ end
15
+
16
+ def test_has_VERSION_MAJOR
17
+
18
+ assert defined? Xqsr3::XML::VERSION_MAJOR
19
+ end
20
+
21
+ def test_has_VERSION_MINOR
22
+
23
+ assert defined? Xqsr3::XML::VERSION_MINOR
24
+ end
25
+
26
+ def test_has_VERSION_REVISION
27
+
28
+ assert defined? Xqsr3::XML::VERSION_REVISION
29
+ end
30
+
31
+ def test_VERSION_has_consistent_format
32
+
33
+ assert_equal Xqsr3::XML::VERSION, "#{Xqsr3::XML::VERSION_MAJOR}.#{Xqsr3::XML::VERSION_MINOR}.#{Xqsr3::XML::VERSION_REVISION}"
34
+ end
35
+ end
36
+
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # executes all other tests
4
+
5
+ this_dir = File.expand_path(File.dirname(__FILE__))
6
+
7
+ # all tc_*rb in current directory
8
+ Dir[File.join(this_dir, 'tc_*rb')].each { |file| require file }
9
+
10
+ # all ts_*rb in immediate sub-directories
11
+ Dir[File.join(this_dir, '*', 'ts_*rb')].each { |file| require file }
12
+
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # executes all other tests
4
+
5
+ this_dir = File.expand_path(File.dirname(__FILE__))
6
+
7
+ # all tc_*rb in current directory
8
+ Dir[File.join(this_dir, 'tc_*rb')].each { |file| require file }
9
+
10
+ # all ts_*rb in immediate sub-directories
11
+ Dir[File.join(this_dir, '*', 'ts_*rb')].each { |file| require file }
12
+
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *(['..'] * 4), 'lib')
4
+
5
+ require 'xqsr3/xml/utilities/compare'
6
+
7
+ require 'xqsr3/extensions/test/unit'
8
+ require 'test/unit'
9
+
10
+ class Test_Xqsr3_XML_Utilities_Compare < Test::Unit::TestCase
11
+
12
+ include ::Xqsr3::XML::Utilities::Compare
13
+
14
+ def test_compare_nil
15
+
16
+ assert xml_compare(nil, nil).same?
17
+
18
+ assert_false xml_compare('', nil).same?
19
+ assert_false xml_compare(nil, '').same?
20
+
21
+ assert xml_compare('', nil, equate_nil_and_empty: true).same?
22
+ assert xml_compare(nil, '', equate_nil_and_empty: true).same?
23
+ end
24
+
25
+ def test_compare_empty
26
+
27
+ assert xml_compare('', '').same?
28
+
29
+ assert_false xml_compare('<abc/>', '').same?
30
+ assert_false xml_compare('', '<abc/>').same?
31
+ end
32
+
33
+ def test_compare_one_level_1
34
+
35
+ assert xml_compare('<abc/>', '<abc/>').same?
36
+ assert xml_compare('<abc/>', '<abc></abc>').same?
37
+ assert_false xml_compare('<abc/>', '<def/>').same?
38
+ end
39
+
40
+ def test_compare_two_level_1
41
+
42
+ assert xml_compare('<parent><child1></child1></parent>', '<parent><child1></child1></parent>').same?
43
+ assert xml_compare('<parent><child1/></parent>', '<parent><child1></child1></parent>').same?
44
+
45
+ r = xml_compare('<parent><child1/></parent>', '<parent><child2/></parent>')
46
+
47
+ assert_false r.same?
48
+ end
49
+
50
+ def test_compare_attributes_1
51
+
52
+ lhs = <<END_OF_lhs
53
+ <node name="John Smith" age="21" />
54
+ END_OF_lhs
55
+
56
+ rhs_same = <<END_OF_lhs
57
+ <node age="21" name="John Smith" />
58
+ END_OF_lhs
59
+
60
+ rhs_diff = <<END_OF_lhs
61
+ <node name="John Smith" age="22" />
62
+ END_OF_lhs
63
+
64
+ r = xml_compare lhs, rhs_same, ignore_attribute_order: false
65
+
66
+ assert r.different?, r.details
67
+ assert_equal :different_attribute_order, r.reason
68
+
69
+ r = xml_compare lhs, rhs_same, ignore_attribute_order: true
70
+
71
+ assert r.same?
72
+
73
+ r = xml_compare lhs, rhs_same, element_order: false
74
+
75
+ assert r.same?
76
+
77
+ r = xml_compare lhs, rhs_diff
78
+
79
+ assert r.different?
80
+ assert_equal :different_attributes, r.reason
81
+ end
82
+
83
+ def test_compare_two_level_2
84
+
85
+ lhs = <<END_OF_lhs
86
+ <parent>
87
+ <child1/>
88
+ </parent>
89
+ END_OF_lhs
90
+ rhs = <<END_OF_rhs
91
+ <parent>
92
+ <child1>
93
+ </child1>
94
+ </parent>
95
+ END_OF_rhs
96
+
97
+ r = xml_compare lhs, rhs, normalize_whitespace: false
98
+
99
+ assert r.different?, "#{r.details}"
100
+ assert_equal :different_node_contents, r.reason
101
+
102
+ r = xml_compare(lhs, rhs, normalize_whitespace: true)
103
+
104
+ assert r.same?, "#{r.details}"
105
+ end
106
+
107
+ def test_compare_two_level_3
108
+
109
+ lhs = <<END_OF_lhs
110
+ <parent>
111
+ <child1/>
112
+ <child2>
113
+ <grandchild2a/>
114
+ </child2>
115
+ </parent>
116
+ END_OF_lhs
117
+ rhs = <<END_OF_rhs
118
+ <parent>
119
+ <child2><grandchild2a/></child2>
120
+ <child1>
121
+ </child1>
122
+ </parent>
123
+ END_OF_rhs
124
+
125
+ r = xml_compare lhs, rhs, normalize_whitespace: true
126
+
127
+ assert r.same?, "#{r.details}"
128
+ end
129
+
130
+ def test_different_declarations_and_dont_ignore
131
+
132
+ lhs_str = <<END_OF_lhs_doc
133
+ <?xml version="1.0"?>
134
+ <outer>
135
+ <mid>
136
+ <inner>some text</inner>
137
+ </mid>
138
+ </outer>
139
+ END_OF_lhs_doc
140
+
141
+ rhs_str = <<END_OF_rhs_doc
142
+ <?xml version="1.0"?>
143
+ <mid>
144
+ <inner>some text</inner>
145
+ </mid>
146
+ END_OF_rhs_doc
147
+
148
+ lhs_doc = Nokogiri::XML lhs_str
149
+ rhs_doc = Nokogiri::XML rhs_str
150
+
151
+ expected = rhs_doc
152
+ actual = lhs_doc.at_xpath('/outer/mid')
153
+
154
+ r = xml_compare expected, actual, normalise_whitespace: true, ignore_xml_declarations: false
155
+
156
+ assert !r.same?
157
+ end
158
+
159
+ def test_different_declarations_and_do_ignore
160
+
161
+ lhs_str = <<END_OF_lhs_doc
162
+ <?xml version="1.0"?>
163
+ <outer>
164
+ <mid>
165
+ <inner>some text</inner>
166
+ </mid>
167
+ </outer>
168
+ END_OF_lhs_doc
169
+
170
+ rhs_str = <<END_OF_rhs_doc
171
+ <?xml version="1.0"?>
172
+ <mid>
173
+ <inner>some text</inner>
174
+ </mid>
175
+ END_OF_rhs_doc
176
+
177
+ lhs_doc = Nokogiri::XML lhs_str
178
+ rhs_doc = Nokogiri::XML rhs_str
179
+
180
+ expected = rhs_doc
181
+ actual = lhs_doc.at_xpath('/outer/mid')
182
+
183
+ r = xml_compare expected, actual, normalise_whitespace: true, ignore_xml_declarations: true
184
+
185
+ assert r.same?, "#{r.details}"
186
+ end
187
+
188
+ def test_different_node_contents_by_child_node_order
189
+
190
+ lhs_str = <<END_OF_lhs_doc
191
+ <?xml version="1.0"?>
192
+ <outer>
193
+ <mid_1>
194
+ <inner>some text</inner>
195
+ </mid_1>
196
+ <mid_2>
197
+ <inner>some more text</inner>
198
+ </mid_2>
199
+ </outer>
200
+ END_OF_lhs_doc
201
+
202
+ rhs_str = <<END_OF_rhs_doc
203
+ <?xml version="1.0"?>
204
+ <outer>
205
+ <mid_2>
206
+ <inner>some more text</inner>
207
+ </mid_2>
208
+ <mid_1>
209
+ <inner>some text</inner>
210
+ </mid_1>
211
+ </outer>
212
+ END_OF_rhs_doc
213
+
214
+ lhs_doc = Nokogiri::XML lhs_str
215
+ rhs_doc = Nokogiri::XML rhs_str
216
+
217
+ expected = rhs_doc
218
+ actual = lhs_doc
219
+
220
+ r = xml_compare expected, actual, ignore_child_node_order: true, normalise_whitespace: true, ignore_xml_declarations: true
221
+
222
+ assert r.same?, "#{r.details}"
223
+ end
224
+
225
+ def test_different_node_contents_by_child_node_order_and_whitespace
226
+
227
+ lhs_str = <<END_OF_lhs_doc
228
+ <?xml version="1.0"?>
229
+ <outer><mid_1><inner>some text</inner></mid_1><mid_2><inner>some more text</inner></mid_2></outer>
230
+ END_OF_lhs_doc
231
+
232
+ rhs_str = <<END_OF_rhs_doc
233
+ <?xml version="1.0"?>
234
+ <outer>
235
+ <mid_2>
236
+ <inner>some more text</inner>
237
+ </mid_2>
238
+ <mid_1>
239
+ <inner>some text</inner>
240
+ </mid_1>
241
+ </outer>
242
+ END_OF_rhs_doc
243
+
244
+ lhs_doc = Nokogiri::XML lhs_str
245
+ rhs_doc = Nokogiri::XML rhs_str
246
+
247
+ expected = rhs_doc
248
+ actual = lhs_doc
249
+
250
+ r = xml_compare expected, actual, ignore_child_node_order: true, normalise_whitespace: true, ignore_xml_declarations: true
251
+
252
+ assert r.same?, "#{r.details}"
253
+ end
254
+ end
255
+
256
+
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *(['..'] * 4), 'lib')
4
+
5
+ require 'xqsr3/xml/utilities/navigation'
6
+
7
+ require 'xqsr3/extensions/test/unit'
8
+ require 'test/unit'
9
+
10
+ require 'nokogiri'
11
+
12
+ class Test_Xqsr3_XML_Utilities_Navigation < Test::Unit::TestCase
13
+
14
+ include ::Xqsr3::XML::Utilities::Navigation
15
+
16
+ def test_get_descendants_1
17
+
18
+ xml_s = <<END_OF_rhs_doc
19
+ <?xml version="1.0"?>
20
+ <document>
21
+ <outer>
22
+ <mid_2>
23
+ <inner>some more text</inner>
24
+ </mid_2>
25
+ <mid_1>
26
+ <inner>some text</inner>
27
+ </mid_1>
28
+ </outer>
29
+ </document>
30
+ END_OF_rhs_doc
31
+
32
+ xml = ::Nokogiri.XML(xml_s)
33
+ doc = xml.children.first
34
+
35
+ descs = self.class.get_descendants xml.children.first
36
+
37
+ assert_not_nil descs
38
+ assert_kind_of ::Array, descs
39
+
40
+ assert_operator 7, :<=, descs.size
41
+ %w{ outer mid_1 mid_2 }.each do |name|
42
+
43
+ assert(descs.find { |el| name == el.name }, "did not find an element named '#{name}' in the collection #{descs}")
44
+ end
45
+
46
+ texts = descs.select { |el| el.text? }
47
+
48
+ [ 'some text', 'some more text' ].each do |text|
49
+
50
+ assert(texts.find { |el| text == el.text }, "did not find an element with the text '#{text}' in the collection #{texts}")
51
+ end
52
+ end
53
+ end
54
+
55
+
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # executes all other tests
4
+
5
+ this_dir = File.expand_path(File.dirname(__FILE__))
6
+
7
+ # all tc_*rb in current directory
8
+ Dir[File.join(this_dir, 'tc_*rb')].each { |file| require file }
9
+
10
+ # all ts_*rb in immediate sub-directories
11
+ Dir[File.join(this_dir, '*', 'ts_*rb')].each { |file| require file }
12
+
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xqsr3-xml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Wilson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: xqsr3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.31.0
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: '1.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.31.0
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: nokogiri
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: '1.6'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '1.6'
47
+ description: |
48
+ xqsr3 - eXtensions by fine Quantum for Standard Ruby and 3rd-party libraries
49
+ - is a lightweight, low-coupling library of assorted extensions to standard
50
+ ruby and 3rd-party libraries.
51
+
52
+ xqsr3-xml contains the XML-related components for xqsr3, so that the core
53
+ library remains independent of any non-standard libraries.
54
+ email: matthew@synesis.com.au
55
+ executables: []
56
+ extensions: []
57
+ extra_rdoc_files: []
58
+ files:
59
+ - lib/xqsr3/xml/utilities/compare.rb
60
+ - lib/xqsr3/xml/utilities/navigation.rb
61
+ - lib/xqsr3/xml/version.rb
62
+ - test/unit/tc_version.rb
63
+ - test/unit/ts_all.rb
64
+ - test/unit/xml/ts_all.rb
65
+ - test/unit/xml/utilities/tc_compare.rb
66
+ - test/unit/xml/utilities/tc_navigation.rb
67
+ - test/unit/xml/utilities/ts_all.rb
68
+ - README.md
69
+ - LICENSE
70
+ homepage: http://github.com/synesissoftware/xqsr3-xml
71
+ licenses:
72
+ - BSD-3-Clause
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.0.14.1
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: xqsr3-xml
94
+ test_files: []