class NRSER::Types::Combinator
Abstract base class for logically combining types to create new ones.
@see Union
@see Intersection
@see XOR
Attributes
The parameterized types, in the order they will be tested.
@return [Array<NRSER::Types::Type>]
Public Class Methods
# File lib/nrser/types/combinators.rb, line 33 def initialize *types, **options super **options @types = types.map { |type| NRSER::Types.make type }.freeze end
Public Instance Methods
# File lib/nrser/types/combinators.rb, line 198 def == other equal?(other) || ( other.class == self.class && other.types == @types ) end
Parse a satisfying value from a {String} or raise a {TypeError}.
If this type has it's own `@from_s` that was provided via the `from_s:` keyword at construction, then that and only that is always used
-
the type will never try any of the combined types' `#from_s`.
It's considered the way to parse a string into a value that satisfies the type. If it fails, a {TypeError} will be raised (or any error the `@from_s` proc itself raises before we get to checking it).
If the type doesn't have it's own `@from_s`, each of the combined types' `#from_s` will be tried in sequence, and the first value that satisfies the combined type will be returned.
This is obviously much less efficient, but provides a nice automatic mechanism for parsing from strings for combined types. If none of the combined types' `#from_s` succeed (or if there are none) a {TypeError} is raised.
@param [String] string
String to parse.
@return [Object]
Object that satisfies the type.
@raise [TypeError]
See write up above.
# File lib/nrser/types/combinators.rb, line 86 def custom_from_s string # If we have an explicit `@from_s` then use that and that only. unless @from_s.nil? return check! @from_s.call( string ) end errors_by_type = {} types.each { |type| if type.has_from_s? begin return check! type.from_s( string ) # We want to catch any standard error here so `from_s` implementations # can kinda "code without care" and if one fails we will move on to # try the next. rescue StandardError => error errors_by_type[type] = error end else errors_by_type[type] = "Does not {#has_from_s?}" end } # TODO This should be "nicer"... teach {NRSER::MultipleErrors} about # {NRSER::NicerError}? raise TypeError.new \ "none of combinator", self.to_s, "types could convert", string.inspect, string: string, errors_by_type: errors_by_type end
# File lib/nrser/types/combinators.rb, line 48 def default_symbolic string_format( :to_s ) end
# File lib/nrser/types/combinators.rb, line 53 def explain return string_format( :explain ) end
Overridden to
# File lib/nrser/types/combinators.rb, line 142 def from_data data unless has_from_data? raise NoMethodError, "#from_data not defined" end errors = [] types.each do |type| if type.has_from_data? begin return check!( type.from_data data ) rescue StandardError => error errors << error end end end raise NRSER::MultipleErrors.new \ errors, headline: "No type successfully loaded data" end
# File lib/nrser/types/combinators.rb, line 136 def has_from_data? @types.any? { |type| type.has_from_data? } end
Overridden to delegate functionality to the combined types.
A combinator may attempt to parse from a string if:
-
It has it's own `@from_s` provided at construction.
-
Any of it's combined types can parse from a string.
See {#from_s} for details of how it actually happens.
@return [Boolean]
# File lib/nrser/types/combinators.rb, line 131 def has_from_s? !@from_s.nil? || @types.any? { |type| type.has_from_s? } end
Overridden to delegate functionality to the combined types:
A combinator can convert a value to data if any of it's types can.
@return [Boolean]
# File lib/nrser/types/combinators.rb, line 171 def has_to_data? @types.any? { |type| type.has_to_data? } end
# File lib/nrser/types/combinators.rb, line 39 def string_format method NRSER::Types::L_PAREN + # ' ' + no spaces @types.map { |type| type.send method }.join( self.class::JOIN_SYMBOL ) + # ' ' + no spaces NRSER::Types::R_PAREN end
Overridden to delegate functionality to the combined types:
The first of the combined types that responds to `#to_data` is used to dump the value.
@param [Object] value
Value of this type (though it is *not* checked).
@return [Object]
The data representation of the value.
# File lib/nrser/types/combinators.rb, line 187 def to_data value @types.each { |type| if type.has_to_data? return type.to_data value end } raise NoMethodError, "#to_data not defined" end