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