class NRSER::Types::Combinator

Abstract base class for logically combining types to create new ones.

@see Union @see Intersection @see XOR

Attributes

types[R]

The parameterized types, in the order they will be tested.

@return [Array<NRSER::Types::Type>]

Public Class Methods

new(*types, **options) click to toggle source
Calls superclass method
# 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

==(other) click to toggle source
# File lib/nrser/types/combinators.rb, line 198
def == other
  equal?(other) || (
    other.class == self.class && other.types == @types
  )
end
custom_from_s(string) click to toggle source

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
default_symbolic() click to toggle source
# File lib/nrser/types/combinators.rb, line 48
def default_symbolic
  string_format( :to_s )
end
explain() click to toggle source
# File lib/nrser/types/combinators.rb, line 53
def explain
  return string_format( :explain )
end
from_data(data) click to toggle source

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
has_from_data?() click to toggle source
# File lib/nrser/types/combinators.rb, line 136
def has_from_data?
  @types.any? { |type| type.has_from_data? }
end
has_from_s?() click to toggle source

Overridden to delegate functionality to the combined types.

A combinator may attempt to parse from a string if:

  1. It has it's own `@from_s` provided at construction.

  2. 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
has_to_data?() click to toggle source

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
string_format(method) click to toggle source
# 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
to_data(value) click to toggle source

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