spy 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -32,7 +32,7 @@ Fail faster, code faster.
32
32
  ## Why not to use this
33
33
 
34
34
  * mocking null objects is not supported(yet)
35
- * no argument matchers for Spy::Method#has\_been\_called\_with
35
+ * no argument matchers for `Spy::Subroutine#has_been_called_with`
36
36
  * cannot watch all calls to an object to check order in which they are called
37
37
  * cannot transfer nested constants when stubbing a constant
38
38
  * i don't think anybody uses this anyway
@@ -78,7 +78,8 @@ You can force the creation of a stub on method that didn't exist but it really i
78
78
  Spy.new(book, :flamethrower).hook(force:true).and_return("burnninante")
79
79
  ```
80
80
 
81
- You can also stub instance methods of Classes and Modules
81
+ You can also stub instance methods of Classes and Modules. This is equivalent to
82
+ rspec-mock's `Module#any_instance`
82
83
 
83
84
  ```ruby
84
85
  Spy.on_instance_method(Book, :title).and_return("Cannery Row")
@@ -105,7 +106,7 @@ Spy.double("book", title: "Grapes of Wrath", author: "John Steinbeck")
105
106
 
106
107
  ### Arbitrary Handling
107
108
 
108
- If you need to have a custom method based in the method inputs just send a block to #and\_return
109
+ If you need to have a custom method based in the method inputs just send a block to `#and_return`
109
110
 
110
111
  ```ruby
111
112
  Spy.on(book, :read_page).and_return do |page, &block|
@@ -142,13 +143,13 @@ Spy.get(validator, :validate)
142
143
  ```
143
144
 
144
145
  ### Calling through
145
- If you just want to make sure if a method is called and not override the output you can just use the and\_call\_through method
146
+ If you just want to make sure if a method is called and not override the output you can just use the `#and_call_through` method
146
147
 
147
148
  ```ruby
148
149
  Spy.on(book, :read_page).and_call_through
149
150
  ```
150
151
 
151
- By if the original method never existed it will call #method\_missing on the spied object.
152
+ By if the original method never existed it will call `#method_missing` on the spied object.
152
153
 
153
154
  ### Call Logs
154
155
 
@@ -170,6 +171,8 @@ first_call.called_from #=> "file_name.rb:line_number"
170
171
 
171
172
  ### MiniTest
172
173
 
174
+ In `test_helper.rb`
175
+
173
176
  ```ruby
174
177
  require "spy"
175
178
  MiniTest::TestCase.add_teardown_hook { Spy.teardown }
@@ -177,7 +180,7 @@ MiniTest::TestCase.add_teardown_hook { Spy.teardown }
177
180
 
178
181
  ### Rspec
179
182
 
180
- In spec\_helper.rb
183
+ In `spec_helper.rb`
181
184
 
182
185
  ```ruby
183
186
  require "rspec/autorun"
@@ -6,17 +6,23 @@ module Spy
6
6
  # @!attribute [r] method_name
7
7
  # @return [Symbol] the name of the method that is being watched
8
8
  #
9
+ # @!attribute [r] singleton_method
10
+ # @return [Boolean] if the spied method is a singleton_method or not
11
+ #
9
12
  # @!attribute [r] calls
10
13
  # @return [Array<CallLog>] the messages that have been sent to the method
11
14
  #
12
15
  # @!attribute [r] original_method
13
16
  # @return [Method] the original method that was hooked if it existed
14
17
  #
18
+ # @!attribute [r] original_method_visibility
19
+ # @return [Method] the original visibility of the method that was hooked if it existed
20
+ #
15
21
  # @!attribute [r] hook_opts
16
22
  # @return [Hash] the options that were sent when it was hooked
17
23
 
18
24
 
19
- attr_reader :base_object, :method_name, :calls, :original_method, :hook_opts
25
+ attr_reader :base_object, :method_name, :singleton_method, :calls, :original_method, :original_method_visibility, :hook_opts
20
26
 
21
27
  # set what object and method the spy should watch
22
28
  # @param object
@@ -34,16 +40,18 @@ module Spy
34
40
  # @option opts [Symbol<:public, :protected, :private>] visibility overrides visibility with whatever method is given
35
41
  # @return [self]
36
42
  def hook(opts = {})
37
- @hook_opts = opts
38
43
  raise "#{base_object} method '#{method_name}' has already been hooked" if hooked?
39
44
 
40
- hook_opts[:force] ||= base_object.is_a?(Double)
45
+ @hook_opts = opts
46
+ @original_method_visibility = method_visibility_of(method_name)
41
47
  hook_opts[:visibility] ||= original_method_visibility
48
+ hook_opts[:force] ||= base_object.is_a?(Double)
42
49
 
43
50
  if original_method_visibility || !hook_opts[:force]
44
51
  @original_method = current_method
45
52
  end
46
53
 
54
+ define_method_with = singleton_method ? :define_singleton_method : :define_method
47
55
  base_object.send(define_method_with, method_name, override_method)
48
56
 
49
57
  if [:public, :protected, :private].include? hook_opts[:visibility]
@@ -61,11 +69,12 @@ module Spy
61
69
  raise "'#{method_name}' method has not been hooked" unless hooked?
62
70
 
63
71
  if original_method && method_owner == original_method.owner
64
- original_method.owner.send(:define_method, method_name, original_method)
65
- original_method.owner.send(original_method_visibility, method_name) if original_method_visibility
72
+ method_owner.send(:define_method, method_name, original_method)
73
+ method_owner.send(original_method_visibility, method_name) if original_method_visibility
66
74
  else
67
75
  method_owner.send(:remove_method, method_name)
68
76
  end
77
+
69
78
  clear_method!
70
79
  Agency.instance.retire(self)
71
80
  self
@@ -74,7 +83,7 @@ module Spy
74
83
  # is the spy hooked?
75
84
  # @return [Boolean]
76
85
  def hooked?
77
- self == self.class.get(base_object, method_name, @singleton_method)
86
+ self == self.class.get(base_object, method_name, singleton_method)
78
87
  end
79
88
 
80
89
  # @overload and_return(value)
@@ -82,10 +91,29 @@ module Spy
82
91
  #
83
92
  # Tells the spy to return a value when the method is called.
84
93
  #
94
+ # If a block is sent it will execute the block when the method is called.
95
+ # The airty of the block will be checked against the original method when
96
+ # you first call `and_return` and when the method is called.
97
+ #
98
+ # If you want to disable the arity checking just pass `{force: true}` to the
99
+ # value
100
+ #
101
+ # @example
102
+ # spy.and_return(true)
103
+ # spy.and_return { true }
104
+ # spy.and_return(force: true) { |invalid_arity| true }
105
+ #
85
106
  # @return [self]
86
107
  def and_return(value = nil)
87
- raise ArgumentError.new("value and block conflict. Choose one") if !(value.nil? || value.is_a?(Hash) && value.has_key?(:force)) && block_given?
108
+ @do_not_check_plan_arity = false
109
+
88
110
  if block_given?
111
+ if value.is_a?(Hash) && value.has_key?(:force)
112
+ @do_not_check_plan_arity = !!value[:force]
113
+ elsif !value.nil?
114
+ raise ArgumentError.new("value and block conflict. Choose one")
115
+ end
116
+
89
117
  @plan = Proc.new
90
118
  check_for_too_many_arguments!(@plan)
91
119
  else
@@ -192,6 +220,9 @@ module Spy
192
220
 
193
221
  private
194
222
 
223
+ # this returns a lambda that calls the spy object.
224
+ # we use eval to set the spy object id as a parameter so it can be extracted
225
+ # and looked up later using `Method#parameters`
195
226
  def override_method
196
227
  eval <<-METHOD, binding, __FILE__, __LINE__ + 1
197
228
  __method_spy__ = self
@@ -202,16 +233,12 @@ module Spy
202
233
  end
203
234
 
204
235
  def clear_method!
205
- @hooked = false
236
+ @hooked = @do_not_check_plan_arity = false
206
237
  @hook_opts = @original_method = @arity_range = @original_method_visibility = @method_owner= nil
207
238
  end
208
239
 
209
- def original_method_visibility
210
- @original_method_visibility ||= method_visibility_of(method_name)
211
- end
212
-
213
240
  def method_visibility_of(method_name, all = true)
214
- if @singleton_method
241
+ if singleton_method
215
242
  if base_object.public_methods(all).include?(method_name)
216
243
  :public
217
244
  elsif base_object.protected_methods(all).include?(method_name)
@@ -230,10 +257,6 @@ module Spy
230
257
  end
231
258
  end
232
259
 
233
- def define_method_with
234
- @singleton_method ? :define_singleton_method : :define_method
235
- end
236
-
237
260
  def check_arity!(arity)
238
261
  return unless arity_range
239
262
  if arity < arity_range.min
@@ -241,10 +264,11 @@ module Spy
241
264
  elsif arity > arity_range.max
242
265
  raise ArgumentError.new("wrong number of arguments (#{arity} for #{arity_range.max})")
243
266
  end
267
+ true
244
268
  end
245
269
 
246
270
  def check_for_too_many_arguments!(block)
247
- return unless arity_range
271
+ return if @do_not_check_plan_arity || arity_range.nil?
248
272
  min_arity = block.arity
249
273
  min_arity = min_arity.abs - 1 if min_arity < 0
250
274
 
@@ -273,7 +297,7 @@ module Spy
273
297
  end
274
298
 
275
299
  def current_method
276
- @singleton_method ? base_object.method(method_name) : base_object.instance_method(method_name)
300
+ singleton_method ? base_object.method(method_name) : base_object.instance_method(method_name)
277
301
  end
278
302
 
279
303
  def method_owner
data/lib/spy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spy
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -107,6 +107,9 @@ module Spy
107
107
  write_spy.and_return do |string, b|
108
108
  end
109
109
  end
110
+
111
+ write_spy.and_return(force: true) do |string, b|
112
+ end
110
113
  end
111
114
 
112
115
  def test_spy_and_return_can_call_a_block_that_recieves_a_block
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: