equivalent-xml 0.2.9 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,11 @@
1
- = equivalent-xml
1
+ # equivalent-xml
2
2
 
3
- == Description
3
+ ## Description
4
+
5
+ ### Problem
4
6
 
5
- === Problem
6
7
  Testing XML output is difficult:
8
+
7
9
  * Comparing text output is brittle due to the vagaries of serialization.
8
10
  * Attribute order doesn't matter.
9
11
  * Element order matters sometimes, but not always.
@@ -11,17 +13,22 @@ Testing XML output is difficult:
11
13
  * Nodes in the same namespace don't always use the same prefix
12
14
  * Etc.
13
15
 
14
- === Solution
16
+ ### Solution
17
+
15
18
  EquivalentXml for Nokogiri
16
19
 
17
- === Use
18
- EquivalentXml.equivalent?(node_1, node_2, opts = { :element_order => false, :normalize_whitespace => true }) { |n1, n2, result| ... }
20
+ [![Build Status](https://secure.travis-ci.org/mbklein/equivalent-xml.png)](http://travis-ci.org/mbklein/equivalent-xml)
21
+ [![Dependency Status](https://gemnasium.com/mbklein/equivalent-xml.png)](https://gemnasium.com/mbklein/equivalent-xml)
22
+
23
+ ### Use
24
+ EquivalentXml.equivalent?(node_1, node_2, opts = { :element_order => false, :normalize_whitespace => true }) { |n1, n2, result| ... }
19
25
 
20
26
  node_1 and node_2 can be any Nokogiri::XML::Node descendants (or any string
21
27
  containing an XML document or document fragment). The most common use case is
22
28
  to compare two Nokogiri::XML::Document instances.
23
29
 
24
30
  node_1 is equivalent to node_2 if and only if:
31
+
25
32
  * node_1 and node_2 are of the same class
26
33
  * node_1 and node_2 are in the same namespace
27
34
  * node_1 and node_2 have the same number of child nodes
@@ -32,51 +39,61 @@ node_1 is equivalent to node_2 if and only if:
32
39
 
33
40
  If a block is given, the block will be called every time two nodes are compared. The parameters will be
34
41
  the two nodes being compared as well as the result of the comparison. If the block explicitly returns
35
- +true+ or +false+ (a real +TrueClass+ or +FalseClass+, not just an expression that can be coerced to
42
+ `true` or `false` (a real `TrueClass` or `FalseClass`, not just an expression that can be coerced to
36
43
  true or false), the return value will override the result of the comparison.
37
44
 
38
- +Element+ nodes are equivalent if they have the same name, and their
45
+ `Element` nodes are equivalent if they have the same name, and their
39
46
  child nodesets are equal (as defined above)
40
47
 
41
- +Attribute+ nodes are equivalent if their names and values match exactly
48
+ `Attribute` nodes are equivalent if their names and values match exactly
42
49
 
43
- +CDATA+ nodes are equivalent if their text strings match exactly,
50
+ `CDATA` nodes are equivalent if their text strings match exactly,
44
51
  including leading, trailing, and internal whitespace
45
52
 
46
- Non-CDATA +CharacterData+ nodes are equivalent if their text strings
53
+ Non-CDATA `CharacterData` nodes are equivalent if their text strings
47
54
  match after stripping leading and trailing whitespace and collapsing
48
55
  internal whitespace to a single space
49
56
 
50
- +Document+ nodes are equivalent if their root nodes are equal
57
+ `Document` nodes are equivalent if their root nodes are equal
51
58
 
52
- +ProcessingInstruction+ and +Comment+ nodes are ignored
59
+ `ProcessingInstruction` and `Comment` nodes are ignored
53
60
 
54
- ==== Options
55
- :element_order => true
61
+ #### Options
62
+
63
+ :element_order => true
56
64
 
57
65
  Require elements to be in the same relative position in order to be
58
66
  considered equivalent.
59
67
 
60
- :normalize_whitespace => false
68
+ :normalize_whitespace => false
61
69
 
62
70
  Don't normalize whitespace within text nodes; require text nodes to
63
71
  match exactly.
64
72
 
65
- === Using with RSpec
73
+ :ignore_content => ["Device > SerialNumber", "Device > ICCID"]
74
+
75
+ A single CSS selector, or an array of CSS selectors, of nodes for which the content (text and child
76
+ nodes) should be ignored when comparing for equivalence. Defaults to `nil`. (Uses Nokogiri's
77
+ `Node#css(*rules)` to conduct the search.)
78
+
79
+ ### Using with RSpec
66
80
 
67
81
  EquivalentXml includes a custom matcher for RSpec (version >=1.2.4) that makes including XML
68
82
  equivalencies in your spec tests a cinch!
69
83
 
70
84
  Equivalency:
71
- node_1.should be_equivalent_to(node_2)
72
- node_1.should_not be_equivalent_to(node_2)
85
+
86
+ node_1.should be_equivalent_to(node_2)
87
+ node_1.should_not be_equivalent_to(node_2)
73
88
 
74
89
  Chained modifiers:
75
- node_1.should be_equivalent_to(node_2).respecting_element_order
76
- node_1.should be_equivalent_to(node_2).with_whitespace_intact
77
- node_1.should be_equivalent_to(node_2).respecting_element_order.with_whitespace_intact
78
90
 
79
- == Contributing to equivalent-xml
91
+ node_1.should be_equivalent_to(node_2).respecting_element_order
92
+ node_1.should be_equivalent_to(node_2).with_whitespace_intact
93
+ node_1.should be_equivalent_to(node_2).respecting_element_order.with_whitespace_intact
94
+ node_1.should be_equivalent_to(node_2).ignoring_content_of("SerialNumber")
95
+
96
+ ## Contributing to equivalent-xml
80
97
 
81
98
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
82
99
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
@@ -84,10 +101,12 @@ Chained modifiers:
84
101
  * Start a feature/bugfix branch
85
102
  * Commit and push until you are happy with your contribution
86
103
  * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
87
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
104
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is
105
+ otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
88
106
 
89
- == History
107
+ ## History
90
108
 
109
+ - <b>0.3.0</b> - Added :ignore_content option (conrib. by moklett)
91
110
  - <b>0.2.9</b> - Fix for rspec-rails >= 2.7 (contrib. by jcoyne)
92
111
  - <b>0.2.8</b> - Allow comparison against nodesets (contrib. by gkellogg)
93
112
  - <b>0.2.7</b> - Auto-require RSpec matchers if RSpec is loaded
@@ -106,7 +125,7 @@ Chained modifiers:
106
125
  - <b>0.1.1</b> - Removed explicit runtime dependency on nokogiri
107
126
  - <b>0.1.0</b> - Initial release
108
127
 
109
- == Copyright
128
+ ## Copyright
110
129
 
111
130
  Copyright (c) 2011 Michael B. Klein. See LICENSE.txt for further details.
112
131
 
@@ -13,6 +13,7 @@ module EquivalentXml
13
13
  # @param [Hash] opts Options that determine how certain comparisons are evaluated
14
14
  # @option opts [Boolean] :element_order (false) Child elements must occur in the same order to be considered equivalent
15
15
  # @option opts [Boolean] :normalize_whitespace (true) Collapse whitespace within Text nodes before comparing
16
+ # @option opts [String, Array] :ignore_content (nil) CSS selector(s) of nodes for which the content (text and child nodes) should be ignored when comparing for equivalence
16
17
  # @yield [n1,n2,result] The two nodes currently being evaluated, and whether they are considered equivalent. The block can return true or false to override the default evaluation
17
18
  # @return [Boolean] true or false
18
19
  def equivalent?(node_1, node_2, opts = {}, &block)
@@ -80,9 +81,14 @@ module EquivalentXml
80
81
  end
81
82
 
82
83
  def compare_children(node_1, node_2, opts, &block)
83
- nodeset_1 = as_nodeset(node_1.children, opts)
84
- nodeset_2 = as_nodeset(node_2.children, opts)
85
- result = self.compare_nodesets(nodeset_1,nodeset_2,opts,&block)
84
+ if ignore_content?(node_1, opts)
85
+ # Stop recursion and state a match on the children
86
+ result = true
87
+ else
88
+ nodeset_1 = as_nodeset(node_1.children, opts)
89
+ nodeset_2 = as_nodeset(node_2.children, opts)
90
+ result = self.compare_nodesets(nodeset_1,nodeset_2,opts,&block)
91
+ end
86
92
 
87
93
  if node_1.respond_to?(:attribute_nodes)
88
94
  attributes_1 = node_1.attribute_nodes
@@ -167,6 +173,16 @@ module EquivalentXml
167
173
  end
168
174
  end
169
175
 
176
+ def ignore_content?(node, opts = {})
177
+ ignore_list = Array(opts[:ignore_content]).flatten.compact
178
+ return false if ignore_list.empty?
179
+
180
+ ignore_list.each do |selector|
181
+ return true if node.document.css(selector).include?(node)
182
+ end
183
+
184
+ return false
185
+ end
170
186
  end
171
187
 
172
188
  end
@@ -22,6 +22,7 @@ module EquivalentXml::RSpecMatchers
22
22
  # node.should be_equivalent_to(other_node).respecting_element_order
23
23
  # node.should be_equivalent_to(other_node).with_whitespace_intact
24
24
  # node.should be_equivalent_to(other_node).respecting_element_order.with_whitespace_intact
25
+ # node.should be_equivalent_to(other_node).ignoring_content_of("Device > SerialNumber")
25
26
  def be_equivalent_to(expected)
26
27
  # Placeholder method for documentation purposes; the actual
27
28
  # method is defined using RSpec's matcher DSL.
@@ -40,6 +41,10 @@ module EquivalentXml::RSpecMatchers
40
41
  chain :with_whitespace_intact do
41
42
  @opts[:normalize_whitespace] = false
42
43
  end
44
+
45
+ chain :ignoring_content_of do |paths|
46
+ @opts[:ignore_content] = paths
47
+ end
43
48
 
44
49
  failure_message_for_should do |actual|
45
50
  [ 'expected:', expected.to_s, 'got:', actual.to_s ].join("\n")
@@ -1,7 +1,10 @@
1
+ if defined?(RUBY_ENGINE) and (RUBY_ENGINE == 'ruby') and (RUBY_VERSION >= '1.9')
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ end
1
5
  $:.push(File.join(File.dirname(__FILE__),'..','lib'))
2
6
  require 'nokogiri'
3
7
  require 'equivalent-xml'
4
- #require 'equivalent-xml/rspec_matchers'
5
8
 
6
9
  describe EquivalentXml do
7
10
 
@@ -146,4 +149,62 @@ describe EquivalentXml do
146
149
  doc1 = Nokogiri::XML("<doc xmlns='foo:bar'><first>foo bar baz</first><second>things</second></doc>")
147
150
  doc1.root.children.should be_equivalent_to("<first xmlns='foo:bar'>foo bar baz</first><second xmlns='foo:bar'>things</second>")
148
151
  end
152
+
153
+ context "with the :ignore_content_paths option set to a CSS selector" do
154
+ it "ignores the text content of a node that matches the given CSS selector when comparing with #equivalent?" do
155
+ doc1 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device></Devices>")
156
+ doc2 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>5678</SerialNumber></Device></Devices>")
157
+
158
+ EquivalentXml.equivalent?(doc1, doc2, :ignore_content => "SerialNumber").should be_true
159
+ EquivalentXml.equivalent?(doc1, doc2, :ignore_content => "Devices>Device>SerialNumber").should be_true
160
+
161
+ doc1.should be_equivalent_to(doc2).ignoring_content_of("SerialNumber")
162
+ doc1.should be_equivalent_to(doc2).ignoring_content_of("Devices>Device>SerialNumber")
163
+ end
164
+
165
+ it "ignores the text content of a node that matches the given CSS selector when comparing with a matcher" do
166
+ doc1 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device></Devices>")
167
+ doc2 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>5678</SerialNumber></Device></Devices>")
168
+
169
+ doc1.should be_equivalent_to(doc2).ignoring_content_of("SerialNumber")
170
+ doc1.should be_equivalent_to(doc2).ignoring_content_of("Devices>Device>SerialNumber")
171
+ end
172
+
173
+ it "ignores all children of a node that matches the given selector when comparing for equivalence" do
174
+ doc1 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device></Devices>")
175
+ doc2 = Nokogiri::XML("<Devices><Device><Name>iPad</Name><SerialNumber>5678</SerialNumber></Device></Devices>")
176
+
177
+ doc1.should be_equivalent_to(doc2).ignoring_content_of("Device")
178
+ end
179
+
180
+ it "still considers the number of elements even if they match the given CSS selector" do
181
+ doc1 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device></Devices>")
182
+ doc2 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device><Device><Name>iPad</Name><SerialNumber>5678</SerialNumber></Device></Devices>")
183
+
184
+ doc1.should_not be_equivalent_to(doc2).ignoring_content_of("Device")
185
+ end
186
+
187
+ it "still considers attributes on the matched path when comparing for equivalence" do
188
+ doc1 = Nokogiri::XML("<Devices><Device status='owned'><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device></Devices>")
189
+ doc2 = Nokogiri::XML("<Devices><Device status='rented'><Name>iPhone</Name><SerialNumber>1234</SerialNumber></Device></Devices>")
190
+
191
+ doc1.should_not be_equivalent_to(doc2).ignoring_content_of("Device")
192
+ end
193
+
194
+ it "ignores all matches of the CSS selector" do
195
+ doc1 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1001</SerialNumber></Device><Device><Name>iPad</Name><SerialNumber>2001</SerialNumber></Device></Devices>")
196
+ doc2 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1002</SerialNumber></Device><Device><Name>iPad</Name><SerialNumber>2002</SerialNumber></Device></Devices>")
197
+
198
+ doc1.should be_equivalent_to(doc2).ignoring_content_of("SerialNumber")
199
+ end
200
+ end
201
+
202
+ context "with the :ignore_content_paths option set to an array of CSS selectors" do
203
+ it "ignores the content of all nodes that match any of the given CSS selectors when comparing for equivalence" do
204
+ doc1 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>1234</SerialNumber><ICCID>AAAA</ICCID></Device></Devices>")
205
+ doc2 = Nokogiri::XML("<Devices><Device><Name>iPhone</Name><SerialNumber>5678</SerialNumber><ICCID>BBBB</ICCID></Device></Devices>")
206
+
207
+ doc1.should be_equivalent_to(doc2).ignoring_content_of(["SerialNumber", "ICCID"])
208
+ end
209
+ end
149
210
  end
metadata CHANGED
@@ -1,137 +1,138 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: equivalent-xml
3
- version: !ruby/object:Gem::Version
4
- hash: 5
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 9
10
- version: 0.2.9
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Michael B. Klein
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-01-25 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-10-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: nokogiri
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 1
29
- segments:
30
- - 1
31
- - 4
32
- - 3
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
33
21
  version: 1.4.3
34
22
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: bundler
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
40
33
  none: false
41
- requirements:
42
- - - ~>
43
- - !ruby/object:Gem::Version
44
- hash: 23
45
- segments:
46
- - 1
47
- - 0
48
- - 0
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
49
37
  version: 1.0.0
50
38
  type: :development
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
53
- name: rcov
54
39
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
56
49
  none: false
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- hash: 59
61
- segments:
62
- - 0
63
- - 9
64
- - 0
65
- version: 0.9.0
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
66
54
  type: :development
67
- version_requirements: *id003
68
- - !ruby/object:Gem::Dependency
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
69
63
  name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.2.4
70
+ type: :development
70
71
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
72
73
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- hash: 23
77
- segments:
78
- - 1
79
- - 2
80
- - 4
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
81
77
  version: 1.2.4
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.8.7
82
86
  type: :development
83
- version_requirements: *id004
84
- description: |-
85
- Compares two XML Nodes (Documents, etc.) for certain semantic equivalencies.
86
- Currently written for Nokogiri, but with an eye toward supporting multiple XML libraries
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.8.7
94
+ description: ! "Compares two XML Nodes (Documents, etc.) for certain semantic equivalencies.
95
+ \n Currently written for Nokogiri, but with an eye toward supporting multiple
96
+ XML libraries"
87
97
  email: mbklein@gmail.com
88
98
  executables: []
89
-
90
99
  extensions: []
91
-
92
- extra_rdoc_files:
100
+ extra_rdoc_files:
93
101
  - LICENSE.txt
94
- - README.rdoc
95
- files:
102
+ - README.md
103
+ files:
96
104
  - Gemfile
97
105
  - LICENSE.txt
98
- - README.rdoc
106
+ - README.md
99
107
  - Rakefile
100
108
  - lib/equivalent-xml.rb
101
109
  - lib/equivalent-xml/rspec_matchers.rb
102
110
  - spec/equivalent-xml_spec.rb
103
111
  homepage: http://github.com/mbklein/equivalent-xml
104
- licenses:
112
+ licenses:
105
113
  - MIT
106
114
  post_install_message:
107
115
  rdoc_options: []
108
-
109
- require_paths:
116
+ require_paths:
110
117
  - lib
111
- required_ruby_version: !ruby/object:Gem::Requirement
118
+ required_ruby_version: !ruby/object:Gem::Requirement
112
119
  none: false
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- hash: 3
117
- segments:
118
- - 0
119
- version: "0"
120
- required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
125
  none: false
122
- requirements:
123
- - - ">="
124
- - !ruby/object:Gem::Version
125
- hash: 3
126
- segments:
127
- - 0
128
- version: "0"
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
129
130
  requirements: []
130
-
131
131
  rubyforge_project:
132
- rubygems_version: 1.8.15
132
+ rubygems_version: 1.8.23
133
133
  signing_key:
134
134
  specification_version: 3
135
135
  summary: Easy equivalency tests for Ruby XML
136
- test_files:
136
+ test_files:
137
137
  - spec/equivalent-xml_spec.rb
138
+ has_rdoc: