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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a262963c1ed562ad509486821cf161a6240459b
4
- data.tar.gz: da505c8ea44f5e4259df12ee84400b9c80c8db35
3
+ metadata.gz: 4256e6a05208bf9e7467ac42d7f1c5393a44ce87
4
+ data.tar.gz: d4a27a39240558415d0b3fc10ca3a9124d765e8d
5
5
  SHA512:
6
- metadata.gz: 5e5602007b1865055cd1ffabe15e07083957b9efc1372755826691512ab34454522e7c517c44df1d86632bd4c4c6b23b1902df5c6dc585c5c036822767aa7f21
7
- data.tar.gz: 99c49688320313eeb31d515fbf60fc473bd821c2548b028a92c3cf9593d2c4ef8eade41d7bc75cc45aa96a1cbfdb1d6c5201c83acf1e901db5343d1337cdf9d5
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
@@ -0,0 +1,6 @@
1
+ module Spy
2
+ module Errors
3
+ MethodNotSpiedError = Class.new(StandardError)
4
+ AlreadySpiedError = Class.new(StandardError)
5
+ end
6
+ end
@@ -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
@@ -1,4 +1,4 @@
1
- require_relative 'spy/api'
1
+ require 'spy/api'
2
2
 
3
3
  module Spy
4
4
  extend API
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.2
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