nrser 0.3.9 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser/char/alpha_numeric_sub.rb +9 -19
  3. data/lib/nrser/char/special.rb +5 -5
  4. data/lib/nrser/core_ext/array.rb +36 -13
  5. data/lib/nrser/core_ext/enumerable.rb +1 -0
  6. data/lib/nrser/core_ext/enumerable/find_map.rb +1 -1
  7. data/lib/nrser/core_ext/hash/bury.rb +3 -0
  8. data/lib/nrser/core_ext/hash/extract_values_at.rb +2 -2
  9. data/lib/nrser/core_ext/method/full_name.rb +1 -1
  10. data/lib/nrser/core_ext/module/method_objects.rb +1 -1
  11. data/lib/nrser/core_ext/module/source_locations.rb +27 -15
  12. data/lib/nrser/core_ext/object/lazy_var.rb +1 -1
  13. data/lib/nrser/core_ext/pathname.rb +67 -12
  14. data/lib/nrser/core_ext/pathname/subpath.rb +86 -0
  15. data/lib/nrser/core_ext/string.rb +28 -1
  16. data/lib/nrser/core_ext/symbol.rb +11 -12
  17. data/lib/nrser/errors/README.md +154 -0
  18. data/lib/nrser/errors/attr_error.rb +146 -53
  19. data/lib/nrser/errors/count_error.rb +61 -12
  20. data/lib/nrser/errors/nicer_error.rb +42 -71
  21. data/lib/nrser/errors/value_error.rb +53 -58
  22. data/lib/nrser/functions.rb +0 -2
  23. data/lib/nrser/functions/enumerable.rb +5 -17
  24. data/lib/nrser/functions/enumerable/associate.rb +14 -5
  25. data/lib/nrser/functions/enumerable/find_all_map.rb +1 -1
  26. data/lib/nrser/functions/enumerable/include_slice/array_include_slice.rb +1 -1
  27. data/lib/nrser/functions/hash/bury.rb +2 -12
  28. data/lib/nrser/functions/merge_by.rb +2 -2
  29. data/lib/nrser/functions/module/method_objects.rb +2 -2
  30. data/lib/nrser/functions/path.rb +185 -165
  31. data/lib/nrser/functions/path/normalized.rb +84 -0
  32. data/lib/nrser/functions/string.rb +4 -4
  33. data/lib/nrser/functions/text/README.md +4 -0
  34. data/lib/nrser/functions/text/format.rb +53 -0
  35. data/lib/nrser/functions/text/indentation.rb +6 -6
  36. data/lib/nrser/functions/text/word_wrap.rb +2 -2
  37. data/lib/nrser/functions/tree/map_leaves.rb +3 -3
  38. data/lib/nrser/functions/tree/map_tree.rb +2 -2
  39. data/lib/nrser/functions/tree/transform.rb +1 -18
  40. data/lib/nrser/gem_ext/README.md +4 -0
  41. data/lib/nrser/labs/README.md +8 -0
  42. data/lib/nrser/labs/config.rb +163 -0
  43. data/lib/nrser/labs/i8.rb +49 -159
  44. data/lib/nrser/labs/i8/struct.rb +167 -0
  45. data/lib/nrser/labs/i8/struct/hash.rb +140 -0
  46. data/lib/nrser/labs/i8/struct/vector.rb +149 -0
  47. data/lib/nrser/labs/i8/surjection.rb +211 -0
  48. data/lib/nrser/labs/lots/consumer.rb +19 -0
  49. data/lib/nrser/labs/lots/parser.rb +21 -1
  50. data/lib/nrser/labs/stash.rb +4 -4
  51. data/lib/nrser/log.rb +25 -21
  52. data/lib/nrser/log/appender/sync.rb +15 -11
  53. data/lib/nrser/log/formatters/color.rb +0 -3
  54. data/lib/nrser/log/formatters/mixin.rb +4 -4
  55. data/lib/nrser/log/logger.rb +54 -6
  56. data/lib/nrser/log/mixin.rb +2 -1
  57. data/lib/nrser/log/plugin.rb +6 -6
  58. data/lib/nrser/log/types.rb +46 -29
  59. data/lib/nrser/mean_streak.rb +0 -8
  60. data/lib/nrser/mean_streak/document.rb +1 -4
  61. data/lib/nrser/message.rb +3 -3
  62. data/lib/nrser/meta/README.md +4 -0
  63. data/lib/nrser/meta/lazy_attr.rb +2 -2
  64. data/lib/nrser/meta/source/location.rb +1 -1
  65. data/lib/nrser/props.rb +34 -3
  66. data/lib/nrser/props/class_methods.rb +2 -1
  67. data/lib/nrser/props/instance_methods.rb +9 -9
  68. data/lib/nrser/props/metadata.rb +4 -12
  69. data/lib/nrser/props/mutable/stash.rb +5 -2
  70. data/lib/nrser/props/prop.rb +10 -19
  71. data/lib/nrser/rspex.rb +1 -20
  72. data/lib/nrser/rspex/example_group/describe_attribute.rb +3 -0
  73. data/lib/nrser/rspex/example_group/describe_called_with.rb +9 -4
  74. data/lib/nrser/rspex/example_group/describe_case.rb +1 -0
  75. data/lib/nrser/rspex/example_group/describe_class.rb +2 -0
  76. data/lib/nrser/rspex/example_group/describe_group.rb +1 -1
  77. data/lib/nrser/rspex/example_group/describe_instance.rb +3 -1
  78. data/lib/nrser/rspex/example_group/describe_message.rb +1 -1
  79. data/lib/nrser/rspex/example_group/describe_method.rb +64 -30
  80. data/lib/nrser/rspex/example_group/describe_response_to.rb +1 -1
  81. data/lib/nrser/rspex/example_group/describe_section.rb +4 -1
  82. data/lib/nrser/rspex/example_group/describe_sent_to.rb +1 -1
  83. data/lib/nrser/rspex/example_group/describe_setup.rb +1 -0
  84. data/lib/nrser/rspex/example_group/describe_source_file.rb +1 -1
  85. data/lib/nrser/rspex/example_group/describe_spec_file.rb +4 -2
  86. data/lib/nrser/rspex/example_group/describe_when.rb +2 -1
  87. data/lib/nrser/rspex/example_group/describe_x.rb +5 -5
  88. data/lib/nrser/rspex/format.rb +0 -15
  89. data/lib/nrser/sugar/method_missing_forwarder.rb +3 -3
  90. data/lib/nrser/sys/env/path.rb +2 -28
  91. data/lib/nrser/types.rb +63 -12
  92. data/lib/nrser/types/README.md +76 -0
  93. data/lib/nrser/types/arrays.rb +192 -137
  94. data/lib/nrser/types/attributes.rb +269 -0
  95. data/lib/nrser/types/booleans.rb +134 -83
  96. data/lib/nrser/types/bounded.rb +110 -47
  97. data/lib/nrser/types/collections.rb +119 -0
  98. data/lib/nrser/types/combinators.rb +283 -196
  99. data/lib/nrser/types/doc/display_table.md +66 -0
  100. data/lib/nrser/types/eqiuvalent.rb +91 -0
  101. data/lib/nrser/types/errors/check_error.rb +5 -11
  102. data/lib/nrser/types/errors/from_string_error.rb +3 -3
  103. data/lib/nrser/types/factory.rb +287 -20
  104. data/lib/nrser/types/hashes.rb +227 -179
  105. data/lib/nrser/types/in.rb +73 -36
  106. data/lib/nrser/types/is.rb +67 -60
  107. data/lib/nrser/types/is_a.rb +141 -84
  108. data/lib/nrser/types/labels.rb +45 -16
  109. data/lib/nrser/types/maybe.rb +6 -3
  110. data/lib/nrser/types/nil.rb +64 -27
  111. data/lib/nrser/types/not.rb +92 -34
  112. data/lib/nrser/types/numbers.rb +224 -169
  113. data/lib/nrser/types/pairs.rb +113 -89
  114. data/lib/nrser/types/paths.rb +250 -137
  115. data/lib/nrser/types/responds.rb +167 -89
  116. data/lib/nrser/types/selector.rb +234 -0
  117. data/lib/nrser/types/shape.rb +136 -65
  118. data/lib/nrser/types/strings.rb +189 -63
  119. data/lib/nrser/types/symbols.rb +83 -33
  120. data/lib/nrser/types/top.rb +89 -0
  121. data/lib/nrser/types/tuples.rb +134 -98
  122. data/lib/nrser/types/type.rb +617 -505
  123. data/lib/nrser/types/when.rb +123 -98
  124. data/lib/nrser/types/where.rb +182 -91
  125. data/lib/nrser/version.rb +1 -1
  126. data/spec/lib/nrser/core_ext/pathname/subpath_spec.rb +22 -0
  127. data/spec/lib/nrser/errors/attr_error_spec.rb +68 -0
  128. data/spec/lib/nrser/errors/count_error_spec.rb +69 -0
  129. data/spec/lib/nrser/functions/path/normalize_path_spec.rb +35 -0
  130. data/spec/lib/nrser/functions/tree/map_tree_spec.rb +74 -96
  131. data/spec/lib/nrser/functions/tree/transform_spec.rb +11 -11
  132. data/spec/lib/nrser/labs/config_spec.rb +22 -0
  133. data/spec/lib/nrser/labs/i8/struct_spec.rb +39 -0
  134. data/spec/lib/nrser/types/display_spec.rb +50 -0
  135. data/spec/lib/nrser/types/paths_spec.rb +16 -10
  136. data/spec/lib/nrser/types/selector_spec.rb +125 -0
  137. data/spec/spec_helper.rb +4 -5
  138. metadata +105 -22
  139. data/lib/nrser/types/any.rb +0 -41
  140. data/lib/nrser/types/attrs.rb +0 -213
  141. data/lib/nrser/types/trees.rb +0 -42
@@ -1,53 +1,116 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # ========================================================================
6
+
7
+ # Project / Package
8
+ # ------------------------------------------------------------------------
9
+
1
10
  require 'nrser/types/type'
11
+
12
+
13
+ # Namespace
14
+ # ========================================================================
15
+
16
+ module NRSER
17
+ module Types
18
+
19
+
20
+ # Definitions
21
+ # ========================================================================
22
+
23
+ # Types whose members satisfy a {#min}, {#max} or both (inclusive).
24
+ #
25
+ # @note
26
+ # Construct {Bounded} types using the {.Bounded} factory.
27
+ #
28
+ class Bounded < Type
2
29
 
3
- module NRSER::Types
4
- class Bounded < NRSER::Types::Type
5
-
6
- # Minimum value.
7
- #
8
- # @return [Number]
9
- #
10
- attr_reader :min
11
-
12
-
13
- # Minimum value.
14
- #
15
- # @return [Number]
16
- #
17
- attr_reader :max
18
-
19
-
20
- def initialize min: nil,
21
- max: nil,
22
- **options
23
- super **options
24
-
25
- @min = min
26
- @max = max
27
- end
28
-
29
- def test? value
30
- return false if @min && value < @min
31
- return false if @max && value > @max
32
- true
33
- end
30
+ # Minimum value.
31
+ #
32
+ # @return [Number]
33
+ #
34
+ attr_reader :min
35
+
36
+
37
+ # Minimum value.
38
+ #
39
+ # @return [Number]
40
+ #
41
+ attr_reader :max
42
+
43
+
44
+ def initialize min: nil,
45
+ max: nil,
46
+ **options
47
+ super **options
34
48
 
35
- def explain
36
- attrs_str = ['min', 'max'].map {|name|
37
- [name, instance_variable_get("@#{ name }")]
38
- }.reject {|name, value|
39
- value.nil?
40
- }.map {|name, value|
41
- "#{ name }=#{ value }"
42
- }.join(', ')
43
-
44
- "#{ self.class.demod_name }<#{ attrs_str }>"
49
+ @min = min
50
+ @max = max
51
+ end
52
+
53
+ def test? value
54
+ return false if @min && value < @min
55
+ return false if @max && value > @max
56
+ true
57
+ end
58
+
59
+ def symbolic
60
+ if min
61
+ if max
62
+ # has min and max, use range notation
63
+ "(#{ min.inspect }..#{ max.inspect })"
64
+ else
65
+ # only has min
66
+ "(#{ min.inspect }..)"
67
+ # "{ x : x #{ NRSER::Types::GEQ } #{ min } }"
68
+ end
69
+ else
70
+ # only has max
71
+ "(..#{ max.inspect })"
72
+ # "{ x : x #{ NRSER::Types::LEQ } #{ max } }"
45
73
  end
46
-
47
- end # Bounded
74
+ end
48
75
 
49
- def_factory :bounded do |**options|
50
- Bounded.new **options
76
+ def explain
77
+ attrs_str = ['min', 'max'].map {|name|
78
+ [name, instance_variable_get("@#{ name }")]
79
+ }.reject {|name, value|
80
+ value.nil?
81
+ }.map {|name, value|
82
+ "#{ name }=#{ value }"
83
+ }.join(', ')
84
+
85
+ "#{ self.class.demod_name }<#{ attrs_str }>"
51
86
  end
52
-
53
- end # NRSER::Types
87
+
88
+ end # Bounded
89
+
90
+
91
+ # @!group Bounded Type Factories
92
+ # ----------------------------------------------------------------------------
93
+
94
+ #@!method self.Bounded **options
95
+ # Create a {Bounded} type instance that matches values between `min` and
96
+ # `max` (inclusive).
97
+ #
98
+ # @param [Hash] options
99
+ # Passed to {Type#initialize}.
100
+ #
101
+ # @return [Type]
102
+ #
103
+ def_type :Bounded,
104
+ parameterize: [ :min, :max ],
105
+ &->( min: nil, max: nil, **options ) do
106
+ Bounded.new min: min, max: max, **options
107
+ end # .Bounded
108
+
109
+ # @!endgroup Bounded Type Factories # ****************************************
110
+
111
+
112
+ # /Namespace
113
+ # ========================================================================
114
+
115
+ end # module Types
116
+ end # module NRSER
@@ -0,0 +1,119 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+
5
+ # Requirements
6
+ # =======================================================================
7
+
8
+ # Project / Package
9
+ # -----------------------------------------------------------------------
10
+ require_relative './combinators'
11
+ require_relative './responds'
12
+ require_relative './is_a'
13
+
14
+
15
+ # Namespace
16
+ # ========================================================================
17
+
18
+ module NRSER
19
+ module Types
20
+
21
+
22
+ # Definitions
23
+ # =======================================================================
24
+
25
+ # @!group Collection Type Factories
26
+ # ----------------------------------------------------------------------------
27
+
28
+ #@!method self.Vector **options
29
+ # An "array-like" {Enumerable} that responds to `#each_index` and
30
+ # `#slice` / `#[]`.
31
+ #
32
+ # @param [Hash] options
33
+ # Passed to {Type#initialize}.
34
+ #
35
+ # @return [Type]
36
+ #
37
+ def_type :Vector,
38
+ aliases: [ :array_like ],
39
+ &->( **options ) do
40
+ intersection \
41
+ is_a( Enumerable ),
42
+ respond_to( :each_index ),
43
+ respond_to( :slice ),
44
+ respond_to( :[] ),
45
+ name: name,
46
+ **options
47
+ end # .Vector
48
+
49
+
50
+ #@!method self.Map **options
51
+ # A "hash-like" {Enumerable} that responds to `#each_pair` and `#[]`.
52
+ #
53
+ # @param [Hash] options
54
+ # Passed to {Type#initialize}.
55
+ #
56
+ # @return [Type]
57
+ #
58
+ def_type :Map,
59
+ aliases: [ :hash_like, :assoc ],
60
+ &->( **options ) do
61
+ intersection \
62
+ is_a( Enumerable ),
63
+ respond_to( :each_pair ),
64
+ respond_to( :[] ),
65
+ name: name,
66
+ **options
67
+ end # .Map
68
+
69
+
70
+ #@!method self.Bag **options
71
+ # An {Enumerable} that does **not** respond to `#each_pair`.
72
+ #
73
+ # Meant to encompass {Set}, {Array} and the like *without* {Hash} and other
74
+ # associative containers.
75
+ #
76
+ # Elements may or may not be indexed.
77
+ #
78
+ # @param [Hash] options
79
+ # Passed to {Type#initialize}.
80
+ #
81
+ # @return [Type]
82
+ #
83
+ def_type :Bag,
84
+ &->( **options ) do
85
+ intersection \
86
+ is_a( Enumerable ),
87
+ self.not( respond_to( :each_pair ) ),
88
+ name: name,
89
+ **options
90
+ end # .Bag
91
+
92
+
93
+ #@!method self.Tree **options
94
+ # Either a {.Vector} or {.Map} - {Enumerable} collections with indexed
95
+ # elements that work with the {NRSER} "tree" functions.
96
+ #
97
+ # @param [Hash] options
98
+ # Passed to {Type#initialize}.
99
+ #
100
+ # @return [Type]
101
+ #
102
+ def_type :Tree,
103
+ &->( **options ) do
104
+ union \
105
+ array_like,
106
+ hash_like,
107
+ name: name,
108
+ **options
109
+ end # .Tree
110
+
111
+ # @!endgroup Collection Type Factories # *************************************
112
+
113
+
114
+ # /Namespace
115
+ # ========================================================================
116
+
117
+ end # module Types
118
+ end # module NRSER
119
+
@@ -3,226 +3,313 @@
3
3
 
4
4
  require 'nrser/types/type'
5
5
 
6
- # base class for Union and Intersection which combine over a set of types.
7
- module NRSER::Types
6
+
7
+
8
+ # Namespace
9
+ # ========================================================================
10
+
11
+ module NRSER
12
+ module Types
13
+
14
+
15
+ # Definitions
16
+ # ========================================================================
17
+
18
+ # Abstract base class for logically combining types to create new ones.
19
+ #
20
+ # @see Union
21
+ # @see Intersection
22
+ # @see XOR
23
+ #
24
+ class Combinator < Type
8
25
 
9
- # Abstract base class for logically combining types to create new ones.
26
+ # The parameterized types, in the order they will be tested.
10
27
  #
11
- class Combinator < NRSER::Types::Type
12
-
13
- # The parametrized types, in the order they will be tested.
14
- #
15
- # @return [Array<NRSER::Types::Type>]
16
- #
17
- attr_reader :types
18
-
19
-
20
- def initialize *types, **options
21
- super **options
22
- @types = types.map { |type| NRSER::Types.make type }
28
+ # @return [Array<NRSER::Types::Type>]
29
+ #
30
+ attr_reader :types
31
+
32
+
33
+ def initialize *types, **options
34
+ super **options
35
+ @types = types.map { |type| NRSER::Types.make type }.freeze
36
+ end
37
+
38
+
39
+ def string_format method
40
+ NRSER::Types::L_PAREN +
41
+ # ' ' + no spaces
42
+ @types.map { |type| type.send method }.join( self.class::JOIN_SYMBOL ) +
43
+ # ' ' + no spaces
44
+ NRSER::Types::R_PAREN
45
+ end
46
+
47
+
48
+ def default_symbolic
49
+ string_format( :to_s )
50
+ end
51
+
52
+
53
+ def explain
54
+ return string_format( :explain )
55
+ end
56
+
57
+
58
+ # Parse a satisfying value from a {String} or raise a {TypeError}.
59
+ #
60
+ # If this type has it's own `@from_s` that was provided via the `from_s:`
61
+ # keyword at construction, then that and **only** that is **always** used
62
+ # - the type will never try any of the combined types' `#from_s`.
63
+ #
64
+ # It's considered *the* way to parse a string into a value that satisfies
65
+ # the type. If it fails, a {TypeError} will be raised (or any error the
66
+ # `@from_s` proc itself raises before we get to checking it).
67
+ #
68
+ # If the type doesn't have it's own `@from_s`, each of the combined types'
69
+ # `#from_s` will be tried in sequence, and the first value that satisfies
70
+ # the combined type will be returned.
71
+ #
72
+ # This is obviously much less efficient, but provides a nice automatic
73
+ # mechanism for parsing from strings for combined types. If none of the
74
+ # combined types' `#from_s` succeed (or if there are none) a {TypeError}
75
+ # is raised.
76
+ #
77
+ # @param [String] s
78
+ # String to parse.
79
+ #
80
+ # @return [Object]
81
+ # Object that satisfies the type.
82
+ #
83
+ # @raise [TypeError]
84
+ # See write up above.
85
+ #
86
+ def custom_from_s string
87
+ # If we have an explicit `@from_s` then use that and that only.
88
+ unless @from_s.nil?
89
+ return check! @from_s.call( string )
23
90
  end
91
+
92
+ errors_by_type = {}
24
93
 
25
-
26
- def explain
27
- if self.class::JOIN_SYMBOL
28
- NRSER::Types::L_PAREN + # ' ' +
29
- @types.map { |type| type.explain }.join( self.class::JOIN_SYMBOL ) +
30
- # ' ' +
31
- NRSER::Types::R_PAREN
94
+ types.each { |type|
95
+ if type.has_from_s?
96
+ begin
97
+ return check! type.from_s( string )
98
+
99
+ # We want to catch any standard error here so `from_s` implementations
100
+ # can kinda "code without care" and if one fails we will move on to
101
+ # try the next.
102
+ rescue StandardError => error
103
+ errors_by_type[type] = error
104
+ end
32
105
  else
33
- "#{ self.class.demod_name }<" +
34
- @types.map { |type| type.explain }.join( ',' ) +
35
- ">"
106
+ errors_by_type[type] = "Does not {#has_from_s?}"
36
107
  end
37
- end
38
-
39
-
40
- # Parse a satisfying value from a {String} or raise a {TypeError}.
41
- #
42
- # If this type has it's own `@from_s` that was provided via the `from_s:`
43
- # keyword at construction, then that and **only** that is **always** used
44
- # - the type will never try any of the combined types' `#from_s`.
45
- #
46
- # It's considered *the* way to parse a string into a value that satisfies
47
- # the type. If it fails, a {TypeError} will be raised (or any error the
48
- # `@from_s` proc itself raises before we get to checking it).
49
- #
50
- # If the type doesn't have it's own `@from_s`, each of the combined types'
51
- # `#from_s` will be tried in sequence, and the first value that satisfies
52
- # the combined type will be returned.
53
- #
54
- # This is obviously much less efficient, but provides a nice automatic
55
- # mechanism for parsing from strings for combined types. If none of the
56
- # combined types' `#from_s` succeed (or if there are none) a {TypeError}
57
- # is raised.
58
- #
59
- # @param [String] s
60
- # String to parse.
61
- #
62
- # @return [Object]
63
- # Object that satisfies the type.
64
- #
65
- # @raise [TypeError]
66
- # See write up above.
67
- #
68
- def custom_from_s s
69
- unless @from_s.nil?
70
- return check @from_s.call( s )
71
- end
72
-
73
- @types.each { |type|
74
- if type.has_from_s?
75
- begin
76
- return check type.from_s(s)
77
- rescue TypeError => e
78
- # pass
79
- end
80
- end
81
- }
82
-
83
- raise TypeError,
84
- "none of combinator #{ self.to_s } types could convert #{ s.inspect }"
85
- end
86
-
108
+ }
87
109
 
88
- # Overridden to delegate functionality to the combined types.
89
- #
90
- # A combinator may attempt to parse from a string if:
91
- #
92
- # 1. It has it's own `@from_s` provided at construction.
93
- #
94
- # 2. Any of it's combined types can parse from a string.
95
- #
96
- # See {#from_s} for details of how it actually happens.
97
- #
98
- # @return [Boolean]
99
- #
100
- def has_from_s?
101
- !@from_s.nil? || @types.any? { |type| type.has_from_s? }
102
- end # has_from_s
103
-
104
-
105
- def has_from_data?
106
- @types.any? { |type| type.has_from_data? }
110
+ # TODO This should be "nicer"... teach {NRSER::MultipleErrors} about
111
+ # {NRSER::NicerError}?
112
+ raise TypeError.new \
113
+ "none of combinator", self.to_s, "types could convert", string.inspect,
114
+ string: string,
115
+ errors_by_type: errors_by_type
116
+ end
117
+
118
+
119
+ # Overridden to delegate functionality to the combined types.
120
+ #
121
+ # A combinator may attempt to parse from a string if:
122
+ #
123
+ # 1. It has it's own `@from_s` provided at construction.
124
+ #
125
+ # 2. Any of it's combined types can parse from a string.
126
+ #
127
+ # See {#from_s} for details of how it actually happens.
128
+ #
129
+ # @return [Boolean]
130
+ #
131
+ def has_from_s?
132
+ !@from_s.nil? || @types.any? { |type| type.has_from_s? }
133
+ end # has_from_s
134
+
135
+
136
+ def has_from_data?
137
+ @types.any? { |type| type.has_from_data? }
138
+ end
139
+
140
+
141
+ # Overridden to
142
+ def from_data data
143
+ unless has_from_data?
144
+ raise NoMethodError, "#from_data not defined"
107
145
  end
108
146
 
147
+ errors = []
109
148
 
110
- # Overridden to
111
- def from_data data
112
- unless has_from_data?
113
- raise NoMethodError, "#from_data not defined"
114
- end
115
-
116
- errors = []
117
-
118
- types.each do |type|
119
- if type.has_from_data?
120
- begin
121
- return check!( type.from_data data )
122
- rescue StandardError => error
123
- errors << error
124
- end
149
+ types.each do |type|
150
+ if type.has_from_data?
151
+ begin
152
+ return check!( type.from_data data )
153
+ rescue StandardError => error
154
+ errors << error
125
155
  end
126
156
  end
127
-
128
- raise NRSER::MultipleErrors.new \
129
- errors,
130
- headline: "No type successfully loaded data"
131
157
  end
132
158
 
133
-
134
- # Overridden to delegate functionality to the combined types:
135
- #
136
- # A combinator can convert a value to data if *any* of it's types can.
137
- #
138
- # @return [Boolean]
139
- #
140
- def has_to_data?
141
- @types.any? { |type| type.has_to_data? }
142
- end # #has_to_data
143
-
144
-
145
- # Overridden to delegate functionality to the combined types:
146
- #
147
- # The first of the combined types that responds to `#to_data` is used to
148
- # dump the value.
149
- #
150
- # @param [Object] value
151
- # Value of this type (though it is *not* checked).
152
- #
153
- # @return [Object]
154
- # The data representation of the value.
155
- #
156
- def to_data value
157
- @types.each { |type|
158
- if type.has_to_data?
159
- return type.to_data value
160
- end
161
- }
162
-
163
- raise NoMethodError, "#to_data not defined"
164
- end # #to_data
165
-
166
-
167
- def == other
168
- equal?(other) || (
169
- other.class == self.class && other.types == @types
170
- )
171
- end
172
-
173
- end # class Combinator
174
-
175
-
176
- class Union < Combinator
177
- JOIN_SYMBOL = ' | ' # ' ⋁ '
178
-
179
- def test? value
180
- @types.any? { |type| type.test value }
181
- end
182
- end # class Union
159
+ raise NRSER::MultipleErrors.new \
160
+ errors,
161
+ headline: "No type successfully loaded data"
162
+ end
183
163
 
184
164
 
185
- # match any of the types
186
- def_factory(
187
- :union,
188
- aliases: [:one_of, :or],
189
- ) do |*types, **options|
190
- Union.new *types, **options
191
- end
165
+ # Overridden to delegate functionality to the combined types:
166
+ #
167
+ # A combinator can convert a value to data if *any* of it's types can.
168
+ #
169
+ # @return [Boolean]
170
+ #
171
+ def has_to_data?
172
+ @types.any? { |type| type.has_to_data? }
173
+ end # #has_to_data
192
174
 
193
175
 
194
- class Intersection < Combinator
195
- JOIN_SYMBOL = ' & ' # ' ⋀ '
176
+ # Overridden to delegate functionality to the combined types:
177
+ #
178
+ # The first of the combined types that responds to `#to_data` is used to
179
+ # dump the value.
180
+ #
181
+ # @param [Object] value
182
+ # Value of this type (though it is *not* checked).
183
+ #
184
+ # @return [Object]
185
+ # The data representation of the value.
186
+ #
187
+ def to_data value
188
+ @types.each { |type|
189
+ if type.has_to_data?
190
+ return type.to_data value
191
+ end
192
+ }
196
193
 
197
- def test? value
198
- @types.all? { |type| type.test? value }
199
- end
200
- end # class Intersection
194
+ raise NoMethodError, "#to_data not defined"
195
+ end # #to_data
201
196
 
202
197
 
203
- # match all of the types
204
- def_factory(
205
- :intersection,
206
- aliases: [:all_of, :and],
207
- ) do |*types, **options|
208
- Intersection.new *types, **options
198
+ def == other
199
+ equal?(other) || (
200
+ other.class == self.class && other.types == @types
201
+ )
209
202
  end
203
+
204
+ end # class Combinator *****************************************************
205
+
206
+
207
+ # Concrete Implementation Classes
208
+ # ----------------------------------------------------------------------------
209
+
210
+
211
+ # Union combinator. (`union`, `one_of`, `or`, `|`).
212
+ #
213
+ class Union < Combinator
214
+ JOIN_SYMBOL = ' | ' # ' ⋁ '
210
215
 
211
-
212
- class XOR < Combinator
213
- JOIN_SYMBOL = ' ⊕ '
214
-
215
- def test? value
216
- @types.count { |type| type === value } == 1
217
- end
216
+ def test? value
217
+ @types.any? { |type| type.test value }
218
218
  end
219
+ end # class Union
220
+
221
+
222
+ # Intersection combinator (`intersection`, `all_of`, `and`, `&).
223
+ #
224
+ class Intersection < Combinator
225
+ JOIN_SYMBOL = ' & ' # ' ⋀ '
219
226
 
220
-
221
- def_factory(
222
- :xor,
223
- aliases: [ :exclusive_or, :only_one_of ],
224
- ) do |*types, **options|
225
- XOR.new *types, **options
227
+ def test? value
228
+ @types.all? { |type| type.test? value }
226
229
  end
230
+ end # class Intersection
231
+
232
+
233
+ # XOR combinator - Exclusive Or (`xor`).
234
+ #
235
+ class XOR < Combinator
236
+ JOIN_SYMBOL = ' ⊕ '
227
237
 
228
- end # NRSER::Types
238
+ def test? value
239
+ @types.count { |type| type === value } == 1
240
+ end
241
+ end
242
+
243
+
244
+ # @!group Combinator Type Factories
245
+ # ----------------------------------------------------------------------------
246
+
247
+
248
+ #@!method self.Union *types, **options
249
+ # Match any of the types.
250
+ #
251
+ # @param [Type | Object] types
252
+ # Types to combine over. Objects that are not {Type} instances will me
253
+ # made into them via {.make}.
254
+ #
255
+ # @param [Hash] options
256
+ # Passed to {Type#initialize}.
257
+ #
258
+ # @return [Type]
259
+ #
260
+ def_type :Union,
261
+ aliases: [ :one_of, :or ],
262
+ parameterize: [ :types ],
263
+ &->( *types, **options ) do
264
+ Union.new *types, **options
265
+ end # .Union
266
+
267
+
268
+ #@!method self.Intersection *types, **options
269
+ # Match all of the types
270
+ #
271
+ # @param [Type | Object] types
272
+ # Types to combine over. Objects that are not {Type} instances will me
273
+ # made into them via {.make}.
274
+ #
275
+ # @param [Hash] options
276
+ # Passed to {Type#initialize}.
277
+ #
278
+ # @return [Type]
279
+ #
280
+ def_type :Intersection,
281
+ aliases: [ :all_of, :and ],
282
+ parameterize: [ :types ],
283
+ &->( *types, **options ) do
284
+ Intersection.new *types, **options
285
+ end # .Intersection
286
+
287
+
288
+ #@!method self.XOR *types, **options
289
+ # Match one of the types only.
290
+ #
291
+ # @param [Type | Object] types
292
+ # Types to combine over. Objects that are not {Type} instances will me
293
+ # made into them via {.make}.
294
+ #
295
+ # @param [Hash] options
296
+ # Passed to {Type#initialize}.
297
+ #
298
+ # @return [Type]
299
+ #
300
+ def_type :XOR,
301
+ aliases: [ :exclusive_or, :only_one_of ],
302
+ parameterize: [ :types ],
303
+ &->( *types, **options ) do
304
+ XOR.new *types, **options
305
+ end # .XOR
306
+
307
+ # @!endgroup Combinator Type Factories # *************************************
308
+
309
+
310
+ # /Namespace
311
+ # ========================================================================
312
+
313
+ end # module Types
314
+ end # module NRSER
315
+