Module: Handshake::ClauseMethods

Defined in:
lib/handshake/handshake.rb

Overview

A collection of methods for defining constraints on method arguments.

Instance Method Summary collapse

Instance Method Details

#all?(*clauses) ⇒ Boolean Also known as: and?

Passes only if all of the subclauses pass on the argument.

contract all?(Integer, nonzero?)

Returns:

  • (Boolean)


615
616
617
# File 'lib/handshake/handshake.rb', line 615

def all?(*clauses)
  clause("all of #{clauses.inspect}") { |o| clauses.all? {|c| c === o} }
end

#any?(*clauses) ⇒ Boolean Also known as: or?

Passes if any of the subclauses pass on the argument.

contract any?(String, Symbol) => anything

Returns:

  • (Boolean)


608
609
610
# File 'lib/handshake/handshake.rb', line 608

def any?(*clauses)
  clause("any of #{clauses.inspect}") { |o| clauses.any? {|c| c === o} }
end

#anythingObject

Always passes.



592
593
594
# File 'lib/handshake/handshake.rb', line 592

def anything
  Clause.new("anything") { true }
end

#boolean?Boolean

Passes if argument is true or false.

contract self => boolean?
def ==(other)
  ...
end

Returns:

  • (Boolean)


601
602
603
604
# File 'lib/handshake/handshake.rb', line 601

def boolean?
  #clause("true or false") { |o| ( o == true ) || ( o == false ) }
  any?(TrueClass, FalseClass)
end

#clause(mesg = nil, &block) ⇒ Object

Passes if the given block returns true when passed the argument.



582
583
584
# File 'lib/handshake/handshake.rb', line 582

def clause(mesg=nil, &block) # :yields: argument
  Clause.new(mesg, &block)
end

#hash_contract(hash) ⇒ Object

Passes if:

  • argument is a hash, and

  • argument contains only the keys explicitly specified in the given hash, and

  • every value contract in the given hash passes every applicable value in the argument hash

E.g. hash_contract(:foo => String, :bar => Integer)

:foo => "foo"               # passes
:bar => 3                   # passes
:foo => "bar", :bar => 42   # passes
:foo => 88, :bar => "none"  # fails


686
687
688
689
690
691
692
693
# File 'lib/handshake/handshake.rb', line 686

def hash_contract(hash)
  value_assertions = hash.keys.map do |k|
    clause("key #{k} requires #{hash[k].inspect}") do |o|
      o.has_key?(k) ? hash[k] === o[k] : true
    end
  end
  all? hash_with_keys(*hash.keys), *value_assertions
end

#hash_of?(key_clause, value_clause) ⇒ Boolean

Passes if argument is a Hash and if the key and value clauses pass all of its keys and values, respectively. E.g. hash_of?(Symbol, String):

:foo => "bar", :baz => "qux" # passes
:foo => "bar", "baz" => 3    # fails

Returns:

  • (Boolean)


653
654
655
656
657
# File 'lib/handshake/handshake.rb', line 653

def hash_of?(key_clause, value_clause)
  all_keys   = many_with_map?(key_clause, "all keys")     { |kv| kv[0] }
  all_values = many_with_map?(value_clause, "all values") { |kv| kv[1] }
  all? Hash, all_keys, all_values
end

#hash_with_keys(*keys) ⇒ Object Also known as: hash_with_options

Passes only if argument is a hash and does not contain any keys except those given. E.g. hash_with_keys(:foo, :bar, :baz):

:foo => 3                                     # passes
:foo => 10, :bar => "foo"                     # passes
:foo => "eight", :chunky_bacon => "delicious" # fails


666
667
668
669
670
671
# File 'lib/handshake/handshake.rb', line 666

def hash_with_keys(*keys)
  key_assertion = clause("contains keys #{keys.inspect}") do |o|
    ( o.keys - keys ).empty?
  end
  all? Hash, key_assertion
end

#is?(class_symbol) ⇒ Boolean

Allows you to check whether the argument is_a? of the given symbol. For example, is?(:String). Useful for situations where you want to check for a class type that hasn’t been defined yet when Ruby evaluates the contract but will have been by the time the code runs. Note that String => anything is equivalent to is?(:String) => anything.

Returns:

  • (Boolean)


709
710
711
712
713
# File 'lib/handshake/handshake.rb', line 709

def is?(class_symbol)
  clause(class_symbol.to_s) { |o|
    Object.const_defined?(class_symbol) && o.is_a?(Object.const_get(class_symbol))
  }
end

#many?(clause) ⇒ Boolean

Passes if argument is Enumerable and the subclause passes on all of its objects.

class StringArray < Array
  include Handshake
  contract :+, many?(String) => self
end

Returns:

  • (Boolean)


632
633
634
# File 'lib/handshake/handshake.rb', line 632

def many?(clause)
  many_with_map?(clause) { |o| o }
end

#many_with_map?(clause, mesg = nil, &block) ⇒ Boolean

Passes if argument is Enumerable and the subclause passes on all of its objects, mapped over the given block.

contract many_with_map?(nonzero?, "person age") { |person| person.age } => anything

Returns:

  • (Boolean)


639
640
641
642
643
644
645
# File 'lib/handshake/handshake.rb', line 639

def many_with_map?(clause, mesg=nil, &block) # :yields: argument
  map_mesg = ( mesg.nil? ? "" : " after map #{mesg}" )
  many_with_map = clause("many of #{clause.inspect}#{map_mesg}") do |o|
    o.map(&block).all? { |p| clause === p }
  end
  all? Enumerable, many_with_map
end

#nonzero?Boolean

Passes if argument is numeric and nonzero.

Returns:

  • (Boolean)


621
622
623
# File 'lib/handshake/handshake.rb', line 621

def nonzero?
  all? Numeric, clause("nonzero") {|o| o != 0}
end

#not?(clause) ⇒ Boolean

Passes if the subclause does not pass on the argument.

Returns:

  • (Boolean)


587
588
589
# File 'lib/handshake/handshake.rb', line 587

def not?(clause)
  clause("not #{clause.inspect}") { |o| not ( clause === o ) }
end

#responds_to?(*methods) ⇒ Boolean

Passes if argument responds to all of the given methods.

Returns:

  • (Boolean)


696
697
698
699
700
701
# File 'lib/handshake/handshake.rb', line 696

def responds_to?(*methods)
  respond_assertions = methods.map do |m| 
    clause("responds to #{m}") { |o| o.respond_to? m }
  end
  all? *respond_assertions
end