xmatch 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  XMatch
2
2
  ==============
3
3
 
4
- XMatch is a Ruby library for comparing two XML documents and reporting on mismatches. An XML document will match another if:
4
+ XMatch is a Ruby library for comparing two XML documents and reporting on matches and mismatches. An XML document will match another if:
5
5
 
6
6
  * elements have the same name
7
7
  * elements have the same number of children
@@ -14,24 +14,38 @@ XMatch uses Nokogiri for xml parsing.
14
14
 
15
15
  Matching XML
16
16
  ------------
17
- Given two XML documents as strings, XMatch is run by:
17
+ Given two XML documents as strings (or Nokogiri Documents), XMatch is run by:
18
18
 
19
- xml = Matcher::Xml.new(lhs)
20
- xml.match(rhs)
19
+ matcher = Matcher::Xml.new(expected)
20
+ matcher.match(actual)
21
21
 
22
22
  A matcher provides access to the match information by xpath values:
23
23
 
24
- xml.matches
25
- xml.mismatches
24
+ matcher.matches
25
+ matcher.mismatches
26
26
 
27
27
  Custom matchers
28
28
  ---------------
29
- The actual values of some xml elements are hard to know in advance (timestamps and ids being typical examples). XMatch allows custom matchers to be applied
30
- to provide a good guess at a match in advance of the match being run. Custom matchers are predicates provided as Ruby Procs, identified by the xpath of the element they should be applied to.
29
+ The actual values of some xml elements are hard to know in advance (timestamps and id's being typical examples). XMatch allows custom matchers to be applied
30
+ to provide a good guess at a match in advance of the match being run. Custom matchers are predicates provided as Ruby Procs, identified by the xpath of the element they should be applied to. They can be applied to text and attribute values.
31
31
 
32
- xml = Matcher::Xml.new("<bookstore id='1'></bookstore>")
33
- xml.match_on("/bookstore/@id") { |actual| actual =~ /\d+/ }
34
- xml.match("<bookstore id='2'></bookstore>") # ==> true
32
+ matcher = Matcher::Xml.new("<bookstore id='1'></bookstore>")
33
+ matcher.on("/bookstore/@id") { |actual| actual =~ /\d+/ }
34
+ matcher.match("<bookstore id='2'></bookstore>") # ==> true
35
+
36
+ An alternate syntax allows a pattern to be excluded from the match. This is useful if values are mostly matching but differ by a pattern that cannot be known in advance (e.g. id's):
37
+
38
+ matcher = Matcher::Xml.new("<book>This is book 123</book>")
39
+ matcher.on("/book/text()", :excluding => /\d{3}$/)
40
+ matcher.match("<book>This is book 456</book>") # ==> true
41
+
42
+ will exclude the first three digits from the actual match. A single capture group used in the pattern:
43
+
44
+ matcher = Matcher::Xml.new("<book>Book 123 is here</book>")
45
+ matcher.on("/book/text()", :excluding => /\.*s(\d{3,6})\s.*/)
46
+ matcher.match("<book>Book 123456 is here</book>") # ==> true
47
+
48
+ will exclude only the matching capture.
35
49
 
36
50
  Formatting match results
37
51
  ------------------------
@@ -50,4 +64,4 @@ XMatch is packaged as a Gem. Install with:
50
64
  Copyright
51
65
  ---------
52
66
 
53
- Copyright (c) 2009 Peter Moran. See LICENSE for details.
67
+ Copyright (c) 2010 Peter Moran. See LICENSE for details.
data/Rakefile CHANGED
@@ -10,7 +10,6 @@ require "spec/rake/spectask"
10
10
  Spec::Rake::SpecTask.new(:spec) do |spec|
11
11
  spec.libs << 'lib' << 'spec'
12
12
  spec.spec_files = FileList['spec/**/*_spec.rb']
13
- # spec.spec_opts << "-Du"
14
13
  spec.spec_opts << "--color"
15
14
  end
16
15
 
@@ -5,19 +5,16 @@ module Nokogiri
5
5
  module XML
6
6
 
7
7
  class Node
8
-
8
+
9
+ def match?(other, matcher)
10
+ matching(other, matcher) ? true : false
11
+ end
12
+
9
13
  def matching(other, matcher)
10
14
  other_elem = other.at_xpath(path)
11
15
  matcher.record(self.path, false, Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND) unless other_elem
12
16
  other_elem
13
17
  end
14
- end
15
-
16
- class Document
17
-
18
- def match?(other, matcher)
19
- matching(other, matcher)
20
- end
21
18
 
22
19
  end
23
20
 
@@ -25,8 +22,7 @@ module Nokogiri
25
22
 
26
23
  def match?(other, matcher)
27
24
  @matcher = matcher
28
- other_elem = matching(other, matcher)
29
- return false unless other_elem
25
+ return false unless other_elem = matching(other, matcher)
30
26
  children_match?(other_elem) & attributes_match?(other_elem)
31
27
  end
32
28
 
@@ -54,14 +50,8 @@ module Nokogiri
54
50
  class Text
55
51
 
56
52
  def match?(other, matcher)
57
- @matcher = matcher
58
- other_elem = matching(other, matcher)
59
- return false unless other_elem
60
-
61
- custom_matcher = matcher.custom_matchers[path]
62
- match = custom_matcher ? custom_matcher.call(other_elem.content) : (content == other_elem.content)
63
- @matcher.record(self.path, match, content, other_elem.content)
64
- match
53
+ return false unless other_elem = matching(other, matcher)
54
+ matcher.evaluate(path, content, other_elem.content)
65
55
  end
66
56
 
67
57
  end
@@ -69,13 +59,8 @@ module Nokogiri
69
59
  class Attr
70
60
 
71
61
  def match?(other, matcher)
72
- other_elem = matching(other, matcher)
73
- return false unless other_elem
74
-
75
- custom_matcher = matcher.custom_matchers[path]
76
- match = custom_matcher ? custom_matcher.call(other_elem.value) : (value == other_elem.value)
77
- matcher.record(self.path, match, value, other_elem.value)
78
- match
62
+ return false unless other_elem = matching(other, matcher)
63
+ matcher.evaluate(path, value, other_elem.value)
79
64
  end
80
65
 
81
66
  end
@@ -1,3 +1,3 @@
1
1
  module Representative
2
- VERSION = "0.2.0".freeze
2
+ VERSION = "0.2.1".freeze
3
3
  end
@@ -3,8 +3,11 @@ require 'ostruct'
3
3
 
4
4
  module Matcher
5
5
 
6
+ class MatchError < StandardError
7
+ end
8
+
6
9
  class Xml
7
-
10
+
8
11
  NOT_FOUND = "[Not found]"
9
12
  EXISTENCE = "[Existence]"
10
13
  UNMATCHED = "[Unmatched]"
@@ -17,10 +20,15 @@ module Matcher
17
20
  @results = {}
18
21
  end
19
22
 
20
- def match_on(path, &blk)
21
- @custom_matchers[path] = blk
23
+ def match_on(path, options = {}, &blk)
24
+ raise ArgumentError.new("Using block AND options is not supported for custom matching") if blk && !options.empty?
25
+ excluding = options[:excluding]
26
+ raise ArgumentError.new "'excluding' option must be a regular expression" if excluding && !excluding.kind_of?(Regexp)
27
+ @custom_matchers[path] = blk || options
22
28
  end
23
29
 
30
+ alias_method :on, :match_on
31
+
24
32
  def match(actual)
25
33
  @results.clear
26
34
  @rhs = parse(actual)
@@ -29,7 +37,7 @@ module Matcher
29
37
 
30
38
  def record(path, result, expected, actual)
31
39
  # support 0 as true (for regex matches)
32
- r = !result || result.nil? ? false : true
40
+ r = (!result || result.nil?) ? false : true
33
41
  was_custom_matched = @custom_matchers[path] ? true : false
34
42
  @results[path] = OpenStruct.new(:result => r, :expected => expected, :actual => actual, :was_custom_matched => was_custom_matched)
35
43
  end
@@ -39,17 +47,47 @@ module Matcher
39
47
  return "mismatched" if mismatches[path]
40
48
  "unmatched"
41
49
  end
42
-
50
+
43
51
  def matches
44
52
  results_that_are(true)
45
53
  end
46
-
54
+
47
55
  def mismatches
48
56
  results_that_are(false)
49
57
  end
50
58
 
59
+ def evaluate(path, expected, actual)
60
+ custom_matcher = custom_matchers[path]
61
+ match = custom_matcher ? evaluate_custom_matcher(custom_matcher, expected, actual) : expected == actual
62
+ record(path, match, expected, actual)
63
+ match
64
+ end
65
+
51
66
  private
52
-
67
+
68
+ def evaluate_custom_matcher(custom_matcher, expected, actual)
69
+ custom_matcher.kind_of?(Hash) ? evaluate_as_regex(custom_matcher, expected, actual) : custom_matcher.call(actual)
70
+ end
71
+
72
+ def evaluate_as_regex(custom_matcher, expected, actual)
73
+ exclude_pattern = custom_matcher[:excluding]
74
+ match_data = expected.match(exclude_pattern)
75
+ return false unless match_data
76
+
77
+ if match_data.captures.empty?
78
+ lhs = expected.sub(exclude_pattern, "")
79
+ rhs = actual.sub(exclude_pattern, "")
80
+ else
81
+ lhs_exclude = expected.match(exclude_pattern).captures.first
82
+ rhs_exclude = actual.match(exclude_pattern).captures.first
83
+ lhs = expected.sub(/#{lhs_exclude}/, "")
84
+ rhs = actual.sub(/#{rhs_exclude}/, "")
85
+ end
86
+
87
+ raise Matcher::MatchError.new("excluding option resulted in comparing empty values") if lhs.empty?
88
+ lhs == rhs
89
+ end
90
+
53
91
  def results_that_are(value)
54
92
  match_info = {}
55
93
  @results.each { |path, info| match_info[path] = info if info.result == value}
@@ -7,12 +7,13 @@ describe Matcher::Xml do
7
7
  def verify_mismatch(path, expected, actual, count = 1)
8
8
  match = @xml.match(@rhs)
9
9
  @xml.mismatches.should have(count).mismatch
10
- @xml.mismatches[path].result.should be_false
11
- @xml.mismatches[path].expected.should == expected.to_s
12
- @xml.mismatches[path].actual.should == actual
10
+ mismatch = @xml.mismatches[path]
11
+ mismatch.result.should be_false
12
+ mismatch.expected.should == expected.to_s
13
+ mismatch.actual.should == actual
13
14
  end
14
15
 
15
- context "attributes" do
16
+ context "when being created" do
16
17
 
17
18
  before(:each) do
18
19
  @xml = Matcher::Xml.new("<foo></foo>")
@@ -33,25 +34,26 @@ describe Matcher::Xml do
33
34
 
34
35
  end
35
36
 
36
- before(:each) do
37
- @lhs = <<-eos
38
- <bookstore>
39
- <book category="COOKING">
40
- <title lang="en">Everyday Italian</title>
41
- </book>
42
- </bookstore>
43
- eos
44
- @xml = Matcher::Xml.new(@lhs)
45
- end
46
37
 
47
38
  context "matching" do
48
39
 
40
+ before(:each) do
41
+ @lhs = <<-eos
42
+ <bookstore>
43
+ <book category="COOKING">
44
+ <title lang="en">Everyday Italian</title>
45
+ </book>
46
+ </bookstore>
47
+ eos
48
+ @xml = Matcher::Xml.new(@lhs)
49
+ end
50
+
49
51
  it "should be true when documents match" do
50
52
  Matcher::Xml.new(@lhs).match(@lhs.clone).should be_true
51
53
  end
52
54
 
53
55
  it "should provide empty mismatches on match" do
54
- @xml.match(@lhs.clone).should be_true
56
+ @xml.match(@lhs.clone)
55
57
  @xml.mismatches.should be_empty
56
58
  end
57
59
 
@@ -68,7 +70,7 @@ describe Matcher::Xml do
68
70
  @xml.match(rhs).should be_true
69
71
  end
70
72
 
71
- it "should be true when a string is matched with a document" do
73
+ it "should be true when a string is matched with a parsed document" do
72
74
  rhs = <<-eos
73
75
  <bookstore>
74
76
  <book category="COOKING">
@@ -82,7 +84,7 @@ describe Matcher::Xml do
82
84
  Matcher::Xml.new(@lhs).match(Nokogiri::XML(rhs)).should be_true
83
85
  end
84
86
 
85
- context "elements" do
87
+ context "against elements" do
86
88
 
87
89
  it "should not match when rhs has an extra element" do
88
90
  @rhs = <<-eos
@@ -108,7 +110,7 @@ describe Matcher::Xml do
108
110
 
109
111
  end
110
112
 
111
- context "names" do
113
+ context "against element names" do
112
114
 
113
115
  it "should not match when rhs has a different element name" do
114
116
  @rhs = <<-eos
@@ -123,7 +125,7 @@ describe Matcher::Xml do
123
125
 
124
126
  end
125
127
 
126
- context "attributes" do
128
+ context "against attributes" do
127
129
 
128
130
  it "should not match when an attribute names don't match" do
129
131
  @rhs = <<-eos
@@ -182,9 +184,9 @@ describe Matcher::Xml do
182
184
 
183
185
  end
184
186
 
185
- context "contents" do
187
+ context "against text element contents" do
186
188
 
187
- it "should not match when inner text contents don't match" do
189
+ it "should not match when contents don't match" do
188
190
  @rhs = <<-eos
189
191
  <bookstore>
190
192
  <book category="COOKING">
@@ -197,18 +199,29 @@ describe Matcher::Xml do
197
199
 
198
200
  end
199
201
 
202
+ it "should provides all results empty by default" do
203
+ Matcher::Xml.new(@lhs).results.should be_empty
204
+ end
205
+
200
206
  end
201
207
 
202
- context "mismatches" do
208
+ context "with mismatches" do
209
+
210
+ before(:each) do
211
+ @lhs = <<-eos
212
+ <bookstore>
213
+ <book category="COOKING">
214
+ <title lang="en">Everyday Italian</title>
215
+ </book>
216
+ </bookstore>
217
+ eos
218
+ @xml = Matcher::Xml.new(@lhs)
219
+ end
203
220
 
204
221
  it "should be empty to start with" do
205
222
  Matcher::Xml.new(@lhs).mismatches.should be_empty
206
223
  end
207
224
 
208
- it "provides all results" do
209
- Matcher::Xml.new(@lhs).results.should be_empty
210
- end
211
-
212
225
  it "should be reset when rematching" do
213
226
  rhs = <<-eos
214
227
  <bookstore>
@@ -237,9 +250,6 @@ describe Matcher::Xml do
237
250
  it "should contain parent's path when an attribute doesn't match" do
238
251
  lhs = <<-eos
239
252
  <bookstore>
240
- <book category="COOKING">
241
- <title lang="en">Everyday Italian</title>
242
- </book>
243
253
  <book category="FOO">
244
254
  <title lang="en">Everyday French</title>
245
255
  </book>
@@ -249,9 +259,6 @@ describe Matcher::Xml do
249
259
 
250
260
  @rhs = <<-eos
251
261
  <bookstore>
252
- <book category="COOKING">
253
- <title lang="en">Everyday Italian</title>
254
- </book>
255
262
  <book foo="bar">
256
263
  <title lang="en">Everyday French</title>
257
264
  </book>
@@ -259,90 +266,148 @@ describe Matcher::Xml do
259
266
  eos
260
267
 
261
268
  @xml = Matcher::Xml.new(lhs)
262
- verify_mismatch("/bookstore/book[2]/@category", Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND)
269
+ verify_mismatch("/bookstore/book/@category", Matcher::Xml::EXISTENCE, Matcher::Xml::NOT_FOUND)
263
270
  end
271
+ end
264
272
 
265
- context 'matches' do
273
+ context 'with matches' do
266
274
 
267
- it "should contain each match" do
268
- lhs = "<bookstore><book>foo</book></bookstore>"
269
- xml = Matcher::Xml.new(lhs)
270
- xml.match(lhs)
271
- xml.matches.should have(3).matches
272
- end
273
-
274
- it "should have expected and actual results" do
275
- lhs = "<bookstore><book>foo</book></bookstore>"
276
- xml = Matcher::Xml.new(lhs)
277
- xml.match(lhs).should be_true
278
- match_info = xml.matches["/bookstore"]
279
- match_info.expected.should == "1 children"
280
- match_info.actual.should == "1 children"
281
- end
275
+ before(:each) do
276
+ lhs = "<bookstore><book>foo</book></bookstore>"
277
+ @xml = Matcher::Xml.new(lhs)
278
+ @xml.match(lhs).should be_true
279
+ end
280
+
281
+ it "should contain each match" do
282
+ @xml.matches.should have(3).matches
283
+ end
282
284
 
285
+ it "should have expected and actual results" do
286
+ match_info = @xml.matches["/bookstore"]
287
+ match_info.expected.should == "1 children"
288
+ match_info.actual.should == "1 children"
283
289
  end
284
290
 
285
291
  end
286
292
 
287
- context "match results" do
288
-
293
+ context "retrieving match results" do
294
+
295
+ before(:each) do
296
+ @xml = Matcher::Xml.new("<bookstore></bookstore>")
297
+ end
298
+
289
299
  it "returns 'matched' for a path that matched correctly" do
290
- xml = Matcher::Xml.new("<bookstore></bookstore>")
291
- xml.match("<bookstore></bookstore>")
292
- xml.result_for("/bookstore").should == "matched"
300
+ @xml.match("<bookstore></bookstore>")
301
+ @xml.result_for("/bookstore").should == "matched"
293
302
  end
294
303
 
295
304
  it "returns 'unmatched' for a path that was not found" do
296
- xml = Matcher::Xml.new("<bookstore></bookstore>")
297
- xml.match("<bookstorex></bookstorex>")
298
- xml.result_for("/bookstore").should == "mismatched"
305
+ @xml.match("<bookstorex></bookstorex>")
306
+ @xml.result_for("/bookstore").should == "mismatched"
299
307
  end
300
-
308
+
301
309
  end
302
-
303
- context "custom matchers" do
304
-
305
- it "can be provided" do
306
- xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", {"my path" => "my predicate"})
307
- xml.custom_matchers.should have(1).matcher
308
- end
309
-
310
- it "can be used on an attribute value" do
311
- custom_matchers = { "/bookstore/@id" => lambda {|actual| actual == '2'} }
312
- xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", custom_matchers)
313
- xml.match("<bookstore id='2'></bookstore>").should be_true
314
- end
315
-
316
- it "can be used on an element value" do
317
- custom_matchers = { "/bookstore/book/text()" => lambda {|actual| actual == 'bar'} }
318
- xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore", custom_matchers)
319
- xml.match("<bookstore><book>bar</book></bookstore").should be_true
320
- end
321
310
 
322
- it "supports regex style matching" do
323
- custom_matchers = { "/bookstore/book/text()" => lambda {|actual| actual =~ /bar/} }
324
- xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore", custom_matchers)
325
- xml.match("<bookstore><book>bar</book></bookstore").should be_true
326
- xml.mismatches.should be_empty
327
- xml.matches.should have(3).matches
328
- end
329
-
330
- it "can be matched on" do
331
- xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore")
332
- xml.match_on("/bookstore/book/text()") { |actual| actual =~ /bar/ }
333
- xml.match("<bookstore><book>bar</book></bookstore").should be_true
334
- xml.mismatches.should be_empty
335
- xml.matches.should have(3).matches
311
+ describe "with custom matchers" do
312
+
313
+ context "provided at create time" do
314
+
315
+ it "should be stored" do
316
+ Matcher::Xml.new("<bookstore</bookstore>", {"my path" => "my predicate"}).custom_matchers.should have(1).matcher
317
+ end
318
+
319
+ it "can be used on an attribute value" do
320
+ custom_matchers = { "/bookstore/@id" => lambda {|actual| actual == '2'} }
321
+ xml = Matcher::Xml.new("<bookstore id='1'></bookstore>", custom_matchers)
322
+ xml.match("<bookstore id='2'></bookstore>").should be_true
323
+ end
324
+
325
+ it "can be used on an element value" do
326
+ custom_matchers = { "/bookstore/book/text()" => lambda {|actual| actual == 'bar'} }
327
+ xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore", custom_matchers)
328
+ xml.match("<bookstore><book>bar</book></bookstore").should be_true
329
+ end
330
+
336
331
  end
337
332
 
338
- it "tells if a custom matcher was used" do
339
- xml = Matcher::Xml.new("<bookstore><book>foo</book></bookstore")
340
- xml.match_on("/bookstore/book/text()") { |actual| actual =~ /bar/ }
341
- xml.match("<bookstore><book>bar</book></bookstore").should be_true
342
- xml.matches["/bookstore/book/text()"].was_custom_matched.should be_true
343
- xml.matches["/bookstore/book"].was_custom_matched.should be_false
333
+ context "using match_on" do
334
+
335
+ before(:each) do
336
+ @matcher = Matcher::Xml.new("<bookstore><book>foo text</book></bookstore")
337
+ end
338
+
339
+ it "supports match_on with a regex predicate" do
340
+ @matcher.match_on("/bookstore/book/text()") { |actual| actual =~ /bar/ }
341
+ @matcher.match("<bookstore><book>bar</book></bookstore").should be_true
342
+ end
343
+
344
+ it "supports match_on with an equality predicate" do
345
+ @matcher.match_on("/bookstore/book/text()") { |actual| actual == "foo text" }
346
+ @matcher.match("<bookstore><book>foo text</book></bookstore").should be_true
347
+ end
348
+
349
+ it "tells if a custom matcher was used" do
350
+ @matcher.match_on("/bookstore/book/text()") { |actual| actual =~ /bar/ }
351
+ @matcher.match("<bookstore><book>bar</book></bookstore")
352
+ @matcher.matches["/bookstore/book/text()"].was_custom_matched.should be_true
353
+ @matcher.matches["/bookstore/book"].was_custom_matched.should be_false
354
+ end
355
+
356
+ it "supports 'on' style" do
357
+ @matcher.on("/bookstore/book/text()") { |actual| actual =~ /bar/ }
358
+ @matcher.match("<bookstore><book>bar</book></bookstore").should be_true
359
+ end
360
+
361
+ context "with excluding option" do
362
+
363
+ [/^\w{3}/, /^.*\s/].each do |regex|
364
+ it "supports regex matching like #{regex}" do
365
+ @matcher.match("<bookstore><book>bar text</book></bookstore").should be_false
366
+ @matcher.on("/bookstore/book/text()", :excluding => regex)
367
+ @matcher.match("<bookstore><book>bar text</book></bookstore").should be_true
368
+ end
369
+ end
370
+
371
+ it "should support group excluding" do
372
+ matcher = Matcher::Xml.new("<book>This is book 123</book>")
373
+ matcher.on("/book/text()", :excluding => /\d{3}$/)
374
+ matcher.match("<book>This is book 456</book>").should be_true
375
+ end
376
+
377
+ it "should support group excludes with a variable length" do
378
+ matcher = Matcher::Xml.new("<book>Book 123 is here</book>")
379
+ matcher.on("/book/text()", :excluding => /.*\s(\d{3,6})\s.*/)
380
+ matcher.match("<book>Book 123456 is here</book>").should be_true
381
+ end
382
+
383
+ it "handles a non captured group" do
384
+ matcher = Matcher::Xml.new("<book>foo text</book>")
385
+ matcher.on("/book/text()", :excluding => /(blah)/)
386
+ matcher.match("<book>bar text</book>").should be_false
387
+ end
388
+
389
+ it "should raise an error if excluding would result in matching empty values" do
390
+ @matcher.on("/bookstore/book/text()", :excluding => /.*/)
391
+ lambda { @matcher.match("<bookstore><book>foo text</book></bookstore") }.should raise_error(Matcher::MatchError)
392
+ end
393
+
394
+ it "should fail a mismatching exclude" do
395
+ @matcher.on("/bookstore/book/text()", :excluding => /^\w{1}/)
396
+ @matcher.match("<bookstore><book>bar text</book></bookstore").should be_false
397
+ end
398
+
399
+ it "throws an error when a non-regex value" do
400
+ lambda { @matcher.on("/bookstore/book/text()", :excluding => "foo") }.should raise_error(ArgumentError)
401
+ end
402
+
403
+ it "should not allow both a block and options" do
404
+ lambda { @matcher.on("foo", :excluding => /bar/) {|actual| actual =~ /foo/} }.should raise_error(ArgumentError)
405
+ end
406
+
407
+ end
408
+
344
409
  end
345
-
410
+
346
411
  end
347
412
 
348
413
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xmatch
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Peter Moran
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-24 00:00:00 +11:00
18
+ date: 2010-12-07 00:00:00 +11:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency