Class: NRSER::Types::Combinator

Inherits:
Type show all
Defined in:
lib/nrser/types/combinators.rb

Overview

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

See Also:

Direct Known Subclasses

Intersection, Union, XOR

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Type

#===, #builtin_inspect, #check, #check!, #default_name, #from_s, #inspect, #intersection, #name, #not, #respond_to?, #symbolic, #test, #test?, #to_proc, #to_s, #union, #xor

Constructor Details

#initialize(*types, **options) ⇒ Combinator

Returns a new instance of Combinator.



33
34
35
36
# File 'lib/nrser/types/combinators.rb', line 33

def initialize *types, **options
  super **options
  @types = types.map { |type| NRSER::Types.make type }.freeze
end

Instance Attribute Details

#typesArray<NRSER::Types::Type> (readonly)

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



30
31
32
# File 'lib/nrser/types/combinators.rb', line 30

def types
  @types
end

Instance Method Details

#==(other) ⇒ Object



198
199
200
201
202
# File 'lib/nrser/types/combinators.rb', line 198

def == other
  equal?(other) || (
    other.class == self.class && other.types == @types
  )
end

#custom_from_s(string) ⇒ Object

Parse a satisfying value from a NRSER::Types.String or raise a NRSER::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 NRSER::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 NRSER::TypeError is raised.

Parameters:

  • string (String)

    String to parse.

Returns:

  • (Object)

    Object that satisfies the type.

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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_symbolicObject



48
49
50
# File 'lib/nrser/types/combinators.rb', line 48

def default_symbolic
  string_format( :to_s )
end

#explainObject



53
54
55
# File 'lib/nrser/types/combinators.rb', line 53

def explain
  return string_format( :explain )
end

#from_data(data) ⇒ Object

Overridden to



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# 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?Boolean

Returns:



136
137
138
# File 'lib/nrser/types/combinators.rb', line 136

def has_from_data?
  @types.any? { |type| type.has_from_data? }
end

#has_from_s?Boolean

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 Type#from_s for details of how it actually happens.

Returns:



131
132
133
# 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?Boolean

Overridden to delegate functionality to the combined types:

A combinator can convert a value to data if any of it’s types can.

Returns:



171
172
173
# File 'lib/nrser/types/combinators.rb', line 171

def has_to_data?
  @types.any? { |type| type.has_to_data? }
end

#string_format(method) ⇒ Object



39
40
41
42
43
44
45
# 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) ⇒ Object

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.

Parameters:

  • value (Object)

    Value of this type (though it is not checked).

Returns:

  • (Object)

    The data representation of the value.

Raises:

  • (NoMethodError)


187
188
189
190
191
192
193
194
195
# 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