module Spec
module Expectations
module Should
class Have
def initialize(target, relativity=:exactly, expected=nil)
@target = target
init_collection_handler(target, relativity, expected)
init_item_handler(target)
end
def init_collection_handler(target, relativity, expected)
@collection_handler = CollectionHandler.new(target, relativity, expected)
end
def init_item_handler(target)
@item_handler = PositiveItemHandler.new(target)
end
def method_missing(sym, *args)
if @collection_handler.wants_to_handle(sym)
@collection_handler.handle_message(sym, *args)
elsif @item_handler.wants_to_handle(sym)
@item_handler.handle_message(sym, *args)
else
Spec::Expectations.fail_with("target does not respond to #has_#{sym}?")
end
end
end
class NotHave < Have
def init_item_handler(target)
@item_handler = NegativeItemHandler.new(target)
end
end
class CollectionHandler
def initialize(target, relativity=:exactly, expected=nil)
@target = target
@expected = expected == :no ? 0 : expected
@at_least = (relativity == :at_least)
@at_most = (relativity == :at_most)
end
def at_least(expected_number=nil)
@at_least = true
@at_most = false
@expected = expected_number == :no ? 0 : expected_number
self
end
def at_most(expected_number=nil)
@at_least = false
@at_most = true
@expected = expected_number == :no ? 0 : expected_number
self
end
def method_missing(sym, *args)
if @target.respond_to?(sym)
handle_message(sym, *args)
end
end
def wants_to_handle(sym)
respond_to?(sym) || @target.respond_to?(sym)
end
def handle_message(sym, *args)
return at_least(args[0]) if sym == :at_least
return at_most(args[0]) if sym == :at_most
Spec::Expectations.fail_with(build_message(sym, args)) unless as_specified?(sym, args)
end
def build_message(sym, args)
message = "expected"
message += " at least" if @at_least
message += " at most" if @at_most
message += " #{@expected} #{sym}, got #{actual_size_of(collection(sym, args))}"
end
def as_specified?(sym, args)
return actual_size_of(collection(sym, args)) >= @expected if @at_least
return actual_size_of(collection(sym, args)) <= @expected if @at_most
return actual_size_of(collection(sym, args)) == @expected
end
def collection(sym, args)
@target.send(sym, *args)
end
def actual_size_of(collection)
return collection.length if collection.respond_to? :length
return collection.size if collection.respond_to? :size
end
end
class ItemHandler
def wants_to_handle(sym)
@target.respond_to?("has_#{sym}?")
end
def initialize(target)
@target = target
end
def fail_with(message)
Spec::Expectations.fail_with(message)
end
end
class PositiveItemHandler < ItemHandler
def handle_message(sym, *args)
fail_with(
"expected #has_#{sym}?(#{args.collect{|arg| arg.inspect}.join(', ')}) to return true, got false"
) unless @target.send("has_#{sym}?", *args)
end
end
class NegativeItemHandler < ItemHandler
def handle_message(sym, *args)
fail_with(
"expected #has_#{sym}?(#{args.collect{|arg| arg.inspect}.join(', ')}) to return false, got true"
) if @target.send("has_#{sym}?", *args)
end
end
end
end
end
syntax highlighted by Code2HTML, v. 0.9.1