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
@@ -20,7 +20,7 @@ module NRSER::RSpex::ExampleGroup
20
20
  #
21
21
  # @param *description (see #describe_x)
22
22
  #
23
- # @param [Hash<Symbol, Object>] **metadata
23
+ # @param [Hash<Symbol, Object>] metadata
24
24
  # RSpec metadata to set for the example group.
25
25
  #
26
26
  # See the `metadata` keyword argument to {#describe_x}.
@@ -38,10 +38,10 @@ module NRSER::RSpex::ExampleGroup
38
38
  # 2. Built description would need to be conditional on what metadata
39
39
  # was found.
40
40
  #
41
- # @param [String] description:
41
+ # @param [String] description
42
42
  # A description of the spec file to add to the RSpec description.
43
43
  #
44
- # @param [String] spec_path:
44
+ # @param [String] spec_path
45
45
  # The path to the spec file (just feed it `__FILE__`).
46
46
  #
47
47
  # Probably possible to extract this somehow without having to provide it?
@@ -85,5 +85,7 @@ module NRSER::RSpex::ExampleGroup
85
85
  &describe_x_body
86
86
 
87
87
  end # #describe_spec_file
88
+
89
+ alias_method :SPEC_FILE, :describe_spec_file
88
90
 
89
91
  end # module NRSER::RSpex::ExampleGroup
@@ -9,7 +9,7 @@ module NRSER::RSpex::ExampleGroup
9
9
  #
10
10
  # @param *description (see #describe_x)
11
11
  #
12
- # @param [Hash<Symbol, Object>] **bindings
12
+ # @param [Hash<Symbol, Object>] bindings
13
13
  # See the `bindings` keyword arg in {#describe_x}.
14
14
  #
15
15
  # @param &body (see #describe_x)
@@ -30,6 +30,7 @@ module NRSER::RSpex::ExampleGroup
30
30
  # Short names (need `_` pre 'cause of `when` Ruby keyword, and suffix fucks
31
31
  # up auto-indent in Atom/VSCode)
32
32
  alias_method :_when, :describe_when
33
+ alias_method :WHEN, :describe_when
33
34
 
34
35
 
35
36
  end # module NRSER::RSpex::ExampleGroup
@@ -11,18 +11,18 @@ module NRSER::RSpex::ExampleGroup
11
11
  # if you want the RSpex functionality but absolutely have to set some
12
12
  # metadata key that we use for something else.
13
13
  #
14
- # @param [Array] *description
14
+ # @param [Array] description
15
15
  # Optional list of elements that compose the custom description.
16
16
  #
17
17
  # Will be passed to {NRSER::RSpex::Format.description} to produce the
18
18
  # string value that is in turn passed to {RSpec.describe}.
19
19
  #
20
- # @param [Symbol] type:
20
+ # @param [Symbol] type
21
21
  # The RSpex "type" of the example group, which is used to determine the
22
22
  # prefix of the final description and is assigned to the `:type` metadata
23
23
  # key.
24
24
  #
25
- # @param [Hash<Symbol, Object>] metadata:
25
+ # @param [Hash<Symbol, Object>] metadata
26
26
  # [RSpec metadata][] to add to the new example group.
27
27
  #
28
28
  # In addition to the keys RSpec will reject, we prohibit `:type` *unless*
@@ -33,7 +33,7 @@ module NRSER::RSpex::ExampleGroup
33
33
  #
34
34
  # [RSpec metadata]: https://relishapp.com/rspec/rspec-core/docs/metadata/user-defined-metadata
35
35
  #
36
- # @param [Hash<Symbol, Object>] bindings:
36
+ # @param [Hash<Symbol, Object>] bindings
37
37
  # Name to value pairs to bind in the new example group.
38
38
  #
39
39
  # All values will be bound at the example group and example levels -
@@ -41,7 +41,7 @@ module NRSER::RSpex::ExampleGroup
41
41
  # group level, while they will be automatically unwrapped at the
42
42
  # example level (as the requisite context is available there).
43
43
  #
44
- # @param [Boolean] bind_subject:
44
+ # @param [Boolean] bind_subject
45
45
  # When `true` (and there is a `subject_block`) bind the `subject` inside
46
46
  # the new example group.
47
47
  #
@@ -114,14 +114,6 @@ module NRSER::RSpex::Format
114
114
  end
115
115
 
116
116
 
117
- # @todo Document format_type method.
118
- #
119
- # @param [type] arg_name
120
- # @todo Add name param description.
121
- #
122
- # @return [return_type]
123
- # @todo Document return value.
124
- #
125
117
  def self.prepend_type type, description
126
118
  return description if type.nil?
127
119
 
@@ -160,13 +152,6 @@ module NRSER::RSpex::Format
160
152
  end
161
153
 
162
154
 
163
- # @todo Document format method.
164
- #
165
- # @param [type] arg_name
166
- # @todo Add name param description.
167
- #
168
- # @return [String]
169
- #
170
155
  def self.description *parts, type: nil
171
156
  parts.
172
157
  flat_map { |part|
@@ -21,7 +21,7 @@ class NRSER::MethodMissingForwarder < BasicObject
21
21
  # Instantiate a new `NRSER::MethodMissingForwarder` holding the forwarding
22
22
  # block.
23
23
  #
24
- # @param [Proc<(symbol:Symbol, *args, &block)>] &forwarder
24
+ # @param [Proc<(symbol:Symbol, *args, &block)>] forwarder
25
25
  # Block that will receive all calls to {#method_missing}.
26
26
  #
27
27
  def initialize &forwarder
@@ -37,10 +37,10 @@ class NRSER::MethodMissingForwarder < BasicObject
37
37
  # @param [Symbol] symbol
38
38
  # The name of the method that was called.
39
39
  #
40
- # @param [Array] *args
40
+ # @param [Array] args
41
41
  # Any parameters the missing method was called with.
42
42
  #
43
- # @param [Proc?] &block
43
+ # @param [Proc?] block
44
44
  # The block the method was called with, if any.
45
45
  #
46
46
  def method_missing symbol, *args, &block
@@ -92,7 +92,7 @@ class NRSER::Sys::Env::Path
92
92
  # @param [String] path
93
93
  # Path to test against.
94
94
  #
95
- # @param [Array<String | Proc<String=>Boolean> | Regexp>] *patterns
95
+ # @param [Array<String | Proc<String=>Boolean> | Regexp>] patterns
96
96
  # Patterns to test:
97
97
  #
98
98
  # - `String` - test if it and `path` are equal (`==`)
@@ -126,14 +126,6 @@ class NRSER::Sys::Env::Path
126
126
  end # .matches_pattern?
127
127
 
128
128
 
129
- # @todo Document from_env method.
130
- #
131
- # @param [type] arg_name
132
- # @todo Add name param description.
133
- #
134
- # @return [return_type]
135
- # @todo Document return value.
136
- #
137
129
  def self.from_ENV env_key
138
130
  new ENV[env_key.to_s], env_key: env_key
139
131
  end # .from_env
@@ -260,7 +252,7 @@ class NRSER::Sys::Env::Path
260
252
  #
261
253
  # @see http://www.rubydoc.info/gems/hamster/Hamster/Vector#each-instance_method
262
254
  #
263
- # @param [nil | Proc<(String)=>*>] &block
255
+ # @param [nil | Proc<(String)=>*>] block
264
256
  # When present, block will be called once for each string path in this
265
257
  # object. First path is most prominent, down to least last.
266
258
  #
@@ -302,23 +294,5 @@ class NRSER::Sys::Env::Path
302
294
  @value.to_a
303
295
  end
304
296
 
305
- protected
306
- # ========================================================================
307
-
308
- # Internal method to remove paths that have already been normalized.
309
- #
310
- # @param [type] arg_name
311
- # @todo Add name param description.
312
- #
313
- # @return [return_type]
314
- # @todo Document return value.
315
- #
316
- def remove_paths paths
317
- # method body...
318
- end # #remove_paths
319
-
320
-
321
- # end protected
322
-
323
297
 
324
298
  end # class NRSER::Env::Path
data/lib/nrser/types.rb CHANGED
@@ -15,7 +15,7 @@ require 'nrser/log'
15
15
 
16
16
  # Stuff to help you define, test, check and match types in Ruby.
17
17
  #
18
- # {include:file:lib/nrser/types/README.md}
18
+ # Read the documentation {file:lib/nrser/types/README.md here}.
19
19
  #
20
20
  module NRSER::Types
21
21
 
@@ -29,7 +29,10 @@ module NRSER::Types
29
29
  # Mixins
30
30
  # ========================================================================
31
31
 
32
+ # Add `.def_type` to define type factories
32
33
  extend Factory
34
+
35
+ # Add `.logger` and `#logger`.
33
36
  include NRSER::Log::Mixin
34
37
 
35
38
 
@@ -38,8 +41,18 @@ module NRSER::Types
38
41
 
39
42
  L_PAREN = '(' # '❪'
40
43
  R_PAREN = ')' # '❫'
41
- RESPONDS_WITH = '→'
42
- ASSOC = '=>'
44
+ RESPONDS_WITH = '→' # '->'
45
+ ASSOC = '=>' # terrible, don't use: '⇒'
46
+ LEQ = '≤'
47
+ GEQ = '≥'
48
+ COMPLEXES = 'ℂ'
49
+ REALS = 'ℝ'
50
+ INTEGERS = 'ℤ'
51
+ RATIONALS = 'ℚ'
52
+ UNION = '∪'
53
+ AND = '&'
54
+ NOT = '¬' # '~'
55
+ COMPLEMENT = '∖'
43
56
 
44
57
 
45
58
  # Module Methods
@@ -67,11 +80,11 @@ module NRSER::Types
67
80
  #
68
81
  def self.make value
69
82
  if value.nil?
70
- self.nil
83
+ self.Nil
71
84
  elsif value.is_a? NRSER::Types::Type
72
85
  value
73
86
  else
74
- self.when value
87
+ self.When value
75
88
  end
76
89
  end
77
90
 
@@ -104,13 +117,34 @@ module NRSER::Types
104
117
  make( type ).check! value
105
118
  end
106
119
 
107
- # Old name
108
- singleton_class.send :alias_method, :check, :check!
120
+
121
+ # Old bang-less name for {.check!}. We like out bangs around here.
122
+ #
123
+ # @deprecated
124
+ #
125
+ # @param (see .check!)
126
+ # @return (see .check!)
127
+ # @raise (see .check!)
128
+ #
129
+ def self.check value, type
130
+ logger.deprecated \
131
+ method: __method__,
132
+ alternative: "NRSER::Types.check!"
133
+
134
+ check! value, type
135
+ end
109
136
 
110
137
 
111
138
  # Create a {NRSER::Types::Type} from `type` with {.make} and test if
112
139
  # `value` satisfies it.
113
140
  #
141
+ # @param [Object] value
142
+ # Value to test for membership.
143
+ #
144
+ # @param [TYPE] type
145
+ # Type to see if value satisfies. Passed through {.make} to make sure it's
146
+ # a {Type} first.
147
+ #
114
148
  # @return [Boolean]
115
149
  # `true` if `value` satisfies `type`.
116
150
  #
@@ -118,13 +152,29 @@ module NRSER::Types
118
152
  make(type).test value
119
153
  end
120
154
 
155
+
156
+ # Old question-less name for {.test?}. We like our marks around here.
157
+ #
158
+ # @param (see .test?)
159
+ # @return (see .test?)
160
+ # @raise (see .test?)
161
+ #
162
+ def self.test value, type
163
+ logger.deprecated \
164
+ method: __method__,
165
+ alternative: "NRSER::Types.test?"
166
+
167
+ test? value, type
168
+ end # .test
169
+
121
170
  # Old name
122
171
  singleton_class.send :alias_method, :test, :test?
123
172
 
124
173
 
174
+ # My own shitty version of pattern matching!
175
+ #
125
176
  # @todo
126
- # Switch {NRSER::Types.match} to use `===`! Should allow us to avoid
127
- # making types for everything?
177
+ # Doc this crap.
128
178
  #
129
179
  def self.match value, *clauses
130
180
  if clauses.empty?
@@ -213,11 +263,11 @@ require_relative './types/is_a'
213
263
  require_relative './types/where'
214
264
  require_relative './types/combinators'
215
265
  require_relative './types/maybe'
216
- require_relative './types/attrs'
266
+ require_relative './types/attributes'
217
267
  require_relative './types/in'
218
268
 
219
269
  require_relative './types/when'
220
- require_relative './types/any'
270
+ require_relative './types/top'
221
271
  require_relative './types/booleans'
222
272
 
223
273
  # Requires `booleans`
@@ -232,5 +282,6 @@ require_relative './types/hashes'
232
282
  require_relative './types/paths'
233
283
  require_relative './types/tuples'
234
284
  require_relative './types/pairs'
235
- require_relative './types/trees'
285
+ require_relative './types/collections'
236
286
  require_relative './types/shape'
287
+ require_relative './types/selector'
@@ -0,0 +1,76 @@
1
+ About NRSER::Types
2
+ ========================================================================
3
+
4
+ Ah, Neil's Shitty Type System (NSTS).
5
+
6
+ Kinda like [tcomb][], but for for Ruby, and worse.
7
+
8
+ Anyways I needed this because I needed some re-usable way for things to make sure they were in some reasonable state at runtime. And it kind-of works for that so far.
9
+
10
+ [tcomb]: https://github.com/gcanti/tcomb
11
+
12
+
13
+ Structure & Design
14
+ ------------------------------------------------------------------------
15
+
16
+ At the bottom of everything, there are *type classes*... which are Ruby classes
17
+ that descend from {NRSER::Types::Type}. We call instances of these classes
18
+ *types*.
19
+
20
+ Types essentially do one thing: they have a {NRSER::Types::Type#test?} method
21
+ that accepts a single arguments and returns a boolean. If the type returns
22
+ `true` when `#test?` is called with an object, then that object is a member of
23
+ that type. That's it.
24
+
25
+ Some of those classes, like the universal type {NRSER::Types::Top} - of which
26
+ every object is a member ({NRSER::Types::Top#test?} always returns `true`) - are
27
+ essentially singletons, but many type classes parameterize over values that
28
+ instances hold as variables.
29
+
30
+ Types are designed to fundamentally immutable once constructed, through the
31
+ standard practice of storing state in instance variables without write
32
+ functions (of course, use of methods like `#instance_variable_set` will still
33
+ mutate types - please don't do this, since any future caching will likely
34
+ reuse instances).
35
+
36
+ API is basically a collection of module class methods attached to
37
+ {NRSER::Types}, like {NRSER::Types.str} or {NRSER::Types.non_neg_int}.
38
+
39
+
40
+ ### Type Maker Method Names
41
+
42
+ I generally went with "short" type names, IDK, to keep things short, and maybe to conflict a bit less with other common names in Ruby.
43
+
44
+ Some examples:
45
+
46
+ - `str`
47
+ - `bool`
48
+ - `non_neg_int`
49
+
50
+ You get the idea. Many have their longer names added as aliases, which I like... would rather have it "just work" with what you type than have to remember what exactly we chose to call stuff.
51
+
52
+
53
+ ### What to Expect
54
+
55
+ These method should all return {NRSER::Types::Type} instances, though many will use a refined subclass of {NRSER::Types::Type} like {NRSER::Types::Bounded} or whatever.
56
+
57
+ I picked this functional approach over a more I guess standard object-oriented architecture - where every type would be it's own class and you would instantiate them - because it's along the line of how [tcomb][] is designed and I think it works pretty well there. It also provides a lot of flexibility as far as combinators and such: we will return a {NRSER::Types::Type} that meets your needs, making no further contract, which allows us a lot more freedom to muck around in the back end.
58
+
59
+
60
+ ### Them Options
61
+
62
+ The {NRSER::Types} module methods should all accept a `options` hash as the last argument that will eventually find it's way up to {NRSER::Types::Type#initialize}. Many should accept other arguments as it makes sense.
63
+
64
+ In cases where the type maker method wants a keyword hash of it's own, I think I've been separating them out into two arguments, like:
65
+
66
+ def some_type kwds, options = {}
67
+ ...
68
+ end
69
+
70
+ which is a bit of a pain 'cause you gotta use parens if you want to pass options as well as keywords:
71
+
72
+ some_type( {x: 1, y: 3}, name: 'SomeType' )
73
+
74
+ but you don't run into any conflicts between options and keywords or whatever else, and it's nice to just pick a style and stick with it rather than have to do one or two different 'cause of a conflict.
75
+
76
+
@@ -8,161 +8,216 @@
8
8
  # -----------------------------------------------------------------------
9
9
 
10
10
  require_relative './is_a'
11
+ require_relative './top'
12
+
13
+
14
+ # Namespace
15
+ # ========================================================================
16
+
17
+ module NRSER
18
+ module Types
11
19
 
12
20
 
13
21
  # Definitions
14
22
  # =======================================================================
23
+
24
+ # Arrays!
25
+ #
26
+ # @note
27
+ # Construct {ArrayType} types using the {.Array} factory.
28
+ #
29
+ # @todo
30
+ # Just call this Array?!
31
+ #
32
+ # Combine with arrays of a type?!
33
+ #
34
+ class ArrayType < IsA
35
+ # Default value to split strings with in {#from_s} if the string provided
36
+ # does is not recognized as an encoding format (as of writing, JSON is
37
+ # the only format we attempt to detect).
38
+ #
39
+ # Splits
40
+ DEFAULT_SPLIT_WITH = /\,\s*/m
15
41
 
16
- module NRSER::Types
42
+ def initialize split_with: DEFAULT_SPLIT_WITH, **options
43
+ super ::Array, **options
44
+ @split_with = split_with
45
+ end
17
46
 
18
- class ArrayType < IsA
19
- # Default value to split strings with in {#from_s} if the string provided
20
- # does is not recognized as an encoding format (as of writing, JSON is
21
- # the only format we attempt to detect).
22
- #
23
- # Splits
24
- DEFAULT_SPLIT_WITH = /\,\s*/m
25
-
26
- def initialize split_with: DEFAULT_SPLIT_WITH, **options
27
- super ::Array, **options
28
- @split_with = split_with
29
- end
30
-
31
-
32
- def item_type; NRSER::Types.any; end
33
-
34
-
35
- # Called on an array of string items that have been split
36
- # from a single string by {#from_s} to convert each individual item before
37
- # {#check} is called on the value.
38
- #
39
- # {NRSER::Types::ArrayType} implementation is a no-op that just returns
40
- # `items` - this method is in place for subclasses to override.
41
- #
42
- # @param [Array<String>] items
43
- #
44
- # @return [Array]
45
- #
46
- def items_from_strings items
47
- items
48
- end
49
-
50
-
51
- def custom_from_s string
52
- # Does it looks like a JSON array?
53
- if NRSER.looks_like_json_array? string
54
- # It does! Load it
55
- begin
56
- return JSON.load( string )
57
- rescue
58
- # pass - if we failed to load as JSON, it may just not be JSON, and
59
- # we can try the split approach below.
60
- end
61
- end
62
-
63
- # Split it with the splitter and check that
64
- items_from_strings( string.split( @split_with ) )
47
+
48
+ def item_type; NRSER::Types.Top; end
49
+
50
+
51
+ # @!group Display Instance Methods
52
+ # ------------------------------------------------------------------------
53
+
54
+ def default_name
55
+ if item_type == NRSER::Types.Top
56
+ 'Array'
57
+ else
58
+ "Array<#{ item_type.name }>"
65
59
  end
66
-
67
- end # ArrayType
60
+ end
61
+
62
+
63
+ def default_symbolic
64
+ "[#{ item_type.symbolic }]"
65
+ end
66
+
67
+ # @!endgroup Display Instance Methods # ************************************
68
68
 
69
69
 
70
- # Type for arrays where every entry satisfies a specific type.
70
+ # Called on an array of string items that have been split
71
+ # from a single string by {#from_s} to convert each individual item before
72
+ # {#check} is called on the value.
71
73
  #
72
- # Broken out from {ArrayType} so that {TupleType} can inherit from
73
- # {ArrayType} and get share it's string handling functionality without
74
- # receiving the entry type stuff (which it handles differently).
74
+ # {NRSER::Types::ArrayType} implementation is a no-op that just returns
75
+ # `items` - this method is in place for subclasses to override.
75
76
  #
76
- class ArrayOfType < ArrayType
77
-
78
- # Attributes
79
- # ======================================================================
80
-
81
- # Type that all items must satisfy for an array to be a member of this
82
- # type.
83
- #
84
- # @return [NRSER::Types::Type]
85
- #
86
- attr_reader :item_type
87
-
88
-
89
- # Constructor
90
- # ======================================================================
91
-
92
- # Instantiate a new `ArrayOfType`.
93
- def initialize item_type, **options
94
- super **options
95
- @item_type = NRSER::Types.make item_type
96
- end # #initialize
97
-
98
-
99
- # Instance Methods
100
- # ======================================================================
101
-
102
- def explain
103
- "#{ super() }<#{ item_type.explain }>"
104
- end
105
-
106
-
107
- def test? value
108
- # Check the super method first, which will test if `value` is an Array
109
- # instance, and return `false` if it's not.
110
- return false unless super( value )
111
-
112
- # Otherwise test all the items
113
- value.all? &@item_type.method( :test )
114
- end
115
-
116
-
117
- # {ArrayOfType} can convert values from strings if it's {#item_type}
118
- # can convert values from strings.
119
- #
120
- # @return [Boolean]
121
- #
122
- def has_from_s?
123
- @from_s || @item_type.has_from_s?
124
- end
125
-
126
-
127
- def items_from_strings items
128
- items.map &@item_type.method( :from_s )
129
- end
130
-
131
-
132
- # @todo
133
- # I'm not even sure why this is implemented... was it used somewhere?
134
- #
135
- # It doesn't seems too well thought out... seems like the reality of
136
- # comparing types is much more complicated?
137
- #
138
- def == other
139
- equal?(other) || (
140
- other.class == self.class && @item_type == other.item_type
141
- )
77
+ # @param [Array<String>] items
78
+ #
79
+ # @return [Array]
80
+ #
81
+ def items_from_strings items
82
+ items
83
+ end
84
+
85
+
86
+ def custom_from_s string
87
+ # Does it looks like a JSON array?
88
+ if NRSER.looks_like_json_array? string
89
+ # It does! Load it
90
+ begin
91
+ return JSON.load( string )
92
+ rescue
93
+ # pass - if we failed to load as JSON, it may just not be JSON, and
94
+ # we can try the split approach below.
95
+ end
142
96
  end
143
97
 
144
- end # class ArrayOfType
98
+ # Split it with the splitter and check that
99
+ items_from_strings( string.split( @split_with ) )
100
+ end
101
+
102
+ end # ArrayType
103
+
104
+
105
+ # Type for arrays where every entry satisfies a specific type.
106
+ #
107
+ # Broken out from {ArrayType} so that {TupleType} can inherit from
108
+ # {ArrayType} and get share it's string handling functionality without
109
+ # receiving the entry type stuff (which it handles differently).
110
+ #
111
+ class ArrayOfType < ArrayType
145
112
 
113
+ # Attributes
114
+ # ======================================================================
146
115
 
147
- # {NRSER::Types::ArrayType} / {NRSER::Types::ArrayOfType} factory function.
148
- #
149
- # @param [Type | Object] item_type
150
- # Optional type of items.
116
+ # Type that all items must satisfy for an array to be a member of this
117
+ # type.
151
118
  #
152
119
  # @return [NRSER::Types::Type]
120
+ #
121
+ attr_reader :item_type
122
+
123
+
124
+ # Constructor
125
+ # ======================================================================
126
+
127
+ # Instantiate a new `ArrayOfType`.
128
+ def initialize item_type, **options
129
+ super **options
130
+ @item_type = NRSER::Types.make item_type
131
+ end # #initialize
132
+
133
+
134
+ # Instance Methods
135
+ # ======================================================================
136
+
137
+ # @!group Display Instance Methods
138
+ # ------------------------------------------------------------------------
139
+
140
+ def explain
141
+ "Array<#{ item_type.explain }>"
142
+ end
143
+
144
+ # @!endgroup Display Instance Methods # ************************************
145
+
146
+
147
+ def test? value
148
+ # Check the super method first, which will test if `value` is an Array
149
+ # instance, and return `false` if it's not.
150
+ return false unless super( value )
151
+
152
+ # Otherwise test all the items
153
+ value.all? &@item_type.method( :test )
154
+ end
155
+
156
+
157
+ # {ArrayOfType} can convert values from strings if it's {#item_type}
158
+ # can convert values from strings.
153
159
  #
160
+ # @return [Boolean]
161
+ #
162
+ def has_from_s?
163
+ @from_s || @item_type.has_from_s?
164
+ end
165
+
166
+
167
+ def items_from_strings items
168
+ items.map &@item_type.method( :from_s )
169
+ end
170
+
171
+
154
172
  # @todo
155
- # Make `list` into it's own looser interface for "array-like" object API.
173
+ # I'm not even sure why this is implemented... was it used somewhere?
174
+ #
175
+ # It doesn't seems too well thought out... seems like the reality of
176
+ # comparing types is much more complicated?
156
177
  #
157
- def_factory(
158
- :array,
159
- aliases: [:list],
160
- ) do |item_type = any, **options|
161
- if item_type == any
162
- ArrayType.new **options
163
- else
164
- ArrayOfType.new item_type, **options
165
- end
166
- end # #array
178
+ def == other
179
+ equal?(other) || (
180
+ other.class == self.class && @item_type == other.item_type
181
+ )
182
+ end
167
183
 
168
- end # NRSER::Types
184
+ end # class ArrayOfType
185
+
186
+
187
+ # @!group Array Type Factories
188
+ # ----------------------------------------------------------------------------
189
+
190
+ # @!method self.Array item_type = self.Top, **options
191
+ # {NRSER::Types::ArrayType} / {NRSER::Types::ArrayOfType} factory function.
192
+ #
193
+ # @param [Type | Object] item_type
194
+ # Optional type of items. If this is not a {Type}, one will be created from
195
+ # it via {NRSER::Types.make}.
196
+ #
197
+ # @param [Hash] options
198
+ # Passed to {Type#initialize}.
199
+ #
200
+ # @return [NRSER::Types::Type]
201
+ #
202
+ # @todo
203
+ # Make `list` into it's own looser interface for "array-like" object API.
204
+ #
205
+ def_type :Array,
206
+ parameterize: :item_type,
207
+ aliases: [ :list ],
208
+ &->( item_type = self.Top, **options ) do
209
+ if item_type == self.Top
210
+ ArrayType.new **options
211
+ else
212
+ ArrayOfType.new item_type, **options
213
+ end
214
+ end # .Array
215
+
216
+ # @!endgroup Array Type Factories # ******************************************
217
+
218
+
219
+ # /Namespace
220
+ # ========================================================================
221
+
222
+ end # module Types
223
+ end # module NRSER