spy_rb 0.1.2 → 0.1.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.
- checksums.yaml +4 -4
- data/lib/spy/api.rb +53 -0
- data/lib/spy/collection/entry.rb +36 -0
- data/lib/spy/collection.rb +46 -0
- data/lib/spy/collection_entry.rb +34 -0
- data/lib/spy/core.rb +26 -0
- data/lib/spy/errors.rb +6 -0
- data/lib/spy/instance.rb +54 -0
- data/lib/spy.rb +1 -1
- metadata +8 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4256e6a05208bf9e7467ac42d7f1c5393a44ce87
|
4
|
+
data.tar.gz: d4a27a39240558415d0b3fc10ca3a9124d765e8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e658ea9847acbdcb43d54526b9da0c7f2d132448f6c6c4e05874d1ee49ae4bdbf518b9d878376141684d95679cedd589caa79a95d691e72af25573bc59930bd2
|
7
|
+
data.tar.gz: 62e3beeb79895b0ecb3de90af60864a0b687529fb4b99b2e33c7964d801824ab350f71a9e7fc4d7d581f14929afaae5d1d0aa10072870e6295d0338da699e519
|
data/lib/spy/api.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spy/core'
|
2
|
+
|
3
|
+
module Spy
|
4
|
+
module API
|
5
|
+
# Initializes a new spy instance for the method
|
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
|
+
return core.add_spy *(args << :method)
|
14
|
+
end
|
15
|
+
raise ArgumentError
|
16
|
+
end
|
17
|
+
|
18
|
+
# TODO docs
|
19
|
+
def on_any_instance(mod, msg)
|
20
|
+
core.add_spy(mod, msg, :instance_method)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Stops spying on the method and restores its original functionality
|
24
|
+
#
|
25
|
+
# @param args - supports multiple signatures
|
26
|
+
#
|
27
|
+
# Spy.restore(:all)
|
28
|
+
# => stops spying on every spied message
|
29
|
+
#
|
30
|
+
# Spy.restore(receiver, msg)
|
31
|
+
# => stops spying on the given receiver and message (assumes :method)
|
32
|
+
#
|
33
|
+
# Spy.restore(reciever, msg, method_type)
|
34
|
+
# => stops spying on the given receiver and message of method_type
|
35
|
+
def restore(*args)
|
36
|
+
case args.length
|
37
|
+
when 1
|
38
|
+
return core.remove_all_spies if args.first == :all
|
39
|
+
when 2
|
40
|
+
return core.remove_spy *(args << :method)
|
41
|
+
when 3
|
42
|
+
return core.remove_spy *args
|
43
|
+
end
|
44
|
+
raise ArgumentError
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def core
|
50
|
+
@core ||= Core.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Spy
|
2
|
+
class Collection
|
3
|
+
class Entry < Struct.new(:receiver, :msg, :method_type)
|
4
|
+
attr_accessor :value
|
5
|
+
|
6
|
+
def key
|
7
|
+
"#{receiver.object_id}|#{msg}|#{method_type}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
key == other.key
|
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
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spy/collection/entry'
|
2
|
+
|
3
|
+
module Spy
|
4
|
+
class Collection
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@store = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def insert(entry)
|
12
|
+
raise Errors::AlreadySpiedError if include?(entry)
|
13
|
+
@store[entry.key] = entry
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(entry)
|
17
|
+
raise Errors::MethodNotSpiedError unless include?(entry)
|
18
|
+
@store.delete(entry.key).value
|
19
|
+
end
|
20
|
+
|
21
|
+
# Removes each element from the collection and calls the block
|
22
|
+
# with each deleted element
|
23
|
+
def remove_all
|
24
|
+
map {|e| yield remove(e)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
@store.keys.each {|k| yield Entry.parse(k)}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add a slicker interface that abstracts away Collection::Entry
|
32
|
+
module SpyHelper
|
33
|
+
def <<(spy)
|
34
|
+
entry = Collection::Entry.new(spy.receiver, spy.msg, spy.method_type)
|
35
|
+
entry.value = spy
|
36
|
+
insert entry
|
37
|
+
end
|
38
|
+
|
39
|
+
def pop(receiver, msg, method_type)
|
40
|
+
remove Collection::Entry.new(receiver, msg, method_type)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
include SpyHelper
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Spy
|
2
|
+
class CollectionEntry < Struct.new(:receiver, :msg, :method_type)
|
3
|
+
attr_accessor :value
|
4
|
+
|
5
|
+
def key
|
6
|
+
"#{receiver.object_id}|#{msg}|#{method_type}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
key == other.key
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def parse(key)
|
15
|
+
new *parse_key(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse_key(key)
|
19
|
+
parts = key.split('|')
|
20
|
+
parts[0] = find_object(parts[0].to_i)
|
21
|
+
parts[1] = parts[1].to_sym
|
22
|
+
parts[2] = parts[2].to_sym
|
23
|
+
parts
|
24
|
+
end
|
25
|
+
|
26
|
+
# Looks up an object in the global ObjectSpace
|
27
|
+
def find_object(object_id)
|
28
|
+
ObjectSpace._id2ref(object_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
extend ClassMethods
|
33
|
+
end
|
34
|
+
end
|
data/lib/spy/core.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spy/instance'
|
2
|
+
require 'spy/collection'
|
3
|
+
require 'spy/errors'
|
4
|
+
|
5
|
+
module Spy
|
6
|
+
class Core
|
7
|
+
def spy_collection
|
8
|
+
@spy_collection ||= Collection.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_spy(receiver, msg, method_type)
|
12
|
+
spy = Instance.new(receiver, msg, method_type)
|
13
|
+
spy_collection << spy
|
14
|
+
spy.start
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_spy(receiver, msg, method_type)
|
18
|
+
spy = spy_collection.pop(receiver, msg, method_type)
|
19
|
+
spy.stop
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove_all_spies
|
23
|
+
spy_collection.remove_all { |spy| spy.stop }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/spy/errors.rb
ADDED
data/lib/spy/instance.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# An instance of a spied method
|
2
|
+
# - Holds a reference to the original method
|
3
|
+
# - Wraps the original method
|
4
|
+
# - Provides hooks for callbacks
|
5
|
+
module Spy
|
6
|
+
class Instance
|
7
|
+
attr_reader :receiver, :method_type, :msg, :original, :call_count
|
8
|
+
|
9
|
+
def initialize(receiver, msg, method_type)
|
10
|
+
@msg = msg
|
11
|
+
@receiver = receiver
|
12
|
+
@method_type = method_type
|
13
|
+
|
14
|
+
# Cache the original method for unwrapping later
|
15
|
+
@original = @receiver.send(method_type, msg)
|
16
|
+
@call_count = 0
|
17
|
+
@match_args = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
context = self
|
22
|
+
original.owner.instance_eval do
|
23
|
+
define_method context.msg do |*args|
|
24
|
+
if context.original.respond_to? :bind
|
25
|
+
result = context.original.bind(self).call(*args)
|
26
|
+
else
|
27
|
+
result = context.original.call(*args)
|
28
|
+
end
|
29
|
+
context.after_call(result, *args)
|
30
|
+
result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def stop
|
37
|
+
context = self
|
38
|
+
original.owner.instance_eval do
|
39
|
+
define_method context.msg, context.original
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def after_call(result, *args)
|
45
|
+
return unless @match_args.empty? || @match_args == args
|
46
|
+
@call_count += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def with_args(*args)
|
50
|
+
@match_args = args || []
|
51
|
+
self
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/spy.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spy_rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Bodah
|
@@ -20,6 +20,13 @@ extensions: []
|
|
20
20
|
extra_rdoc_files: []
|
21
21
|
files:
|
22
22
|
- lib/spy.rb
|
23
|
+
- lib/spy/api.rb
|
24
|
+
- lib/spy/collection.rb
|
25
|
+
- lib/spy/collection/entry.rb
|
26
|
+
- lib/spy/collection_entry.rb
|
27
|
+
- lib/spy/core.rb
|
28
|
+
- lib/spy/errors.rb
|
29
|
+
- lib/spy/instance.rb
|
23
30
|
homepage: https://github.com/jbodah/spy_rb
|
24
31
|
licenses:
|
25
32
|
- MIT
|