nrser 0.1.4 → 0.2.0.pre.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser.rb +3 -0
  3. data/lib/nrser/char.rb +2 -3
  4. data/lib/nrser/char/alpha_numeric_sub.rb +233 -0
  5. data/lib/nrser/ext.rb +1 -0
  6. data/lib/nrser/ext/binding.rb +36 -0
  7. data/lib/nrser/ext/string.rb +29 -0
  8. data/lib/nrser/functions/binding.rb +40 -15
  9. data/lib/nrser/functions/string.rb +17 -1
  10. data/lib/nrser/functions/string/style.rb +71 -0
  11. data/lib/nrser/mean_streak.rb +33 -8
  12. data/lib/nrser/mean_streak/document.rb +221 -36
  13. data/lib/nrser/refinements/binding.rb +3 -4
  14. data/lib/nrser/rspex.rb +3 -17
  15. data/lib/nrser/rspex/example.rb +49 -0
  16. data/lib/nrser/rspex/example_group.rb +61 -33
  17. data/lib/nrser/rspex/example_group/describe_called_with.rb +42 -0
  18. data/lib/nrser/rspex/example_group/describe_spec_file.rb +2 -0
  19. data/lib/nrser/rspex/format.rb +48 -78
  20. data/lib/nrser/types.rb +71 -14
  21. data/lib/nrser/types/attrs.rb +121 -110
  22. data/lib/nrser/types/bounded.rb +3 -2
  23. data/lib/nrser/types/combinators.rb +39 -8
  24. data/lib/nrser/types/hashes.rb +5 -4
  25. data/lib/nrser/types/is.rb +11 -1
  26. data/lib/nrser/types/is_a.rb +11 -2
  27. data/lib/nrser/types/maybe.rb +4 -5
  28. data/lib/nrser/types/nil.rb +1 -10
  29. data/lib/nrser/types/not.rb +53 -0
  30. data/lib/nrser/types/numbers.rb +20 -17
  31. data/lib/nrser/types/shape.rb +75 -0
  32. data/lib/nrser/types/strings.rb +49 -59
  33. data/lib/nrser/types/symbols.rb +13 -18
  34. data/lib/nrser/types/type.rb +32 -9
  35. data/lib/nrser/types/when.rb +102 -0
  36. data/lib/nrser/version.rb +1 -1
  37. data/spec/{nrser → lib/nrser}/collection/each_spec.rb +0 -0
  38. data/spec/{nrser → lib/nrser}/collection/map_spec.rb +0 -0
  39. data/spec/{nrser → lib/nrser}/env/path/insert_spec.rb +0 -0
  40. data/spec/{nrser → lib/nrser}/env/path_spec.rb +0 -0
  41. data/spec/{nrser → lib/nrser}/errors/abstract_method_error_spec.rb +0 -0
  42. data/spec/{nrser → lib/nrser}/functions/binding/template_spec.rb +0 -0
  43. data/spec/{nrser → lib/nrser}/functions/enumerable/find_all_map_spec.rb +0 -0
  44. data/spec/{nrser → lib/nrser}/functions/enumerable/find_bounded_spec.rb +0 -0
  45. data/spec/{nrser → lib/nrser}/functions/enumerable/find_map_spec.rb +0 -0
  46. data/spec/{nrser → lib/nrser}/functions/enumerable/find_only_spec.rb +0 -0
  47. data/spec/{nrser → lib/nrser}/functions/enumerable/include_slice_spec.rb +0 -0
  48. data/spec/{nrser → lib/nrser}/functions/enumerable/to_h_by_spec.rb +0 -0
  49. data/spec/{nrser → lib/nrser}/functions/exception/format_exception_spec.rb +0 -0
  50. data/spec/{nrser → lib/nrser}/functions/hash/bury_spec.rb +0 -0
  51. data/spec/{nrser → lib/nrser}/functions/hash/guess_label_key_type_spec.rb +0 -0
  52. data/spec/{nrser → lib/nrser}/functions/hash_spec.rb +0 -0
  53. data/spec/{nrser → lib/nrser}/functions/merge_by_spec.rb +0 -0
  54. data/spec/{nrser → lib/nrser}/functions/object/truthy_spec.rb +0 -0
  55. data/spec/{nrser → lib/nrser}/functions/open_struct_spec.rb +0 -0
  56. data/spec/{nrser → lib/nrser}/functions/string/common_prefix_spec.rb +0 -0
  57. data/spec/{nrser → lib/nrser}/functions/string/looks_like_spec.rb +0 -0
  58. data/spec/{nrser → lib/nrser}/functions/string/truncate_spec.rb +0 -0
  59. data/spec/{nrser → lib/nrser}/functions/text/dedent/gotchas_spec.rb +0 -0
  60. data/spec/{nrser → lib/nrser}/functions/text/dedent_spec.rb +0 -0
  61. data/spec/{nrser → lib/nrser}/functions/text/indent_spec.rb +0 -0
  62. data/spec/{nrser → lib/nrser}/functions/text/words_spec.rb +0 -0
  63. data/spec/{nrser → lib/nrser}/functions/tree/each_branch_spec.rb +0 -0
  64. data/spec/{nrser → lib/nrser}/functions/tree/leaves_spec.rb +0 -0
  65. data/spec/{nrser → lib/nrser}/functions/tree/map_branch_spec.rb +0 -0
  66. data/spec/{nrser → lib/nrser}/functions/tree/map_tree_spec.rb +0 -0
  67. data/spec/{nrser → lib/nrser}/functions/tree/transform_spec.rb +0 -0
  68. data/spec/{nrser → lib/nrser}/functions/tree/transformer_spec.rb +0 -0
  69. data/spec/{nrser → lib/nrser}/labs/globlin_spec.rb +0 -0
  70. data/spec/{nrser → lib/nrser}/labs/index_spec.rb +0 -0
  71. data/spec/{nrser → lib/nrser}/logger/dest_spec.rb +0 -0
  72. data/spec/{nrser → lib/nrser}/logger/die_spec.rb +0 -0
  73. data/spec/{nrser → lib/nrser}/logger/install_spec.rb +0 -0
  74. data/spec/{nrser → lib/nrser}/logger/level_int_spec.rb +0 -0
  75. data/spec/{nrser → lib/nrser}/logger/level_name_spec.rb +0 -0
  76. data/spec/{nrser → lib/nrser}/logger/level_sym_spec.rb +0 -0
  77. data/spec/{nrser → lib/nrser}/logger/send_log_spec.rb +0 -0
  78. data/spec/{nrser → lib/nrser}/logger/use_spec.rb +0 -0
  79. data/spec/lib/nrser/mean_streak/design_spec.rb +68 -0
  80. data/spec/lib/nrser/mean_streak/identity_instance_spec.rb +21 -0
  81. data/spec/{nrser → lib/nrser}/meta/class_attrs_spec.rb +0 -0
  82. data/spec/{nrser → lib/nrser}/meta/props/to_and_from_data_spec.rb +17 -11
  83. data/spec/{nrser → lib/nrser}/meta/props_spec.rb +0 -0
  84. data/spec/{nrser → lib/nrser}/op/message_spec.rb +0 -0
  85. data/spec/{nrser → lib/nrser}/refinements/array_spec.rb +0 -0
  86. data/spec/{nrser → lib/nrser}/refinements/erb_spec.rb +0 -0
  87. data/spec/{nrser → lib/nrser}/refinements/format_exception_spec.rb +0 -0
  88. data/spec/{nrser → lib/nrser}/refinements/hash_spec.rb +0 -0
  89. data/spec/{nrser → lib/nrser}/refinements/indent_spec.rb +0 -0
  90. data/spec/{nrser → lib/nrser}/refinements/object_spec.rb +0 -0
  91. data/spec/{nrser → lib/nrser}/refinements/pathname_spec.rb +0 -0
  92. data/spec/{nrser → lib/nrser}/refinements/set_spec.rb +0 -0
  93. data/spec/{nrser → lib/nrser}/refinements/truncate_spec.rb +0 -0
  94. data/spec/{nrser → lib/nrser}/types/array_spec.rb +0 -0
  95. data/spec/{nrser → lib/nrser}/types/attrs_spec.rb +0 -0
  96. data/spec/{nrser → lib/nrser}/types/combinators_spec.rb +0 -0
  97. data/spec/{nrser → lib/nrser}/types/is_spec.rb +0 -0
  98. data/spec/{nrser → lib/nrser}/types/pairs_spec.rb +0 -0
  99. data/spec/{nrser → lib/nrser}/types/paths_spec.rb +0 -0
  100. data/spec/{nrser → lib/nrser}/types/strings_spec.rb +0 -0
  101. data/spec/{nrser → lib/nrser}/types/symbols_spec.rb +1 -1
  102. data/spec/{nrser → lib/nrser}/types/tuples_spec.rb +0 -0
  103. data/spec/{nrser → lib/nrser}/types_spec.rb +0 -0
  104. data/spec/{nrser_spec.rb → lib/nrser_spec.rb} +0 -0
  105. metadata +148 -136
data/lib/nrser/types.rb CHANGED
@@ -7,15 +7,6 @@
7
7
  # need to be required in the "Post-Processing" section at the bottom.
8
8
  #
9
9
  require_relative './types/type'
10
- require_relative './types/is'
11
- require_relative './types/nil'
12
- require_relative './types/is_a'
13
- require_relative './types/where'
14
- require_relative './types/combinators'
15
- require_relative './types/maybe'
16
- require_relative './types/attrs'
17
- require_relative './types/in'
18
-
19
10
 
20
11
  # Refinements
21
12
  # =======================================================================
@@ -29,6 +20,10 @@ using NRSER
29
20
  #
30
21
  module NRSER::Types
31
22
 
23
+ L_PAREN = '(' # '❪'
24
+ R_PAREN = ')' # '❫'
25
+ RESPONDS_WITH = '→'
26
+
32
27
  # Make a {NRSER::Types::Type} from a value.
33
28
  #
34
29
  # If the `value` argument is...
@@ -50,12 +45,12 @@ module NRSER::Types
50
45
  # @return [NRSER::Types::Type]
51
46
  #
52
47
  def self.make value
53
- if value.is_a? NRSER::Types::Type
48
+ if value.nil?
49
+ self.nil
50
+ elsif value.is_a? NRSER::Types::Type
54
51
  value
55
- elsif value.is_a? ::Class
56
- IsA.new value
57
52
  else
58
- Is.new value
53
+ self.when value
59
54
  end
60
55
  end
61
56
 
@@ -138,11 +133,62 @@ module NRSER::Types
138
133
  },
139
134
 
140
135
  Hash => ->(hash) {
141
-
136
+ raise NotImplementedError, "Haven't gotten to it yet!"
142
137
  },
143
138
  }
144
139
  end # .from_repr
145
140
 
141
+
142
+ # Define a type factory.
143
+ #
144
+ # @!macro [attach] factory
145
+ # @param [Hash] **options
146
+ # Common type construction options, see {Type#initialize}.
147
+ #
148
+ # @return [NRSER::Types::Type]
149
+ # The type.
150
+ #
151
+ def self.def_factory name, maybe: true, aliases: [], &body
152
+ define_singleton_method name, &body
153
+
154
+ aliases.each do |alias_name|
155
+ singleton_class.send :alias_method, alias_name, name
156
+ end
157
+
158
+ if maybe && !name.to_s.end_with?( '?' )
159
+ maybe_name = "#{ name }?".to_sym
160
+
161
+ # HACK Ugh maybe I wrote this quick to fix it, not sure if it's a decent
162
+ # idea.. basically, need to figure out what `options` keys go
163
+ # to {.maybe} and which ones go to the regular factory... matters
164
+ # for shit like {.attrs} and {.hash_type} 'cause they use option
165
+ # keys (whether they *should* is something I've debated... sigh,
166
+ # it is what it is for now).
167
+ #
168
+ # So they options that go to {.maybe} just go strait through to
169
+ # {Type#initialize}, so just grab that method, see what keys it
170
+ # takes, and then can slice and dice off that...
171
+ #
172
+ maybe_option_keys = Set.new \
173
+ Type.
174
+ instance_method( :initialize ).
175
+ parameters.
176
+ select { |param_type, name| param_type == :key }.
177
+ map { |param_type, name| name }
178
+
179
+ define_singleton_method maybe_name do |*args, **options|
180
+ maybe_options = options.slice *maybe_option_keys
181
+ factory_options = options.except *maybe_option_keys
182
+
183
+ maybe public_send( name, *args, **factory_options ), **maybe_options
184
+ end
185
+
186
+ aliases.each do |alias_name|
187
+ singleton_class.send :alias_method, "#{ alias_name }?", maybe_name
188
+ end
189
+ end
190
+ end
191
+
146
192
  end # NRSER::Types
147
193
 
148
194
 
@@ -152,6 +198,16 @@ end # NRSER::Types
152
198
  # Files that define constants that need the proceeding infrastructure.
153
199
  #
154
200
 
201
+ require_relative './types/is'
202
+ require_relative './types/nil'
203
+ require_relative './types/is_a'
204
+ require_relative './types/where'
205
+ require_relative './types/combinators'
206
+ require_relative './types/maybe'
207
+ require_relative './types/attrs'
208
+ require_relative './types/in'
209
+
210
+ require_relative './types/when'
155
211
  require_relative './types/any'
156
212
  require_relative './types/booleans'
157
213
 
@@ -168,3 +224,4 @@ require_relative './types/paths'
168
224
  require_relative './types/tuples'
169
225
  require_relative './types/pairs'
170
226
  require_relative './types/trees'
227
+ require_relative './types/shape'
@@ -8,6 +8,12 @@ module NRSER::Types
8
8
  class Attrs < NRSER::Types::Type
9
9
  def initialize attrs, **options
10
10
  super **options
11
+
12
+ if attrs.empty?
13
+ raise ArgumentError,
14
+ "Must provide at least one attribute name/type pair"
15
+ end
16
+
11
17
  @attrs = NRSER.map_values(attrs) { |name, type|
12
18
  NRSER::Types.make type
13
19
  }
@@ -15,10 +21,14 @@ module NRSER::Types
15
21
 
16
22
  def default_name
17
23
  attrs_str = @attrs.map { |name, type|
18
- "#{ name }=#{ type.name }"
24
+ "##{ name }#{ RESPONDS_WITH }#{ type.name }"
19
25
  }.join(', ')
20
26
 
21
- "#{ self.class.short_name } #{ attrs_str }"
27
+ if @attrs.length < 2
28
+ attrs_str
29
+ else
30
+ L_PAREN + attrs_str + R_PAREN
31
+ end
22
32
  end
23
33
 
24
34
  def test value
@@ -29,121 +39,122 @@ module NRSER::Types
29
39
  end # Attrs
30
40
 
31
41
 
32
- # Eigenclass (Singleton Class)
33
- # ========================================================================
42
+ # @!group Type Factory Functions
43
+
44
+ # Get a {Type} that checks the types of one or more attributes on values.
34
45
  #
35
- class << self
36
- def attrs attrs, options = {}
37
- Attrs.new attrs, **options
38
- end
39
-
46
+ # @example Type where first element of an Enumerable is a String
47
+ # string_first = intersection Enumerable, attrs(first: String)
48
+ #
49
+ def_factory :attrs do |attrs, **options|
50
+ Attrs.new attrs, **options
51
+ end
52
+
53
+
54
+ # @overload length exact, options = {}
55
+ # Get a length attribute type that specifies an `exact` value.
56
+ #
57
+ # @example
58
+ # only_type = NRSER::Types.length 1
59
+ #
60
+ # only_type.test []
61
+ # # => false
62
+ #
63
+ # only_type.test [:x]
64
+ # # => true
65
+ #
66
+ # only_type.test [:x, :y]
67
+ # # => false
68
+ #
69
+ # @param [Integer] exact
70
+ # Exact non-negative integer that the length must be to satisfy the
71
+ # type created.
72
+ #
73
+ # @param [Hash] options
74
+ # Options hash passed up to {NRSER::Types::Type} constructor.
75
+ #
76
+ # @return [NRSER::Types::Attrs]
77
+ # Type satisfied by a `#length` attribute that is exactly `exact`.
78
+ #
79
+ #
80
+ # @overload length bounds, options = {}
81
+ # Get a length attribute type satisfied by values within a `:min` and
82
+ # `:max` (inclusive).
83
+ #
84
+ # @example
85
+ # three_to_five = NRSER::Types.length( {min: 3, max: 5}, name: '3-5' )
86
+ # three_to_five.test [1, 2] # => false
87
+ # three_to_five.test [1, 2, 3] # => true
88
+ # three_to_five.test [1, 2, 3, 4] # => true
89
+ # three_to_five.test [1, 2, 3, 4, 5] # => true
90
+ # three_to_five.test [1, 2, 3, 4, 5, 6] # => false
91
+ #
92
+ # @param [Hash] bounds
93
+ #
94
+ # @option bounds [Integer] :min
95
+ # An optional minimum value that the `#length` should not be less than.
96
+ #
97
+ # @option bounds [Integer] :max
98
+ # An optional maximum value that the `#length` should not be more than.
99
+ #
100
+ # @option bounds [Integer] :length
101
+ # An optional value for both the minimum and maximum.
102
+ #
103
+ # @param [Hash] options
104
+ # Options hash passed up to {NRSER::Types::Type} constructor.
105
+ #
106
+ # @return [NRSER::Types::Attrs]
107
+ # Type satisfied by a `#length` attribute between the `:min` and `:max`
108
+ # (inclusive).
109
+ #
110
+ def self.length *args
111
+ bounds = {}
112
+ options = if args[1].is_a?( Hash ) then args[1] else {} end
40
113
 
41
- # @overload length exact, options = {}
42
- # Get a length attribute type that specifies an `exact` value.
43
- #
44
- # @example
45
- # only_type = NRSER::Types.length 1
46
- #
47
- # only_type.test []
48
- # # => false
49
- #
50
- # only_type.test [:x]
51
- # # => true
52
- #
53
- # only_type.test [:x, :y]
54
- # # => false
55
- #
56
- # @param [Integer] exact
57
- # Exact non-negative integer that the length must be to satisfy the
58
- # type created.
59
- #
60
- # @param [Hash] options
61
- # Options hash passed up to {NRSER::Types::Type} constructor.
62
- #
63
- # @return [NRSER::Types::Attrs]
64
- # Type satisfied by a `#length` attribute that is exactly `exact`.
65
- #
66
- #
67
- # @overload length bounds, options = {}
68
- # Get a length attribute type satisfied by values within a `:min` and
69
- # `:max` (inclusive).
70
- #
71
- # @example
72
- # three_to_five = NRSER::Types.length( {min: 3, max: 5}, name: '3-5' )
73
- # three_to_five.test [1, 2] # => false
74
- # three_to_five.test [1, 2, 3] # => true
75
- # three_to_five.test [1, 2, 3, 4] # => true
76
- # three_to_five.test [1, 2, 3, 4, 5] # => true
77
- # three_to_five.test [1, 2, 3, 4, 5, 6] # => false
78
- #
79
- # @param [Hash] bounds
80
- #
81
- # @option bounds [Integer] :min
82
- # An optional minimum value that the `#length` should not be less than.
83
- #
84
- # @option bounds [Integer] :max
85
- # An optional maximum value that the `#length` should not be more than.
86
- #
87
- # @option bounds [Integer] :length
88
- # An optional value for both the minimum and maximum.
89
- #
90
- # @param [Hash] options
91
- # Options hash passed up to {NRSER::Types::Type} constructor.
92
- #
93
- # @return [NRSER::Types::Attrs]
94
- # Type satisfied by a `#length` attribute between the `:min` and `:max`
95
- # (inclusive).
96
- #
97
- def length *args
98
- bounds = {}
99
- options = if args[1].is_a?( Hash ) then args[1] else {} end
114
+ case args[0]
115
+ when ::Integer
116
+ # It's just a length
117
+ bounds[:min] = bounds[:max] = non_neg_int.check args[0]
100
118
 
101
- case args[0]
102
- when ::Integer
103
- # It's just a length
104
- bounds[:min] = bounds[:max] = non_neg_int.check args[0]
105
-
106
- when ::Hash
107
- # It's keyword args
108
- kwds = NRSER.symbolize_keys args[0]
109
-
110
- # Pull any :min and :max in the keywords
111
- bounds[:min] = kwds.delete :min
112
- bounds[:max] = kwds.delete :max
113
-
114
- # But override with :length if we got it
115
- if length = kwds.delete(:length)
116
- bounds[:min] = length
117
- bounds[:max] = length
118
- end
119
-
120
- # (Reverse) merge anything else into the options (options hash values
121
- # take precedence)
122
- options = kwds.merge options
123
-
124
- else
125
- raise ArgumentError, <<-END.squish
126
- arg must be positive integer or option hash, found:
127
- #{ args[0].inspect } of type #{ args[0].class }
128
- END
129
-
130
- end
119
+ when ::Hash
120
+ # It's keyword args
121
+ kwds = NRSER.symbolize_keys args[0]
131
122
 
132
- bounded_type = bounded bounds
123
+ # Pull any :min and :max in the keywords
124
+ bounds[:min] = kwds.delete :min
125
+ bounds[:max] = kwds.delete :max
133
126
 
134
- length_type = if !bounded_type.min.nil? && bounded_type.min >= 0
135
- # We don't need the non-neg check
136
- bounded_type
137
- else
138
- # We do need the non-neg check
139
- intersection(non_neg_int, bounded_type)
127
+ # But override with :length if we got it
128
+ if length = kwds.delete(:length)
129
+ bounds[:min] = length
130
+ bounds[:max] = length
140
131
  end
141
132
 
142
- options[:name] ||= "Length<#{ bounded_type.name }>"
133
+ # (Reverse) merge anything else into the options (options hash values
134
+ # take precedence)
135
+ options = kwds.merge options
136
+
137
+ else
138
+ raise ArgumentError, <<-END.squish
139
+ arg must be positive integer or option hash, found:
140
+ #{ args[0].inspect } of type #{ args[0].class }
141
+ END
143
142
 
144
- attrs({ length: length_type }, options)
145
- end # #length
143
+ end
144
+
145
+ bounded_type = bounded bounds
146
+
147
+ length_type = if !bounded_type.min.nil? && bounded_type.min >= 0
148
+ # We don't need the non-neg check
149
+ bounded_type
150
+ else
151
+ # We do need the non-neg check
152
+ intersection(non_neg_int, bounded_type)
153
+ end
154
+
155
+ options[:name] ||= "Length<#{ bounded_type.name }>"
146
156
 
147
- end # class << self (Eigenclass)
157
+ attrs({ length: length_type }, options)
158
+ end # #length
148
159
 
149
- end # NRSER::Types
160
+ end # NRSER::Types
@@ -3,7 +3,7 @@ require 'nrser/types/type'
3
3
 
4
4
  using NRSER
5
5
 
6
- module NRSER::Types
6
+ module NRSER::Types
7
7
  class Bounded < NRSER::Types::Type
8
8
 
9
9
  # @!attribute [r] min
@@ -44,10 +44,11 @@ module NRSER::Types
44
44
 
45
45
  "#{ self.class.short_name } #{ attrs_str }"
46
46
  end
47
+
47
48
  end # Bounded
48
49
 
49
50
  def self.bounded **options
50
51
  Bounded.new **options
51
52
  end
52
53
 
53
- end # NRSER::Types
54
+ end # NRSER::Types
@@ -1,3 +1,6 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
1
4
  require 'nrser/refinements'
2
5
  require 'nrser/types/type'
3
6
 
@@ -16,9 +19,15 @@ module NRSER::Types
16
19
 
17
20
 
18
21
  def default_name
19
- "#{ self.class.short_name }<" +
20
- @types.map {|type| type.name }.join(',') +
21
- ">"
22
+ if self.class::JOIN_SYMBOL
23
+ NRSER::Types::L_PAREN +
24
+ @types.map { |type| type.name }.join( self.class::JOIN_SYMBOL ) +
25
+ NRSER::Types::R_PAREN
26
+ else
27
+ "#{ self.class.short_name }<" +
28
+ @types.map {|type| type.name }.join(',') +
29
+ ">"
30
+ end
22
31
  end
23
32
 
24
33
 
@@ -101,6 +110,8 @@ module NRSER::Types
101
110
  end
102
111
 
103
112
  class Union < Combinator
113
+ JOIN_SYMBOL = ' ⋁ '
114
+
104
115
  def test value
105
116
  @types.any? {|type| type.test value}
106
117
  end
@@ -112,14 +123,14 @@ module NRSER::Types
112
123
  end
113
124
 
114
125
  singleton_class.send :alias_method, :one_of, :union
126
+ singleton_class.send :alias_method, :or, :union
115
127
 
116
128
  class Intersection < Combinator
117
- def test value
118
- @types.all? {|type| type.test value}
119
- end
129
+ # JOIN_SYMBOL = ', '
130
+ JOIN_SYMBOL = ' ⋀ '
120
131
 
121
- def default_name
122
- "( #{ @types.map { |t| t.name }.join ' | ' } )"
132
+ def test value
133
+ @types.all? { |type| type.test value}
123
134
  end
124
135
  end
125
136
 
@@ -129,5 +140,25 @@ module NRSER::Types
129
140
  end
130
141
 
131
142
  singleton_class.send :alias_method, :all_of, :intersection
143
+ singleton_class.send :alias_method, :and, :intersection
144
+
145
+
146
+ class XOR < Combinator
147
+ JOIN_SYMBOL = ' ⊕ '
148
+
149
+ def test value
150
+ @types.count { |type| type === value } == 1
151
+ end
152
+
153
+ # def default_name
154
+ # '⊕'
155
+ # "( #{ @types.map { |t| t.name }.join ' ⊕ ' } )"
156
+ # end
157
+ end
158
+
159
+
160
+ def self.xor *types, **options
161
+ XOR.new *types, **options
162
+ end
132
163
 
133
164
  end # NRSER::Types