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 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