Module: Handshake::ClauseMethods
- Defined in:
- lib/handshake/clause_methods.rb,
lib/handshake/block_contract.rb
Overview
A collection of methods for defining constraints on method arguments. Use them inline with method signatures:
contract any?(String, nil) => all?(Fixnum, nonzero?)
Constant Summary collapse
Instance Method Summary collapse
-
#all?(*clauses) ⇒ Boolean
(also: #and?)
Passes only if all of the subclauses pass on the argument.
-
#any?(*clauses) ⇒ Boolean
(also: #or?)
Passes if any of the subclauses pass on the argument.
-
#anything ⇒ Object
Always passes.
-
#Block(contract_hash) ⇒ Object
Block signature definition.
-
#boolean? ⇒ Boolean
Passes if argument is true or false.
-
#clause(mesg = nil, &block) ⇒ Object
Passes if the given block returns true when passed the argument.
-
#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_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.
-
#hash_with_keys(*keys) ⇒ Object
(also: #hash_with_options)
Passes only if argument is a hash and does not contain any keys except those given.
-
#is?(class_symbol) ⇒ Boolean
Allows you to check whether the argument is_a? of the given symbol.
-
#many?(clause) ⇒ Boolean
Passes if argument is Enumerable and the subclause passes on all of its objects.
-
#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.
-
#nonzero? ⇒ Boolean
Passes if argument is numeric and nonzero.
-
#not?(clause) ⇒ Boolean
Passes if the subclause does not pass on the argument.
-
#nothing ⇒ Object
Distinct from nil, and only for accepts: passes if zero arguments.
-
#responds_to?(*methods) ⇒ Boolean
Passes if argument responds to all of the given methods.
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?)
69 70 71 |
# File 'lib/handshake/clause_methods.rb', line 69 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
62 63 64 |
# File 'lib/handshake/clause_methods.rb', line 62 def any?(*clauses) clause("any of #{clauses.inspect}") { |o| clauses.any? {|c| c === o} } end |
#anything ⇒ Object
Always passes.
44 |
# File 'lib/handshake/clause_methods.rb', line 44 def anything; ANYTHING; end |
#Block(contract_hash) ⇒ Object
Block signature definition. Returns a ProcContract with the given attributes
46 47 48 49 50 |
# File 'lib/handshake/block_contract.rb', line 46 def Block(contract_hash) pc = Handshake::ProcContract.new pc.signature = contract_hash pc end |
#boolean? ⇒ Boolean
Passes if argument is true or false.
contract self => boolean?
def ==(other)
...
end
56 57 58 |
# File 'lib/handshake/clause_methods.rb', line 56 def boolean? any?(TrueClass, FalseClass) end |
#clause(mesg = nil, &block) ⇒ Object
Passes if the given block returns true when passed the argument.
34 35 36 |
# File 'lib/handshake/clause_methods.rb', line 34 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
140 141 142 143 144 145 146 147 |
# File 'lib/handshake/clause_methods.rb', line 140 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
107 108 109 110 111 |
# File 'lib/handshake/clause_methods.rb', line 107 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
120 121 122 123 124 125 |
# File 'lib/handshake/clause_methods.rb', line 120 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
.
163 164 165 166 167 |
# File 'lib/handshake/clause_methods.rb', line 163 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
86 87 88 |
# File 'lib/handshake/clause_methods.rb', line 86 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
93 94 95 96 97 98 99 |
# File 'lib/handshake/clause_methods.rb', line 93 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.
75 76 77 |
# File 'lib/handshake/clause_methods.rb', line 75 def nonzero? all? Numeric, clause("nonzero") {|o| o != 0} end |
#not?(clause) ⇒ Boolean
Passes if the subclause does not pass on the argument.
39 40 41 |
# File 'lib/handshake/clause_methods.rb', line 39 def not?(clause) clause("not #{clause.inspect}") { |o| not ( clause === o ) } end |
#nothing ⇒ Object
Distinct from nil, and only for accepts: passes if zero arguments. Since Ruby will throw an arity exception anyway, this is essentially aesthetics.
49 |
# File 'lib/handshake/clause_methods.rb', line 49 def nothing; []; end |
#responds_to?(*methods) ⇒ Boolean
Passes if argument responds to all of the given methods.
150 151 152 153 154 155 |
# File 'lib/handshake/clause_methods.rb', line 150 def responds_to?(*methods) respond_assertions = methods.map do |m| clause("responds to #{m}") { |o| o.respond_to? m } end all? *respond_assertions end |