Class: Smartest::SimpleStub

Inherits:
Object
  • Object
show all
Defined in:
lib/smartest/simple_stub.rb

Defined Under Namespace

Classes: AlreadyAppliedError, NotAppliedError

Constant Summary collapse

STORAGE_KEY =
:__smartest_simple_stub

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass, method_name, &implementation) ⇒ SimpleStub

Returns a new instance of SimpleStub.

Raises:

  • (ArgumentError)


115
116
117
118
119
120
121
122
# File 'lib/smartest/simple_stub.rb', line 115

def initialize(klass, method_name, &implementation)
  raise ArgumentError, "klass must be a Class. #{klass.class} specified." unless klass.is_a?(Class)
  raise ArgumentError, "method name must be a Symbol." unless method_name.is_a?(Symbol)

  @klass = klass
  @method_name = method_name
  @implementation = implementation
end

Class Method Details

.active_stubsObject



17
18
19
# File 'lib/smartest/simple_stub.rb', line 17

def active_stubs
  Thread.current[STORAGE_KEY] ||= {}
end

.call_implementation(receiver, implementation, args, kwargs, block) ⇒ Object



52
53
54
55
56
57
58
59
60
# File 'lib/smartest/simple_stub.rb', line 52

def call_implementation(receiver, implementation, args, kwargs, block)
  return call_implementation_with_block(receiver, implementation, args, kwargs, block) if block

  if kwargs.empty?
    receiver.instance_exec(*args, &implementation)
  else
    receiver.instance_exec(*args, **kwargs, &implementation)
  end
end

.call_implementation_with_block(receiver, implementation, args, kwargs, block) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/smartest/simple_stub.rb', line 62

def call_implementation_with_block(receiver, implementation, args, kwargs, block)
  method_name = next_call_method_name
  singleton_class = receiver.singleton_class
  singleton_class.__send__(:define_method, method_name, &implementation)

  if kwargs.empty?
    receiver.__send__(method_name, *args, &block)
  else
    receiver.__send__(method_name, *args, **kwargs, &block)
  end
ensure
  if singleton_class &&
      (singleton_class.method_defined?(method_name) || singleton_class.private_method_defined?(method_name))
    singleton_class.__send__(:remove_method, method_name)
  end
end

.clear_active_stubs_if_emptyObject



21
22
23
# File 'lib/smartest/simple_stub.rb', line 21

def clear_active_stubs_if_empty
  Thread.current[STORAGE_KEY] = nil if current_stubs&.empty?
end

.current_stubsObject



48
49
50
# File 'lib/smartest/simple_stub.rb', line 48

def current_stubs
  Thread.current[STORAGE_KEY]
end

.ensure_dispatcher_method(klass, klass_key, method_name) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/smartest/simple_stub.rb', line 25

def ensure_dispatcher_method(klass, klass_key, method_name)
  dispatcher_mutex.synchronize do
    mod = dispatcher_module_for(klass, klass_key)
    next if mod.method_defined?(method_name) || mod.private_method_defined?(method_name)

    mod.define_method(method_name) do |*args, **kwargs, &block|
      implementation = SimpleStub.implementation_for(klass_key, method_name)

      if implementation
        SimpleStub.call_implementation(self, implementation, args, kwargs, block)
      elsif kwargs.empty?
        super(*args, &block)
      else
        super(*args, **kwargs, &block)
      end
    end
  end
end

.implementation_for(klass_key, method_name) ⇒ Object



13
14
15
# File 'lib/smartest/simple_stub.rb', line 13

def implementation_for(klass_key, method_name)
  current_stubs&.fetch(stub_key(klass_key, method_name), nil)
end

.stub_key(klass_key, method_name) ⇒ Object



44
45
46
# File 'lib/smartest/simple_stub.rb', line 44

def stub_key(klass_key, method_name)
  [klass_key, method_name]
end

Instance Method Details

#applyObject



124
125
126
127
128
# File 'lib/smartest/simple_stub.rb', line 124

def apply
  return if stub_defined?

  apply_stub
end

#apply!Object



130
131
132
133
134
# File 'lib/smartest/simple_stub.rb', line 130

def apply!
  raise AlreadyAppliedError, "stub for #{@klass}##{@method_name} is already applied" if stub_defined?

  apply_stub
end

#resetObject



136
137
138
139
140
# File 'lib/smartest/simple_stub.rb', line 136

def reset
  return unless stub_defined?

  reset_stub
end

#reset!Object

Raises:



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

def reset!
  raise NotAppliedError, "stub for #{@klass}##{@method_name} is not applied" unless stub_defined?

  reset_stub
end