class Obvious::Contract
Public Class Methods
Public: Defines a contract for a method
method - a symbol representing the method name contract - a hash with the keys :input and :output holding the respective shapes.
Examples
class FooJackContract < Contract contract_for :save, { :input => Foo.shape, :output => Foo.shape, } end
# File lib/obvious/contract.rb, line 46 def self.contract_for method, contract method_alias = "#{method}_alias".to_sym method_contract = "#{method}_contract".to_sym define_method method_contract do |*args| input = args[0] call_method method_alias, input, contract[:input], contract[:output] end contracts( *contract_list, method ) end
Provides a default empty array for method_added
Overriden by self.contracts
# File lib/obvious/contract.rb, line 8 def self.contract_list [] end
Public: Sets the contracts
contracts - a list of contracts, as String or as Symbols.
Examples
class FooJackContract < Contract contracts :save, :get, :list end
Returns Nothing.
# File lib/obvious/contract.rb, line 24 def self.contracts *contracts singleton_class.send :define_method, :contract_list do contracts end end
This method will move methods defined in @contracts into new methods. Each entry in @contracts will cause the method with the same name to become method_name_alias and for the original method to point to method_name_contract.
# File lib/obvious/contract.rb, line 62 def self.method_added name unless @@disable_override self.contract_list.each do |method| if name == method.to_sym method_alias = "#{method}_alias".to_sym method_contract = "#{method}_contract".to_sym @@disable_override = true # to stop the new build method self.send :alias_method, method_alias, name self.send :remove_method, name self.send :alias_method, name, method_contract @@disable_override = false else # puts self.inspect # puts "defining other method #{name}" end end end end
Public Instance Methods
This method is used as a shorthand to mak the contract method calling pattern more DRY It starts by checking if you are sending in input and if so will check the input shape for errors. If no errors are found it calls the method via the passed in symbol(method).
Output checking is more complicated because of the types of output we check for. Nil is never valid output. If we pass in the output shape of true, that means we are looking for result to be the object True. If the output shape is an array, that is actually a shorthand for telling our output check to look at the output as an array and compare it to the shape stored in output_shape. If we pass in the symbol :true_false it means we are looking for the result to be either true or false. The default case will just check if result has the shape of the output_shape.
# File lib/obvious/contract.rb, line 94 def call_method method, input, input_shape, output_shape if input != nil && input_shape != nil has_shape, error_field = input.has_shape? input_shape, true unless has_shape raise Obvious::ContractInputError, "incorrect input data format field #{error_field}" end result = self.send method, input else result = self.send method end # check output # output should never be nil if result == nil raise Obvious::ContractOutputError, 'incorrect output data format' end if result === {} raise Obvious::DataNotFoundError, 'data was not found' end # we are looking for result to be a True object if output_shape === true if output_shape == result return result else raise Obvious::ContractOutputError, 'incorrect output data format' end end # we want to check the shape of each item in the result array if output_shape.class == Array if result.class == Array inner_shape = output_shape[0] result.each do |item| has_shape, error_field = item.has_shape? inner_shape, true unless has_shape raise Obvious::ContractOutputError, "incorrect output data format field #{error_field}" end end return result end raise Obvious::ContractOutputError, 'incorrect output data format' end # we want result to be true or false if output_shape == :true_false unless result == true || result == false raise Obvious::ContractOutputError, 'incorrect output data format' end return result end # we want result to be output_shape's shape has_shape, error_field = result.has_shape? output_shape, true unless has_shape raise Obvious::ContractOutputError, "incorrect output data format field #{error_field}" end result end