spy_rb 0.2.2 → 0.3.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: 62ad15e17abbe8f04e140b6b979a00405fcc9208
4
- data.tar.gz: 21ad45535f0a10322498d1f12e7adf1a31b23282
3
+ metadata.gz: 47b608d05670cc1e49baec91115ecfdf0e87feb6
4
+ data.tar.gz: a7519779804fa1ac600149a87bfed851cacca168
5
5
  SHA512:
6
- metadata.gz: f28be3353f3e99e509a9ecb1720d5a6eabd0b469900143089323acb1cc4c2a96db064ec90c360469dcc4d2f295d5e2411dbd824c30a1006c8b0a3539c78f421b
7
- data.tar.gz: 7e78f8d5a0a580a1aaaf99f1fef8a95f948c09ebe013c0faf0ac2e75624938a98d2e318d1a2ae869e5c2311072ac46bbb8542041dd9adbe82e8b6207b84789c0
6
+ metadata.gz: 8a3853df7bcb3ae2440b6585ea519d4466b7130ddb061a2d29db1ba88a9e31a410c1a22adb614782c337a035356d82d8350d9024aeabb1b588ae00c97cc04078
7
+ data.tar.gz: dc92ea6560aa6b5fbfbc260cdbc9b2cdb2acae4bc92b0662b56b65b40c34dd149c0a195cf18872f41f5a05284dce90eaf514ea9ca973979828ffecbd025e3f47
@@ -2,23 +2,22 @@ require 'spy/core'
2
2
 
3
3
  module Spy
4
4
  module API
5
- # Initializes a new spy instance for the method
5
+ # Spies on calls to a method made on an object
6
6
  #
7
- # With two args:
8
- # @param receiver - the receiver of the message you want to spy on
9
- # @param msg - the message passed to the receiver that you want to spy on
10
- def on(*args)
11
- case args.length
12
- when 2
13
- spied, msg = *args
14
- return core.add_spy(spied, spied.method(msg))
15
- end
16
- raise ArgumentError
7
+ # @param target - the object you want to spy on
8
+ # @param msg - the name of the method to spy on
9
+ def on(target, msg)
10
+ core.add_spy(target, target.method(msg))
17
11
  end
18
12
 
19
- # TODO docs
20
- def on_any_instance(spied, msg)
21
- core.add_spy(spied, spied.instance_method(msg))
13
+ # Spies on calls to a method made on any instance of some class or module
14
+ #
15
+ # @param target - class or module to spy on
16
+ # @param msg - name of the method to spy on
17
+ # @returns [Spy::Instance]
18
+ def on_any_instance(target, msg)
19
+ raise ArgumentError unless target.respond_to?(:instance_method)
20
+ core.add_spy(target, target.instance_method(msg))
22
21
  end
23
22
 
24
23
  # Stops spying on the method and restores its original functionality
@@ -36,15 +35,16 @@ module Spy
36
35
  def restore(*args)
37
36
  case args.length
38
37
  when 1
39
- return core.remove_all_spies if args.first == :all
38
+ core.remove_all_spies if args.first == :all
40
39
  when 2
41
- spied, msg = *args
42
- return core.remove_spy(spied, spied.method(msg))
40
+ target, msg = *args
41
+ core.remove_spy(target, target.method(msg))
43
42
  when 3
44
- spied, msg, method_type = *args
45
- return core.remove_spy(spied, spied.send(method_type, msg))
43
+ target, msg, method_type = *args
44
+ core.remove_spy(target, target.send(method_type, msg))
45
+ else
46
+ raise ArgumentError
46
47
  end
47
- raise ArgumentError
48
48
  end
49
49
 
50
50
  private
@@ -1,6 +1,5 @@
1
- require 'spy/callbacks/with_args'
2
- require 'spy/callbacks/when'
3
1
  require 'spy/instance/strategy'
2
+ require 'spy/instance/api/internal'
4
3
 
5
4
  # An instance of a spied method
6
5
  # - Holds a reference to the original method
@@ -8,17 +7,26 @@ require 'spy/instance/strategy'
8
7
  # - Provides hooks for callbacks
9
8
  module Spy
10
9
  class Instance
11
- attr_reader :original, :spied, :strategy, :call_count, :visibility
10
+ include API::Internal
11
+
12
+ attr_reader :original, :spied, :strategy, :visibility, :call_history
12
13
 
13
14
  def initialize(spied, original)
14
15
  @spied = spied
15
16
  @original = original
16
17
  @visibility = extract_visibility
17
- @before_filters = []
18
- @call_count = 0
18
+ @conditional_filters = []
19
+ @before_callbacks = []
20
+ @after_callbacks = []
21
+ @around_procs = []
22
+ @call_history = []
19
23
  @strategy = Strategy.factory_build(self)
20
24
  end
21
25
 
26
+ def call_count
27
+ @call_history.size
28
+ end
29
+
22
30
  def start
23
31
  @strategy.apply
24
32
  self
@@ -29,25 +37,30 @@ module Spy
29
37
  self
30
38
  end
31
39
 
32
- def before_call(*args)
33
- @call_count += 1 if @before_filters.all? {|f| f.before_call(*args)}
40
+ def when(&block)
41
+ @conditional_filters << block
42
+ self
34
43
  end
35
44
 
36
- def with_args(*args)
37
- add_before_filter Callbacks::WithArgs.new(*args)
45
+ # Expect block to yield. Call the rest of the chain
46
+ # when it does
47
+ def wrap(&block)
48
+ @around_procs << block
49
+ self
38
50
  end
39
51
 
40
- def when(&block)
41
- add_before_filter Callbacks::When.new(block)
52
+ def before(&block)
53
+ @before_callbacks << block
54
+ self
42
55
  end
43
56
 
44
- private
45
-
46
- def add_before_filter(filter)
47
- @before_filters << filter
57
+ def after(&block)
58
+ @after_callbacks << block
48
59
  self
49
60
  end
50
61
 
62
+ private
63
+
51
64
  def extract_visibility
52
65
  owner = @original.owner
53
66
  [:public, :protected, :private].each do |vis|
@@ -0,0 +1,75 @@
1
+ require 'spy/method_call'
2
+
3
+ module Spy
4
+ class Instance
5
+ module API
6
+ # The API we expose internally to our collaborators
7
+ module Internal
8
+ # TODO: Not sure if this is the best place for this
9
+ #
10
+ # Defines the spy on the target object
11
+ def attach_to(target)
12
+ spy = self
13
+ target.class_eval do
14
+ define_method spy.original.name do |*args|
15
+ spy.call(self, *args)
16
+ end
17
+ send(spy.visibility, spy.original.name)
18
+ end
19
+ end
20
+
21
+ # Call the spied method using the given context and arguments.
22
+ #
23
+ # Context is required for calling UnboundMethods such as
24
+ # instance methods defined on a Class
25
+ def call(context, *args)
26
+ is_active = @conditional_filters.all? {|f| f.call(*args)}
27
+
28
+ if is_active
29
+ @before_callbacks.each {|f| f.call(*args)}
30
+ end
31
+
32
+ if @around_procs.any?
33
+ # Procify the original call
34
+ original_proc = Proc.new do
35
+ record = track_call(context, *args) if is_active
36
+ result = call_original(context, *args)
37
+ record.result = result if is_active
38
+ end
39
+
40
+ # Keep wrapping the original proc with each around_proc
41
+ @around_procs.reduce(original_proc) do |p, wrapper|
42
+ Proc.new { wrapper.call context, *args, &p }
43
+ end.call
44
+ else
45
+ record = track_call(context, *args) if is_active
46
+ result = call_original(context, *args)
47
+ record.result = result if is_active
48
+ end
49
+
50
+ if is_active
51
+ @after_callbacks.each {|f| f.call(*args)}
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+ private
58
+
59
+ def track_call(context, *args)
60
+ record = Spy::MethodCall.new(context, *args)
61
+ @call_history << record
62
+ record
63
+ end
64
+
65
+ def call_original(context, *args)
66
+ if original.is_a?(UnboundMethod)
67
+ original.bind(context).call(*args)
68
+ else
69
+ original.call(*args)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -8,18 +8,7 @@ module Spy
8
8
  end
9
9
 
10
10
  def apply
11
- spy = @spy
12
- @intercept_target.class_eval do
13
- define_method spy.original.name do |*args|
14
- spy.before_call(*args)
15
- if spy.original.is_a?(UnboundMethod)
16
- spy.original.bind(self).call(*args)
17
- else
18
- spy.original.call(*args)
19
- end
20
- end
21
- send(spy.visibility, spy.original.name)
22
- end
11
+ @spy.attach_to(@intercept_target)
23
12
  end
24
13
 
25
14
  def undo
@@ -7,18 +7,7 @@ module Spy
7
7
  end
8
8
 
9
9
  def apply
10
- spy = @spy
11
- spy.original.owner.class_eval do
12
- define_method spy.original.name do |*args|
13
- spy.before_call(*args)
14
- if spy.original.is_a?(UnboundMethod)
15
- spy.original.bind(self).call(*args)
16
- else
17
- spy.original.call(*args)
18
- end
19
- end
20
- send(spy.visibility, spy.original.name)
21
- end
10
+ @spy.attach_to(@spy.original.owner)
22
11
  end
23
12
 
24
13
  def undo
@@ -0,0 +1,11 @@
1
+ module Spy
2
+ class MethodCall
3
+ attr_reader :context, :args
4
+ attr_accessor :result
5
+
6
+ def initialize(context, *args)
7
+ @context = context
8
+ @args = args
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Spy
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.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: 0.2.2
4
+ version: 0.3.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: 2015-02-27 00:00:00.000000000 Z
11
+ date: 2015-05-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Spy brings everything that's great about Sinon.JS to Ruby. Mocking frameworks
14
14
  work by stubbing out functionality. Spy works by listening in on functionality and
@@ -21,16 +21,16 @@ extra_rdoc_files: []
21
21
  files:
22
22
  - lib/spy.rb
23
23
  - lib/spy/api.rb
24
- - lib/spy/callbacks/when.rb
25
- - lib/spy/callbacks/with_args.rb
26
24
  - lib/spy/collection.rb
27
25
  - lib/spy/collection/entry.rb
28
26
  - lib/spy/core.rb
29
27
  - lib/spy/errors.rb
30
28
  - lib/spy/instance.rb
29
+ - lib/spy/instance/api/internal.rb
31
30
  - lib/spy/instance/strategy.rb
32
31
  - lib/spy/instance/strategy/intercept.rb
33
32
  - lib/spy/instance/strategy/wrap.rb
33
+ - lib/spy/method_call.rb
34
34
  - lib/spy/version.rb
35
35
  homepage: https://github.com/jbodah/spy_rb
36
36
  licenses:
@@ -1,13 +0,0 @@
1
- module Spy
2
- module Callbacks
3
- class When
4
- def initialize(filter)
5
- @filter = filter
6
- end
7
-
8
- def before_call(*args)
9
- @filter.call(*args)
10
- end
11
- end
12
- end
13
- end
@@ -1,14 +0,0 @@
1
- module Spy
2
- module Callbacks
3
- class WithArgs
4
- def initialize(*args)
5
- puts 'Spy::Callbacks::WithArgs is deprecated; use Spy::Callbacks::When instead'
6
- @match_args = args
7
- end
8
-
9
- def before_call(*args)
10
- @match_args == args
11
- end
12
- end
13
- end
14
- end