nrser 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser/char/alpha_numeric_sub.rb +1 -2
  3. data/lib/nrser/char.rb +0 -6
  4. data/lib/nrser/core_ext/array.rb +120 -0
  5. data/lib/nrser/core_ext/binding.rb +44 -0
  6. data/lib/nrser/{functions → core_ext}/enumerable/find_map.rb +18 -15
  7. data/lib/nrser/{ext → core_ext}/enumerable.rb +10 -24
  8. data/lib/nrser/core_ext/exception.rb +30 -0
  9. data/lib/nrser/core_ext/hash/extract_values_at.rb +49 -0
  10. data/lib/nrser/core_ext/hash/transform_values_with_keys.rb +24 -0
  11. data/lib/nrser/core_ext/hash.rb +50 -0
  12. data/lib/nrser/core_ext/module/method_objects.rb +96 -0
  13. data/lib/nrser/core_ext/module/names.rb +69 -0
  14. data/lib/nrser/core_ext/module/source_locations.rb +214 -0
  15. data/lib/nrser/core_ext/module.rb +2 -0
  16. data/lib/nrser/core_ext/object/lazy_var.rb +31 -0
  17. data/lib/nrser/core_ext/object.rb +46 -0
  18. data/lib/nrser/core_ext/open_struct.rb +6 -0
  19. data/lib/nrser/{ext → core_ext}/pathname.rb +8 -5
  20. data/lib/nrser/{ext → core_ext}/string.rb +6 -12
  21. data/lib/nrser/core_ext/symbol.rb +13 -0
  22. data/lib/nrser/core_ext/time.rb +46 -0
  23. data/lib/nrser/core_ext.rb +13 -0
  24. data/lib/nrser/errors/abstract_method_error.rb +150 -0
  25. data/lib/nrser/errors/argument_error.rb +42 -0
  26. data/lib/nrser/errors/nicer_error.rb +298 -72
  27. data/lib/nrser/errors/type_error.rb +46 -0
  28. data/lib/nrser/errors.rb +4 -53
  29. data/lib/nrser/ext/tree.rb +3 -0
  30. data/lib/nrser/functions/enumerable/associate.rb +6 -9
  31. data/lib/nrser/functions/enumerable/include_slice.rb +2 -3
  32. data/lib/nrser/functions/enumerable.rb +1 -3
  33. data/lib/nrser/functions/exception.rb +1 -1
  34. data/lib/nrser/functions/hash.rb +0 -6
  35. data/lib/nrser/functions/merge_by.rb +2 -2
  36. data/lib/nrser/functions/module/method_objects.rb +77 -0
  37. data/lib/nrser/functions/module.rb +1 -2
  38. data/lib/nrser/functions/open_struct.rb +25 -35
  39. data/lib/nrser/functions/proc.rb +1 -6
  40. data/lib/nrser/functions/string/looks_like.rb +32 -1
  41. data/lib/nrser/functions/string.rb +1 -40
  42. data/lib/nrser/functions/text/lines.rb +2 -1
  43. data/lib/nrser/functions.rb +0 -1
  44. data/lib/nrser/graph/tsorter.rb +41 -0
  45. data/lib/nrser/labs/core_ext/binding.rb +37 -0
  46. data/lib/nrser/labs/stash.rb +372 -0
  47. data/lib/nrser/{logging → log}/appender/sync.rb +3 -3
  48. data/lib/nrser/log/appender.rb +3 -0
  49. data/lib/nrser/{logging → log}/formatters/color.rb +47 -20
  50. data/lib/nrser/log/formatters/mixin.rb +270 -0
  51. data/lib/nrser/{logging → log}/formatters.rb +0 -0
  52. data/lib/nrser/log/logger.rb +229 -0
  53. data/lib/nrser/log/mixin.rb +56 -0
  54. data/lib/nrser/log.rb +723 -0
  55. data/lib/nrser/message.rb +24 -3
  56. data/lib/nrser/meta/source/location.rb +158 -0
  57. data/lib/nrser/meta.rb +1 -1
  58. data/lib/nrser/props/class_methods.rb +118 -0
  59. data/lib/nrser/props/immutable/hash.rb +111 -0
  60. data/lib/nrser/props/immutable/hash_variable.rb +82 -0
  61. data/lib/nrser/props/immutable/instance_variables.rb +48 -0
  62. data/lib/nrser/props/immutable/vector.rb +107 -0
  63. data/lib/nrser/props/instance_methods.rb +184 -0
  64. data/lib/nrser/props/metadata.rb +359 -0
  65. data/lib/nrser/props/mutable/instance_variables.rb +60 -0
  66. data/lib/nrser/props/mutable/stash.rb +199 -0
  67. data/lib/nrser/{meta/props → props}/prop.rb +217 -112
  68. data/lib/nrser/props/storage/instance_variable.rb +85 -0
  69. data/lib/nrser/props/storage/instance_variables.rb +67 -0
  70. data/lib/nrser/props/storage/key.rb +88 -0
  71. data/lib/nrser/props.rb +9 -0
  72. data/lib/nrser/refinements/sugar.rb +41 -0
  73. data/lib/nrser/refinements/types.rb +2 -2
  74. data/lib/nrser/refinements.rb +14 -16
  75. data/lib/nrser/rspex/example_group/describe_attribute.rb +24 -0
  76. data/lib/nrser/rspex/example_group/describe_called_with.rb +1 -6
  77. data/lib/nrser/rspex/example_group/{describe_use_case.rb → describe_case.rb} +6 -3
  78. data/lib/nrser/rspex/example_group/describe_class.rb +1 -0
  79. data/lib/nrser/rspex/example_group/describe_group.rb +29 -0
  80. data/lib/nrser/rspex/example_group/describe_instance_method.rb +2 -2
  81. data/lib/nrser/rspex/example_group/describe_message.rb +35 -0
  82. data/lib/nrser/rspex/example_group/describe_method.rb +23 -2
  83. data/lib/nrser/rspex/example_group/describe_module.rb +19 -0
  84. data/lib/nrser/rspex/example_group/describe_response_to.rb +32 -0
  85. data/lib/nrser/rspex/example_group/describe_section.rb +38 -0
  86. data/lib/nrser/rspex/example_group/describe_sent_to.rb +52 -0
  87. data/lib/nrser/rspex/example_group/describe_source_file.rb +49 -0
  88. data/lib/nrser/rspex/example_group/describe_spec_file.rb +41 -108
  89. data/lib/nrser/rspex/example_group/describe_when.rb +14 -7
  90. data/lib/nrser/rspex/example_group/describe_x.rb +39 -12
  91. data/lib/nrser/rspex/example_group/overrides.rb +66 -0
  92. data/lib/nrser/rspex/example_group.rb +20 -252
  93. data/lib/nrser/rspex/format.rb +83 -17
  94. data/lib/nrser/rspex.rb +4 -34
  95. data/lib/nrser/sugar/method_missing_forwarder.rb +50 -0
  96. data/lib/nrser/{env → sys/env}/path.rb +1 -2
  97. data/lib/nrser/{env.rb → sys/env.rb} +2 -1
  98. data/lib/nrser/sys.rb +5 -0
  99. data/lib/nrser/types/any.rb +36 -7
  100. data/lib/nrser/types/{array.rb → arrays.rb} +32 -81
  101. data/lib/nrser/types/attrs.rb +68 -15
  102. data/lib/nrser/types/booleans.rb +95 -34
  103. data/lib/nrser/types/bounded.rb +12 -10
  104. data/lib/nrser/types/combinators.rb +74 -37
  105. data/lib/nrser/types/errors/check_error.rb +86 -0
  106. data/lib/nrser/types/errors/from_string_error.rb +82 -0
  107. data/lib/nrser/types/factory.rb +91 -0
  108. data/lib/nrser/types/hashes.rb +171 -26
  109. data/lib/nrser/types/in.rb +25 -12
  110. data/lib/nrser/types/is.rb +50 -18
  111. data/lib/nrser/types/is_a.rb +52 -33
  112. data/lib/nrser/types/labels.rb +6 -33
  113. data/lib/nrser/types/maybe.rb +12 -4
  114. data/lib/nrser/types/nil.rb +24 -4
  115. data/lib/nrser/types/not.rb +6 -16
  116. data/lib/nrser/types/numbers.rb +94 -57
  117. data/lib/nrser/types/pairs.rb +57 -57
  118. data/lib/nrser/types/paths.rb +112 -133
  119. data/lib/nrser/types/responds.rb +64 -74
  120. data/lib/nrser/types/shape.rb +29 -24
  121. data/lib/nrser/types/strings.rb +25 -17
  122. data/lib/nrser/types/symbols.rb +19 -17
  123. data/lib/nrser/types/trees.rb +18 -70
  124. data/lib/nrser/types/tuples.rb +36 -40
  125. data/lib/nrser/types/type.rb +342 -91
  126. data/lib/nrser/types/when.rb +40 -18
  127. data/lib/nrser/types/where.rb +94 -9
  128. data/lib/nrser/types.rb +72 -63
  129. data/lib/nrser/version.rb +1 -1
  130. data/lib/nrser.rb +18 -18
  131. data/spec/lib/nrser/{functions/binding/template_spec.rb → core_ext/binding/erb_spec.rb} +5 -5
  132. data/spec/lib/nrser/{functions → core_ext}/enumerable/find_map_spec.rb +8 -6
  133. data/spec/lib/nrser/{refinements → core_ext}/hash_spec.rb +9 -22
  134. data/spec/lib/nrser/errors/abstract_method_error_spec.rb +12 -5
  135. data/spec/lib/nrser/functions/enumerable/{to_h_by_spec.rb → associate_spec.rb} +1 -1
  136. data/spec/lib/nrser/functions/merge_by_spec.rb +1 -1
  137. data/spec/lib/nrser/functions/tree/each_branch_spec.rb +3 -3
  138. data/spec/lib/nrser/functions/tree/transform_spec.rb +14 -15
  139. data/spec/lib/nrser/gem_ext/hamster/json_spec.rb +4 -0
  140. data/spec/lib/nrser/meta/source/location_spec.rb +86 -0
  141. data/spec/lib/nrser/props/immutable/hash_spec.rb +297 -0
  142. data/spec/lib/nrser/props/immutable/vector_spec.rb +296 -0
  143. data/spec/lib/nrser/{meta/props_spec.rb → props/original_props_spec.rb} +11 -16
  144. data/spec/lib/nrser/{meta/props → props}/to_and_from_data_spec.rb +10 -8
  145. data/spec/lib/nrser/refinements/array_spec.rb +2 -15
  146. data/spec/lib/nrser/refinements/erb_spec.rb +5 -7
  147. data/spec/lib/nrser/refinements/set_spec.rb +2 -15
  148. data/spec/lib/nrser/{env → sys/env}/path/insert_spec.rb +4 -2
  149. data/spec/lib/nrser/{env → sys/env}/path_spec.rb +4 -2
  150. data/spec/lib/nrser/types/array_spec.rb +8 -8
  151. data/spec/lib/nrser/types/paths_spec.rb +15 -18
  152. data/spec/spec_helper.rb +4 -0
  153. metadata +109 -69
  154. data/lib/nrser/ext/binding.rb +0 -36
  155. data/lib/nrser/ext/module.rb +0 -62
  156. data/lib/nrser/ext.rb +0 -8
  157. data/lib/nrser/functions/binding.rb +0 -76
  158. data/lib/nrser/functions/enumerable/map_keys.rb +0 -0
  159. data/lib/nrser/functions/enumerable/map_values.rb +0 -94
  160. data/lib/nrser/functions/hash/deep_merge.rb +0 -57
  161. data/lib/nrser/functions/hash/except_keys.rb +0 -44
  162. data/lib/nrser/functions/hash/slice_keys.rb +0 -43
  163. data/lib/nrser/functions/hash/stringify_keys.rb +0 -55
  164. data/lib/nrser/functions/hash/symbolize_keys.rb +0 -57
  165. data/lib/nrser/functions/hash/transform_keys.rb +0 -140
  166. data/lib/nrser/functions/module/methods.rb +0 -206
  167. data/lib/nrser/functions/module/source_locations.rb +0 -213
  168. data/lib/nrser/logging/appender.rb +0 -3
  169. data/lib/nrser/logging.rb +0 -353
  170. data/lib/nrser/meta/props/base.rb +0 -31
  171. data/lib/nrser/meta/props.rb +0 -357
  172. data/lib/nrser/refinements/array.rb +0 -133
  173. data/lib/nrser/refinements/binding.rb +0 -6
  174. data/lib/nrser/refinements/enumerator.rb +0 -5
  175. data/lib/nrser/refinements/exception.rb +0 -35
  176. data/lib/nrser/refinements/hash.rb +0 -150
  177. data/lib/nrser/refinements/module.rb +0 -5
  178. data/lib/nrser/refinements/object.rb +0 -42
  179. data/lib/nrser/refinements/open_struct.rb +0 -28
  180. data/lib/nrser/refinements/pathname.rb +0 -5
  181. data/lib/nrser/refinements/set.rb +0 -5
  182. data/lib/nrser/refinements/string.rb +0 -5
  183. data/lib/nrser/refinements/symbol.rb +0 -20
  184. data/lib/nrser/rspex/described.rb +0 -99
  185. data/spec/design/mapping_spec.rb +0 -42
  186. data/spec/lib/nrser/functions/hash_spec.rb +0 -41
  187. data/spec/lib/nrser/functions/string/truncate_spec.rb +0 -11
  188. data/spec/lib/nrser/refinements/truncate_spec.rb +0 -10
@@ -1,8 +1,21 @@
1
- # Refinements
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
2
5
  # =======================================================================
3
6
 
4
- require 'nrser/refinements'
5
- using NRSER
7
+ # Stdlib
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Deps
11
+ # -----------------------------------------------------------------------
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+ # Just require the errors here so we don't need to do it everywhere
17
+ require_relative './errors/check_error'
18
+ require_relative './errors/from_string_error'
6
19
 
7
20
 
8
21
  # Definitions
@@ -10,10 +23,6 @@ using NRSER
10
23
 
11
24
  module NRSER::Types
12
25
  class Type
13
- def self.short_name
14
- name.split('::').last
15
- end
16
-
17
26
 
18
27
  # Constructor
19
28
  # =====================================================================
@@ -82,88 +91,183 @@ module NRSER::Types
82
91
  end # #initialize
83
92
 
84
93
 
94
+ # Instance Methods
95
+ # ========================================================================
96
+
97
+ # @!group Display Instance Methods
98
+ # ------------------------------------------------------------------------
99
+
100
+ # What this type likes to be called (and displayed as by default).
101
+ #
102
+ # Custom names can be provided when constructing most types via the
103
+ # `name:` keyword, which allows thinking about composite and complicated
104
+ # types in simpler and application-specific terms.
105
+ #
106
+ # Realizing subclasses **should not** override this method - they should
107
+ # pass a `name:` keyword up to {#initialize}, which sets the `@name`
108
+ # instance variable that is then used here.
109
+ #
110
+ # If no name is provided to {#initialize}, this method will fall back to
111
+ # {#explain}.
112
+ #
113
+ # @return [String]
114
+ #
85
115
  def name
86
- @name || default_name
116
+ @name || explain
87
117
  end
88
118
 
89
- def default_name
90
- self.class.short_name
119
+
120
+ # A string that gives our best concise description of the type's logic,
121
+ # in particular exposing any composite types that it's made up of.
122
+ #
123
+ # Used as the {#name} when a custom one is not provided.
124
+ #
125
+ # Meant for inline display, so the result *should not* contain newlines.
126
+ #
127
+ # Realizing subclasses **should** override this method, as this
128
+ # implementation only returns the class' name (and just the last segment,
129
+ # for brevity's sake).
130
+ #
131
+ # @example Base implementation is not very interesting
132
+ # MyType = Class.new NRSER::Types::Type
133
+ # my_type = MyType.new
134
+ # my_type.explain
135
+ # # => "MyType"
136
+ #
137
+ # @return [String]
138
+ #
139
+ def explain
140
+ self.class.demod_name
91
141
  end
92
142
 
143
+ # @!endgroup Display Instance Methods # **********************************
144
+
145
+
146
+ # @!group Validation Instance Methods
147
+ # ------------------------------------------------------------------------
148
+ #
149
+ # The core of what a type does.
150
+ #
93
151
 
94
152
  # See if a value satisfies the type.
95
153
  #
154
+ # Realizing classes **must** implement this method.
155
+ #
156
+ # This implementation just defines the API; it always raises
157
+ # {NRSER::AbstractMethodError}.
158
+ #
96
159
  # @param [Object] value
97
160
  # Value to test for type satisfaction.
98
161
  #
99
162
  # @return [Boolean]
100
163
  # `true` if the `value` satisfies the type.
101
164
  #
102
- def test value
103
- raise NotImplementedError
165
+ def test? value
166
+ raise NRSER::AbstractMethodError.new( self, __method__ )
104
167
  end
105
168
 
169
+ # Old name for {#test?}.
170
+ #
171
+ # @deprecated
172
+ #
173
+ # @param (see #test?)
174
+ # @return (see #test?)
175
+ # @raise (see #test?)
176
+ #
177
+ def test value; test? value; end
106
178
 
107
- def check value, &make_fail_message
179
+
180
+ # Check that a `value` satisfies the type.
181
+ #
182
+ # @see #test?
183
+ #
184
+ # @return [Object]
185
+ # The value itself.
186
+ #
187
+ # @raise [NRSER::Types::CheckError]
188
+ # If the value does not satisfy this type.
189
+ #
190
+ def check! value, &details
108
191
  # success case
109
- return value if test value
110
-
111
- msg = if make_fail_message
112
- make_fail_message.call type: self, value: value
113
- else
114
- NRSER.squish <<-END
115
- value #{ value.inspect } failed check #{ self.to_s }
116
- END
117
- end
192
+ return value if test? value
118
193
 
119
- raise TypeError.new msg
194
+ raise NRSER::Types::CheckError.new \
195
+ value: value,
196
+ type: self,
197
+ details: details
120
198
  end
121
199
 
200
+ # Old name for {#check!} without the bang.
201
+ def check *args, &block; check! *args, &block; end
202
+
203
+ # @!endgroup Validation Instance Methods # *******************************
122
204
 
123
- # Overridden to customize behavior for the {#from_s} and {#to_data}
124
- # methods - those methods are always defined, but we have {#respond_to?}
125
- # return `false` if they lack the underlying instance variables needed
126
- # to execute.
205
+
206
+ # @!group Loading Values Instance Methods
207
+ # ------------------------------------------------------------------------
127
208
  #
128
- # @example
129
- # t1 = t.where { |value| true }
130
- # t1.respond_to? :from_s
131
- # # => false
132
- #
133
- # t2 = t.where( from_s: ->(s){ s.split ',' } ) { |value| true }
134
- # t2.respond_to? :from_s
135
- # # => true
209
+ # Types include facilities for loading values from representations and
210
+ # encodings.
136
211
  #
137
- # @param [Symbol | String] name
138
- # Method name to ask about.
212
+ # This was initially driven by the desire to use types to
213
+ # declare CLI parameter schemas, as way to dispatch with the often
214
+ # limited and arbitrary support most "CLI frameworks" have for declaring
215
+ # option types - things like "you can have an integer, and you can
216
+ # have an array, but you can't have an array of integers".
139
217
  #
140
- # @param [Boolean] include_all
141
- # IDK, part of Ruby API that is passed up to `super`.
218
+ # By using compossible types that can load values from strings we get a
219
+ # system where you can easily declare whatever complex and granular types
220
+ # you desire and have the machine automatically load and validate them,
221
+ # as well as provide reasonable generated feedback when something doesn't
222
+ # meet expectations, which has worked out quite well so far start to cut
223
+ # down the amount of repetitive and error-prone "did I get what I need?
224
+ # No, did I get exactly what I need?" bullshit in receiving data.
225
+ #
226
+ # This approach is now being expanded to "data" - an ill-formed concept
227
+ # I've been brewing of "reasonable common and portable data
228
+ # representation" and the {NRSER::Props} system, which has been coming
229
+ # along as well.
230
+ #
231
+
232
+ # Test if the type knows how to load values from strings.
233
+ #
234
+ # Looks for the `@from_s` instance variable or a `#custom_from_s`
235
+ # method.
236
+ #
237
+ # @note
238
+ # When this method returns `true` it simply indicates that some method
239
+ # of loading from strings exists - the load itself can of course still
240
+ # fail.
241
+ #
242
+ # Realizing classes should only need to override this method to limited or
243
+ # expand the scope relative to parameterized types.
142
244
  #
143
245
  # @return [Boolean]
144
246
  #
145
- def respond_to? name, include_all = false
146
- if name == :from_s || name == 'from_s'
147
- has_from_s?
148
- elsif name == :to_data || name == 'to_data'
149
- has_to_data?
150
- else
151
- super name, include_all
152
- end
153
- end # #respond_to?
247
+ def has_from_s?
248
+ !@from_s.nil? ||
249
+ # Need the `true` second arg to include protected methods
250
+ respond_to?( :custom_from_s, true )
251
+ end
154
252
 
155
253
 
156
- # Load a value of this type from a string representation by passing `s`
157
- # to the {@from_s} {Proc}.
254
+ # Load a value of this type from a string representation by passing
255
+ # `string` to the {@from_s} {Proc}.
158
256
  #
159
- # Checks the value {@from_s} returns with {#check} before returning it, so
257
+ # Checks the value {@from_s} returns with {#check!} before returning it, so
160
258
  # you know it satisfies this type.
161
259
  #
162
- # @param [String] s
260
+ # Realizing classes **should not** need to override this - they can define
261
+ # a `#custom_from_s` instance method for it to use, allowing individual
262
+ # types to still override that by providing a `from_s:` proc keyword
263
+ # arg at construction. This also lets them avoid checking the returned
264
+ # value, since we do so here.
265
+ #
266
+ # @param [String] string
163
267
  # String representation.
164
268
  #
165
269
  # @return [Object]
166
- # Value that has passed {#check}.
270
+ # Value that has passed {#check!}.
167
271
  #
168
272
  # @raise [NoMethodError]
169
273
  # If this type doesn't know how to load values from strings.
@@ -180,34 +284,67 @@ module NRSER::Types
180
284
  # @raise [TypeError]
181
285
  # If the value loaded does not pass {#check}.
182
286
  #
183
- def from_s s
184
- if @from_s.nil?
185
- raise NoMethodError, "#from_s not defined"
287
+ def from_s string
288
+ unless has_from_s?
289
+ raise NoMethodError, "#from_s not defined for type #{ name }"
186
290
  end
187
291
 
188
- check @from_s.call( s )
189
- end
190
-
191
-
192
- def from_data data
193
- if @from_data.nil?
194
- raise NoMethodError, "#from_data not defined"
292
+ value = if @from_s
293
+ @from_s.call string
294
+ else
295
+ custom_from_s string
195
296
  end
196
297
 
197
- check @from_data.call( data )
298
+ check! value
198
299
  end
199
300
 
200
301
 
201
- # Test if the type knows how to load values from strings.
302
+ # Test if the type can load values from "data" - basic values and
303
+ # collections like {Array} and {Hash} forming tree-like structures.
202
304
  #
203
- # If this method returns `true`, then we expect {#from_s} to succeed.
305
+ # Realizing classes *may* need to override this to limited or expand
306
+ # responses relative to parameterized types.
204
307
  #
205
308
  # @return [Boolean]
206
309
  #
207
- def has_from_s?
208
- ! @from_s.nil?
310
+ def has_from_data?
311
+ !@from_data.nil? ||
312
+ # Need the `true` second arg to include protected methods
313
+ respond_to?( :custom_from_data, true )
314
+ end
315
+
316
+
317
+ # Try to load a value from "data" - basic values and
318
+ # collections like {Array} and {Hash} forming tree-like structures.
319
+ #
320
+ # @param [*] data
321
+ # Data to try to load from.
322
+ #
323
+ # @raise [NoMethodError]
324
+ # If {#has_from_data?} returns `false`.
325
+ #
326
+ # @raise [NRSER::Types::CheckError]
327
+ # If the load result does not satisfy the type (see {#check!}).
328
+ #
329
+ def from_data data
330
+ unless has_from_data?
331
+ raise NoMethodError, "#from_data not defined"
332
+ end
333
+
334
+ value = if @from_data
335
+ @from_data.call data
336
+ else
337
+ custom_from_data data
338
+ end
339
+
340
+ check! value
209
341
  end
210
342
 
343
+ # @!endgroup Loading Values Instance Methods # ***************************
344
+
345
+
346
+ # @!group Dumping Values Instance Methods
347
+ # ------------------------------------------------------------------------
211
348
 
212
349
  # Test if the type has custom information about how to convert it's values
213
350
  # into "data" - structures and values suitable for transportation and
@@ -222,11 +359,6 @@ module NRSER::Types
222
359
  end # #has_to_data?
223
360
 
224
361
 
225
- def has_from_data?
226
- ! @from_data.nil?
227
- end
228
-
229
-
230
362
  # Dumps a value of this type to "data" - structures and values suitable
231
363
  # for transport and storage, such as dumping to JSON or YAML, etc.
232
364
  #
@@ -244,22 +376,67 @@ module NRSER::Types
244
376
  @to_data.call value
245
377
  end # #to_data
246
378
 
379
+ # @!endgroup Dumping Values Instance Methods # ***************************
247
380
 
248
- # Language Inter-Op
249
- # =====================================================================
250
381
 
382
+ # @!group Language Integration Instance Methods
383
+ # ------------------------------------------------------------------------
251
384
 
385
+ # Proxies to {#name}.
386
+ #
252
387
  # @return [String]
253
- # a brief string description of the type - just it's {#name} surrounded
254
- # by some back-ticks to make it easy to see where it starts and stops.
255
388
  #
256
- def to_s
257
- "{ x ∈ #{ name } }"
389
+ def to_s; name; end
390
+
391
+
392
+ # Hook into Ruby's *case subsumption* operator to allow usage in `case`
393
+ # statements! Forwards to {#test?}.
394
+ #
395
+ # @param value (see #test?)
396
+ # @return (see #test?)
397
+ #
398
+ def === value
399
+ test? value
258
400
  end
259
401
 
260
402
 
261
- # Inspecting
262
- # ---------------------------------------------------------------------
403
+ # Overridden to customize behavior for the {#from_s}, {#from_data} and
404
+ # {#to_data} methods - those methods are always defined, but we have
405
+ # {#respond_to?} return `false` if they lack the underlying instance
406
+ # variables needed to execute.
407
+ #
408
+ # @example
409
+ # t1 = t.where { |value| true }
410
+ # t1.respond_to? :from_s
411
+ # # => false
412
+ #
413
+ # t2 = t.where( from_s: ->(s){ s.split ',' } ) { |value| true }
414
+ # t2.respond_to? :from_s
415
+ # # => true
416
+ #
417
+ # @param [Symbol | String] name
418
+ # Method name to ask about.
419
+ #
420
+ # @param [Boolean] include_all
421
+ # IDK, part of Ruby API that is passed up to `super`.
422
+ #
423
+ # @return [Boolean]
424
+ #
425
+ def respond_to? name, include_all = false
426
+ case name.to_sym
427
+ when :from_s
428
+ has_from_s?
429
+ when :from_data
430
+ has_from_data?
431
+ when :to_data
432
+ has_to_data?
433
+ else
434
+ super name, include_all
435
+ end
436
+ end # #respond_to?
437
+
438
+
439
+ ### Inspecting
263
440
  #
264
441
  # Due to their combinatoric nature, types can quickly become large data
265
442
  # hierarchies, and the built-in {#inspect} will produce a massive dump
@@ -270,24 +447,98 @@ module NRSER::Types
270
447
  #
271
448
  # As a solution, we alias the built-in `#inspect` as {#builtin_inspect},
272
449
  # so it's available in situations where you really want all those gory
273
- # details, and point {#inspect} to {#to_s}.
450
+ # details, and point {#inspect} to {#explain}.
274
451
  #
275
452
 
276
453
  alias_method :builtin_inspect, :inspect
277
- alias_method :inspect, :to_s
454
+ def inspect
455
+ name = self.name
456
+ explain = self.explain
457
+
458
+ if name == explain
459
+ explain
460
+ else
461
+ "#{ name } := #{ explain }"
462
+ end
463
+ end
278
464
 
465
+ # @!endgroup Language Integration Instance Methods # *********************
279
466
 
280
- # Hook into Ruby's *case subsumption* operator to allow usage in `case`
281
- # statements!
467
+
468
+ # @!group Derivation Instance Methods
469
+ # ------------------------------------------------------------------------
282
470
  #
283
- # TODO Want to switch {NRSER::Types.match} over to use `===`!
471
+ # Methods for deriving new types from `self`.
284
472
  #
285
- # @param value (see #test)
286
- # @return (see #test)
287
- #
288
- def === value
289
- test value
473
+
474
+ # Return a *union* type satisfied by values that satisfy either `self`
475
+ # *or* and of `others`.
476
+ #
477
+ # @param [*] other
478
+ # Values passed through {NRSER::Types.make} to create the other types.
479
+ #
480
+ # @return [NRSER::Types::Union]
481
+ #
482
+ def union *others
483
+ require_relative './combinators'
484
+
485
+ NRSER::Types.union self, *others
486
+ end # #union
487
+
488
+ alias_method :|, :union
489
+ alias_method :or, :union
490
+
491
+
492
+ # Return an *intersection* type satisfied by values that satisfy both
493
+ # `self` *and* all of `others`.
494
+ #
495
+ # @param [Array] *others
496
+ # Values passed through {NRSER::Types.make} to create the other types.
497
+ #
498
+ # @return [NRSER::Types::Intersection]
499
+ #
500
+ def intersection *others
501
+ require_relative './combinators'
502
+
503
+ NRSER::Types.intersection self, *others
504
+ end # #intersection
505
+
506
+ alias_method :&, :intersection
507
+ alias_method :and, :intersection
508
+
509
+
510
+ # Return an *exclusive or* type satisfied by values that satisfy either
511
+ # `self` *or* `other` *but not both*.
512
+ #
513
+ # @param [*] other
514
+ # Value passed through {NRSER::Types.make} to create the other type.
515
+ #
516
+ # @return [NRSER::Types::Intersection]
517
+ #
518
+ def xor *others
519
+ require_relative './combinators'
520
+
521
+ NRSER::Types.xor self, *others
522
+ end # #^
523
+
524
+ alias_method :^, :xor
525
+
526
+
527
+ # Return a "negation" type satisfied by all values that do *not* satisfy
528
+ # `self`.
529
+ #
530
+ # @return [NRSER::Types::Not]
531
+ #
532
+ def not
533
+ require_relative './not'
534
+
535
+ NRSER::Types.not self
290
536
  end
291
537
 
538
+ alias_method :~, :not
539
+
540
+ # @!endgroup Derivation Instance Methods # *******************************
541
+
542
+
292
543
  end # Type
293
544
  end # NRSER::Types
@@ -1,24 +1,13 @@
1
+ # encoding: UTF-8
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # Requirements
4
5
  # =======================================================================
5
6
 
6
- # Stdlib
7
- # -----------------------------------------------------------------------
8
-
9
- # Deps
10
- # -----------------------------------------------------------------------
11
-
12
7
  # Project / Package
13
8
  # -----------------------------------------------------------------------
14
9
 
15
-
16
- # Refinements
17
- # =======================================================================
18
-
19
-
20
- # Declarations
21
- # =======================================================================
10
+ require_relative './type'
22
11
 
23
12
 
24
13
  # Definitions
@@ -26,9 +15,31 @@
26
15
 
27
16
  module NRSER::Types
28
17
 
18
+ # Wraps an object as a type, using Ruby's "case equality" `===` to test
19
+ # membership (like a `when` clause in a `case` expression).
20
+ #
21
+ # Deals with some data loading too.
22
+ #
23
+ # @note
24
+ # This was kinda hacked in when my idiot-ass figured out that all this
25
+ # types BS could fit in real well with Ruby's `===`, allowing types to
26
+ # be used in `when` clauses.
27
+ #
28
+ # Previously, {NRSER::Types.make} used to see if something was a module,
29
+ # and turn those into `is_a` types, and turn everything else into
30
+ # `is`, but this kind of sucked for a bunch of reasons I don't totally
31
+ # remember.
32
+ #
33
+ # Now, if a value is not a special case (like `nil`) or already a type,
34
+ # {NRSER::Types.make} turns it into a {When}.
35
+ #
36
+ # {When} instances are totally Ruby-centric, and are thus mostly to
37
+ # support in-runtime testing - you wouldn't want a {When} type to
38
+ # be part of an API schema or something - but they're really nice for
39
+ # the internal stuff.
40
+ #
29
41
  class When < Type
30
42
 
31
-
32
43
  # The wrapped {Object} whose `#===` will be used to test membership.
33
44
  #
34
45
  # @return [Object]
@@ -49,15 +60,26 @@ module NRSER::Types
49
60
  # Instance Methods
50
61
  # ======================================================================
51
62
 
52
- def test value
63
+ def test? value
53
64
  @object === value
54
65
  end
55
66
 
56
67
 
57
- def default_name
58
- @object.to_s
68
+ def explain
69
+ @object.inspect
70
+ end
71
+
72
+
73
+ def has_from_s?
74
+ @from_s || object.respond_to?( :from_s )
59
75
  end
60
76
 
77
+
78
+ def custom_from_s string
79
+ object.from_s string
80
+ end
81
+
82
+
61
83
  # If {#object} responds to `#from_data`, call that and check results.
62
84
  #
63
85
  # Otherwise, forward up to {NRSER::Types::Type#from_data}.
@@ -95,7 +117,7 @@ module NRSER::Types
95
117
  end # class When
96
118
 
97
119
 
98
- def self.when value, **options
120
+ def_factory :when do |value, **options|
99
121
  When.new value, **options
100
122
  end
101
123