spy_rb 3.3.0 → 4.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c273641c4e1545c5b41bca19c98f20a5bc1fcac
4
- data.tar.gz: 766f1883942aa7fbee3ccc76815b2c21a2d5fba0
3
+ metadata.gz: 53c61d7800f4dfd159fc081d34ad780b225cc5b1
4
+ data.tar.gz: 68c92a4fbadef7bf61acfcb1aa84d5405e16e41a
5
5
  SHA512:
6
- metadata.gz: 0df0382a994eae8fe1d650713c7239d4daf0d2c9545eae5636c58bd07c249f744726978922cabe21cc42202070137f8c05046b44f8d15821d0d86345c175694b
7
- data.tar.gz: 67000b046fa2d28f5159d9f8c13e48625f05fa620007367a979e9c1cbd4eb730d7f098f081fe60b7c06f93caf3769f95f1e01d09b4bcf6276e4cc899c35fd5de
6
+ metadata.gz: ccd3238f674765a9d9ee84f5fad2fd66f9c336118f8bf1a5698b927a719bec36257da6c4792522a5ae917faf75e7c5f2c7bb03ee3d98246ac58043ac2c91740f
7
+ data.tar.gz: '0984f22b7e030d699c503319e18701931398632255c6ddc487e0e285fe6c3949b1d08e45327e476dbcc0aa05a204822f65a554bf0081df2a915f857b93e07fde'
@@ -87,8 +87,69 @@ module Spy
87
87
  @call_history.any?
88
88
  end
89
89
 
90
+ # @private
91
+ def call_original(*args)
92
+ if original.is_a?(UnboundMethod)
93
+ call_original_unbound_method(*args)
94
+ else
95
+ call_original_method(*args)
96
+ end
97
+ end
98
+
99
+ # @private
100
+ def apply(method_call)
101
+ return method_call.call_original unless passes_all_conditions?(method_call)
102
+
103
+ run_before_callbacks(method_call)
104
+
105
+ result = nil
106
+ runner =
107
+ if @internal[:instead]
108
+ proc do
109
+ result = @internal[:instead].call(method_call)
110
+ end
111
+ else
112
+ proc do
113
+ @call_history << method_call
114
+ result = method_call.call_original(true)
115
+ end
116
+ end
117
+
118
+ if @internal[:around_procs].any?
119
+ runner = @internal[:around_procs].reduce(runner) do |p, wrapper|
120
+ proc { wrapper[method_call, &p] }
121
+ end
122
+ end
123
+
124
+ runner.call
125
+
126
+ run_after_callbacks(method_call)
127
+
128
+ result
129
+ end
130
+
90
131
  private
91
132
 
133
+ def passes_all_conditions?(method_call)
134
+ @internal[:conditional_filters].all? { |f| f[method_call] }
135
+ end
136
+
137
+ def run_before_callbacks(method_call)
138
+ @internal[:before_callbacks].each { |f| f[method_call] }
139
+ end
140
+
141
+ def run_after_callbacks(method_call)
142
+ @internal[:after_callbacks].each { |f| f[method_call] }
143
+ end
144
+
145
+ def call_original_unbound_method(receiver, args, block)
146
+ original.bind(receiver).call(*args, &block)
147
+ end
148
+
149
+ def call_original_method(_receiver, args, block)
150
+ original.call(*args, &block)
151
+ end
152
+
92
153
  def choose_strategy(blueprint)
93
154
  if blueprint.type == :dynamic_delegation
94
155
  Strategy::Intercept.new(self)
@@ -1,21 +1,26 @@
1
1
  module Spy
2
2
  class MethodCall
3
- attr_reader :name, :receiver, :caller, :args, :block
4
- attr_accessor :result
3
+ attr_reader :receiver, :backtrace, :args, :block, :result
5
4
 
6
- def initialize(replayer, name, receiver, method_caller, *args)
7
- @replayer = replayer
8
- @name = name
5
+ def initialize(spy, receiver, args, block, backtrace)
6
+ @spy = spy
9
7
  @receiver = receiver
10
- @args = args
11
- @caller = method_caller
12
- @block = Proc.new if block_given?
8
+ @args = args
9
+ @block = block
10
+ @backtrace = backtrace
13
11
  end
14
12
 
15
- def replay
16
- @replayer.call
13
+ def name
14
+ @spy.original.name
17
15
  end
18
16
 
19
- alias call_original replay
17
+ def call_original(persist_result = false)
18
+ result = @spy.call_original(@receiver, @args, @block)
19
+ @result = result if persist_result
20
+ result
21
+ end
22
+
23
+ alias replay call_original
24
+ alias caller backtrace
20
25
  end
21
26
  end
@@ -0,0 +1,28 @@
1
+ require 'spy/method_call'
2
+
3
+ module Spy
4
+ module ReplaceMethod
5
+ def self.call(klass, spy, mode: nil, remove_existing: false)
6
+ klass.class_eval do
7
+ name = spy.original.name
8
+
9
+ remove_method(name) if remove_existing
10
+
11
+ case mode
12
+ when :stub
13
+ define_method(name, ReplaceMethod.impl(spy))
14
+ when :restore
15
+ define_method(name, spy.original)
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.impl(spy)
21
+ proc do |*args, &block|
22
+ backtrace = caller.drop_while { |path| path =~ /lib\/spy\/replace_method\.rb$/ }
23
+ method_call = MethodCall.new(spy, self, args, block, backtrace)
24
+ spy.apply(method_call)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,4 @@
1
- require 'spy/determine_visibility'
2
- require 'spy/strategy/base'
1
+ require 'spy/replace_method'
3
2
 
4
3
  module Spy
5
4
  module Strategy
@@ -18,26 +17,11 @@ module Spy
18
17
  end
19
18
 
20
19
  def apply
21
- spy = @spy
22
- @target.class_eval do
23
- # Add the spy to the intercept target
24
- define_method spy.original.name do |*args, &block|
25
- Spy::Strategy::Base.call(spy, self, *args, &block)
26
- end
27
-
28
- # Make the visibility of the spy match the spied original
29
- unless spy.original.is_a?(FakeMethod)
30
- visibility = DetermineVisibility.call(spy.original)
31
- send(visibility, spy.original.name)
32
- end
33
- end
20
+ ReplaceMethod.call(@target, @spy, mode: :stub)
34
21
  end
35
22
 
36
23
  def undo
37
- spy = @spy
38
- @target.class_eval do
39
- remove_method spy.original.name
40
- end
24
+ ReplaceMethod.call(@target, @spy, remove_existing: true)
41
25
  end
42
26
  end
43
27
  end
@@ -1,38 +1,18 @@
1
- require 'spy/determine_visibility'
2
- require 'spy/strategy/base'
1
+ require 'spy/replace_method'
3
2
 
4
3
  module Spy
5
4
  module Strategy
6
5
  class Wrap
7
6
  def initialize(spy)
8
7
  @spy = spy
9
- @visibility = DetermineVisibility.call(spy.original)
10
8
  end
11
9
 
12
10
  def apply
13
- spy = @spy
14
- visibility = @visibility
15
- @spy.original.owner.class_eval do
16
- undef_method spy.original.name
17
-
18
- # Replace the method with the spy
19
- define_method spy.original.name do |*args, &block|
20
- Spy::Strategy::Base.call(spy, self, *args, &block)
21
- end
22
-
23
- # Make the visibility of the spy match the spied original
24
- send(visibility, spy.original.name)
25
- end
11
+ ReplaceMethod.call(@spy.original.owner, @spy, mode: :stub)
26
12
  end
27
13
 
28
14
  def undo
29
- spy = @spy
30
- visibility = @visibility
31
- spy.original.owner.class_eval do
32
- remove_method spy.original.name
33
- define_method spy.original.name, spy.original
34
- send(visibility, spy.original.name)
35
- end
15
+ ReplaceMethod.call(@spy.original.owner, @spy, mode: :restore)
36
16
  end
37
17
  end
38
18
  end
@@ -1,3 +1,3 @@
1
1
  module Spy
2
- VERSION = '3.3.0'
2
+ VERSION = '4.0.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spy_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Bodah
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-28 00:00:00.000000000 Z
11
+ date: 2019-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -51,14 +51,13 @@ files:
51
51
  - lib/spy/api.rb
52
52
  - lib/spy/blueprint.rb
53
53
  - lib/spy/core.rb
54
- - lib/spy/determine_visibility.rb
55
54
  - lib/spy/errors.rb
56
55
  - lib/spy/fake_method.rb
57
56
  - lib/spy/instance.rb
58
57
  - lib/spy/method_call.rb
59
58
  - lib/spy/multi.rb
60
59
  - lib/spy/registry.rb
61
- - lib/spy/strategy/base.rb
60
+ - lib/spy/replace_method.rb
62
61
  - lib/spy/strategy/intercept.rb
63
62
  - lib/spy/strategy/wrap.rb
64
63
  - lib/spy/version.rb
@@ -1,16 +0,0 @@
1
- module Spy
2
- module DetermineVisibility
3
- # @param [Method, UnboundMethod] method
4
- # @returns [Symbol] whether the method is public, private, or protected
5
- def self.call(method)
6
- owner = method.owner
7
- %w(public private protected).each do |vis|
8
- query = "#{vis}_method_defined?"
9
- if owner.respond_to?(query) && owner.send(query, method.name)
10
- return vis
11
- end
12
- end
13
- raise NoMethodError, "couldn't find method #{method.name} belonging to #{owner}"
14
- end
15
- end
16
- end
@@ -1,89 +0,0 @@
1
- require 'spy/method_call'
2
-
3
- module Spy
4
- module Strategy
5
- module Base
6
- class << self
7
- def call(spy, receiver, *args, &block)
8
- spy.instance_eval do
9
- # TODO - abstract the method call into an object and cache this in
10
- # method using an instance variable instead of a local variable.
11
- # This will let us be a bit more elegant about how we do before/after
12
- # callbacks. We can also merge MethodCall with this responsibility so
13
- # it isn't just a data struct
14
- is_active = if @internal[:conditional_filters].any?
15
- mc = Spy::Strategy::Base._build_method_call(spy, receiver, *args, &block)
16
- @internal[:conditional_filters].all? { |f| f.call(mc) }
17
- else
18
- true
19
- end
20
-
21
- return Spy::Strategy::Base._call_original(spy, receiver, *args, &block) unless is_active
22
-
23
- if @internal[:before_callbacks].any?
24
- mc = Spy::Strategy::Base._build_method_call(spy, receiver, *args, &block)
25
- @internal[:before_callbacks].each { |f| f.call(mc) }
26
- end
27
-
28
- if @internal[:around_procs].any?
29
- mc = Spy::Strategy::Base._build_method_call(spy, receiver, *args, &block)
30
-
31
- # Procify the original call
32
- # Still return the result from it
33
- result = nil
34
- original_proc = proc do
35
- result = Spy::Strategy::Base._call_and_record(spy, receiver, args, { :record => mc }, &block)
36
- end
37
-
38
- # Keep wrapping the original proc with each around_proc
39
- @internal[:around_procs].reduce(original_proc) do |p, wrapper|
40
- proc { wrapper.call(mc, &p) }
41
- end.call
42
- else
43
- result = Spy::Strategy::Base._call_and_record(spy, receiver, args, &block)
44
- end
45
-
46
- if @internal[:after_callbacks].any?
47
- mc = @call_history.last
48
- @internal[:after_callbacks].each { |f| f.call(mc) }
49
- end
50
-
51
- result
52
- end
53
- end
54
-
55
- def _build_method_call(spy, receiver, *args, &block)
56
- Spy::MethodCall.new(
57
- proc { Spy::Strategy::Base._call_original(spy, receiver, *args, &block) },
58
- spy.original.name,
59
- receiver,
60
- caller.drop_while { |path| path =~ /lib\/spy\/strategy/ },
61
- *args,
62
- &block)
63
- end
64
-
65
- def _call_and_record(spy, receiver, args, opts = {}, &block)
66
- spy.instance_eval do
67
- if @internal[:instead]
68
- @internal[:instead].call(Spy::Strategy::Base._build_method_call(spy, receiver, *args, &block))
69
- else
70
- record = opts[:record] || Spy::Strategy::Base._build_method_call(spy, receiver, *args, &block)
71
- @call_history << record
72
-
73
- result = Spy::Strategy::Base._call_original(spy, receiver, *args, &block)
74
- record.result = result
75
- end
76
- end
77
- end
78
-
79
- def _call_original(spy, receiver, *args, &block)
80
- if spy.original.is_a?(UnboundMethod)
81
- spy.original.bind(receiver).call(*args, &block)
82
- else
83
- spy.original.call(*args, &block)
84
- end
85
- end
86
- end
87
- end
88
- end
89
- end