]> git.openstreetmap.org Git - rails.git/blob - vendor/gems/rspec-1.1.2/lib/spec/mocks/proxy.rb
03db3b113403f392beff6037f92a719320b05799
[rails.git] / vendor / gems / rspec-1.1.2 / lib / spec / mocks / proxy.rb
1 module Spec
2   module Mocks
3     class Proxy
4       DEFAULT_OPTIONS = {
5         :null_object => false,
6       }
7
8       def initialize(target, name, options={})
9         @target = target
10         @name = name
11         @error_generator = ErrorGenerator.new target, name
12         @expectation_ordering = OrderGroup.new @error_generator
13         @expectations = []
14         @messages_received = []
15         @stubs = []
16         @proxied_methods = []
17         @options = options ? DEFAULT_OPTIONS.dup.merge(options) : DEFAULT_OPTIONS
18       end
19
20       def null_object?
21         @options[:null_object]
22       end
23
24       def add_message_expectation(expected_from, sym, opts={}, &block)
25         __add sym
26         @expectations << MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts)
27         @expectations.last
28       end
29
30       def add_negative_message_expectation(expected_from, sym, &block)
31         __add sym
32         @expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil)
33         @expectations.last
34       end
35
36       def add_stub(expected_from, sym, opts={})
37         __add sym
38         @stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, nil, :any, opts)
39         @stubs.first
40       end
41
42       def verify #:nodoc:
43         verify_expectations
44       ensure
45         reset
46       end
47
48       def reset
49         clear_expectations
50         clear_stubs
51         reset_proxied_methods
52         clear_proxied_methods
53       end
54
55       def received_message?(sym, *args, &block)
56         @messages_received.any? {|array| array == [sym, args, block]}
57       end
58
59       def has_negative_expectation?(sym)
60         @expectations.detect {|expectation| expectation.negative_expectation_for?(sym)}
61       end
62
63       def message_received(sym, *args, &block)
64         if expectation = find_matching_expectation(sym, *args)
65           expectation.invoke(args, block)
66         elsif stub = find_matching_method_stub(sym, *args)
67           stub.invoke([], block)
68         elsif expectation = find_almost_matching_expectation(sym, *args)
69           raise_unexpected_message_args_error(expectation, *args) unless has_negative_expectation?(sym) unless null_object?
70         else
71           @target.send :method_missing, sym, *args, &block
72         end
73       end
74
75       def raise_unexpected_message_args_error(expectation, *args)
76         @error_generator.raise_unexpected_message_args_error expectation, *args
77       end
78
79       def raise_unexpected_message_error(sym, *args)
80         @error_generator.raise_unexpected_message_error sym, *args
81       end
82       
83     private
84
85       def __add(sym)
86         $rspec_mocks.add(@target) unless $rspec_mocks.nil?
87         define_expected_method(sym)
88       end
89       
90       def define_expected_method(sym)
91         if target_responds_to?(sym) && !metaclass.method_defined?(munge(sym))
92           munged_sym = munge(sym)
93           metaclass.instance_eval do
94             alias_method munged_sym, sym if method_defined?(sym.to_s)
95           end
96           @proxied_methods << sym
97         end
98         
99         metaclass_eval(<<-EOF, __FILE__, __LINE__)
100           def #{sym}(*args, &block)
101             __mock_proxy.message_received :#{sym}, *args, &block
102           end
103         EOF
104       end
105
106       def target_responds_to?(sym)
107         return @target.send(munge(:respond_to?),sym) if @already_proxied_respond_to
108         return @already_proxied_respond_to = true if sym == :respond_to?
109         return @target.respond_to?(sym)
110       end
111
112       def munge(sym)
113         "proxied_by_rspec__#{sym.to_s}".to_sym
114       end
115
116       def clear_expectations
117         @expectations.clear
118       end
119
120       def clear_stubs
121         @stubs.clear
122       end
123
124       def clear_proxied_methods
125         @proxied_methods.clear
126       end
127
128       def metaclass_eval(str, filename, lineno)
129         metaclass.class_eval(str, filename, lineno)
130       end
131       
132       def metaclass
133         (class << @target; self; end)
134       end
135
136       def verify_expectations
137         @expectations.each do |expectation|
138           expectation.verify_messages_received
139         end
140       end
141
142       def reset_proxied_methods
143         @proxied_methods.each do |sym|
144           munged_sym = munge(sym)
145           metaclass.instance_eval do
146             if method_defined?(munged_sym.to_s)
147               alias_method sym, munged_sym
148               undef_method munged_sym
149             else
150               undef_method sym
151             end
152           end
153         end
154       end
155
156       def find_matching_expectation(sym, *args)
157         @expectations.find {|expectation| expectation.matches(sym, args)}
158       end
159
160       def find_almost_matching_expectation(sym, *args)
161         @expectations.find {|expectation| expectation.matches_name_but_not_args(sym, args)}
162       end
163
164       def find_matching_method_stub(sym, *args)
165         @stubs.find {|stub| stub.matches(sym, args)}
166       end
167
168     end
169   end
170 end