Skip to content
master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
lib
 
 
 
 
 
 
 
 
 
 
 
 
 
 

mimic

Copy a class's instance interface to an anonymous, new object that acts as a substitutable mimic for the class

Example

class SomeClass
  def some_method
    puts 'In some_method'
  end
end

mimic = Mimic.(SomeClass)

mimic.some_method
# (does nothing)

mimic.some_other_method
# => undefined method `some_other_method' for #<Mimic::Class::SomeClass_0...:0x...> (NoMethodError)

mimic.is_a?(SomeClass)
# => true

mimic.class
# => Mimic::Class::SomeClass_0...

Method Signatures

A mimicked method will have the same signature as the source method.

class SomeClass
  def some_method(some_parameter, some_other_parameter)
  end
end

mimic = Mimic.(SomeClass)

mimic.some_method
# => `some_method': wrong number of arguments (given 0, expected 2) (ArgumentError)

Recording Invocations of a Mimic Object

A mimic can be optionally configured to to record invocations made against it.

Recording is activated using the record named parameter.

class SomeClass
  def some_method(some_parameter, some_other_parameter)
  end
end

mimic = Mimic.(SomeClass, record: true)

When the mimic's methods are invoked, the invocation and its arguments will be recorded.

mimic.some_method('some argument', 'some other argument')

mimic.invocation(:some_method)
# => #<Invocation:0x...
 @method_name=:some_method,
 @parameters={:some_parameter=>"some argument", :some_other_parameter=>"some other argument"}>

mimic.invocation(:some_random_method)
# => nil

The invocation can be retrieved based on parameter values.

mimic.some_method('some argument', 'some other argument')

mimic.invocation(:some_method) do |parameter_name, parameter_value|
  parameter_name == :some_parameter && parameter_value == 'some argument'
end
# => #<Invocation:0x...
 @method_name=:some_method,
 @parameters={:some_parameter=>"some argument", :some_other_parameter=>"some other argument"}>

The mimic provides predicates for detecting whether an invocation has been made.

mimic.some_method('some argument', 'some other argument')

mimic.invoked?(:some_method)
# => true

mimic.invoked?(:some_random_method)
# => false

mimic.invoked?(:some_method) do |parameter_name, parameter_value|
  parameter_name == :some_parameter && parameter_value == 'some argument'
end
# => true

If a method is invoked more than once, the multiple invocation records can be retrieved.

mimic.some_method('some argument', 'some other argument')
mimic.some_method('another argument', 'yet another argument')

mimic.invocations(:some_method)
# => [#<Invocation:0x...
  @method_name=:some_method,
  @parameters={:some_parameter=>"some argument", :some_other_parameter=>"some other argument"}>,
 #<Invocation:0x...
  @method_name=:some_method,
  @parameters={:some_parameter=>"another argument", :some_other_parameter=>"yet another argument"}>]

mimic.invocations(:some_method) do |parameter_name, parameter_value|
  parameter_name == :some_parameter && parameter_value == 'some argument'
end
# => [#<Invocation:0x...
  @method_name=:some_method,
  @parameters={:some_parameter=>"some argument", :some_other_parameter=>"some other argument"}>,
 #<Invocation:0x...
  @method_name=:some_method,
  @parameters={:some_parameter=>"another argument", :some_other_parameter=>"yet another argument"}>]

The methods of the recorder may conflict with the methods implemented on the mimicked class. If so, a second set of method names may be used:

  • __invocation
  • __invocations
  • __invoked?

Mimicked Methods and the Void Return Type

Methods on a mimicked object are replaced with an implementation that returns an instance of Mimic::Void.

The purpose of this is to ensure that code that attempts to invoke any subsequent method on a value returned from a replaced method will result in an error.

mimic = Mimic.(SomeClass)

result = mimic.some_method
puts result.class
# => Mimic::Void

mimic.some_method.any_method
# => Mimic::Void::Error (Cannot invoke `any_method' on a void)

NOTE: Method chaining is not possible with a mimicked object. This is done with respect to the Law of Demeter.

Specializing Mimic Objects

A mimicked object can be specialized by passing a block argument to the Mimic actuator.

mimic = Mimic.(SomeClass) do
  def an_instance_method
    puts 'In an_instance_method'
  end

  def self.a_class_method
    puts 'In a_class_method'
  end
end

mimic.an_instance_method
# => "In an_instance_method"

mimic.class.a_class_method
# => "In a_class_method"

Preserved Methods

Mimicked objects' instance methods are replaced with voided methods except for instance methods defined on Ruby's Object class and the method_missing method.

A list of methods that are preserved can be retrieved from the Mimic::ReplaceMethods module.

puts Mimic.preserved_methods

Method Missing and the Null Object Mimic

If the class being mimicked implements the method_missing method, it will not be replaced with a voided method. This makes it possible to construct a general purpose null object implementation.

class SomeClass
  def method_missing(*)
  end
end

mimic = Mimic.(SomeClass)

mimic.any_method
# (does nothing)

Uses

The Mimic library is the substitute generator in the Dependency library. It's used for creating both null objects and mimics of declared dependencies.

class SomeDependencyClass
  def call
  end
end

class SomeClass
  dependency :some_dependency, SomeDependencyClass
end

obj = SomeClass.new

obj.some_dependency.()

obj.some_method_that_doesnt_exist
# => undefined method `some_method_that_doesnt_exist' for #<Mimic::Class::SomeDependencyClass_0..:0x..> (NoMethodError)

obj.dependency.class
# => Mimic::Class::SomeDependencyClass_0..

Acknowledgment

The Eventide Project made use of the Naught library for a number of years before implementing Mimic. Its implementation has been both instructive and inspirational for Mimic. The Naught library is a far more thorough implementation of the breadth of null object patterns that address scenarios above and beyond the Mimic library's purpose.

License

The mimic library is released under the MIT License.

About

No description, website, or topics provided.

Resources

License

Releases

No releases published

Packages

No packages published