spy_rb 0.3.0 → 0.4.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 +4 -4
- data/lib/spy/collection/entry.rb +4 -25
- data/lib/spy/collection/store.rb +32 -0
- data/lib/spy/collection.rb +25 -35
- data/lib/spy/core.rb +12 -7
- data/lib/spy/errors.rb +3 -2
- data/lib/spy/instance/api/internal.rb +41 -31
- data/lib/spy/instance.rb +4 -0
- data/lib/spy/method_call.rb +13 -3
- data/lib/spy/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96ccd939b58cbad0c0ab01af781177fb2d974e14
|
4
|
+
data.tar.gz: 352f27f11583dd07e856ce6522cd89dbe8d4d0bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f459c3813f339b9f5c7a6a59ba934e1f47f68b1a25a1ec609550e435190415827b6407858a5865bb120d17d7e221d9477b4ff67c1d965dedb573837fcb35ba95
|
7
|
+
data.tar.gz: 50c0d68b2043f6f91646f983c24ac4acabdd8baab4d97e71a7f1dbeade9c56a9677274fa29025351536686f2e106f47474cb046bc5480a07871ff269df7f1bcd
|
data/lib/spy/collection/entry.rb
CHANGED
@@ -1,36 +1,15 @@
|
|
1
1
|
module Spy
|
2
2
|
class Collection
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# Abstraction to isolate domain logic
|
4
|
+
class Entry < Struct.new(:spied, :method, :spy)
|
6
5
|
def key
|
7
|
-
|
6
|
+
receiver = method.is_a?(Method) ? method.receiver : nil
|
7
|
+
"#{receiver.object_id}|#{method.name}|#{method.class}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def ==(other)
|
11
11
|
key == other.key
|
12
12
|
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def parse(key)
|
16
|
-
new *parse_key(key)
|
17
|
-
end
|
18
|
-
|
19
|
-
def parse_key(key)
|
20
|
-
parts = key.split('|')
|
21
|
-
parts[0] = find_object(parts[0].to_i)
|
22
|
-
parts[1] = parts[1].to_sym
|
23
|
-
parts[2] = parts[2].to_sym
|
24
|
-
parts
|
25
|
-
end
|
26
|
-
|
27
|
-
# Looks up an object in the global ObjectSpace
|
28
|
-
def find_object(object_id)
|
29
|
-
ObjectSpace._id2ref(object_id)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
extend ClassMethods
|
34
13
|
end
|
35
14
|
end
|
36
15
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Spy
|
2
|
+
class Collection
|
3
|
+
# Works with Entry abstractions to allow the
|
4
|
+
# store data structure to be easily swapped
|
5
|
+
class Store
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@internal = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert(entry)
|
13
|
+
@internal[entry.key] = entry
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(entry)
|
17
|
+
@internal.delete(entry.key)
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
e = Enumerator.new do |y|
|
22
|
+
@internal.values.each {|v| y << v}
|
23
|
+
end
|
24
|
+
block_given? ? e.each(&Proc.new) : e
|
25
|
+
end
|
26
|
+
|
27
|
+
def empty?
|
28
|
+
none?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/spy/collection.rb
CHANGED
@@ -1,52 +1,42 @@
|
|
1
|
+
require 'spy/errors'
|
2
|
+
require 'spy/collection/store'
|
1
3
|
require 'spy/collection/entry'
|
2
4
|
|
3
5
|
module Spy
|
6
|
+
# Responsible for error handling and mapping to Entry
|
4
7
|
class Collection
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def insert(entry)
|
12
|
-
raise Errors::AlreadySpiedError if include?(entry)
|
13
|
-
@store[entry.key] = entry
|
8
|
+
def insert(spied, method, spy)
|
9
|
+
entry = Entry.new(spied, method, spy)
|
10
|
+
if store.include? entry
|
11
|
+
raise Errors::AlreadySpiedError
|
12
|
+
end
|
13
|
+
store.insert(entry)
|
14
14
|
end
|
15
15
|
|
16
|
-
def remove(
|
17
|
-
|
18
|
-
|
16
|
+
def remove(spied, method)
|
17
|
+
entry = Entry.new(spied, method, nil)
|
18
|
+
if !store.include? entry
|
19
|
+
raise Errors::MethodNotSpiedError
|
20
|
+
end
|
21
|
+
store.remove(entry).spy
|
19
22
|
end
|
20
23
|
|
21
|
-
# Removes each element from the collection and calls the block
|
22
|
-
# with each deleted element
|
23
24
|
def remove_all
|
24
|
-
map {|e| yield remove(e)}
|
25
|
+
store.map {|e| yield remove(e.spied, e.method)}
|
26
|
+
if !store.empty?
|
27
|
+
raise Errors::UnableToEmptySpyCollectionError
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
28
|
-
|
31
|
+
def include?(spied, method)
|
32
|
+
entry = Entry.new(spied, method)
|
33
|
+
store.include? entry
|
29
34
|
end
|
30
35
|
|
31
|
-
|
32
|
-
module SpyHelper
|
33
|
-
def <<(spy)
|
34
|
-
receiver = spy.original.is_a?(Method) ? spy.original.receiver : nil
|
35
|
-
name = spy.original.name
|
36
|
-
klass = spy.original.class
|
37
|
-
entry = Collection::Entry.new(receiver, name, klass)
|
38
|
-
entry.value = spy
|
39
|
-
insert entry
|
40
|
-
end
|
36
|
+
private
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
name = method.name
|
45
|
-
klass = method.class
|
46
|
-
remove Collection::Entry.new(receiver, name, klass)
|
47
|
-
end
|
38
|
+
def store
|
39
|
+
@store ||= Store.new
|
48
40
|
end
|
49
|
-
|
50
|
-
include SpyHelper
|
51
41
|
end
|
52
42
|
end
|
data/lib/spy/core.rb
CHANGED
@@ -4,23 +4,28 @@ require 'spy/errors'
|
|
4
4
|
|
5
5
|
module Spy
|
6
6
|
class Core
|
7
|
-
def spy_collection
|
8
|
-
@spy_collection ||= Collection.new
|
9
|
-
end
|
10
|
-
|
11
7
|
def add_spy(spied, method)
|
8
|
+
if collection.include?(spied, method)
|
9
|
+
raise Errors::AlreadySpiedError
|
10
|
+
end
|
12
11
|
spy = Instance.new(spied, method)
|
13
|
-
|
12
|
+
collection.insert(spied, method, spy)
|
14
13
|
spy.start
|
15
14
|
end
|
16
15
|
|
17
16
|
def remove_spy(spied, method)
|
18
|
-
spy =
|
17
|
+
spy = collection.remove(spied, method)
|
19
18
|
spy.stop
|
20
19
|
end
|
21
20
|
|
22
21
|
def remove_all_spies
|
23
|
-
|
22
|
+
collection.remove_all { |spy| spy.stop }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def collection
|
28
|
+
@collection ||= Collection.new
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
data/lib/spy/errors.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Spy
|
2
2
|
module Errors
|
3
|
-
MethodNotSpiedError
|
4
|
-
AlreadySpiedError
|
3
|
+
MethodNotSpiedError = Class.new(StandardError)
|
4
|
+
AlreadySpiedError = Class.new(StandardError)
|
5
|
+
UnableToEmptySpyCollection = Class.new(StandardError)
|
5
6
|
end
|
6
7
|
end
|
@@ -11,62 +11,72 @@ module Spy
|
|
11
11
|
def attach_to(target)
|
12
12
|
spy = self
|
13
13
|
target.class_eval do
|
14
|
-
define_method spy.original.name do |*args|
|
15
|
-
spy.call(self, *args)
|
14
|
+
define_method spy.original.name do |*args, &block|
|
15
|
+
spy.call(self, *args, &block)
|
16
16
|
end
|
17
17
|
send(spy.visibility, spy.original.name)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
# Call the spied method using the given
|
21
|
+
# Call the spied method using the given receiver and arguments.
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# receiver is required to allow calling of UnboundMethods such as
|
24
24
|
# instance methods defined on a Class
|
25
|
-
def call(
|
25
|
+
def call(receiver, *args, &block)
|
26
|
+
# TODO - abstract the method call into an object and cache this in
|
27
|
+
# method using an instance variable instead of a local variable.
|
28
|
+
# This will let us be a bit more elegant about how we do before/after
|
29
|
+
# callbacks. We can also merge MethodCall with this responsibility so
|
30
|
+
# it isn't just a data struct
|
26
31
|
is_active = @conditional_filters.all? {|f| f.call(*args)}
|
27
32
|
|
28
|
-
if is_active
|
33
|
+
if !is_active
|
34
|
+
call_original(receiver, *args, &block)
|
35
|
+
else
|
29
36
|
@before_callbacks.each {|f| f.call(*args)}
|
30
|
-
end
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
if @around_procs.any?
|
39
|
+
# Procify the original call
|
40
|
+
# Still return the result from it
|
41
|
+
result = nil
|
42
|
+
original_proc = Proc.new do
|
43
|
+
result = call_and_record(receiver, *args, &block)
|
44
|
+
end
|
39
45
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
record.result = result if is_active
|
48
|
-
end
|
46
|
+
# Keep wrapping the original proc with each around_proc
|
47
|
+
@around_procs.reduce(original_proc) do |p, wrapper|
|
48
|
+
Proc.new { wrapper.call receiver, *args, &p }
|
49
|
+
end.call
|
50
|
+
else
|
51
|
+
result = call_and_record(receiver, *args, &block)
|
52
|
+
end
|
49
53
|
|
50
|
-
if is_active
|
51
54
|
@after_callbacks.each {|f| f.call(*args)}
|
52
|
-
end
|
53
55
|
|
54
|
-
|
56
|
+
result
|
57
|
+
end
|
55
58
|
end
|
56
59
|
|
57
60
|
private
|
58
61
|
|
59
|
-
def
|
60
|
-
record =
|
62
|
+
def call_and_record(receiver, *args, &block)
|
63
|
+
record = track_call(receiver, *args, &block)
|
64
|
+
result = call_original(receiver, *args, &block)
|
65
|
+
record.result = result
|
66
|
+
end
|
67
|
+
|
68
|
+
def track_call(receiver, *args, &block)
|
69
|
+
replayer = proc { call_original(receiver, *args, &block) }
|
70
|
+
record = Spy::MethodCall.new(replayer, original.name, receiver, *args, &block)
|
61
71
|
@call_history << record
|
62
72
|
record
|
63
73
|
end
|
64
74
|
|
65
|
-
def call_original(
|
75
|
+
def call_original(receiver, *args, &block)
|
66
76
|
if original.is_a?(UnboundMethod)
|
67
|
-
original.bind(
|
77
|
+
original.bind(receiver).call(*args, &block)
|
68
78
|
else
|
69
|
-
original.call(*args)
|
79
|
+
original.call(*args, &block)
|
70
80
|
end
|
71
81
|
end
|
72
82
|
end
|
data/lib/spy/instance.rb
CHANGED
data/lib/spy/method_call.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
module Spy
|
2
2
|
class MethodCall
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :name, :receiver, :args, :block
|
4
4
|
attr_accessor :result
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@
|
6
|
+
def initialize(replayer, name, receiver, *args)
|
7
|
+
@replayer = replayer
|
8
|
+
@name = name
|
9
|
+
@receiver = receiver
|
8
10
|
@args = args
|
11
|
+
|
12
|
+
if block_given?
|
13
|
+
@block = proc { receiver.instance_eval &Proc.new }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def replay
|
18
|
+
@replayer.call
|
9
19
|
end
|
10
20
|
end
|
11
21
|
end
|
data/lib/spy/version.rb
CHANGED
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.
|
4
|
+
version: 0.4.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-
|
11
|
+
date: 2015-06-18 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
|
@@ -23,6 +23,7 @@ files:
|
|
23
23
|
- lib/spy/api.rb
|
24
24
|
- lib/spy/collection.rb
|
25
25
|
- lib/spy/collection/entry.rb
|
26
|
+
- lib/spy/collection/store.rb
|
26
27
|
- lib/spy/core.rb
|
27
28
|
- lib/spy/errors.rb
|
28
29
|
- lib/spy/instance.rb
|