Class Contract
In: lib/contract/exception.rb
lib/contract/overrides.rb
lib/contract/assertions.rb
lib/contract/integration.rb
lib/contract.rb
Parent: Test::Unit::TestCase
Contract lib/contract.rb

Represents a contract between Objects as a collection of test cases. Objects are said to fulfill a contract if all test cases suceed. This is useful for ensuring that Objects your code is getting behave in a way that you expect them to behave so you can fail early or execute different logic for Objects with different interfaces.

The tests of the test suite will be run on a copy of the tested Object so you can safely test its behavior without having to fear data loss. By default Contracts obtain deep copies of Objects by serializing and unserializing them with Ruby’s Marshal functionality. This will work in most cases but can fail for Objects containing unserializable parts like Procs, Files or Sockets. In those cases it is currently of your responsibility to provide a fitting implementation by overwriting the Contract.deep_copy method. In the future the contract library might provide different implementations of it via Ruby’s mixin mechanism.

Methods

adapt   deep_copy   enforce   fulfilled_by?   implies   provides   test   test_all  

Classes and Modules

Module Contract::Check
Module Contract::ContractException
Class Contract::ContractError
Class Contract::ContractMismatch

Constants

Version = current_version   The Version of the contract library you are using as String of the 1.2.3 form where the digits stand for release, major and minor version respectively.

External Aliases

check_signatures -> check_signatures?
check_fulfills -> check_fulfills?
fulfilled_by? -> ===
  You can use contracts in casewhen statements or in Module#signature checks. For example:
  case obj
    when Numeric then obj + 1
    when ListContract then obj + [1]
  end

Attributes

check_fulfills  [RW]  Whether fulfills should be checked. This is enabled by default.

Note: If you want to change this you need to do so before doing any Module#fulfills calls or it will not be applied. It’s probably best set right after requiring the contract library.

check_signatures  [RW]  Whether signatures should be checked. By default signatures are checked only when the application is run in $DEBUG mode. (By specifying the -d switch on the invocation of Ruby.)

Note: If you want to change this you need to do so before doing any Module#signature calls or it will not be applied. It’s probably best set right after requiring the contract library.

implications  [R]  Returns all implications of a given contract that were stated via calling Contract.implies.

Public Class methods

Tries to adapt the specified object to the specified type. Returns the old object if no suitable adaption route was found or if it already is of the specified type.

This will only use adaptions where the :to part is equal to the specified type. No multi-step conversion will be performed.

[Source]

     # File lib/contract/integration.rb, line 124
124:   def self.adapt(object, type)
125:     return object if type === object
126: 
127:     @adaptions[type].each do |adaption|
128:       if adaption[:from] === object and
129:         (adaption[:if].nil? or adaption[:if] === object)
130:       then
131:         result = adaption[:via].call(object)
132:         return result if type === result
133:       end
134:     end
135: 
136:     return object
137:   end

This method is used internally for getting a copy of Objects that the contract is checked against. By default it uses Ruby’s Marshal functionality for obtaining a copy, but this can fail if the Object contains unserializable parts like Procs, Files or Sockets. It is currently your responsibility to provide a fitting implementation of this by overwriting the method in case the default implementation does not work for you. In the future the contract library might offer different implementations for this via Ruby’s mixin mechanism.

[Source]

     # File lib/contract.rb, line 104
104:   def self.deep_copy(object)
105:     Marshal.load(Marshal.dump(object))
106:   end

Enforces that object implements this contract. If it does not an Exception will be raised. This is useful for example useful when you need to ensure that the arguments given to a method fulfill a given contract.

Note that using Module#enforce is a higher-level way of checking arguments and return values for the conformance of a given type. You might however still want to use Contract.enforce directly when you need more flexibility.

[Source]

    # File lib/contract.rb, line 65
65:   def self.enforce(object)
66:     reason = self.test(object)
67:     raise reason if reason
68:   end

Returns true if the given object fulfills this contract. This is useful for implementing dispatching mechanisms where you want to hit different code branches based on whether an Object has one or another interface.

[Source]

    # File lib/contract.rb, line 42
42:   def self.fulfilled_by?(object)
43:     self.test(object).nil?
44:   end

Fulfilling this Contract (via Module#fulfills) implies that the Object is automatically compatible with the specified mixins which will then be included automatically. For example the Enumerable relationship could be expressed like this:

  class EnumerableContract < Contract
    provides :each
    implies Enumerable
  end

[Source]

     # File lib/contract.rb, line 117
117:   def self.implies(*mixins)
118:     mixins.each do |mixin|
119:       if not mixin.is_a?(Module) then
120:         raise(TypeError, "wrong argument type #{mixin.class} for " +
121:           "#{mixin.inspect} (expected Module)")
122:       end
123:     end
124: 
125:     @implications ||= Array.new
126:     @implications += mixins
127:   end

Tests that the tested Object provides the specified methods with the specified behavior.

If a block is supplied it will be evaluated in the context of the contract so @object will refer to the object being tested.

This can be used like this:

  class ListContract < Contract
    provides :size do
      assert(@object.size >= 0, "#size should never be negative.")
    end

    provides :include?

    provides :each do
      count = 0
      @object.each do |item|
        assert(@object.include?(item),
          "#each should only yield items that the list includes.")
        count += 1
      end
      assert_equal(@object.size, count,
        "#each should yield #size items.")
    end
  end

[Source]

    # File lib/contract/assertions.rb, line 34
34:   def self.provides(*symbols, &block) # :yields:
35:     symbols.each do |symbol|
36:       define_method("test_provides_#{symbol}".intern) do
37:         assert_respond_to(@object, symbol)
38:         instance_eval(&block) if block
39:       end
40:     end
41:   end

Tests whether the given Object fulfils this contract.

Note: This will return the first reason for the Object not fulfilling the contract or nil in case it fulfills it.

[Source]

    # File lib/contract.rb, line 74
74:   def self.test(object, return_all = false)
75:     reasons = []
76: 
77:     result = Test::Unit::TestResult.new
78:     result.add_listener(Test::Unit::TestResult::FAULT) do |fault|
79:       reason = Contract.fault_to_exception(fault, object, self)
80:       return reason unless return_all
81:       reasons << reason
82:     end
83: 
84:     self.suite.run(result, deep_copy(object))
85: 
86:     return reasons unless result.passed?
87:   end

Same as Contract.test, but will return all reasons for the Object not fulfilling the contract in an Array or nil in case of fulfillment. (as an Array of Exceptions) or nil in the case it does fulfill it.

[Source]

    # File lib/contract.rb, line 92
92:   def self.test_all(object)
93:     test(object, true)
94:   end

[Validate]