spy 0.2.3 → 0.2.4

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.
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: