nrser 0.3.9 → 0.3.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/nrser/char/alpha_numeric_sub.rb +9 -19
- data/lib/nrser/char/special.rb +5 -5
- data/lib/nrser/core_ext/array.rb +36 -13
- data/lib/nrser/core_ext/enumerable.rb +1 -0
- data/lib/nrser/core_ext/enumerable/find_map.rb +1 -1
- data/lib/nrser/core_ext/hash/bury.rb +3 -0
- data/lib/nrser/core_ext/hash/extract_values_at.rb +2 -2
- data/lib/nrser/core_ext/method/full_name.rb +1 -1
- data/lib/nrser/core_ext/module/method_objects.rb +1 -1
- data/lib/nrser/core_ext/module/source_locations.rb +27 -15
- data/lib/nrser/core_ext/object/lazy_var.rb +1 -1
- data/lib/nrser/core_ext/pathname.rb +67 -12
- data/lib/nrser/core_ext/pathname/subpath.rb +86 -0
- data/lib/nrser/core_ext/string.rb +28 -1
- data/lib/nrser/core_ext/symbol.rb +11 -12
- data/lib/nrser/errors/README.md +154 -0
- data/lib/nrser/errors/attr_error.rb +146 -53
- data/lib/nrser/errors/count_error.rb +61 -12
- data/lib/nrser/errors/nicer_error.rb +42 -71
- data/lib/nrser/errors/value_error.rb +53 -58
- data/lib/nrser/functions.rb +0 -2
- data/lib/nrser/functions/enumerable.rb +5 -17
- data/lib/nrser/functions/enumerable/associate.rb +14 -5
- data/lib/nrser/functions/enumerable/find_all_map.rb +1 -1
- data/lib/nrser/functions/enumerable/include_slice/array_include_slice.rb +1 -1
- data/lib/nrser/functions/hash/bury.rb +2 -12
- data/lib/nrser/functions/merge_by.rb +2 -2
- data/lib/nrser/functions/module/method_objects.rb +2 -2
- data/lib/nrser/functions/path.rb +185 -165
- data/lib/nrser/functions/path/normalized.rb +84 -0
- data/lib/nrser/functions/string.rb +4 -4
- data/lib/nrser/functions/text/README.md +4 -0
- data/lib/nrser/functions/text/format.rb +53 -0
- data/lib/nrser/functions/text/indentation.rb +6 -6
- data/lib/nrser/functions/text/word_wrap.rb +2 -2
- data/lib/nrser/functions/tree/map_leaves.rb +3 -3
- data/lib/nrser/functions/tree/map_tree.rb +2 -2
- data/lib/nrser/functions/tree/transform.rb +1 -18
- data/lib/nrser/gem_ext/README.md +4 -0
- data/lib/nrser/labs/README.md +8 -0
- data/lib/nrser/labs/config.rb +163 -0
- data/lib/nrser/labs/i8.rb +49 -159
- data/lib/nrser/labs/i8/struct.rb +167 -0
- data/lib/nrser/labs/i8/struct/hash.rb +140 -0
- data/lib/nrser/labs/i8/struct/vector.rb +149 -0
- data/lib/nrser/labs/i8/surjection.rb +211 -0
- data/lib/nrser/labs/lots/consumer.rb +19 -0
- data/lib/nrser/labs/lots/parser.rb +21 -1
- data/lib/nrser/labs/stash.rb +4 -4
- data/lib/nrser/log.rb +25 -21
- data/lib/nrser/log/appender/sync.rb +15 -11
- data/lib/nrser/log/formatters/color.rb +0 -3
- data/lib/nrser/log/formatters/mixin.rb +4 -4
- data/lib/nrser/log/logger.rb +54 -6
- data/lib/nrser/log/mixin.rb +2 -1
- data/lib/nrser/log/plugin.rb +6 -6
- data/lib/nrser/log/types.rb +46 -29
- data/lib/nrser/mean_streak.rb +0 -8
- data/lib/nrser/mean_streak/document.rb +1 -4
- data/lib/nrser/message.rb +3 -3
- data/lib/nrser/meta/README.md +4 -0
- data/lib/nrser/meta/lazy_attr.rb +2 -2
- data/lib/nrser/meta/source/location.rb +1 -1
- data/lib/nrser/props.rb +34 -3
- data/lib/nrser/props/class_methods.rb +2 -1
- data/lib/nrser/props/instance_methods.rb +9 -9
- data/lib/nrser/props/metadata.rb +4 -12
- data/lib/nrser/props/mutable/stash.rb +5 -2
- data/lib/nrser/props/prop.rb +10 -19
- data/lib/nrser/rspex.rb +1 -20
- data/lib/nrser/rspex/example_group/describe_attribute.rb +3 -0
- data/lib/nrser/rspex/example_group/describe_called_with.rb +9 -4
- data/lib/nrser/rspex/example_group/describe_case.rb +1 -0
- data/lib/nrser/rspex/example_group/describe_class.rb +2 -0
- data/lib/nrser/rspex/example_group/describe_group.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_instance.rb +3 -1
- data/lib/nrser/rspex/example_group/describe_message.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_method.rb +64 -30
- data/lib/nrser/rspex/example_group/describe_response_to.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_section.rb +4 -1
- data/lib/nrser/rspex/example_group/describe_sent_to.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_setup.rb +1 -0
- data/lib/nrser/rspex/example_group/describe_source_file.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_spec_file.rb +4 -2
- data/lib/nrser/rspex/example_group/describe_when.rb +2 -1
- data/lib/nrser/rspex/example_group/describe_x.rb +5 -5
- data/lib/nrser/rspex/format.rb +0 -15
- data/lib/nrser/sugar/method_missing_forwarder.rb +3 -3
- data/lib/nrser/sys/env/path.rb +2 -28
- data/lib/nrser/types.rb +63 -12
- data/lib/nrser/types/README.md +76 -0
- data/lib/nrser/types/arrays.rb +192 -137
- data/lib/nrser/types/attributes.rb +269 -0
- data/lib/nrser/types/booleans.rb +134 -83
- data/lib/nrser/types/bounded.rb +110 -47
- data/lib/nrser/types/collections.rb +119 -0
- data/lib/nrser/types/combinators.rb +283 -196
- data/lib/nrser/types/doc/display_table.md +66 -0
- data/lib/nrser/types/eqiuvalent.rb +91 -0
- data/lib/nrser/types/errors/check_error.rb +5 -11
- data/lib/nrser/types/errors/from_string_error.rb +3 -3
- data/lib/nrser/types/factory.rb +287 -20
- data/lib/nrser/types/hashes.rb +227 -179
- data/lib/nrser/types/in.rb +73 -36
- data/lib/nrser/types/is.rb +67 -60
- data/lib/nrser/types/is_a.rb +141 -84
- data/lib/nrser/types/labels.rb +45 -16
- data/lib/nrser/types/maybe.rb +6 -3
- data/lib/nrser/types/nil.rb +64 -27
- data/lib/nrser/types/not.rb +92 -34
- data/lib/nrser/types/numbers.rb +224 -169
- data/lib/nrser/types/pairs.rb +113 -89
- data/lib/nrser/types/paths.rb +250 -137
- data/lib/nrser/types/responds.rb +167 -89
- data/lib/nrser/types/selector.rb +234 -0
- data/lib/nrser/types/shape.rb +136 -65
- data/lib/nrser/types/strings.rb +189 -63
- data/lib/nrser/types/symbols.rb +83 -33
- data/lib/nrser/types/top.rb +89 -0
- data/lib/nrser/types/tuples.rb +134 -98
- data/lib/nrser/types/type.rb +617 -505
- data/lib/nrser/types/when.rb +123 -98
- data/lib/nrser/types/where.rb +182 -91
- data/lib/nrser/version.rb +1 -1
- data/spec/lib/nrser/core_ext/pathname/subpath_spec.rb +22 -0
- data/spec/lib/nrser/errors/attr_error_spec.rb +68 -0
- data/spec/lib/nrser/errors/count_error_spec.rb +69 -0
- data/spec/lib/nrser/functions/path/normalize_path_spec.rb +35 -0
- data/spec/lib/nrser/functions/tree/map_tree_spec.rb +74 -96
- data/spec/lib/nrser/functions/tree/transform_spec.rb +11 -11
- data/spec/lib/nrser/labs/config_spec.rb +22 -0
- data/spec/lib/nrser/labs/i8/struct_spec.rb +39 -0
- data/spec/lib/nrser/types/display_spec.rb +50 -0
- data/spec/lib/nrser/types/paths_spec.rb +16 -10
- data/spec/lib/nrser/types/selector_spec.rb +125 -0
- data/spec/spec_helper.rb +4 -5
- metadata +105 -22
- data/lib/nrser/types/any.rb +0 -41
- data/lib/nrser/types/attrs.rb +0 -213
- 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>]
|
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>]
|
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]
|
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
|
#
|
data/lib/nrser/rspex/format.rb
CHANGED
@@ -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)>]
|
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]
|
40
|
+
# @param [Array] args
|
41
41
|
# Any parameters the missing method was called with.
|
42
42
|
#
|
43
|
-
# @param [Proc?]
|
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
|
data/lib/nrser/sys/env/path.rb
CHANGED
@@ -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>]
|
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)=>*>]
|
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
|
-
# {
|
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.
|
83
|
+
self.Nil
|
71
84
|
elsif value.is_a? NRSER::Types::Type
|
72
85
|
value
|
73
86
|
else
|
74
|
-
self.
|
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
|
-
|
108
|
-
|
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
|
-
#
|
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/
|
266
|
+
require_relative './types/attributes'
|
217
267
|
require_relative './types/in'
|
218
268
|
|
219
269
|
require_relative './types/when'
|
220
|
-
require_relative './types/
|
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/
|
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
|
+
|
data/lib/nrser/types/arrays.rb
CHANGED
@@ -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
|
-
|
42
|
+
def initialize split_with: DEFAULT_SPLIT_WITH, **options
|
43
|
+
super ::Array, **options
|
44
|
+
@split_with = split_with
|
45
|
+
end
|
17
46
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
73
|
-
#
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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 #
|
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
|