Class: Spy

Inherits:
Object
  • Object
show all
Defined in:
lib/spy.rb,
lib/spy/version.rb

Defined Under Namespace

Classes: Call

Constant Summary collapse

THREAD_LOCAL_ACTIVE_SPIES_KEY =
'ruby_spy_active_spies'.freeze
VERSION =
'0.1.0'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(obj, all_instances: false) ⇒ Spy

Returns a new instance of Spy


90
91
92
93
94
95
# File 'lib/spy.rb', line 90

def initialize(obj, all_instances: false)
  @obj = obj
  @calls = []
  @all_instances = all_instances
  @spied_methods_map = {}
end

Instance Attribute Details

#objObject (readonly)

Returns the instance, class, or module being spied on by this spy

Returns:

  • (Object)

    the instance, class, or module being spied on by this spy


88
89
90
# File 'lib/spy.rb', line 88

def obj
  @obj
end

Class Method Details

.active_spiesArray<Spy>

Active spies in the current thread.

Returns:

  • (Array<Spy>)

    instances of Spy that have active method spies on their target obj


15
16
17
# File 'lib/spy.rb', line 15

def self.active_spies
  Thread.current[THREAD_LOCAL_ACTIVE_SPIES_KEY] ||= []
end

.clean { ... } ⇒ Object

Remove all active Spy instances in the current thread and restore their spied methods to the original state.

Yields:

  • Optionally provide a block, and spies created within the block will be cleaned, leaving the outer scope untouched. Blocks can be nested.


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/spy.rb', line 71

def self.clean
  if block_given?
    outer_active_spies = active_spies
    Thread.current[THREAD_LOCAL_ACTIVE_SPIES_KEY] = nil

    begin
      yield
    ensure
      clean
      Thread.current[THREAD_LOCAL_ACTIVE_SPIES_KEY] = outer_active_spies
    end
  else
    active_spies.dup.each(&:clean)
  end
end

.on(obj, method_name = nil) ⇒ Spy

Spy on an instance, class, or module.

By default, this will spy on all user-defined methods (not methods defined on Ruby's base Class).

Parameters:

  • obj (Object)

    the instance, class, or module to spy on

  • method_name (Symbol) (defaults to: nil)

    optionally limit spying to a single method

Returns:

  • (Spy)

    an active instance of Spy for the given obj


38
39
40
# File 'lib/spy.rb', line 38

def self.on(obj, method_name = nil)
  spy_with_options(obj, method_name)
end

.on_all_instances_of(obj, method_name = nil) ⇒ Spy

Spy on all instances of a class.

By default, this will spy on all user-defined methods (not methods defined on Ruby's base Class).

Parameters:

  • obj (Object)

    the class to spy on

  • method_name (Symbol) (defaults to: nil)

    optionally limit spying to a single method

Returns:

  • (Spy)

    an active instance of Spy for the given obj


53
54
55
# File 'lib/spy.rb', line 53

def self.on_all_instances_of(obj, method_name = nil)
  spy_with_options(obj, method_name, all_instances: true)
end

.register(spy) ⇒ Object


19
20
21
# File 'lib/spy.rb', line 19

def self.register(spy)
  active_spies << spy unless active_spies.include? spy
end

.spy_with_options(obj, method_name, options = {}) ⇒ Object


57
58
59
60
61
62
# File 'lib/spy.rb', line 57

def self.spy_with_options(obj, method_name, options = {})
  new(obj, options).tap do |spy|
    method_name ? spy.on(method_name) : spy.on_all
    Spy.register(spy)
  end
end

.unregister(spy) ⇒ Object


23
24
25
# File 'lib/spy.rb', line 23

def self.unregister(spy)
  active_spies.delete(spy)
end

Instance Method Details

#calls(method_name = nil) ⇒ Array<Call>

Information about the calls received by this spy

Parameters:

  • method_name (Symbol) (defaults to: nil)

    optionally filter results to the given method

Returns:

  • (Array<Call>)

    set of Call objects containing information about each method call since the spy was activated.


129
130
131
132
133
134
135
# File 'lib/spy.rb', line 129

def calls(method_name = nil)
  if method_name
    @calls.select { |c| c.method_name == method_name }
  else
    @calls
  end
end

#cleanSpy

Remove spy and return spied methods on obj to the original state.

Returns:

  • (Spy)

    self


142
143
144
145
146
# File 'lib/spy.rb', line 142

def clean
  spied_methods_map.keys.each { |m| remove_spy(m) }
  Spy.unregister(self)
  self
end

#dirty?Boolean

Check if spy is actively spying on any methods on obj

Returns:

  • (Boolean)

    dirty state


153
154
155
# File 'lib/spy.rb', line 153

def dirty?
  spied_methods_map.keys.any?
end

#on(method_name) ⇒ Spy

Spy on a single method on obj

Parameters:

  • method_name (Symbol)

    the method to spy on

Returns:

  • (Spy)

    self


115
116
117
118
119
# File 'lib/spy.rb', line 115

def on(method_name)
  spy_on(method_name)
  Spy.register(self)
  self
end

#on_allSpy

Spy on all user-defined methods on obj

Returns:

  • (Spy)

    self


102
103
104
105
106
# File 'lib/spy.rb', line 102

def on_all
  all_methods.each { |m| spy_on(m) }
  Spy.register(self)
  self
end