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