spy_rb 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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