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)