nrser 0.2.2 → 0.3.0
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 +1 -2
- data/lib/nrser/char.rb +0 -6
- data/lib/nrser/core_ext/array.rb +120 -0
- data/lib/nrser/core_ext/binding.rb +44 -0
- data/lib/nrser/{functions → core_ext}/enumerable/find_map.rb +18 -15
- data/lib/nrser/{ext → core_ext}/enumerable.rb +10 -24
- data/lib/nrser/core_ext/exception.rb +30 -0
- data/lib/nrser/core_ext/hash/extract_values_at.rb +49 -0
- data/lib/nrser/core_ext/hash/transform_values_with_keys.rb +24 -0
- data/lib/nrser/core_ext/hash.rb +50 -0
- data/lib/nrser/core_ext/module/method_objects.rb +96 -0
- data/lib/nrser/core_ext/module/names.rb +69 -0
- data/lib/nrser/core_ext/module/source_locations.rb +214 -0
- data/lib/nrser/core_ext/module.rb +2 -0
- data/lib/nrser/core_ext/object/lazy_var.rb +31 -0
- data/lib/nrser/core_ext/object.rb +46 -0
- data/lib/nrser/core_ext/open_struct.rb +6 -0
- data/lib/nrser/{ext → core_ext}/pathname.rb +8 -5
- data/lib/nrser/{ext → core_ext}/string.rb +6 -12
- data/lib/nrser/core_ext/symbol.rb +13 -0
- data/lib/nrser/core_ext/time.rb +46 -0
- data/lib/nrser/core_ext.rb +13 -0
- data/lib/nrser/errors/abstract_method_error.rb +150 -0
- data/lib/nrser/errors/argument_error.rb +42 -0
- data/lib/nrser/errors/nicer_error.rb +298 -72
- data/lib/nrser/errors/type_error.rb +46 -0
- data/lib/nrser/errors.rb +4 -53
- data/lib/nrser/ext/tree.rb +3 -0
- data/lib/nrser/functions/enumerable/associate.rb +6 -9
- data/lib/nrser/functions/enumerable/include_slice.rb +2 -3
- data/lib/nrser/functions/enumerable.rb +1 -3
- data/lib/nrser/functions/exception.rb +1 -1
- data/lib/nrser/functions/hash.rb +0 -6
- data/lib/nrser/functions/merge_by.rb +2 -2
- data/lib/nrser/functions/module/method_objects.rb +77 -0
- data/lib/nrser/functions/module.rb +1 -2
- data/lib/nrser/functions/open_struct.rb +25 -35
- data/lib/nrser/functions/proc.rb +1 -6
- data/lib/nrser/functions/string/looks_like.rb +32 -1
- data/lib/nrser/functions/string.rb +1 -40
- data/lib/nrser/functions/text/lines.rb +2 -1
- data/lib/nrser/functions.rb +0 -1
- data/lib/nrser/graph/tsorter.rb +41 -0
- data/lib/nrser/labs/core_ext/binding.rb +37 -0
- data/lib/nrser/labs/stash.rb +372 -0
- data/lib/nrser/{logging → log}/appender/sync.rb +3 -3
- data/lib/nrser/log/appender.rb +3 -0
- data/lib/nrser/{logging → log}/formatters/color.rb +47 -20
- data/lib/nrser/log/formatters/mixin.rb +270 -0
- data/lib/nrser/{logging → log}/formatters.rb +0 -0
- data/lib/nrser/log/logger.rb +229 -0
- data/lib/nrser/log/mixin.rb +56 -0
- data/lib/nrser/log.rb +723 -0
- data/lib/nrser/message.rb +24 -3
- data/lib/nrser/meta/source/location.rb +158 -0
- data/lib/nrser/meta.rb +1 -1
- data/lib/nrser/props/class_methods.rb +118 -0
- data/lib/nrser/props/immutable/hash.rb +111 -0
- data/lib/nrser/props/immutable/hash_variable.rb +82 -0
- data/lib/nrser/props/immutable/instance_variables.rb +48 -0
- data/lib/nrser/props/immutable/vector.rb +107 -0
- data/lib/nrser/props/instance_methods.rb +184 -0
- data/lib/nrser/props/metadata.rb +359 -0
- data/lib/nrser/props/mutable/instance_variables.rb +60 -0
- data/lib/nrser/props/mutable/stash.rb +199 -0
- data/lib/nrser/{meta/props → props}/prop.rb +217 -112
- data/lib/nrser/props/storage/instance_variable.rb +85 -0
- data/lib/nrser/props/storage/instance_variables.rb +67 -0
- data/lib/nrser/props/storage/key.rb +88 -0
- data/lib/nrser/props.rb +9 -0
- data/lib/nrser/refinements/sugar.rb +41 -0
- data/lib/nrser/refinements/types.rb +2 -2
- data/lib/nrser/refinements.rb +14 -16
- data/lib/nrser/rspex/example_group/describe_attribute.rb +24 -0
- data/lib/nrser/rspex/example_group/describe_called_with.rb +1 -6
- data/lib/nrser/rspex/example_group/{describe_use_case.rb → describe_case.rb} +6 -3
- data/lib/nrser/rspex/example_group/describe_class.rb +1 -0
- data/lib/nrser/rspex/example_group/describe_group.rb +29 -0
- data/lib/nrser/rspex/example_group/describe_instance_method.rb +2 -2
- data/lib/nrser/rspex/example_group/describe_message.rb +35 -0
- data/lib/nrser/rspex/example_group/describe_method.rb +23 -2
- data/lib/nrser/rspex/example_group/describe_module.rb +19 -0
- data/lib/nrser/rspex/example_group/describe_response_to.rb +32 -0
- data/lib/nrser/rspex/example_group/describe_section.rb +38 -0
- data/lib/nrser/rspex/example_group/describe_sent_to.rb +52 -0
- data/lib/nrser/rspex/example_group/describe_source_file.rb +49 -0
- data/lib/nrser/rspex/example_group/describe_spec_file.rb +41 -108
- data/lib/nrser/rspex/example_group/describe_when.rb +14 -7
- data/lib/nrser/rspex/example_group/describe_x.rb +39 -12
- data/lib/nrser/rspex/example_group/overrides.rb +66 -0
- data/lib/nrser/rspex/example_group.rb +20 -252
- data/lib/nrser/rspex/format.rb +83 -17
- data/lib/nrser/rspex.rb +4 -34
- data/lib/nrser/sugar/method_missing_forwarder.rb +50 -0
- data/lib/nrser/{env → sys/env}/path.rb +1 -2
- data/lib/nrser/{env.rb → sys/env.rb} +2 -1
- data/lib/nrser/sys.rb +5 -0
- data/lib/nrser/types/any.rb +36 -7
- data/lib/nrser/types/{array.rb → arrays.rb} +32 -81
- data/lib/nrser/types/attrs.rb +68 -15
- data/lib/nrser/types/booleans.rb +95 -34
- data/lib/nrser/types/bounded.rb +12 -10
- data/lib/nrser/types/combinators.rb +74 -37
- data/lib/nrser/types/errors/check_error.rb +86 -0
- data/lib/nrser/types/errors/from_string_error.rb +82 -0
- data/lib/nrser/types/factory.rb +91 -0
- data/lib/nrser/types/hashes.rb +171 -26
- data/lib/nrser/types/in.rb +25 -12
- data/lib/nrser/types/is.rb +50 -18
- data/lib/nrser/types/is_a.rb +52 -33
- data/lib/nrser/types/labels.rb +6 -33
- data/lib/nrser/types/maybe.rb +12 -4
- data/lib/nrser/types/nil.rb +24 -4
- data/lib/nrser/types/not.rb +6 -16
- data/lib/nrser/types/numbers.rb +94 -57
- data/lib/nrser/types/pairs.rb +57 -57
- data/lib/nrser/types/paths.rb +112 -133
- data/lib/nrser/types/responds.rb +64 -74
- data/lib/nrser/types/shape.rb +29 -24
- data/lib/nrser/types/strings.rb +25 -17
- data/lib/nrser/types/symbols.rb +19 -17
- data/lib/nrser/types/trees.rb +18 -70
- data/lib/nrser/types/tuples.rb +36 -40
- data/lib/nrser/types/type.rb +342 -91
- data/lib/nrser/types/when.rb +40 -18
- data/lib/nrser/types/where.rb +94 -9
- data/lib/nrser/types.rb +72 -63
- data/lib/nrser/version.rb +1 -1
- data/lib/nrser.rb +18 -18
- data/spec/lib/nrser/{functions/binding/template_spec.rb → core_ext/binding/erb_spec.rb} +5 -5
- data/spec/lib/nrser/{functions → core_ext}/enumerable/find_map_spec.rb +8 -6
- data/spec/lib/nrser/{refinements → core_ext}/hash_spec.rb +9 -22
- data/spec/lib/nrser/errors/abstract_method_error_spec.rb +12 -5
- data/spec/lib/nrser/functions/enumerable/{to_h_by_spec.rb → associate_spec.rb} +1 -1
- data/spec/lib/nrser/functions/merge_by_spec.rb +1 -1
- data/spec/lib/nrser/functions/tree/each_branch_spec.rb +3 -3
- data/spec/lib/nrser/functions/tree/transform_spec.rb +14 -15
- data/spec/lib/nrser/gem_ext/hamster/json_spec.rb +4 -0
- data/spec/lib/nrser/meta/source/location_spec.rb +86 -0
- data/spec/lib/nrser/props/immutable/hash_spec.rb +297 -0
- data/spec/lib/nrser/props/immutable/vector_spec.rb +296 -0
- data/spec/lib/nrser/{meta/props_spec.rb → props/original_props_spec.rb} +11 -16
- data/spec/lib/nrser/{meta/props → props}/to_and_from_data_spec.rb +10 -8
- data/spec/lib/nrser/refinements/array_spec.rb +2 -15
- data/spec/lib/nrser/refinements/erb_spec.rb +5 -7
- data/spec/lib/nrser/refinements/set_spec.rb +2 -15
- data/spec/lib/nrser/{env → sys/env}/path/insert_spec.rb +4 -2
- data/spec/lib/nrser/{env → sys/env}/path_spec.rb +4 -2
- data/spec/lib/nrser/types/array_spec.rb +8 -8
- data/spec/lib/nrser/types/paths_spec.rb +15 -18
- data/spec/spec_helper.rb +4 -0
- metadata +109 -69
- data/lib/nrser/ext/binding.rb +0 -36
- data/lib/nrser/ext/module.rb +0 -62
- data/lib/nrser/ext.rb +0 -8
- data/lib/nrser/functions/binding.rb +0 -76
- data/lib/nrser/functions/enumerable/map_keys.rb +0 -0
- data/lib/nrser/functions/enumerable/map_values.rb +0 -94
- data/lib/nrser/functions/hash/deep_merge.rb +0 -57
- data/lib/nrser/functions/hash/except_keys.rb +0 -44
- data/lib/nrser/functions/hash/slice_keys.rb +0 -43
- data/lib/nrser/functions/hash/stringify_keys.rb +0 -55
- data/lib/nrser/functions/hash/symbolize_keys.rb +0 -57
- data/lib/nrser/functions/hash/transform_keys.rb +0 -140
- data/lib/nrser/functions/module/methods.rb +0 -206
- data/lib/nrser/functions/module/source_locations.rb +0 -213
- data/lib/nrser/logging/appender.rb +0 -3
- data/lib/nrser/logging.rb +0 -353
- data/lib/nrser/meta/props/base.rb +0 -31
- data/lib/nrser/meta/props.rb +0 -357
- data/lib/nrser/refinements/array.rb +0 -133
- data/lib/nrser/refinements/binding.rb +0 -6
- data/lib/nrser/refinements/enumerator.rb +0 -5
- data/lib/nrser/refinements/exception.rb +0 -35
- data/lib/nrser/refinements/hash.rb +0 -150
- data/lib/nrser/refinements/module.rb +0 -5
- data/lib/nrser/refinements/object.rb +0 -42
- data/lib/nrser/refinements/open_struct.rb +0 -28
- data/lib/nrser/refinements/pathname.rb +0 -5
- data/lib/nrser/refinements/set.rb +0 -5
- data/lib/nrser/refinements/string.rb +0 -5
- data/lib/nrser/refinements/symbol.rb +0 -20
- data/lib/nrser/rspex/described.rb +0 -99
- data/spec/design/mapping_spec.rb +0 -42
- data/spec/lib/nrser/functions/hash_spec.rb +0 -41
- data/spec/lib/nrser/functions/string/truncate_spec.rb +0 -11
- data/spec/lib/nrser/refinements/truncate_spec.rb +0 -10
data/lib/nrser/types/type.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
|
-
#
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
2
5
|
# =======================================================================
|
3
6
|
|
4
|
-
|
5
|
-
|
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 ||
|
116
|
+
@name || explain
|
87
117
|
end
|
88
118
|
|
89
|
-
|
90
|
-
|
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
|
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
|
-
|
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
|
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, █ check! *args, █ end
|
202
|
+
|
203
|
+
# @!endgroup Validation Instance Methods # *******************************
|
122
204
|
|
123
|
-
|
124
|
-
#
|
125
|
-
#
|
126
|
-
# to execute.
|
205
|
+
|
206
|
+
# @!group Loading Values Instance Methods
|
207
|
+
# ------------------------------------------------------------------------
|
127
208
|
#
|
128
|
-
#
|
129
|
-
#
|
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
|
-
#
|
138
|
-
#
|
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
|
-
#
|
141
|
-
#
|
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
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
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
|
-
#
|
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
|
184
|
-
|
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
|
-
|
189
|
-
|
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
|
298
|
+
check! value
|
198
299
|
end
|
199
300
|
|
200
301
|
|
201
|
-
# Test if the type
|
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
|
-
#
|
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
|
208
|
-
|
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
|
-
|
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
|
-
#
|
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 {#
|
450
|
+
# details, and point {#inspect} to {#explain}.
|
274
451
|
#
|
275
452
|
|
276
453
|
alias_method :builtin_inspect, :inspect
|
277
|
-
|
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
|
-
|
281
|
-
#
|
467
|
+
|
468
|
+
# @!group Derivation Instance Methods
|
469
|
+
# ------------------------------------------------------------------------
|
282
470
|
#
|
283
|
-
#
|
471
|
+
# Methods for deriving new types from `self`.
|
284
472
|
#
|
285
|
-
|
286
|
-
#
|
287
|
-
#
|
288
|
-
|
289
|
-
|
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
|
data/lib/nrser/types/when.rb
CHANGED
@@ -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
|
58
|
-
@object.
|
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
|
-
|
120
|
+
def_factory :when do |value, **options|
|
99
121
|
When.new value, **options
|
100
122
|
end
|
101
123
|
|