Project

evt-mimic

0.01
There's a lot of open issues
Copy a class's instance interface to an anonymous, new object that acts as a substitutable mimic for the class
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

 Project Readme

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. When the mimic's methods are invoked, the invocation and its arguments will be recorded.

Recording is activated using the record keyword parameter.

class SomeClass
  def some_method(some_parameter, some_other_parameter)
  end
end

mimic = Mimic.(SomeClass, record: true)

Querying Invocations

An invocation can be retrieved based on its method name and parameter values.

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, some_parameter: 'some argument')
# => #<Invocation:0x...
 @method_name=:some_method,
 @parameters={:some_parameter=>"some argument", :some_other_parameter=>"some other argument"}>

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

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

Detecting Invocations

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

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

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

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

recorder.invoked?(:some_method, some_parameter: 'some argument')
# => true

recorder.invoked?(:some_method, some_other_parameter: 'some other argument')
# => true

recorder.invoked?(:some_method, some_parameter: 'some argument', some_other_parameter: 'some other argument')
# => true

Further Documentation on Method Recording

For further documentation on method recording and querying, see the RecordInvocation library's documentation, which is the basis of the Mimic library's method invocation recorder.

https://github.com/eventide-project/record-invocation

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
    # Some implementation logic
  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..

For further documentation on the Dependency library, see the library's documentation.

(https://github.com/eventide-project/dependency)(https://github.com/eventide-project/dependency)

License

The mimic library is released under the MIT License.