nrser 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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