yadtfp 1.0.2
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 +7 -0
- data/.gitignore +22 -0
- data/.yardopts +9 -0
- data/CONTRIBUTING.md +9 -0
- data/Gemfile +4 -0
- data/Guardfile +16 -0
- data/MIT-LICENSE.txt +20 -0
- data/README.md +285 -0
- data/Rakefile +2 -0
- data/bin/yadtfp +3 -0
- data/lib/yadtfp.rb +21 -0
- data/lib/yadtfp/cli.rb +55 -0
- data/lib/yadtfp/configuration.rb +82 -0
- data/lib/yadtfp/outputters.rb +3 -0
- data/lib/yadtfp/outputters/diffable.rb +74 -0
- data/lib/yadtfp/outputters/pretty.rb +128 -0
- data/lib/yadtfp/outputters_factory.rb +26 -0
- data/lib/yadtfp/parsers.rb +1 -0
- data/lib/yadtfp/parsers/ox.rb +328 -0
- data/lib/yadtfp/parsers_factory.rb +24 -0
- data/lib/yadtfp/version.rb +3 -0
- data/spec/cli_spec.rb +44 -0
- data/spec/configuration_spec.rb +115 -0
- data/spec/outputters/diffable_spec.rb +119 -0
- data/spec/outputters/pretty_spec.rb +260 -0
- data/spec/outputters_factory_spec.rb +20 -0
- data/spec/parsers/ox_spec.rb +966 -0
- data/spec/parsers_factory_spec.rb +18 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/helpers.rb +22 -0
- data/spec/yadtfp_spec.rb +26 -0
- data/yadtfp.gemspec +34 -0
- metadata +206 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
|
4
|
+
module Yadtfp
|
5
|
+
|
6
|
+
class Configuration
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
|
10
|
+
attr_accessor :filter
|
11
|
+
attr_reader :parser, :outputter
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
# Initializes `@filter`, `@parser`, and `@outputter` to their defaults based on the following table:
|
16
|
+
#
|
17
|
+
# filter = '*'
|
18
|
+
# parser = :ox
|
19
|
+
# outputter = :pretty
|
20
|
+
def initialize
|
21
|
+
# Path to begin traversing the input XML from
|
22
|
+
@filter = '*'
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
# Parser
|
27
|
+
@parser = :ox
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
# Diff result output formatter
|
32
|
+
@outputter = :pretty
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
# Assigns supplied parser after converting it to symbol
|
40
|
+
#
|
41
|
+
# Raises `ArgumentError` if supplied `parser` does not `respond_to` to `to_sym`
|
42
|
+
def parser=(parser)
|
43
|
+
raise ArgumentError, "Invalid parser `#{parser}`" if !parser.respond_to?(:to_sym)
|
44
|
+
|
45
|
+
@parser = parser.to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
# Assigns supplied outputter after converting it to symbol
|
53
|
+
#
|
54
|
+
# Raises `ArgumentError` if supplied `outputter` does not `respond_to` to `to_sym`
|
55
|
+
def outputter=(outputter)
|
56
|
+
raise ArgumentError, "Invalid outputter `#{outputter}`" if !outputter.respond_to?(:to_sym)
|
57
|
+
|
58
|
+
@outputter = outputter.to_sym
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
# Parses options, prepares this configuration instance and return the same.
|
66
|
+
#
|
67
|
+
# Assigns default values to each configuration attributes as defined in the `initialize` method.
|
68
|
+
#
|
69
|
+
# Raises `ArugmentError` if `options` is not a hash
|
70
|
+
def self.parse_options(options = {})
|
71
|
+
raise ArgumentError, "Invalid options hash" if !options.is_a?(::Hash)
|
72
|
+
|
73
|
+
config = self.instance
|
74
|
+
config.filter = options[:filter] || '*'
|
75
|
+
config.parser = options[:parser] || :ox
|
76
|
+
config.outputter = options[:outputter] || :pretty
|
77
|
+
|
78
|
+
config
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Yadtfp::Outputters
|
2
|
+
|
3
|
+
|
4
|
+
# Module exists to extract out the common methods in outputters.
|
5
|
+
#
|
6
|
+
# Include this module `Diffable` in each outputters defined.
|
7
|
+
module Diffable
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
attr_reader :diff
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
# Saves supplied difference array.
|
16
|
+
#
|
17
|
+
# Parameter `arr` must be an array of difference hashes.
|
18
|
+
#
|
19
|
+
# Raises `ArgumentError` if supplied `arr` is not an `Array` type.
|
20
|
+
def initialize(arr = [])
|
21
|
+
throw ArgumentError, "`arr` must be an array" if !arr.is_a?(::Array)
|
22
|
+
|
23
|
+
@diff = arr
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
# Returns an array containing all the changes.
|
31
|
+
def changes
|
32
|
+
by_type('c')
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
# Returns an array containing all the appends.
|
40
|
+
def appends
|
41
|
+
by_type('a')
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
# Returns an array containing all the deletes.
|
49
|
+
def deletes
|
50
|
+
by_type('d')
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
# Filters and returns the subset of difference by type
|
63
|
+
#
|
64
|
+
# `type` can be one of:
|
65
|
+
# `c` - Change
|
66
|
+
# `a` - Append
|
67
|
+
# `d` - Delete
|
68
|
+
def by_type(type)
|
69
|
+
@diff.select { |d| d[:type] == type }
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Yadtfp::Outputters
|
2
|
+
|
3
|
+
class TypeValuesMismatchError < StandardError; end
|
4
|
+
|
5
|
+
|
6
|
+
class Pretty
|
7
|
+
|
8
|
+
include Yadtfp::Outputters::Diffable
|
9
|
+
|
10
|
+
|
11
|
+
TYPES = [ 'c', 'a', 'd' ]
|
12
|
+
|
13
|
+
|
14
|
+
DiffValue = ->(value) do
|
15
|
+
value.is_a?(::Array) ? value.to_enum.with_index(1).map { |v, i| "\xA#{"\x20" * 5}#{i}.\x20#{v}" }.join : value
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def print
|
23
|
+
content = TYPES.map { |type| header(type) + body(type) }.join ''
|
24
|
+
|
25
|
+
$stdout.print "#{content}#{repeatc("\xA", 1)}#{summary}"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
# Returns header information for given type
|
33
|
+
#
|
34
|
+
# Type can be one of either Change `c`, Append `a` or Delete `d`:
|
35
|
+
#
|
36
|
+
# Returns empty string if type is not one of TYPES ('c', 'a' or 'd')
|
37
|
+
def header(type)
|
38
|
+
return '' if !TYPES.include?(type)
|
39
|
+
|
40
|
+
header = "Changes (Replace left value with right value)" if type === 'c'
|
41
|
+
header = "Appends (Add values to left)" if type === 'a'
|
42
|
+
header = "Deletes (Remove values from left)" if type === 'd'
|
43
|
+
|
44
|
+
header << "\xA#{ "-" * header.length }\xA"
|
45
|
+
header
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
# Returns body for given type
|
53
|
+
#
|
54
|
+
# Type can be one of either Change `c`, Append `a` or Delete `d`:
|
55
|
+
#
|
56
|
+
# Returns empty string if type is not one of `TYPES ('c', 'a' or 'd')
|
57
|
+
#
|
58
|
+
# Raises TypeValuesMismatchError if type is 'd' but diff contains `nil` left
|
59
|
+
# Raises TypeValuesMismatchError if type is 'a' but diff contains `nil` right
|
60
|
+
def body(type)
|
61
|
+
return '' if !TYPES.include?(type)
|
62
|
+
|
63
|
+
diffs = case type
|
64
|
+
when 'c'
|
65
|
+
changes
|
66
|
+
when 'a'
|
67
|
+
appends
|
68
|
+
when 'd'
|
69
|
+
deletes
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
if diffs.any? { |d| d[:lvalue].nil? } && type == 'd'
|
74
|
+
raise Yadtfp::Outputters::TypeValuesMismatchError, "Unexpected Left value"
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
if diffs.any? { |d| d[:rvalue].nil? } && type == 'a'
|
79
|
+
raise Yadtfp::Outputters::TypeValuesMismatchError, "Unexpected Right value"
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
body = diffs.to_enum.with_index(1).inject("") do |body, (diff, index)|
|
84
|
+
body << "\xA#{index}.\x20Path:\x20#{diff[:path]}"
|
85
|
+
body << "\xA#{repeatc("\x20", 3)}Left:\x20#{DiffValue.call(diff[:lvalue])}" if diff.key?(:lvalue)
|
86
|
+
body << "\xA#{repeatc("\x20", 3)}Right:\x20#{DiffValue.call(diff[:rvalue])}" if diff.key?(:rvalue)
|
87
|
+
body << "\xA"
|
88
|
+
end
|
89
|
+
|
90
|
+
body << "\xA"
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
# Returns summary of diff
|
98
|
+
#
|
99
|
+
# Prints total number of differences altogether including changes, appends and deletes.
|
100
|
+
def summary
|
101
|
+
summary = "Summary of differences"
|
102
|
+
summary << "\xA#{ "-" * summary.length }\xA"
|
103
|
+
|
104
|
+
summary << "Number of differences: #{@diff.length}"
|
105
|
+
summary << "\xA Changes 'c': #{changes.length}" if changes.length > 0
|
106
|
+
summary << "\xA Appends 'a': #{appends.length}" if appends.length > 0
|
107
|
+
summary << "\xA Deletes 'd': #{deletes.length}" if deletes.length > 0
|
108
|
+
summary << "\xA"
|
109
|
+
|
110
|
+
summary
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
# Repeats character `char` for `length` number of times
|
122
|
+
def repeatc(char, length)
|
123
|
+
char * length
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Yadtfp
|
2
|
+
|
3
|
+
class OutputtersFactory
|
4
|
+
|
5
|
+
# Creates and returns one of outputters
|
6
|
+
#
|
7
|
+
# Note that parameter `outputter` needs to be a symbol, and diff needs to be an array.
|
8
|
+
# The allowed values for `outputter` are:
|
9
|
+
# :pretty
|
10
|
+
#
|
11
|
+
# Raises `ArgumentError` if `outputter` is not one of the values specified above.
|
12
|
+
def self.create(outputter, diff)
|
13
|
+
|
14
|
+
case outputter
|
15
|
+
when :pretty
|
16
|
+
return Outputters::Pretty.new(diff)
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
raise ArgumentError, "Invalid outputter `#{outputter}`"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'yadtfp/parsers_factory'
|
@@ -0,0 +1,328 @@
|
|
1
|
+
require 'ox'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Yadtfp::Parsers
|
5
|
+
|
6
|
+
|
7
|
+
class UnsupportedError < StandardError; end
|
8
|
+
|
9
|
+
|
10
|
+
class Ox
|
11
|
+
|
12
|
+
|
13
|
+
# Parses `xml` into a `::Ox::Document` and returns the same `::Ox::Document`
|
14
|
+
#
|
15
|
+
# Parameter `xml` is either a file path or a xml string.
|
16
|
+
# Method adds `xml` processing instruction with `version: '1.0'` and `encoding: 'UTF-8'`
|
17
|
+
#
|
18
|
+
# Method returns empty document `::Ox::Document` with processing instruction if supplied `xml` is nil or empty.
|
19
|
+
def parse(xml)
|
20
|
+
doc = ::Ox::Document.new(version: '1.0', encoding: 'UTF-8')
|
21
|
+
return doc if xml.nil? || xml.empty?
|
22
|
+
|
23
|
+
|
24
|
+
file = ::Pathname.new(xml).absolute? ? xml : File.expand_path(xml)
|
25
|
+
if File.exists?(file)
|
26
|
+
parsed = ::Ox.load_file(file, node: :generic)
|
27
|
+
else
|
28
|
+
parsed = ::Ox.parse(xml)
|
29
|
+
end
|
30
|
+
parsed_doc = doc.dup << parsed if parsed.is_a?(::Ox::Element)
|
31
|
+
|
32
|
+
|
33
|
+
# Apply filter
|
34
|
+
filtered = filter(parsed_doc) if parsed_doc.is_a?(::Ox::Element)
|
35
|
+
filtered.each { |f| doc << f } if filtered.is_a?(::Array)
|
36
|
+
|
37
|
+
doc
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
def filter(doc, path = Yadtfp::Configuration.instance.filter)
|
45
|
+
raise ArgumentError, "Document cannot be nil" if doc.nil?
|
46
|
+
|
47
|
+
doc.locate(path)
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
# Generates array of difference hashes between left and right.
|
56
|
+
#
|
57
|
+
# The difference hash contains the following keys:
|
58
|
+
# type: Type of difference. Either `c` for change, `a` for append or `d` for delete.
|
59
|
+
# path: Path needs to be in the format described in the documentation of
|
60
|
+
# [`::Ox::Element#locate`](http://www.ohler.com/ox/Ox/Element.html#locate-instance_method).
|
61
|
+
# lvalue: Left value
|
62
|
+
# rvalue: Right value
|
63
|
+
#
|
64
|
+
# Method loads xml document from `left` and `right`.
|
65
|
+
# Method parameters `left` and `right` both must be a `::Ox::Document`, each of which can be result of `parse`
|
66
|
+
# method.
|
67
|
+
#
|
68
|
+
# Method returns `[]` if both `left` or `right` are `nil` or both are equal.
|
69
|
+
# Method returns `right` translated into difference hash if `left` is `nil`.
|
70
|
+
# Method returns `left` translated into difference hash if `right` is `nil`.
|
71
|
+
def diff(left, right)
|
72
|
+
return [] if left == right || (left.nil? && right.nil?)
|
73
|
+
|
74
|
+
|
75
|
+
left = flat_h(left)
|
76
|
+
right = flat_h(right)
|
77
|
+
|
78
|
+
|
79
|
+
return right.values if left.nil? && right.is_a?(::Hash)
|
80
|
+
return left.values if right.nil? && left.is_a?(::Hash)
|
81
|
+
|
82
|
+
|
83
|
+
return flat_h_diff(left, right)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
# `flat_h`: Flat hash
|
93
|
+
#
|
94
|
+
# Returns hash of xml document. Traverses xml document recursively and generates a flat hash.
|
95
|
+
#
|
96
|
+
# Each element is converted to a hash where `key` is path to the node, attribute, comment or CDATA and value is the
|
97
|
+
# string or nil value held by the node, attribute, comment or CDATA
|
98
|
+
#
|
99
|
+
# Example:
|
100
|
+
#
|
101
|
+
# <xml id='root', name='Foo'>Bar</xml>
|
102
|
+
# # => { '/xml/id' => 'root', '/xml/@name' => 'Foo', '/xml' => 'Bar' }
|
103
|
+
def flat_h(element)
|
104
|
+
raise UnsupportedError, "Multiple root elements are not allowed" if element.is_a?(::Array)
|
105
|
+
|
106
|
+
|
107
|
+
# Nothing to process
|
108
|
+
return nil if element.nil?
|
109
|
+
|
110
|
+
|
111
|
+
# Result hash
|
112
|
+
result = {}
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
# Attributes
|
117
|
+
attrs = process_attributes(element) || {}
|
118
|
+
|
119
|
+
attrs.map do |key, value|
|
120
|
+
path = element.value.to_s != '' ? "/#{element.value}/#{key}" : "/#{key}"
|
121
|
+
result[path] = value
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
# Nodes
|
129
|
+
if !element.is_a?(::Ox::Comment)
|
130
|
+
|
131
|
+
if element.nodes.length.zero?
|
132
|
+
path = "/#{element.value}"
|
133
|
+
result[path] = nil if attrs.length.zero?
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
element.nodes.each do |node|
|
140
|
+
|
141
|
+
if node.is_a?(::String)
|
142
|
+
|
143
|
+
path = "/#{element.value}"
|
144
|
+
|
145
|
+
if result.key?(path)
|
146
|
+
result[path] = [ result[path] ] if !result[path].is_a?(::Array)
|
147
|
+
result[path] << node
|
148
|
+
else
|
149
|
+
result[path] = node
|
150
|
+
end
|
151
|
+
|
152
|
+
elsif node.is_a?(::Ox::Comment)
|
153
|
+
|
154
|
+
path = element.value.to_s != '' ? "/#{element.value}/comment()" : '/comment()'
|
155
|
+
result[path] = process_comments(element)
|
156
|
+
|
157
|
+
elsif node.is_a?(::Ox::CData)
|
158
|
+
|
159
|
+
path = element.value.to_s != '' ? "/#{element.value}/cdata()" : '/cdata()'
|
160
|
+
result[path] = process_cdata(element)
|
161
|
+
|
162
|
+
else
|
163
|
+
|
164
|
+
n = flat_h(node)
|
165
|
+
|
166
|
+
|
167
|
+
n.each do |key, value|
|
168
|
+
|
169
|
+
path = element.value.to_s == '' ? "#{key}" : "/#{element.value}#{key}"
|
170
|
+
|
171
|
+
if result.key?(path)
|
172
|
+
|
173
|
+
result[path] = [ result[path] ] if !result[path].is_a?(::Array)
|
174
|
+
result[path] = [ result[path] ] if key.end_with?('comment()') || key.end_with?('cdata()')
|
175
|
+
result[path] << value
|
176
|
+
|
177
|
+
else
|
178
|
+
|
179
|
+
result[path] = value
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
result
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
# Returns array of difference hashes for left and right flat hashes.
|
202
|
+
#
|
203
|
+
# The resultant array consists of difference hashes in the form of:
|
204
|
+
#
|
205
|
+
# { type: type, path: path, lvalue: lvalue, rvalue: rvalue }
|
206
|
+
#
|
207
|
+
# where:
|
208
|
+
#
|
209
|
+
# `type` - Type of difference, either `c`, `a` or `d`
|
210
|
+
# `path` - Path to the node, attribute, cdata, comment.
|
211
|
+
# `lvalue` - Value at path in left
|
212
|
+
# `rvalue` - Value at path in right
|
213
|
+
#
|
214
|
+
#
|
215
|
+
# Three possible `types` of differences are:
|
216
|
+
# `c` - When both left and right contain different values for same path
|
217
|
+
# `a` - Append values to left at the specified path. Occurs when path is not present in left.
|
218
|
+
# `d` - Delete values from left at the specified path. Occurs when path is not present in right.
|
219
|
+
#
|
220
|
+
# Returns nil if left and right are equal.
|
221
|
+
def flat_h_diff(left, right)
|
222
|
+
return nil if left == right
|
223
|
+
|
224
|
+
diff_hash = ( left.keys | right.keys ).inject({}) do |hash, key|
|
225
|
+
type = 'c'
|
226
|
+
type = 'd' if !right.key?(key) && left.key?(key)
|
227
|
+
type = 'a' if !left.key?(key) && right.key?(key)
|
228
|
+
|
229
|
+
if left[key] != right[key]
|
230
|
+
|
231
|
+
if left[key].is_a?(::Array) && right[key].is_a?(::Array)
|
232
|
+
|
233
|
+
type = 'd' if left[key].length > right[key].length
|
234
|
+
type = 'a' if left[key].length < right[key].length
|
235
|
+
type = 'c' if (left[key] - right[key]).length >= 1 && (right[key] - left[key]).length >= 1
|
236
|
+
|
237
|
+
|
238
|
+
if (left[key] - right[key]).empty? && (right[key] - left[key]).empty?
|
239
|
+
hash[key] = { type: type, path: key, lvalue: left[key].uniq, rvalue: nil } if type == 'd'
|
240
|
+
hash[key] = { type: type, path: key, lvalue: nil, rvalue: right[key].uniq } if type == 'a'
|
241
|
+
|
242
|
+
else
|
243
|
+
|
244
|
+
hash[key] = { type: type, path: key, lvalue: left[key] - right[key], rvalue: nil } if type == 'd'
|
245
|
+
hash[key] = { type: type, path: key, lvalue: nil, rvalue: right[key] - left[key] } if type == 'a'
|
246
|
+
hash[key] = { type: type, path: key, lvalue: (left[key] - right[key]), rvalue: (right[key] - left[key]) } if type == 'c'
|
247
|
+
end
|
248
|
+
|
249
|
+
else
|
250
|
+
|
251
|
+
hash[key] = { type: type, path: key, lvalue: left[key], rvalue: right[key] }
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
hash
|
258
|
+
end
|
259
|
+
|
260
|
+
diff_hash.values
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
|
267
|
+
|
268
|
+
# Process element comments
|
269
|
+
#
|
270
|
+
# Returns array of comments
|
271
|
+
# Returns nil if element does not contain any comment nodes.
|
272
|
+
def process_comments(element)
|
273
|
+
return if !element.respond_to?(:nodes)
|
274
|
+
|
275
|
+
comments = element.nodes.dup.inject([]) do |result, element|
|
276
|
+
result << element.value if element.is_a?(::Ox::Comment) && !element.value.empty?
|
277
|
+
result
|
278
|
+
end
|
279
|
+
|
280
|
+
return comments if comments.length >= 1
|
281
|
+
nil
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
# Process element cdata
|
290
|
+
#
|
291
|
+
# Returns array of cdata
|
292
|
+
# Returns nil if element does not contain any cdata markup.
|
293
|
+
def process_cdata(element)
|
294
|
+
return if !element.respond_to?(:nodes)
|
295
|
+
|
296
|
+
cdata = element.nodes.dup.inject([]) do |result, element|
|
297
|
+
result << element.value if element.is_a?(::Ox::CData) && !element.value.empty?
|
298
|
+
result
|
299
|
+
end
|
300
|
+
|
301
|
+
return cdata if cdata.length >= 1
|
302
|
+
nil
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
|
310
|
+
# Process node attributes
|
311
|
+
# Returns hash of node attributes applying `@` prefix to every attribute name
|
312
|
+
#
|
313
|
+
# Returns nil if node does not contain any attributes
|
314
|
+
def process_attributes(node)
|
315
|
+
return nil if !node.respond_to?(:attributes)
|
316
|
+
|
317
|
+
attributes = node.attributes.dup.inject({}) do |hash, (key, value)|
|
318
|
+
hash["@#{key}"] = value
|
319
|
+
hash
|
320
|
+
end
|
321
|
+
|
322
|
+
return attributes if attributes.length >= 1
|
323
|
+
nil
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|