xqsr3 0.12.2 → 0.13.3

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
  SHA1:
3
- metadata.gz: 44bdb62fca711d8ee10e0da50888cae988043f26
4
- data.tar.gz: 22b017fcf0f07ef6395c123d4ed81650d27d3967
3
+ metadata.gz: a62fcdc4c1aa7176aa20acf04a477f9ce9e32dde
4
+ data.tar.gz: 9e3085307d16b630cbe34d5e7d243b4f3ec18622
5
5
  SHA512:
6
- metadata.gz: 49916563cc5b10a5103716b0555d8d2dcea1b38481d8d6b7bdf951834505a10a79aac39f8e5c6bbba513966da207f2a2f57e90aef7fa28e1563a39d6a68ab453
7
- data.tar.gz: 3e03a355c86b3227d7a9c5f4966c99f55c3ab5953569becc6f2d1696ff08ee1de5ab7a9fbc9c76dd598159846b48aa3e113fe211a529f1c8142980686897be77
6
+ metadata.gz: 947e35e638cbd5ce7bdef5811d8c3f9de6923f697aff9212f371fda83b049b4032dd7f789f7183720c53714967c13e5ce62501071dbc11c93bb85a06adea8e41
7
+ data.tar.gz: b458cea2b2fb99ceb8a42b5262f6556fa926aecb2c2489212ede046d503d82a60acc1fb735049bf5e4eff8815aee6caab20dc23f71abae962f560bb12b4b4f3c
@@ -2,17 +2,17 @@
2
2
  # ######################################################################## #
3
3
  # File: lib/xqsr3/command_line_utilities/map_option_string.rb
4
4
  #
5
- # Purpose: Definition of the ::Xqsr3::CommandLineUtilities::ToSymbol
6
- # module
5
+ # Purpose: Definition of the
6
+ # ::Xqsr3::CommandLineUtilities::MapOptionString module
7
7
  #
8
8
  # Created: 15th April 2016
9
- # Updated: 10th June 2016
9
+ # Updated: 2nd August 2017
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2016, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2016-2017, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -45,6 +45,8 @@
45
45
  # ######################################################################## #
46
46
 
47
47
 
48
+ # ##########################################################
49
+ # ::Xqsr3::CommandLineUtilities::MapOptionString
48
50
 
49
51
  require 'xqsr3/string_utilities/to_symbol'
50
52
 
@@ -5,13 +5,13 @@
5
5
  # Purpose: FrequencyMap container
6
6
  #
7
7
  # Created: 28th January 2005
8
- # Updated: 10th June 2016
8
+ # Updated: 30th July 2017
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2005-2016, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2005-2017, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,9 @@
44
44
  # ######################################################################## #
45
45
 
46
46
 
47
+ # ##########################################################
48
+ # ::Xqsr3::Containers::FrequencyMap
49
+
47
50
  =begin
48
51
  =end
49
52
 
@@ -5,13 +5,13 @@
5
5
  # Purpose: multimap container
6
6
  #
7
7
  # Created: 21st March 2007
8
- # Updated: 2nd October 2016
8
+ # Updated: 30th July 2017
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2007-2016, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2007-2017, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,9 @@
44
44
  # ######################################################################## #
45
45
 
46
46
 
47
+ # ##########################################################
48
+ # ::Xqsr3::Containers::MultiMap
49
+
47
50
  =begin
48
51
  =end
49
52
 
@@ -6,7 +6,7 @@
6
6
  # module
7
7
  #
8
8
  # Created: 3rd June 2017
9
- # Updated: 7th June 2017
9
+ # Updated: 28th July 2017
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
@@ -46,7 +46,7 @@
46
46
 
47
47
 
48
48
  # ##########################################################
49
- # ::Xqsr3::Conversion::ToBool
49
+ # ::Xqsr3::Conversion::BoolParser
50
50
 
51
51
  =begin
52
52
  =end
@@ -116,5 +116,3 @@ end # module Xqsr3
116
116
 
117
117
  # ############################## end of file ############################# #
118
118
 
119
-
120
-
@@ -5,7 +5,7 @@
5
5
  # Purpose: Definition of the ExceptionUtilities module
6
6
  #
7
7
  # Created: 12th February 2015
8
- # Updated: 22nd June 2017
8
+ # Updated: 2nd August 2017
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -44,6 +44,9 @@
44
44
  # ######################################################################## #
45
45
 
46
46
 
47
+ # ##########################################################
48
+ # ::Xqsr3::Diagnostics::ExceptionUtilities
49
+
47
50
  =begin
48
51
  =end
49
52
 
@@ -6,7 +6,7 @@
6
6
  # module
7
7
  #
8
8
  # Created: 3rd June 2017
9
- # Updated: 22nd June 2017
9
+ # Updated: 28th July 2017
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
@@ -152,4 +152,3 @@ end # module Xqsr3
152
152
 
153
153
  # ############################## end of file ############################# #
154
154
 
155
-
@@ -5,13 +5,13 @@
5
5
  # Purpose: Adds a writelines() method to the IO module
6
6
  #
7
7
  # Created: 13th April 2007
8
- # Updated: 10th June 2016
8
+ # Updated: 2nd August 2017
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2007-2016, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2007-2017, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,9 @@
44
44
  # ######################################################################## #
45
45
 
46
46
 
47
+ # ##########################################################
48
+ # ::Xqsr3::IO
49
+
47
50
  require 'xqsr3/quality/parameter_checking'
48
51
 
49
52
  =begin
@@ -5,7 +5,7 @@
5
5
  # Purpose: Definition of the ParameterChecking module
6
6
  #
7
7
  # Created: 12th February 2015
8
- # Updated: 26th February 2017
8
+ # Updated: 1st November 2017
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -44,6 +44,9 @@
44
44
  # ######################################################################## #
45
45
 
46
46
 
47
+ # ##########################################################
48
+ # ::Xqsr3::Quality::ParameterChecking
49
+
47
50
  =begin
48
51
  =end
49
52
 
@@ -69,8 +72,32 @@ module ParameterChecking
69
72
  end
70
73
  end
71
74
  end # module Util_
72
-
73
75
  public
76
+
77
+ def self.included base
78
+
79
+ base.extend self
80
+
81
+ base.class_eval do
82
+
83
+ public
84
+ def self.check_parameter value, name, options = {}, &block
85
+
86
+ Util_.check_parameter value, name, options, &block
87
+ end
88
+
89
+ # @see check_parameter
90
+ #
91
+ # @note This is obsolete, and will be removed in a future
92
+ # version. Please use +check_parameter+ instead
93
+ public
94
+ def self.check_param value, name, options = {}, &block
95
+
96
+ Util_.check_parameter value, name, options, &block
97
+ end
98
+ end
99
+ end
100
+
74
101
  # Check a given parameter (value=+value+, name=+name+) for type and value
75
102
  #
76
103
  # @param +value+ the parameter whose value and type is to be checked
@@ -83,6 +110,8 @@ module ParameterChecking
83
110
  # must be derived from). One of these types may be an array
84
111
  # of types, in which case +value+ may be an array that must
85
112
  # consist wholly of those types
113
+ # @option +:type+ a single type parameter, used only if +:types+ is not
114
+ # specified
86
115
  # @option +:values+ an array of values one of which +value+ must be
87
116
  # @option +:responds_to+ an array of symbols specifying all messages to
88
117
  # which the parameter will respond
@@ -96,8 +125,70 @@ module ParameterChecking
96
125
  # exception, which suppresses internal message preparation
97
126
  # @option +:treat_as_option+ if true, the value will be treated as an
98
127
  # option when reporting check failure
128
+ #
129
+ # This method is private, because it should only be used within methods
130
+ private
99
131
  def check_parameter value, name, options = {}, &block
100
132
 
133
+ Util_.check_parameter value, name, options, &block
134
+ end
135
+
136
+ # @see check_parameter
137
+ #
138
+ # @note This is obsolete, and will be removed in a future version.
139
+ # Please use +check_parameter+ instead
140
+ private
141
+ def check_param value, name, options = {}, &block
142
+
143
+ Util_.check_parameter value, name, options, &block
144
+ end
145
+
146
+ # Check a given parameter (value=+value+, name=+name+) for type and value
147
+ #
148
+ # @param +value+ the parameter whose value and type is to be checked
149
+ # @param +name+ the name of the parameter to be checked
150
+ # @param +options+ options
151
+ #
152
+ # @option +:allow_nil+ the +value+ must not be +nil+ unless this option
153
+ # is true
154
+ # @option +:types+ an array of types one of which +value+ must be (or
155
+ # must be derived from). One of these types may be an array
156
+ # of types, in which case +value+ may be an array that must
157
+ # consist wholly of those types
158
+ # @option +:type+ a single type parameter, used only if +:types+ is not
159
+ # specified
160
+ # @option +:values+ an array of values one of which +value+ must be
161
+ # @option +:responds_to+ an array of symbols specifying all messages to
162
+ # which the parameter will respond
163
+ # @option +:reject_empty+ requires value to respond to +empty?+
164
+ # message and to do so with false, unless +nil+
165
+ # @option +:require_empty+ requires value to respond to +empty?+
166
+ # message and to do so with true, unless +nil+
167
+ # @option +:nothrow+ causes failure to be indicated by a +nil+ return
168
+ # rather than a thrown exception
169
+ # @option +:message+ specifies a message to be used in any thrown
170
+ # exception, which suppresses internal message preparation
171
+ # @option +:treat_as_option+ if true, the value will be treated as an
172
+ # option when reporting check failure
173
+ public
174
+ def self.check_parameter value, name, options = {}, &block
175
+
176
+ Util_.check_parameter value, name, options, &block
177
+ end
178
+
179
+ # @see check_parameter
180
+ #
181
+ # @note This is obsolete, and will be removed in a future version.
182
+ # Please use +check_parameter+ instead
183
+ public
184
+ def self.check_param value, name, options = {}, &block
185
+
186
+ Util_.check_parameter value, name, options, &block
187
+ end
188
+
189
+ private
190
+ def Util_.check_parameter value, name, options, &block
191
+
101
192
  failed_check = false
102
193
  options ||= {}
103
194
  message = options[:message]
@@ -140,6 +231,10 @@ module ParameterChecking
140
231
  # types
141
232
 
142
233
  types = options[:types] || []
234
+ if options.has_key? :type
235
+
236
+ types << options[:type] if types.empty?
237
+ end
143
238
  types = [value.class] if types.empty?
144
239
 
145
240
  warn "#{self}::check_parameter: options[:types] of type #{types.class} - should be #{::Array}" unless types.is_a?(Array)
@@ -311,13 +406,19 @@ module ParameterChecking
311
406
 
312
407
  if value and block
313
408
 
314
- warn "#{self}::check_parameter: block arity must be 1" unless block.arity == 1
409
+ warn "#{self}::check_parameter: block arity must be 1 or 2" unless (1..2).include? block.arity
315
410
 
316
411
  r = nil
317
412
 
318
413
  begin
319
414
 
320
- r = block.call(value)
415
+ if 1 == block.arity
416
+
417
+ r = block.call(value)
418
+ else
419
+
420
+ r = block.call(value, options)
421
+ end
321
422
 
322
423
  rescue StandardError => x
323
424
 
@@ -392,10 +493,6 @@ module ParameterChecking
392
493
  failed_check ? nil : return_value
393
494
  end
394
495
 
395
- alias check_param check_parameter
396
-
397
- module_function :check_parameter
398
-
399
496
  end # module ParameterChecking
400
497
 
401
498
  end # module Quality
data/lib/xqsr3/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # Purpose: Version for Xqsr3 library
6
6
  #
7
7
  # Created: 3rd April 2016
8
- # Updated: 22nd June 2017
8
+ # Updated: 1st November 2017
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -50,7 +50,7 @@
50
50
  module Xqsr3
51
51
 
52
52
  # Current version of the Xqsr3 library
53
- VERSION = '0.12.2'
53
+ VERSION = '0.13.3'
54
54
 
55
55
  private
56
56
  VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
@@ -0,0 +1,466 @@
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: 1st November 2017
10
+ #
11
+ # Home: http://github.com/synesissoftware/xqsr3
12
+ #
13
+ # Author: Matthew Wilson
14
+ #
15
+ # Copyright (c) 2017, 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/quality/parameter_checking'
55
+
56
+ require 'nokogiri'
57
+
58
+ module Xqsr3
59
+ module XML
60
+ module Utilities
61
+
62
+ module Compare
63
+
64
+ # Class that represents the result of an XML comparison
65
+ #
66
+ # NOTE: Sadly, we cannot create instances of +FalseClass+/+TrueClass+,
67
+ # to which we could then add a +reason+ attribute, so instead we must
68
+ # have a results class
69
+ class Result
70
+
71
+ include ::Xqsr3::Quality::ParameterChecking
72
+
73
+ protected :check_parameter
74
+
75
+ #
76
+ # Options:
77
+ #
78
+ # +:different_attributes+
79
+ # +:different_attribute_count+
80
+ # +:different_attribute_order+
81
+ # +:different_child_node_count+
82
+ # +:different_child_node_order+
83
+ # +:different_child_nodes+
84
+ # +:different_node_names+
85
+ # +:different_node_contents+
86
+ # +:parameter_is_empty+
87
+ # +:parameter_is_nil+
88
+ # +:+
89
+
90
+ def initialize status, reason, **options
91
+
92
+ check_parameter status, 'status', types: [ ::FalseClass, ::TrueClass ]
93
+ check_parameter reason, 'reason', type: ::Symbol, allow_nil: true
94
+
95
+ @status = status
96
+ @reason = reason
97
+
98
+ @lhs_node = options[:lhs_node]
99
+ @rhs_node = options[:rhs_node]
100
+ end
101
+
102
+ def self.return status, reason, **options
103
+
104
+ return self.new status, reason, **options
105
+ end
106
+
107
+ def self.same reason = nil, **options
108
+
109
+ return self.new true, reason, **options
110
+ end
111
+
112
+ def self.different reason, **options
113
+
114
+ return self.new false, reason, **options
115
+ end
116
+
117
+ attr_reader :status
118
+ attr_reader :reason
119
+
120
+ def different?
121
+
122
+ !status
123
+ end
124
+
125
+ def same?
126
+
127
+ status
128
+ end
129
+
130
+ def details
131
+
132
+ r = reason.to_s.gsub /_/, ' '
133
+
134
+ qualifying = ''
135
+
136
+ if @lhs_node
137
+
138
+ qualifying += '; ' unless qualifying.empty?
139
+ qualifying += "lhs-node=#{@lhs_node}"
140
+ end
141
+
142
+ if @rhs_node
143
+
144
+ qualifying += '; ' unless qualifying.empty?
145
+ qualifying += "rhs-node=#{@rhs_node}"
146
+ end
147
+
148
+ r = "#{r}: #{qualifying}" unless qualifying.empty?
149
+
150
+ r
151
+ end
152
+ end
153
+
154
+ module Internal_Compare_
155
+
156
+ extend ::Xqsr3::Quality::ParameterChecking
157
+
158
+ DEFAULT_OPTIONS = {
159
+
160
+ debug: false,
161
+ # element_order: false,
162
+ equate_nil_and_empty: false,
163
+ ignore_attributes: false,
164
+ ignore_attribute_order: true,
165
+ ignore_child_node_order: true,
166
+ normalise_whitespace: true,
167
+ # normalize_whitespace: true,
168
+ validate_params: true,
169
+ }
170
+
171
+ ORDER_OPTIONS_SYMBOLS = [
172
+
173
+ :element_order,
174
+ :ignore_attribute_order,
175
+ :ignore_child_node_order,
176
+ ]
177
+
178
+ WHITESPACE_OPTIONS_SYMBOLS = [
179
+
180
+ :normalise_whitespace,
181
+ :normalize_whitespace,
182
+ ]
183
+
184
+ def self.derive_options_ given_options
185
+
186
+ default_options = DEFAULT_OPTIONS
187
+ derived_options = {}.merge given_options
188
+
189
+
190
+ # sort whitespace
191
+
192
+ if WHITESPACE_OPTIONS_SYMBOLS.any? { |sym| given_options.has_key? sym }
193
+
194
+ default_options = default_options.reject { |k, v| WHITESPACE_OPTIONS_SYMBOLS.include? k }
195
+ end
196
+
197
+ if given_options.has_key? :normalise_whitespace
198
+
199
+ derived_options.delete :normalize_whitespace
200
+ elsif given_options.has_key? :normalize_whitespace
201
+
202
+ derived_options[:normalise_whitespace] = given_options[:normalize_whitespace]
203
+
204
+ derived_options.delete :normalize_whitespace
205
+ end
206
+
207
+
208
+ # sort element-order
209
+
210
+ if ORDER_OPTIONS_SYMBOLS.any? { |sym| given_options.has_key? sym }
211
+
212
+ default_options = default_options.reject { |k, v| ORDER_OPTIONS_SYMBOLS.include? k }
213
+ end
214
+
215
+ if given_options.has_key? :element_order
216
+
217
+ element_order = given_options[:element_order]
218
+
219
+ derived_options[:ignore_attribute_order] = !element_order
220
+ derived_options[:ignore_child_node_order] = !element_order
221
+ end
222
+
223
+ derived_options[:ignore_attribute_order] = given_options[:ignore_attribute_order] if given_options.has_key? :ignore_attribute_order
224
+ derived_options[:ignore_child_node_order] = given_options[:ignore_child_node_order] if given_options.has_key? :ignore_child_node_order
225
+
226
+ default_options.merge derived_options
227
+ end
228
+
229
+ def self.one_line_ s
230
+
231
+ s = s.to_s.gsub(/\s+/, ' ')
232
+ end
233
+
234
+ #
235
+ # +:debug+
236
+ # +:element_order+
237
+ # +:equate_nil_and_empty+
238
+ # +:ignore_attributes+
239
+ # +:ignore_attribute_order+
240
+ # +:normalise_whitespace+
241
+ # +:normalize_whitespace+
242
+ # +:validate_params+
243
+ #
244
+
245
+ def self.xml_compare_ lhs, rhs, options
246
+
247
+ $stderr.puts "#{self}#{__method__}(lhs (#{lhs.class})=#{self.one_line_ lhs}, rhs (#{rhs.class})=#{self.one_line_ rhs}, options (#{options.class})=#{options})" if $DEBUG
248
+
249
+ # validate parameter(s)
250
+
251
+ check_parameter options, 'options', type: ::Hash if $DEBUG
252
+
253
+ validate_params = $DEBUG || options[:debug] || options[:validate_params]
254
+
255
+ check_parameter lhs, 'lhs', types: [ ::String, ::Nokogiri::XML::Node ], allow_nil: true if validate_params
256
+ check_parameter rhs, 'rhs', types: [ ::String, ::Nokogiri::XML::Node ], allow_nil: true if validate_params
257
+
258
+ options = self.derive_options_ options
259
+
260
+ # deal with nil(s)
261
+
262
+ return Result.same if lhs.nil? && rhs.nil?
263
+
264
+ if lhs.nil?
265
+
266
+ return Result.same if options[:equate_nil_and_empty] && ::String === rhs && rhs.empty?
267
+
268
+ return Result.different :parameter_is_nil
269
+ end
270
+
271
+ if rhs.nil?
272
+
273
+ return Result.same if options[:equate_nil_and_empty] && ::String === lhs && lhs.empty?
274
+
275
+ return Result.different :parameter_is_nil
276
+ end
277
+
278
+
279
+ # deal with string(s)
280
+
281
+ lhs = Nokogiri::XML(lhs) if ::String === lhs
282
+ rhs = Nokogiri::XML(rhs) if ::String === rhs
283
+
284
+
285
+ self.xml_compare_nodes_ lhs, rhs, options
286
+ end
287
+
288
+ def self.xml_compare_nodes_ lhs, rhs, options
289
+
290
+ $stderr.puts "#{self}#{__method__}(lhs (#{lhs.class})=#{self.one_line_ lhs}, rhs (#{rhs.class})=#{self.one_line_ rhs}, options (#{options.class})=#{options})" if $DEBUG
291
+
292
+
293
+ # Compare:
294
+ #
295
+ # - name
296
+ # - attributes
297
+ # - content
298
+ # - children
299
+ # -
300
+
301
+
302
+ # ##########################
303
+ # name
304
+
305
+ lhs_name = lhs.name
306
+ rhs_name = rhs.name
307
+
308
+ return Result.different :different_node_names, lhs_node: lhs, rhs_node: rhs if lhs_name != rhs_name
309
+
310
+
311
+ # ##########################
312
+ # attributes
313
+
314
+ unless options[:ignore_attributes]
315
+
316
+ lhs_attributes = lhs.attribute_nodes
317
+ rhs_attributes = rhs.attribute_nodes
318
+
319
+ return Result.different :different_attribute_count, lhs_node: lhs, rhs_node: rhs if lhs_attributes.count != rhs_attributes.count
320
+
321
+
322
+ lhs_attr_list = lhs_attributes.map { |attr| [ attr.name, attr.content ] }
323
+ rhs_attr_list = rhs_attributes.map { |attr| [ attr.name, attr.content ] }
324
+
325
+ if lhs_attr_list != rhs_attr_list
326
+
327
+ # do the sort first
328
+
329
+ lhs_attr_list.sort! { |l, r| l[0] <=> r[0] }
330
+ rhs_attr_list.sort! { |l, r| l[0] <=> r[0] }
331
+
332
+ # Now there are four possibiliies:
333
+ #
334
+ # 1. Different attributes
335
+ # 2. Different attribute order
336
+ # 3. Same (when reordered)
337
+
338
+ if lhs_attr_list == rhs_attr_list
339
+
340
+ if options[:ignore_attribute_order]
341
+
342
+ # 3
343
+ else
344
+
345
+ # 2
346
+
347
+ return Result.different :different_attribute_order, lhs_node: lhs, rhs_node: rhs
348
+ end
349
+ else
350
+
351
+ return Result.different :different_attributes, lhs_node: lhs, rhs_node: rhs
352
+ end
353
+ end
354
+ end
355
+
356
+ # ##########################
357
+ # content
358
+
359
+ normalise_ws = options[:normalise_whitespace]
360
+
361
+ lhs_content = normalise_ws ? lhs.content.gsub(/\s+/, ' ').strip : lhs.content
362
+ rhs_content = normalise_ws ? rhs.content.gsub(/\s+/, ' ').strip : rhs.content
363
+
364
+ return Result.different :different_node_contents, lhs_node: lhs, rhs_node: rhs if lhs_content != rhs_content
365
+
366
+
367
+ # ##########################
368
+ # children (preparation)
369
+
370
+ lhs_children = lhs.children.to_a
371
+ rhs_children = rhs.children.to_a
372
+
373
+ lhs_children.reject! { |child| child.text? && child.content.strip.empty? }
374
+ rhs_children.reject! { |child| child.text? && child.content.strip.empty? }
375
+
376
+
377
+ # ##########################
378
+ # children - count
379
+
380
+ lhs_children_count = lhs_children.count
381
+ rhs_children_count = rhs_children.count
382
+
383
+ return Result.different :different_child_node_count, lhs_node: lhs, rhs_node: rhs if lhs_children_count != rhs_children_count
384
+
385
+
386
+ # ##########################
387
+ # children - names
388
+
389
+ lhs_children_names = lhs_children.map { |ch| ch.name }
390
+ rhs_children_names = rhs_children.map { |ch| ch.name }
391
+
392
+ if lhs_children_names != rhs_children_names
393
+
394
+ # At this point, the lists of names of child elements are
395
+ # different. This may be because there are different
396
+ # elements or because they are in a different order. Either
397
+ # way, in order to provide detailed reasons for
398
+ # inequivalency, we must do an order-independent comparison
399
+
400
+ children_sorted_lhs = lhs_children.sort { |x, y| x.name <=> y.name }
401
+ children_sorted_rhs = rhs_children.sort { |x, y| x.name <=> y.name }
402
+
403
+ ch_names_sorted_lhs = children_sorted_lhs.map { |ch| ch.name }
404
+ ch_names_sorted_rhs = children_sorted_rhs.map { |ch| ch.name }
405
+
406
+ ignore_order = options[:ignore_child_node_order]
407
+
408
+ if ignore_order
409
+
410
+ return Result.different :different_child_nodes, lhs_node: lhs, rhs_node: rhs if ch_names_sorted_lhs != ch_names_sorted_rhs
411
+
412
+ # Since they are the same (when reordered), we need to
413
+ # adopt the ordered sequences so that the comparison of
414
+ # the children are meaningful
415
+
416
+ lhs_children = children_sorted_lhs
417
+ rhs_children = children_sorted_rhs
418
+ else
419
+
420
+ # failed, so need to determine whether it's due to
421
+ # different nodes or different order
422
+
423
+ if ch_names_sorted_lhs == ch_names_sorted_rhs
424
+
425
+ return Result.different :different_child_node_order, lhs_node: lhs, rhs_node: rhs
426
+ else
427
+
428
+ return Result.different :different_child_nodes, lhs_node: lhs, rhs_node: rhs
429
+ end
430
+ end
431
+ end
432
+
433
+ (0 ... lhs_children.count).each do |index|
434
+
435
+ ch_lhs = lhs_children[index]
436
+ ch_rhs = rhs_children[index]
437
+
438
+ r = self.xml_compare_nodes_ ch_lhs, ch_rhs, options
439
+
440
+ return r unless r.status
441
+ end
442
+
443
+ return Result.same
444
+ end
445
+ end
446
+
447
+ def self.xml_compare lhs, rhs, **options
448
+
449
+ $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
450
+
451
+ Internal_Compare_.xml_compare_ lhs, rhs, options
452
+ end
453
+
454
+ def xml_compare lhs, rhs, **options
455
+
456
+ $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
457
+
458
+ Internal_Compare_.xml_compare_ lhs, rhs, options
459
+ end
460
+
461
+ end # module Compare
462
+
463
+ end # module Utilities
464
+ end # module XML
465
+ end # module Xqsr3
466
+
@@ -58,8 +58,7 @@ class Test_String_ends_with < Test::Unit::TestCase
58
58
  prefixes = %w{ a c def }
59
59
 
60
60
  assert_not ''.ends_with?(*prefixes)
61
- assert_nil ''.ends_with?(*prefixes)
62
- assert ''.ends_with?(*prefixes), ''
61
+ assert_nil ''.ends_with?(*prefixes), 'empty string does not yield nil with given non-empty prefix(es)'
63
62
  assert 'abc'.ends_with?(*prefixes)
64
63
  assert_not 'd'.ends_with?(*prefixes)
65
64
  assert_nil 'd'.ends_with?(*prefixes)
@@ -59,7 +59,6 @@ class Test_String_starts_with < Test::Unit::TestCase
59
59
 
60
60
  assert_not ''.starts_with?(*prefixes)
61
61
  assert_nil ''.starts_with?(*prefixes)
62
- assert ''.starts_with?(*prefixes), ''
63
62
  assert 'abc'.starts_with?(*prefixes)
64
63
  assert_not 'd'.starts_with?(*prefixes)
65
64
  assert_nil 'd'.starts_with?(*prefixes)
@@ -12,7 +12,7 @@ class Test_parameter_checks_as_separate_module < Test::Unit::TestCase
12
12
  end
13
13
  include TestConstants
14
14
 
15
- extend ::Xqsr3::Quality::ParameterChecking
15
+ include ::Xqsr3::Quality::ParameterChecking
16
16
 
17
17
 
18
18
  # test 1
@@ -239,5 +239,24 @@ end
239
239
  check_responds_to Hash.new, [ :this_is_not_a_Hash_method ]
240
240
  end
241
241
  end
242
+
243
+
244
+
245
+ # test type:
246
+
247
+ def check_method_type a, type
248
+
249
+ self.class.check_parameter a, 'a', type: type
250
+ end
251
+
252
+ def test_type
253
+
254
+ check_method_type '', ::String
255
+
256
+ assert_raise TypeError do
257
+
258
+ check_method_type :sym, ::String
259
+ end
260
+ end
242
261
  end
243
262
 
@@ -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,131 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '../../../../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
+ end
131
+
@@ -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 CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xqsr3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.2
4
+ version: 0.13.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Wilson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-22 00:00:00.000000000 Z
11
+ date: 2017-11-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  eXtensions by fine Quantum for Standard Ruby and 3rd-party libraries is a
@@ -57,6 +57,7 @@ files:
57
57
  - lib/xqsr3/string_utilities/starts_with.rb
58
58
  - lib/xqsr3/string_utilities/to_symbol.rb
59
59
  - lib/xqsr3/version.rb
60
+ - lib/xqsr3/xml/utilities/compare.rb
60
61
  - test/unit/command_line_utilities/tc_map_option_string.rb
61
62
  - test/unit/command_line_utilities/ts_all.rb
62
63
  - test/unit/containers/tc_frequency_map.rb
@@ -89,6 +90,9 @@ files:
89
90
  - test/unit/quality/ts_all.rb
90
91
  - test/unit/tc_version.rb
91
92
  - test/unit/ts_all.rb
93
+ - test/unit/xml/ts_all.rb
94
+ - test/unit/xml/utilities/tc_compare.rb
95
+ - test/unit/xml/utilities/ts_all.rb
92
96
  homepage: http://github.com/synesissoftware/xqsr3
93
97
  licenses:
94
98
  - 3-clause BSD