1class MockObject
2  def initialize(name, options={})
3    @name = name
4    @null = options[:null_object]
5  end
6
7  def method_missing(sym, *args, &block)
8    @null ? self : super
9  end
10  private :method_missing
11end
12
13class NumericMockObject < Numeric
14  def initialize(name, options={})
15    @name = name
16    @null = options[:null_object]
17  end
18
19  def method_missing(sym, *args, &block)
20    @null ? self : super
21  end
22
23  def singleton_method_added(val)
24  end
25end
26
27class MockIntObject
28  def initialize(val)
29    @value = val
30    @calls = 0
31
32    key = [self, :to_int]
33
34    Mock.objects[key] = self
35    Mock.mocks[key] << self
36  end
37
38  attr_reader :calls
39
40  def to_int
41    @calls += 1
42    @value.to_int
43  end
44
45  def count
46    [:at_least, 1]
47  end
48end
49
50class MockProxy
51  attr_reader :raising, :yielding
52
53  def initialize(type=nil)
54    @multiple_returns = nil
55    @returning = nil
56    @raising   = nil
57    @yielding  = []
58    @arguments = :any_args
59    @type      = type || :mock
60  end
61
62  def mock?
63    @type == :mock
64  end
65
66  def stub?
67    @type == :stub
68  end
69
70  def count
71    @count ||= mock? ? [:exactly, 1] : [:any_number_of_times, 0]
72  end
73
74  def arguments
75    @arguments
76  end
77
78  def returning
79    if @multiple_returns
80      if @returning.size == 1
81        @multiple_returns = false
82        return @returning = @returning.shift
83      end
84      return @returning.shift
85    end
86    @returning
87  end
88
89  def times
90    self
91  end
92
93  def calls
94    @calls ||= 0
95  end
96
97  def called
98    @calls = calls + 1
99  end
100
101  def exactly(n)
102    @count = [:exactly, n_times(n)]
103    self
104  end
105
106  def at_least(n)
107    @count = [:at_least, n_times(n)]
108    self
109  end
110
111  def at_most(n)
112    @count = [:at_most, n_times(n)]
113    self
114  end
115
116  def once
117    exactly 1
118  end
119
120  def twice
121    exactly 2
122  end
123
124  def any_number_of_times
125    @count = [:any_number_of_times, 0]
126    self
127  end
128
129  def with(*args)
130    raise ArgumentError, "you must specify the expected arguments" if args.empty?
131    if args.length == 1
132      @arguments = args.first
133    else
134      @arguments = args
135    end
136    self
137  end
138
139  def and_return(*args)
140    case args.size
141    when 0
142      @returning = nil
143    when 1
144      @returning = args[0]
145    else
146      @multiple_returns = true
147      @returning = args
148      count[1] = args.size if count[1] < args.size
149    end
150    self
151  end
152
153  def and_raise(exception)
154    if exception.kind_of? String
155      @raising = RuntimeError.new exception
156    else
157      @raising = exception
158    end
159  end
160
161  def raising?
162    @raising != nil
163  end
164
165  def and_yield(*args)
166    @yielding << args
167    self
168  end
169
170  def yielding?
171    !@yielding.empty?
172  end
173
174  private
175
176  def n_times(n)
177    case n
178    when :once
179      1
180    when :twice
181      2
182    else
183      Integer n
184    end
185  end
186end
187