redinger-rr 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. data/CHANGES +221 -0
  2. data/README.rdoc +343 -0
  3. data/Rakefile +88 -0
  4. data/VERSION.yml +4 -0
  5. data/lib/rr.rb +88 -0
  6. data/lib/rr/adapters/rr_methods.rb +122 -0
  7. data/lib/rr/adapters/rspec.rb +59 -0
  8. data/lib/rr/adapters/test_unit.rb +29 -0
  9. data/lib/rr/double.rb +152 -0
  10. data/lib/rr/double_definitions/child_double_definition_creator.rb +27 -0
  11. data/lib/rr/double_definitions/double_definition.rb +348 -0
  12. data/lib/rr/double_definitions/double_definition_creator.rb +167 -0
  13. data/lib/rr/double_definitions/double_definition_creator_proxy.rb +37 -0
  14. data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +15 -0
  15. data/lib/rr/double_definitions/strategies/implementation/proxy.rb +62 -0
  16. data/lib/rr/double_definitions/strategies/implementation/reimplementation.rb +14 -0
  17. data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
  18. data/lib/rr/double_definitions/strategies/scope/instance.rb +15 -0
  19. data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +50 -0
  20. data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +15 -0
  21. data/lib/rr/double_definitions/strategies/strategy.rb +70 -0
  22. data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +34 -0
  23. data/lib/rr/double_definitions/strategies/verification/mock.rb +44 -0
  24. data/lib/rr/double_definitions/strategies/verification/stub.rb +45 -0
  25. data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +15 -0
  26. data/lib/rr/double_injection.rb +180 -0
  27. data/lib/rr/double_matches.rb +51 -0
  28. data/lib/rr/errors/argument_equality_error.rb +6 -0
  29. data/lib/rr/errors/double_definition_error.rb +6 -0
  30. data/lib/rr/errors/double_not_found_error.rb +6 -0
  31. data/lib/rr/errors/double_order_error.rb +6 -0
  32. data/lib/rr/errors/rr_error.rb +20 -0
  33. data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
  34. data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
  35. data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
  36. data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
  37. data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
  38. data/lib/rr/errors/times_called_error.rb +6 -0
  39. data/lib/rr/expectations/any_argument_expectation.rb +21 -0
  40. data/lib/rr/expectations/argument_equality_expectation.rb +41 -0
  41. data/lib/rr/expectations/times_called_expectation.rb +57 -0
  42. data/lib/rr/hash_with_object_id_key.rb +44 -0
  43. data/lib/rr/method_dispatches/base_method_dispatch.rb +108 -0
  44. data/lib/rr/method_dispatches/method_dispatch.rb +61 -0
  45. data/lib/rr/method_dispatches/method_missing_dispatch.rb +49 -0
  46. data/lib/rr/proc_from_block.rb +7 -0
  47. data/lib/rr/recorded_calls.rb +103 -0
  48. data/lib/rr/space.rb +123 -0
  49. data/lib/rr/spy_verification.rb +48 -0
  50. data/lib/rr/spy_verification_proxy.rb +18 -0
  51. data/lib/rr/times_called_matchers/any_times_matcher.rb +18 -0
  52. data/lib/rr/times_called_matchers/at_least_matcher.rb +15 -0
  53. data/lib/rr/times_called_matchers/at_most_matcher.rb +23 -0
  54. data/lib/rr/times_called_matchers/integer_matcher.rb +19 -0
  55. data/lib/rr/times_called_matchers/non_terminal.rb +27 -0
  56. data/lib/rr/times_called_matchers/proc_matcher.rb +11 -0
  57. data/lib/rr/times_called_matchers/range_matcher.rb +21 -0
  58. data/lib/rr/times_called_matchers/terminal.rb +20 -0
  59. data/lib/rr/times_called_matchers/times_called_matcher.rb +44 -0
  60. data/lib/rr/wildcard_matchers.rb +158 -0
  61. data/lib/rr/wildcard_matchers/anything.rb +18 -0
  62. data/lib/rr/wildcard_matchers/boolean.rb +23 -0
  63. data/lib/rr/wildcard_matchers/duck_type.rb +32 -0
  64. data/lib/rr/wildcard_matchers/hash_including.rb +29 -0
  65. data/lib/rr/wildcard_matchers/is_a.rb +25 -0
  66. data/lib/rr/wildcard_matchers/numeric.rb +13 -0
  67. data/lib/rr/wildcard_matchers/range.rb +7 -0
  68. data/lib/rr/wildcard_matchers/regexp.rb +7 -0
  69. data/lib/rr/wildcard_matchers/satisfy.rb +26 -0
  70. data/spec/core_spec_suite.rb +19 -0
  71. data/spec/environment_fixture_setup.rb +7 -0
  72. data/spec/high_level_spec.rb +398 -0
  73. data/spec/proc_from_block_spec.rb +14 -0
  74. data/spec/rr/adapters/rr_methods_argument_matcher_spec.rb +67 -0
  75. data/spec/rr/adapters/rr_methods_creator_spec.rb +149 -0
  76. data/spec/rr/adapters/rr_methods_space_spec.rb +115 -0
  77. data/spec/rr/adapters/rr_methods_spec_helper.rb +11 -0
  78. data/spec/rr/adapters/rr_methods_times_matcher_spec.rb +17 -0
  79. data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +112 -0
  80. data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +155 -0
  81. data/spec/rr/double_definitions/double_definition_creator_spec.rb +502 -0
  82. data/spec/rr/double_definitions/double_definition_spec.rb +1165 -0
  83. data/spec/rr/double_injection/double_injection_spec.rb +339 -0
  84. data/spec/rr/double_injection/double_injection_verify_spec.rb +29 -0
  85. data/spec/rr/double_spec.rb +352 -0
  86. data/spec/rr/errors/rr_error_spec.rb +67 -0
  87. data/spec/rr/expectations/any_argument_expectation_spec.rb +47 -0
  88. data/spec/rr/expectations/anything_argument_equality_expectation_spec.rb +14 -0
  89. data/spec/rr/expectations/argument_equality_expectation_spec.rb +135 -0
  90. data/spec/rr/expectations/boolean_argument_equality_expectation_spec.rb +34 -0
  91. data/spec/rr/expectations/hash_including_argument_equality_expectation_spec.rb +82 -0
  92. data/spec/rr/expectations/hash_including_spec.rb +17 -0
  93. data/spec/rr/expectations/satisfy_argument_equality_expectation_spec.rb +59 -0
  94. data/spec/rr/expectations/satisfy_spec.rb +14 -0
  95. data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +46 -0
  96. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +69 -0
  97. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +71 -0
  98. data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +23 -0
  99. data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +104 -0
  100. data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +81 -0
  101. data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +83 -0
  102. data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +38 -0
  103. data/spec/rr/rspec/invocation_matcher_spec.rb +279 -0
  104. data/spec/rr/rspec/rspec_adapter_spec.rb +66 -0
  105. data/spec/rr/rspec/rspec_backtrace_tweaking_spec.rb +31 -0
  106. data/spec/rr/rspec/rspec_backtrace_tweaking_spec_fixture.rb +11 -0
  107. data/spec/rr/rspec/rspec_usage_spec.rb +86 -0
  108. data/spec/rr/space/hash_with_object_id_key_spec.rb +88 -0
  109. data/spec/rr/space/space_spec.rb +550 -0
  110. data/spec/rr/test_unit/test_helper.rb +7 -0
  111. data/spec/rr/test_unit/test_unit_backtrace_test.rb +36 -0
  112. data/spec/rr/test_unit/test_unit_integration_test.rb +57 -0
  113. data/spec/rr/times_called_matchers/any_times_matcher_spec.rb +47 -0
  114. data/spec/rr/times_called_matchers/at_least_matcher_spec.rb +55 -0
  115. data/spec/rr/times_called_matchers/at_most_matcher_spec.rb +70 -0
  116. data/spec/rr/times_called_matchers/integer_matcher_spec.rb +70 -0
  117. data/spec/rr/times_called_matchers/proc_matcher_spec.rb +55 -0
  118. data/spec/rr/times_called_matchers/range_matcher_spec.rb +76 -0
  119. data/spec/rr/times_called_matchers/times_called_matcher_spec.rb +118 -0
  120. data/spec/rr/wildcard_matchers/anything_spec.rb +24 -0
  121. data/spec/rr/wildcard_matchers/boolean_spec.rb +36 -0
  122. data/spec/rr/wildcard_matchers/duck_type_spec.rb +52 -0
  123. data/spec/rr/wildcard_matchers/is_a_spec.rb +32 -0
  124. data/spec/rr/wildcard_matchers/numeric_spec.rb +32 -0
  125. data/spec/rr/wildcard_matchers/range_spec.rb +35 -0
  126. data/spec/rr/wildcard_matchers/regexp_spec.rb +43 -0
  127. data/spec/rr_spec.rb +28 -0
  128. data/spec/rspec_spec_suite.rb +17 -0
  129. data/spec/spec_helper.rb +109 -0
  130. data/spec/spec_suite.rb +31 -0
  131. data/spec/spy_verification_spec.rb +129 -0
  132. data/spec/test_unit_spec_suite.rb +21 -0
  133. metadata +193 -0
@@ -0,0 +1,27 @@
1
+ module RR
2
+ module DoubleDefinitions
3
+ class ChildDoubleDefinitionCreator < DoubleDefinitionCreator # :nodoc
4
+ attr_reader :parent_double_definition
5
+ def initialize(parent_double_definition)
6
+ @parent_double_definition = parent_double_definition
7
+ super()
8
+ end
9
+
10
+ def root_subject
11
+ parent_double_definition.root_subject
12
+ end
13
+
14
+ def instance_of(*args)
15
+ raise NoMethodError
16
+ end
17
+
18
+ protected
19
+ def add_strategy(subject, method_name, definition_eval_block, &block)
20
+ super do
21
+ block.call
22
+ parent_double_definition.implemented_by(lambda {subject})
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,348 @@
1
+ module RR
2
+ module DoubleDefinitions
3
+ class DoubleDefinition #:nodoc:
4
+ class << self
5
+ def register_strategy_class(strategy_class, method_name)
6
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
7
+ def #{method_name}(subject=DoubleDefinitionCreator::NO_SUBJECT, method_name=nil, &definition_eval_block)
8
+ ChildDoubleDefinitionCreator.new(self).#{method_name}(subject, method_name, &definition_eval_block)
9
+ end
10
+ CLASS
11
+
12
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
13
+ def #{method_name}!(method_name=nil, &definition_eval_block)
14
+ ChildDoubleDefinitionCreator.new(self).#{method_name}!(method_name, &definition_eval_block)
15
+ end
16
+ CLASS
17
+ end
18
+ end
19
+
20
+ ORIGINAL_METHOD = Object.new
21
+ attr_accessor(
22
+ :argument_expectation,
23
+ :times_matcher,
24
+ :implementation,
25
+ :after_call_proc,
26
+ :yields_value,
27
+ :double,
28
+ :double_definition_creator,
29
+ :subject
30
+ )
31
+
32
+ include Space::Reader
33
+
34
+ def initialize(double_definition_creator, subject)
35
+ @implementation = nil
36
+ @argument_expectation = nil
37
+ @times_matcher = nil
38
+ @after_call_proc = nil
39
+ @yields_value = nil
40
+ @double_definition_creator = double_definition_creator
41
+ @subject = subject
42
+ end
43
+
44
+ attr_reader :argument_expectation
45
+
46
+ def root_subject
47
+ double_definition_creator.root_subject
48
+ end
49
+
50
+ module ArgumentDefinitionConstructionMethods
51
+ # Double#with sets the expectation that the Double will receive
52
+ # the passed in arguments.
53
+ #
54
+ # Passing in a block sets the return value.
55
+ #
56
+ # mock(subject).method_name.with(1, 2) {:return_value}
57
+ def with(*args, &return_value_block)
58
+ @argument_expectation = Expectations::ArgumentEqualityExpectation.new(*args)
59
+ install_method_callback return_value_block
60
+ self
61
+ end
62
+
63
+ # Double#with_any_args sets the expectation that the Double can receive
64
+ # any arguments.
65
+ #
66
+ # Passing in a block sets the return value.
67
+ #
68
+ # mock(subject).method_name.with_any_args {:return_value}
69
+ def with_any_args(&return_value_block)
70
+ @argument_expectation = Expectations::AnyArgumentExpectation.new
71
+ install_method_callback return_value_block
72
+ self
73
+ end
74
+
75
+ # Double#with_no_args sets the expectation that the Double will receive
76
+ # no arguments.
77
+ #
78
+ # Passing in a block sets the return value.
79
+ #
80
+ # mock(subject).method_name.with_no_args {:return_value}
81
+ def with_no_args(&return_value_block)
82
+ @argument_expectation = Expectations::ArgumentEqualityExpectation.new()
83
+ install_method_callback return_value_block
84
+ self
85
+ end
86
+ end
87
+ include ArgumentDefinitionConstructionMethods
88
+
89
+ module TimesDefinitionConstructionMethods
90
+ # Double#never sets the expectation that the Double will never be
91
+ # called.
92
+ #
93
+ # This method does not accept a block because it will never be called.
94
+ #
95
+ # mock(subject).method_name.never
96
+ def never
97
+ @times_matcher = TimesCalledMatchers::IntegerMatcher.new(0)
98
+ self
99
+ end
100
+
101
+ # Double#once sets the expectation that the Double will be called
102
+ # 1 time.
103
+ #
104
+ # Passing in a block sets the return value.
105
+ #
106
+ # mock(subject).method_name.once {:return_value}
107
+ def once(&return_value_block)
108
+ @times_matcher = TimesCalledMatchers::IntegerMatcher.new(1)
109
+ install_method_callback return_value_block
110
+ self
111
+ end
112
+
113
+ # Double#twice sets the expectation that the Double will be called
114
+ # 2 times.
115
+ #
116
+ # Passing in a block sets the return value.
117
+ #
118
+ # mock(subject).method_name.twice {:return_value}
119
+ def twice(&return_value_block)
120
+ @times_matcher = TimesCalledMatchers::IntegerMatcher.new(2)
121
+ install_method_callback return_value_block
122
+ self
123
+ end
124
+
125
+ # Double#at_least sets the expectation that the Double
126
+ # will be called at least n times.
127
+ # It works by creating a TimesCalledExpectation.
128
+ #
129
+ # Passing in a block sets the return value.
130
+ #
131
+ # mock(subject).method_name.at_least(4) {:return_value}
132
+ def at_least(number, &return_value_block)
133
+ @times_matcher = TimesCalledMatchers::AtLeastMatcher.new(number)
134
+ install_method_callback return_value_block
135
+ self
136
+ end
137
+
138
+ # Double#at_most allows sets the expectation that the Double
139
+ # will be called at most n times.
140
+ # It works by creating a TimesCalledExpectation.
141
+ #
142
+ # Passing in a block sets the return value.
143
+ #
144
+ # mock(subject).method_name.at_most(4) {:return_value}
145
+ def at_most(number, &return_value_block)
146
+ @times_matcher = TimesCalledMatchers::AtMostMatcher.new(number)
147
+ install_method_callback return_value_block
148
+ self
149
+ end
150
+
151
+ # Double#any_number_of_times sets an that the Double will be called
152
+ # any number of times. This effectively removes the times called expectation
153
+ # from the Doublen
154
+ #
155
+ # Passing in a block sets the return value.
156
+ #
157
+ # mock(subject).method_name.any_number_of_times
158
+ def any_number_of_times(&return_value_block)
159
+ @times_matcher = TimesCalledMatchers::AnyTimesMatcher.new
160
+ install_method_callback return_value_block
161
+ self
162
+ end
163
+ alias_method :any_times, :any_number_of_times
164
+
165
+ # Double#times creates an TimesCalledExpectation of the passed
166
+ # in number.
167
+ #
168
+ # Passing in a block sets the return value.
169
+ #
170
+ # mock(subject).method_name.times(4) {:return_value}
171
+ def times(matcher_value, &return_value_block)
172
+ @times_matcher = TimesCalledMatchers::TimesCalledMatcher.create(matcher_value)
173
+ install_method_callback return_value_block
174
+ self
175
+ end
176
+ end
177
+ include TimesDefinitionConstructionMethods
178
+
179
+ module DefinitionConstructionMethods
180
+ # Double#ordered sets the Double to have an ordered
181
+ # expectation.
182
+ #
183
+ # Passing in a block sets the return value.
184
+ #
185
+ # mock(subject).method_name.ordered {return_value}
186
+ def ordered(&return_value_block)
187
+ raise(
188
+ Errors::DoubleDefinitionError,
189
+ "Double Definitions must have a dedicated Double to be ordered. " <<
190
+ "For example, using instance_of does not allow ordered to be used. " <<
191
+ "proxy the class's #new method instead."
192
+ ) unless @double
193
+ @ordered = true
194
+ space.register_ordered_double(@double)
195
+ install_method_callback return_value_block
196
+ DoubleDefinitionCreatorProxy.new(double_definition_creator)
197
+ end
198
+ alias_method :then, :ordered
199
+
200
+ # Double#yields sets the Double to invoke a passed in block when
201
+ # the Double is called.
202
+ # An Expection will be raised if no block is passed in when the
203
+ # Double is called.
204
+ #
205
+ # Passing in a block sets the return value.
206
+ #
207
+ # mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
208
+ # subject.method_name {|yield_arg1, yield_arg2|}
209
+ def yields(*args, &return_value_block)
210
+ @yields_value = args
211
+ install_method_callback return_value_block
212
+ self
213
+ end
214
+
215
+ # Double#after_call creates a callback that occurs after call
216
+ # is called. The passed in block receives the return value of
217
+ # the Double being called.
218
+ # An Expection will be raised if no block is passed in.
219
+ #
220
+ # mock(subject).method_name {return_value}.after_call {|return_value|}
221
+ # subject.method_name # return_value
222
+ #
223
+ # This feature is built into proxies.
224
+ # mock.proxy(User).find('1') {|user| mock(user).valid? {false}}
225
+ def after_call(&after_call_proc)
226
+ raise ArgumentError, "after_call expects a block" unless after_call_proc
227
+ @after_call_proc = after_call_proc
228
+ self
229
+ end
230
+
231
+ # Double#verbose sets the Double to print out each method call it receives.
232
+ #
233
+ # Passing in a block sets the return value
234
+ def verbose(&after_call_proc)
235
+ @verbose = true
236
+ @after_call_proc = after_call_proc
237
+ self
238
+ end
239
+
240
+ # Double#returns accepts an argument value or a block.
241
+ # It will raise an ArgumentError if both are passed in.
242
+ #
243
+ # Passing in a block causes Double to return the return value of
244
+ # the passed in block.
245
+ #
246
+ # Passing in an argument causes Double to return the argument.
247
+ def returns(*args, &implementation)
248
+ if !args.empty? && implementation
249
+ raise ArgumentError, "returns cannot accept both an argument and a block"
250
+ end
251
+ if implementation
252
+ install_method_callback implementation
253
+ else
254
+ install_method_callback(lambda do
255
+ return *args
256
+ end)
257
+ end
258
+ self
259
+ end
260
+
261
+ def implemented_by_original_method
262
+ implemented_by ORIGINAL_METHOD
263
+ self
264
+ end
265
+
266
+ # Double#implemented_by sets the implementation of the Double.
267
+ # This method takes a Proc or a Method. Passing in a Method allows
268
+ # the Double to accept blocks.
269
+ #
270
+ # obj = Object.new
271
+ # def obj.foobar
272
+ # yield(1)
273
+ # end
274
+ # mock(obj).method_name.implemented_by(obj.method(:foobar))
275
+ def implemented_by(implementation)
276
+ @implementation = implementation
277
+ self
278
+ end
279
+
280
+ def verify_method_signature
281
+ @verify_method_signature = true
282
+ self
283
+ end
284
+ alias_method :strong, :verify_method_signature
285
+
286
+ protected
287
+ def install_method_callback(block)
288
+ return unless block
289
+ if implementation_is_original_method?
290
+ after_call(&block)
291
+ else
292
+ implemented_by block
293
+ end
294
+ end
295
+ end
296
+ include DefinitionConstructionMethods
297
+
298
+ module StateQueryMethods
299
+ # Double#ordered? returns true when the Double is ordered.
300
+ #
301
+ # mock(subject).method_name.ordered?
302
+ def ordered?
303
+ @ordered
304
+ end
305
+
306
+ # Double#verbose? returns true when verbose has been called on it. It returns
307
+ # true when the double is set to print each method call it receives.
308
+ def verbose?
309
+ @verbose ? true : false
310
+ end
311
+
312
+ def exact_match?(*arguments)
313
+ raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @argument_expectation
314
+ @argument_expectation.exact_match?(*arguments)
315
+ end
316
+
317
+ def wildcard_match?(*arguments)
318
+ raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @argument_expectation
319
+ @argument_expectation.wildcard_match?(*arguments)
320
+ end
321
+
322
+ def terminal?
323
+ raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @times_matcher
324
+ @times_matcher.terminal?
325
+ end
326
+
327
+ def expected_arguments
328
+ argument_expectation ? argument_expectation.expected_arguments : []
329
+ end
330
+
331
+ def implementation_is_original_method?
332
+ implementation_strategy.is_a?(Strategies::Implementation::Proxy)
333
+ end
334
+
335
+ def verify_method_signature?
336
+ !!@verify_method_signature
337
+ end
338
+ alias_method :strong?, :verify_method_signature?
339
+
340
+ protected
341
+ def implementation_strategy
342
+ double_definition_creator.implementation_strategy
343
+ end
344
+ end
345
+ include StateQueryMethods
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,167 @@
1
+ module RR
2
+ module DoubleDefinitions
3
+ class DoubleDefinitionCreator # :nodoc
4
+ class << self
5
+ def register_verification_strategy_class(strategy_class, method_name)
6
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
7
+ def #{method_name}(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
8
+ add_strategy(subject, method_name, definition_eval_block) do
9
+ self.verification_strategy = #{strategy_class.name}.new(self)
10
+ end
11
+ end
12
+ CLASS
13
+
14
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
15
+ def #{method_name}!(method_name=nil, &definition_eval_block)
16
+ #{method_name}(Object.new, method_name, &definition_eval_block)
17
+ end
18
+ CLASS
19
+ end
20
+
21
+ def register_implementation_strategy_class(strategy_class, method_name)
22
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
23
+ def #{method_name}(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
24
+ add_strategy(subject, method_name, definition_eval_block) do
25
+ self.implementation_strategy = #{strategy_class.name}.new(self)
26
+ end
27
+ end
28
+ CLASS
29
+
30
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
31
+ def #{method_name}!(method_name=nil, &definition_eval_block)
32
+ #{method_name}(Object.new, method_name, &definition_eval_block)
33
+ end
34
+ CLASS
35
+ end
36
+
37
+ def register_scope_strategy_class(strategy_class, method_name)
38
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
39
+ def #{method_name}(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
40
+ add_strategy(subject, method_name, definition_eval_block) do
41
+ self.scope_strategy = #{strategy_class.name}.new(self)
42
+ end
43
+ end
44
+ CLASS
45
+
46
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
47
+ def #{method_name}!(method_name=nil, &definition_eval_block)
48
+ #{method_name}(Object.new, method_name, &definition_eval_block)
49
+ end
50
+ CLASS
51
+ end
52
+ end
53
+
54
+ attr_reader :subject,
55
+ :method_name,
56
+ :args, :handler,
57
+ :definition,
58
+ :verification_strategy,
59
+ :implementation_strategy,
60
+ :scope_strategy
61
+ NO_SUBJECT = Object.new
62
+
63
+ include Space::Reader
64
+
65
+ def initialize
66
+ @verification_strategy = nil
67
+ @implementation_strategy = Strategies::Implementation::Reimplementation.new(self)
68
+ @scope_strategy = Strategies::Scope::Instance.new(self)
69
+ end
70
+
71
+ def root_subject
72
+ subject
73
+ end
74
+
75
+ def method_name
76
+ @verification_strategy.method_name
77
+ end
78
+
79
+ module StrategySetupMethods
80
+ def no_subject?
81
+ subject.__id__ === NO_SUBJECT.__id__
82
+ end
83
+
84
+ protected
85
+ def add_strategy(subject, method_name, definition_eval_block)
86
+ if method_name && definition_eval_block
87
+ raise ArgumentError, "Cannot pass in a method name and a block"
88
+ end
89
+ @subject = subject
90
+ yield
91
+ if no_subject?
92
+ self
93
+ elsif method_name
94
+ create(method_name)
95
+ else
96
+ DoubleDefinitionCreatorProxy.new(self, &definition_eval_block)
97
+ end
98
+ end
99
+
100
+ def verification_strategy=(verification_strategy)
101
+ verify_no_verification_strategy
102
+ verify_not_proxy_and_dont_allow(verification_strategy, implementation_strategy)
103
+ @verification_strategy = verification_strategy
104
+ verification_strategy
105
+ end
106
+
107
+ def implementation_strategy=(implementation_strategy)
108
+ verify_not_proxy_and_dont_allow(verification_strategy, implementation_strategy)
109
+ @implementation_strategy = implementation_strategy
110
+ end
111
+
112
+ def scope_strategy=(scope_strategy)
113
+ verify_not_proxy_and_dont_allow(verification_strategy, implementation_strategy)
114
+ @scope_strategy = scope_strategy
115
+ end
116
+
117
+ def verify_no_verification_strategy
118
+ strategy_already_defined_error if verification_strategy
119
+ end
120
+
121
+ def strategy_already_defined_error
122
+ raise(
123
+ Errors::DoubleDefinitionError,
124
+ "This Double already has a #{verification_strategy.name} strategy"
125
+ )
126
+ end
127
+
128
+ def verify_not_proxy_and_dont_allow(verification_strategy, implementation_strategy)
129
+ proxy_when_dont_allow_error if
130
+ verification_strategy.is_a?(Strategies::Verification::DontAllow) &&
131
+ implementation_strategy.is_a?(Strategies::Implementation::Proxy)
132
+ end
133
+
134
+ def proxy_when_dont_allow_error
135
+ raise(
136
+ Errors::DoubleDefinitionError,
137
+ "Doubles cannot be proxied when using dont_allow strategy"
138
+ )
139
+ end
140
+
141
+ def no_strategy_error
142
+ raise(
143
+ Errors::DoubleDefinitionError,
144
+ "This Double has no strategy"
145
+ )
146
+ end
147
+ end
148
+ include StrategySetupMethods
149
+
150
+ module StrategyExecutionMethods
151
+ def create(method_name, *args, &handler)
152
+ raise DoubleDefinitionCreatorError if no_subject?
153
+ @method_name, @args, @handler = method_name, args, handler
154
+ @definition = DoubleDefinition.new(self, subject)
155
+ verification_strategy ? verification_strategy.call(definition, method_name, args, handler) : no_strategy_error
156
+ implementation_strategy.call(definition, method_name, args, handler)
157
+ scope_strategy.call(definition, method_name, args, handler)
158
+ definition
159
+ end
160
+ end
161
+ include StrategyExecutionMethods
162
+
163
+ class DoubleDefinitionCreatorError < Errors::RRError
164
+ end
165
+ end
166
+ end
167
+ end