#!/usr/bin/env ruby # Example demonstrating "Design by Contract", Bertrand Meyer's idea for # programmatically-specifying the contract of use for a class or module and # testing it at runtime (usually during the testing process). This example # is adapted from spec/extras/design_by_contract_spec.rb. # Note: the DesignByContract module adds the #precondition, #postcondition, # and #invariant methods shown below to Object and they use "self" as the # :object to advise. $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' require 'aquarium/extras/design_by_contract' module Aquarium class PreCond def action *args p "inside :action" end precondition :calls_to => :action, :message => "Must pass more than one argument." do |jp, obj, *args| args.size > 0 end end end p "This call will fail because the precondition is not satisfied:" begin Aquarium::PreCond.new.action rescue Aquarium::Extras::DesignByContract::ContractError => e p e.inspect end p "This call will pass because the precondition is satisfied:" Aquarium::PreCond.new.action :a1 module Aquarium class PostCond def action *args args.empty? ? args.dup : args + [:a] end postcondition :calls_to => :action, :message => "Must return new array, [:a] + args." do |jp, obj, *args| jp.context.returned_value.size == args.size + 1 && jp.context.returned_value[-1] == :a end end end p "These two calls will fail because the postcondition is not satisfied:" begin Aquarium::PostCond.new.action rescue Aquarium::Extras::DesignByContract::ContractError => e p e.inspect end p "This call will pass because the postcondition is satisfied:" Aquarium::PostCond.new.action :x1, :x2 module Aquarium class InvarCond def initialize @invar = 0 end attr_reader :invar def good_action p "inside :good_action" end def bad_action p "inside :bad_action" @invar = 1 end invariant :calls_to => /action$/, :message => "The @invar value must not change." do |jp, obj, *args| obj.invar == 0 end end end p "This call will fail because the invariant is not satisfied:" begin Aquarium::InvarCond.new.bad_action rescue Aquarium::Extras::DesignByContract::ContractError => e p e.inspect end p "This call will pass because the invariant is satisfied:" Aquarium::InvarCond.new.good_action