module Functional::Protocol
Protocols provide a polymorphism and method-dispatch mechanism that exchews stong typing and embraces the dynamic duck typing of Ruby. Rather than interrogate a module, class, or object for its type and ancestry, protocols allow modules, classes, and methods to be interrogated based on their behavior. It is a logical extension of the ‘respond_to?` method, but vastly more powerful.
{include:file:doc/protocol.md}
Public Class Methods
Does the given module/class/object fully satisfy the given protocol(s)? Raises a {Functional::ProtocolError} on failure.
@param [Object] target the method/class/object to interrogate @param [Symbol] protocols one or more protocols to check against the target @return [Symbol] the target
@raise [Functional::ProtocolError] when one or more protocols are not satisfied @raise [ArgumentError] when no protocols given
# File lib/functional/protocol.rb, line 87 def Satisfy!(target, *protocols) Protocol::Satisfy?(target, *protocols) or Protocol.error(target, 'does not', *protocols) target end
Does the given module/class/object fully satisfy the given protocol(s)?
@param [Object] target the method/class/object to interrogate @param [Symbol] protocols one or more protocols to check against the target @return [Boolean] true if the target satisfies all given protocols else false
@raise [ArgumentError] when no protocols given
# File lib/functional/protocol.rb, line 72 def Satisfy?(target, *protocols) raise ArgumentError.new('no protocols given') if protocols.empty? protocols.all?{|protocol| Protocol.satisfies?(target, protocol.to_sym) } end
Have the given protocols been specified? Raises a {Functional::ProtocolError} on failure.
@param [Symbol] protocols the list of protocols to check @return [Boolean] true if all given protocols have been specified
@raise [Functional::ProtocolError] if one or more of the given protocols have
not been specified
@raise [ArgumentError] when no protocols are given
# File lib/functional/protocol.rb, line 115 def Specified!(*protocols) raise ArgumentError.new('no protocols given') if protocols.empty? (unspecified = Protocol.unspecified(*protocols)).empty? or raise ProtocolError.new("The following protocols are unspecified: :#{unspecified.join('; :')}.") end
Have the given protocols been specified?
@param [Symbol] protocols the list of protocols to check @return [Boolean] true if all given protocols have been specified else false
@raise [ArgumentError] when no protocols are given
# File lib/functional/protocol.rb, line 100 def Specified?(*protocols) raise ArgumentError.new('no protocols given') if protocols.empty? Protocol.unspecified(*protocols).empty? end
Private Class Methods
Raise a {Functional::ProtocolError} formatted with the given data.
@param [Object] target the object that was being interrogated @param [String] message the message fragment to inject into the error @param [Symbol] protocols list of protocols that were being checked against the target
@raise [Functional::ProtocolError] the formatted exception object
# File lib/functional/protocol.rb, line 151 def self.error(target, message, *protocols) target = target.class unless target.is_a?(Module) raise ProtocolError, "Value (#{target.class}) '#{target}' #{message} behave as all of: :#{protocols.join('; :')}." end
Does the target satisfy the given protocol?
@param [Object] target the module/class/object to check @param [Symbol] protocol the protocol to check against the target @return [Boolean] true if the target satisfies the protocol else false
# File lib/functional/protocol.rb, line 129 def self.satisfies?(target, protocol) info = @@info[protocol] return info && info.satisfies?(target) end
Reduces a list of protocols to a list of unspecified protocols.
@param [Symbol] protocols the list of protocols to check @return [Array] zero or more unspecified protocols
# File lib/functional/protocol.rb, line 138 def self.unspecified(*protocols) protocols.drop_while do |protocol| @@info.has_key? protocol.to_sym end end
Private Instance Methods
Does the given module/class/object fully satisfy the given protocol(s)? Raises a {Functional::ProtocolError} on failure.
@param [Object] target the method/class/object to interrogate @param [Symbol] protocols one or more protocols to check against the target @return [Symbol] the target
@raise [Functional::ProtocolError] when one or more protocols are not satisfied @raise [ArgumentError] when no protocols given
# File lib/functional/protocol.rb, line 87 def Satisfy!(target, *protocols) Protocol::Satisfy?(target, *protocols) or Protocol.error(target, 'does not', *protocols) target end
Does the given module/class/object fully satisfy the given protocol(s)?
@param [Object] target the method/class/object to interrogate @param [Symbol] protocols one or more protocols to check against the target @return [Boolean] true if the target satisfies all given protocols else false
@raise [ArgumentError] when no protocols given
# File lib/functional/protocol.rb, line 72 def Satisfy?(target, *protocols) raise ArgumentError.new('no protocols given') if protocols.empty? protocols.all?{|protocol| Protocol.satisfies?(target, protocol.to_sym) } end
Have the given protocols been specified? Raises a {Functional::ProtocolError} on failure.
@param [Symbol] protocols the list of protocols to check @return [Boolean] true if all given protocols have been specified
@raise [Functional::ProtocolError] if one or more of the given protocols have
not been specified
@raise [ArgumentError] when no protocols are given
# File lib/functional/protocol.rb, line 115 def Specified!(*protocols) raise ArgumentError.new('no protocols given') if protocols.empty? (unspecified = Protocol.unspecified(*protocols)).empty? or raise ProtocolError.new("The following protocols are unspecified: :#{unspecified.join('; :')}.") end
Have the given protocols been specified?
@param [Symbol] protocols the list of protocols to check @return [Boolean] true if all given protocols have been specified else false
@raise [ArgumentError] when no protocols are given
# File lib/functional/protocol.rb, line 100 def Specified?(*protocols) raise ArgumentError.new('no protocols given') if protocols.empty? Protocol.unspecified(*protocols).empty? end