5 def initialize(expected, relativity=:exactly)
6 @expected = (expected == :no ? 0 : expected)
7 @relativity = relativity
13 :at_least => "at least ",
14 :at_most => "at most "
18 def method_missing(sym, *args, &block)
19 @collection_name = sym
20 @plural_collection_name = Inflector.pluralize(sym.to_s) if Object.const_defined?(:Inflector)
26 def matches?(collection_owner)
27 if collection_owner.respond_to?(@collection_name)
28 collection = collection_owner.send(@collection_name, *@args, &@block)
29 elsif (@plural_collection_name && collection_owner.respond_to?(@plural_collection_name))
30 collection = collection_owner.send(@plural_collection_name, *@args, &@block)
31 elsif (collection_owner.respond_to?(:length) || collection_owner.respond_to?(:size))
32 collection = collection_owner
34 collection_owner.send(@collection_name, *@args, &@block)
36 @actual = collection.size if collection.respond_to?(:size)
37 @actual = collection.length if collection.respond_to?(:length)
38 raise not_a_collection if @actual.nil?
39 return @actual >= @expected if @relativity == :at_least
40 return @actual <= @expected if @relativity == :at_most
41 return @actual == @expected
45 "expected #{@collection_name} to be a collection but it does not respond to #length or #size"
49 "expected #{relative_expectation} #{@collection_name}, got #{@actual}"
52 def negative_failure_message
53 if @relativity == :exactly
54 return "expected target not to have #{@expected} #{@collection_name}, got #{@actual}"
55 elsif @relativity == :at_most
57 Isn't life confusing enough?
58 Instead of having to figure out the meaning of this:
59 should_not have_at_most(#{@expected}).#{@collection_name}
60 We recommend that you use this instead:
61 should have_at_least(#{@expected + 1}).#{@collection_name}
63 elsif @relativity == :at_least
65 Isn't life confusing enough?
66 Instead of having to figure out the meaning of this:
67 should_not have_at_least(#{@expected}).#{@collection_name}
68 We recommend that you use this instead:
69 should have_at_most(#{@expected - 1}).#{@collection_name}
75 "have #{relative_expectation} #{@collection_name}"
80 def relative_expectation
81 "#{relativities[@relativity]}#{@expected}"
86 # should have(number).named_collection__or__sugar
87 # should_not have(number).named_collection__or__sugar
89 # Passes if receiver is a collection with the submitted
90 # number of items OR if the receiver OWNS a collection
91 # with the submitted number of items.
93 # If the receiver OWNS the collection, you must use the name
94 # of the collection. So if a <tt>Team</tt> instance has a
95 # collection named <tt>#players</tt>, you must use that name
96 # to set the expectation.
98 # If the receiver IS the collection, you can use any name
99 # you like for <tt>named_collection</tt>. We'd recommend using
100 # either "elements", "members", or "items" as these are all
101 # standard ways of describing the things IN a collection.
103 # This also works for Strings, letting you set an expectation
108 # # Passes if team.players.size == 11
109 # team.should have(11).players
111 # # Passes if [1,2,3].length == 3
112 # [1,2,3].should have(3).items #"items" is pure sugar
114 # # Passes if "this string".length == 11
115 # "this string".should have(11).characters #"characters" is pure sugar
117 Matchers::Have.new(n)
119 alias :have_exactly :have
122 # should have_at_least(number).items
124 # Exactly like have() with >=.
128 # +should_not+ +have_at_least+ is not supported
130 Matchers::Have.new(n, :at_least)
134 # should have_at_most(number).items
136 # Exactly like have() with <=.
140 # +should_not+ +have_at_most+ is not supported
142 Matchers::Have.new(n, :at_most)