Class: RR::Injections::DoubleInjection

Inherits:
Injection
  • Object
show all
Includes:
ClassInstanceMethodDefined
Defined in:
lib/rr/injections/double_injection.rb

Overview

RR::DoubleInjection is the binding of an subject and a method. A double_injection has 0 to many Double objects. Each Double has Argument Expectations and Times called Expectations.

Defined Under Namespace

Classes: MethodArguments

Constant Summary collapse

BoundObjects =
{}

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ClassInstanceMethodDefined

#class_instance_method_defined

Methods inherited from Injection

#original_method, #subject_has_method_defined?, #subject_has_original_method?

Methods included from Space::Reader

#space

Constructor Details

#initialize(subject_class, method_name) ⇒ DoubleInjection

Returns a new instance of DoubleInjection.



103
104
105
106
107
108
# File 'lib/rr/injections/double_injection.rb', line 103

def initialize(subject_class, method_name)
  @subject_class = subject_class
  @method_name = method_name.to_sym
  @doubles = []
  @dispatch_method_delegates_to_dispatch_original_method = nil
end

Instance Attribute Details

#doublesObject (readonly)

Returns the value of attribute doubles.



97
98
99
# File 'lib/rr/injections/double_injection.rb', line 97

def doubles
  @doubles
end

#method_nameObject (readonly)

Returns the value of attribute method_name.



97
98
99
# File 'lib/rr/injections/double_injection.rb', line 97

def method_name
  @method_name
end

#subject_classObject (readonly)

Returns the value of attribute subject_class.



97
98
99
# File 'lib/rr/injections/double_injection.rb', line 97

def subject_class
  @subject_class
end

Instance Method Details

#bindObject

RR::DoubleInjection#bind injects a method that acts as a dispatcher that dispatches to the matching Double when the method is called.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rr/injections/double_injection.rb', line 119

def bind
  if subject_has_method_defined?(method_name)
    if subject_has_original_method?
      bind_method
    else
      bind_method_with_alias
    end
  else
    Injections::MethodMissingInjection.find_or_create(subject_class)
    Injections::SingletonMethodAddedInjection.find_or_create(subject_class)
    bind_method_that_self_destructs_and_delegates_to_method_missing
  end
  self
end

#bind_methodObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/rr/injections/double_injection.rb', line 152

def bind_method
  id = BoundObjects.size
  BoundObjects[id] = subject_class

  if KeywordArguments.fully_supported?
    subject_class.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
      def #{method_name}(*args, **kwargs, &block)
        arguments = MethodArguments.new(args, kwargs, block)
        obj = ::RR::Injections::DoubleInjection::BoundObjects[#{id}]
        ::RR::Injections::DoubleInjection.dispatch_method(
          self,
          obj,
          :#{method_name},
          arguments.arguments,
          arguments.keyword_arguments,
          arguments.block
        )
      end
    RUBY
  else
    subject_class.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
      def #{method_name}(*args, &block)
        arguments = MethodArguments.new(args, {}, block)
        obj = ::RR::Injections::DoubleInjection::BoundObjects[#{id}]
        ::RR::Injections::DoubleInjection.dispatch_method(
          self,
          obj,
          :#{method_name},
          arguments.arguments,
          arguments.keyword_arguments,
          arguments.block
        )
      end
      ruby2_keywords(:#{method_name}) if respond_to?(:ruby2_keywords, true)
    RUBY
  end
  self
end

#bind_method_that_self_destructs_and_delegates_to_method_missingObject



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/rr/injections/double_injection.rb', line 136

def bind_method_that_self_destructs_and_delegates_to_method_missing
  id = BoundObjects.size
  BoundObjects[id] = subject_class

  subject_class.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
    def #{method_name}(*args, &block)
      ::RR::Injections::DoubleInjection::BoundObjects[#{id}].class_eval do
        remove_method(:#{method_name})
      end
      method_missing(:#{method_name}, *args, &block)
    end
    ruby2_keywords(:#{method_name}) if respond_to?(:ruby2_keywords, true)
  RUBY
  self
end

#dispatch_method(subject, args, kwargs, block) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/rr/injections/double_injection.rb', line 215

def dispatch_method(subject, args, kwargs, block)
  if @dispatch_method_delegates_to_dispatch_original_method
    dispatch_original_method(subject, args, kwargs, block)
  else
    dispatch = MethodDispatches::MethodDispatch.new(
      self,
      subject,
      args,
      kwargs,
      block
    )
    dispatch.call
  end
end

#dispatch_method_delegates_to_dispatch_original_methodObject



249
250
251
252
253
254
# File 'lib/rr/injections/double_injection.rb', line 249

def dispatch_method_delegates_to_dispatch_original_method
  @dispatch_method_delegates_to_dispatch_original_method = true
  yield
ensure
  @dispatch_method_delegates_to_dispatch_original_method = nil
end

#dispatch_original_method(subject, args, kwargs, block) ⇒ Object



230
231
232
233
234
235
236
237
238
239
# File 'lib/rr/injections/double_injection.rb', line 230

def dispatch_original_method(subject, args, kwargs, block)
  dispatch = MethodDispatches::MethodDispatch.new(
    self,
    subject,
    args,
    kwargs,
    block
  )
  dispatch.call_original_method
end

#original_method_alias_nameObject



245
246
247
# File 'lib/rr/injections/double_injection.rb', line 245

def original_method_alias_name
  "__rr__original_#{@method_name}"
end

#register_double(double) ⇒ Object

RR::DoubleInjection#register_double adds the passed in Double into this DoubleInjection’s list of Double objects.



112
113
114
# File 'lib/rr/injections/double_injection.rb', line 112

def register_double(double)
  @doubles << double
end

#resetObject

It binds the original method implementation on the subject if one exists.



203
204
205
206
207
208
209
210
211
212
213
# File 'lib/rr/injections/double_injection.rb', line 203

def reset
  if subject_has_original_method?
    subject_class.__send__(:remove_method, method_name)
    subject_class.__send__(:alias_method, method_name, original_method_alias_name)
    subject_class.__send__(:remove_method, original_method_alias_name)
  else
    if subject_has_method_defined?(method_name)
      subject_class.__send__(:remove_method, method_name)
    end
  end
end

#subject_has_original_method_missing?Boolean

Returns:

  • (Boolean)


241
242
243
# File 'lib/rr/injections/double_injection.rb', line 241

def subject_has_original_method_missing?
  class_instance_method_defined(subject_class, MethodDispatches::MethodMissingDispatch.original_method_missing_alias_name)
end

#verifyObject

RR::DoubleInjection#verify verifies each Double TimesCalledExpectation are met.



193
194
195
196
197
# File 'lib/rr/injections/double_injection.rb', line 193

def verify
  @doubles.each do |double|
    double.verify
  end
end