nrser 0.0.25 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +39 -11
- data/lib/nrser.rb +5 -1
- data/lib/nrser/array.rb +10 -53
- data/lib/nrser/enumerable.rb +21 -0
- data/lib/nrser/hash.rb +13 -476
- data/lib/nrser/hash/bury.rb +154 -0
- data/lib/nrser/hash/deep_merge.rb +57 -0
- data/lib/nrser/hash/except_keys.rb +42 -0
- data/lib/nrser/hash/guess_label_key_type.rb +37 -0
- data/lib/nrser/hash/slice_keys.rb +41 -0
- data/lib/nrser/hash/stringify_keys.rb +37 -0
- data/lib/nrser/hash/symbolize_keys.rb +41 -0
- data/lib/nrser/hash/transform_keys.rb +45 -0
- data/lib/nrser/merge_by.rb +26 -0
- data/lib/nrser/message.rb +125 -0
- data/lib/nrser/meta/props.rb +2 -2
- data/lib/nrser/meta/props/prop.rb +5 -2
- data/lib/nrser/object.rb +5 -0
- data/lib/nrser/object/as_array.rb +37 -0
- data/lib/nrser/object/as_hash.rb +101 -0
- data/lib/nrser/{truthy.rb → object/truthy.rb} +0 -0
- data/lib/nrser/proc.rb +132 -0
- data/lib/nrser/refinements.rb +1 -2
- data/lib/nrser/refinements/array.rb +94 -5
- data/lib/nrser/refinements/enumerable.rb +5 -0
- data/lib/nrser/refinements/hash.rb +43 -6
- data/lib/nrser/refinements/object.rb +22 -2
- data/lib/nrser/refinements/symbol.rb +12 -0
- data/lib/nrser/refinements/tree.rb +41 -0
- data/lib/nrser/rspex.rb +329 -0
- data/lib/nrser/string.rb +3 -0
- data/lib/nrser/string/looks_like.rb +51 -0
- data/lib/nrser/temp/where.rb +52 -0
- data/lib/nrser/tree.rb +86 -0
- data/lib/nrser/tree/leaves.rb +92 -0
- data/lib/nrser/tree/map_leaves.rb +63 -0
- data/lib/nrser/tree/transform.rb +30 -0
- data/lib/nrser/types.rb +9 -4
- data/lib/nrser/types/any.rb +1 -1
- data/lib/nrser/types/array.rb +167 -25
- data/lib/nrser/types/{hash.rb → hashes.rb} +19 -5
- data/lib/nrser/types/in.rb +47 -0
- data/lib/nrser/types/is_a.rb +2 -2
- data/lib/nrser/types/labels.rb +49 -0
- data/lib/nrser/types/numbers.rb +63 -27
- data/lib/nrser/types/pairs.rb +109 -0
- data/lib/nrser/types/responds.rb +2 -3
- data/lib/nrser/types/strings.rb +17 -18
- data/lib/nrser/types/symbols.rb +39 -0
- data/lib/nrser/types/trees.rb +93 -0
- data/lib/nrser/types/tuples.rb +116 -0
- data/lib/nrser/types/type.rb +26 -2
- data/lib/nrser/version.rb +1 -1
- data/spec/nrser/hash/{guess_name_type_spec.rb → guess_label_key_type_spec.rb} +3 -3
- data/spec/nrser/hash_spec.rb +0 -20
- data/spec/nrser/merge_by_spec.rb +73 -0
- data/spec/nrser/meta/props_spec.rb +136 -43
- data/spec/nrser/op/message_spec.rb +62 -0
- data/spec/nrser/refinements/array_spec.rb +36 -0
- data/spec/nrser/refinements/hash_spec.rb +34 -0
- data/spec/nrser/string/looks_like_spec.rb +31 -0
- data/spec/nrser/tree/each_branch_spec.rb +82 -0
- data/spec/nrser/tree/leaves_spec.rb +112 -0
- data/spec/nrser/tree/transform_spec.rb +165 -0
- data/spec/nrser/types/array_spec.rb +82 -0
- data/spec/nrser/types/attrs_spec.rb +4 -4
- data/spec/nrser/types/pairs_spec.rb +41 -0
- data/spec/nrser/types/paths_spec.rb +3 -3
- data/spec/nrser/types/strings_spec.rb +66 -0
- data/spec/nrser/types/symbols_spec.rb +38 -0
- data/spec/nrser/types/tuples_spec.rb +37 -0
- data/spec/nrser/types_spec.rb +0 -13
- data/spec/spec_helper.rb +71 -22
- metadata +58 -10
- data/lib/nrser/spex.rb +0 -68
- data/lib/nrser/types/symbol.rb +0 -23
data/lib/nrser/string.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative './string/looks_like'
|
2
|
+
|
1
3
|
module NRSER
|
2
4
|
class << self
|
3
5
|
# Functions the operate on strings.
|
@@ -155,5 +157,6 @@ module NRSER
|
|
155
157
|
end # constantize
|
156
158
|
|
157
159
|
alias_method :to_const, :constantize
|
160
|
+
|
158
161
|
end # class << self
|
159
162
|
end # module NRSER
|
@@ -0,0 +1,51 @@
|
|
1
|
+
##
|
2
|
+
# Functional methods that try to tell what format a string that
|
3
|
+
# is presumed to encode structural data is encoded in.
|
4
|
+
##
|
5
|
+
|
6
|
+
# Requirements
|
7
|
+
# =======================================================================
|
8
|
+
|
9
|
+
# Stdlib
|
10
|
+
# -----------------------------------------------------------------------
|
11
|
+
|
12
|
+
# Deps
|
13
|
+
# -----------------------------------------------------------------------
|
14
|
+
|
15
|
+
# Project / Package
|
16
|
+
# -----------------------------------------------------------------------
|
17
|
+
|
18
|
+
|
19
|
+
# Definitions
|
20
|
+
# =======================================================================
|
21
|
+
|
22
|
+
module NRSER
|
23
|
+
|
24
|
+
# Constants
|
25
|
+
# =====================================================================
|
26
|
+
|
27
|
+
JSON_ARRAY_RE = /\A\s*\[.*\]\s*\z/m
|
28
|
+
|
29
|
+
# Eigenclass (Singleton Class)
|
30
|
+
# ========================================================================
|
31
|
+
#
|
32
|
+
class << self
|
33
|
+
|
34
|
+
# Test if a string looks like it might encode an array in JSON format by
|
35
|
+
# seeing if it's first non-whitespace character is `[` and last
|
36
|
+
# non-whitespace character is `]`.
|
37
|
+
#
|
38
|
+
# @param [String] string
|
39
|
+
# String to test.
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
# `true` if we think `string` encodes a JSON array.
|
43
|
+
#
|
44
|
+
def looks_like_json_array? string
|
45
|
+
!!( string =~ JSON_ARRAY_RE )
|
46
|
+
end # #looks_like_json_array
|
47
|
+
|
48
|
+
end # class << self (Eigenclass)
|
49
|
+
|
50
|
+
end # module NRSER
|
51
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# Definitions
|
4
|
+
# =======================================================================
|
5
|
+
|
6
|
+
module NRSER
|
7
|
+
|
8
|
+
# A class to hold info about how to find a record (besides by primary key).
|
9
|
+
#
|
10
|
+
# Needs to support "template/placeholder"-type information - instructions
|
11
|
+
# about *how* to find the values to match against depending on a source
|
12
|
+
# object available later.
|
13
|
+
#
|
14
|
+
# @todo
|
15
|
+
# Not sure if this is the best name... there's already
|
16
|
+
# {NRSER::Types::Where}...
|
17
|
+
#
|
18
|
+
# Maybe this functionality has something to do with the types system?
|
19
|
+
# It seems like the placeholder stuff would be hard to integrate with
|
20
|
+
# that, but having a whole other very similar system sucks too.
|
21
|
+
#
|
22
|
+
class Where
|
23
|
+
|
24
|
+
# Constants
|
25
|
+
# ======================================================================
|
26
|
+
|
27
|
+
|
28
|
+
# Class Methods
|
29
|
+
# ======================================================================
|
30
|
+
|
31
|
+
|
32
|
+
# Attributes
|
33
|
+
# ======================================================================
|
34
|
+
|
35
|
+
|
36
|
+
# Constructor
|
37
|
+
# ======================================================================
|
38
|
+
|
39
|
+
# Instantiate a new `Where`.
|
40
|
+
def initialize
|
41
|
+
|
42
|
+
end # #initialize
|
43
|
+
|
44
|
+
|
45
|
+
# Instance Methods
|
46
|
+
# ======================================================================
|
47
|
+
|
48
|
+
|
49
|
+
end # class Where
|
50
|
+
|
51
|
+
|
52
|
+
end # module NRSER
|
data/lib/nrser/tree.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# Requirements
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
# Project / Package
|
5
|
+
# -----------------------------------------------------------------------
|
6
|
+
require_relative './tree/leaves'
|
7
|
+
require_relative './tree/map_leaves'
|
8
|
+
require_relative './tree/transform'
|
9
|
+
|
10
|
+
|
11
|
+
# Definitions
|
12
|
+
# =======================================================================
|
13
|
+
|
14
|
+
module NRSER
|
15
|
+
|
16
|
+
# Enumerate over the immediate "branches" of a structure that can be used
|
17
|
+
# to compose our idea of a *tree*: nested hash-like and array-like structures
|
18
|
+
# like you would get from parsing a JSON document.
|
19
|
+
#
|
20
|
+
# Written and tested against Hash and Array instances, but should work with
|
21
|
+
# anything hash-like that responds to `#each_pair` appropriately or
|
22
|
+
# array-like that responds to `#each_index` and `#each_with_index`.
|
23
|
+
#
|
24
|
+
# @note Not sure what will happen if the tree has circular references!
|
25
|
+
#
|
26
|
+
# @param [#each_pair | (#each_index & #each_with_index)] tree
|
27
|
+
# Structure representing a tree via hash-like and array-like containers.
|
28
|
+
#
|
29
|
+
# @yieldparam [Object] key
|
30
|
+
# The first yielded param is the key or index for the value branch at the
|
31
|
+
# top level of `tree`.
|
32
|
+
#
|
33
|
+
# @yieldparam [Object] value
|
34
|
+
# The second yielded param is the branch at the key or index at the top
|
35
|
+
# level of `tree`.
|
36
|
+
#
|
37
|
+
# @yieldreturn
|
38
|
+
# Ignored.
|
39
|
+
#
|
40
|
+
# @return [Enumerator]
|
41
|
+
# If no block is provided.
|
42
|
+
#
|
43
|
+
# @return [#each_pair | (#each_index & #each_with_index)]
|
44
|
+
# If a block is provided, the result of the `#each_pair` or
|
45
|
+
# `#each_with_index` call.
|
46
|
+
#
|
47
|
+
# @raise [NoMethodError]
|
48
|
+
# If `tree` does not respond to `#each_pair` or to `#each_index` and
|
49
|
+
# `#each_with_index`.
|
50
|
+
#
|
51
|
+
def self.each_branch tree, &block
|
52
|
+
if tree.respond_to? :each_pair
|
53
|
+
# Hash-like
|
54
|
+
tree.each_pair &block
|
55
|
+
|
56
|
+
elsif tree.respond_to? :each_index
|
57
|
+
# Array-like... we test for `each_index` because - unintuitively -
|
58
|
+
# `#each_with_index` is a method of {Enumerable}, meaning that {Set}
|
59
|
+
# responds to it, though sets are unordered and the values can't be
|
60
|
+
# accessed via those indexes. Hence we look for `#each_index`, which
|
61
|
+
# {Set} does not respond to.
|
62
|
+
|
63
|
+
if block.nil?
|
64
|
+
index_enumerator = tree.each_with_index
|
65
|
+
|
66
|
+
Enumerator.new( index_enumerator.size ) { |yielder|
|
67
|
+
index_enumerator.each { |value, index|
|
68
|
+
yielder.yield index, value
|
69
|
+
}
|
70
|
+
}
|
71
|
+
else
|
72
|
+
tree.each_with_index.map { |value, index|
|
73
|
+
block.call index, value
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
else
|
78
|
+
raise NoMethodError.new NRSER.squish <<-END
|
79
|
+
`tree` param must respond to `#each_pair` or `#each_index`,
|
80
|
+
found #{ tree.inspect }
|
81
|
+
END
|
82
|
+
|
83
|
+
end # if / else
|
84
|
+
end # .each_branch
|
85
|
+
|
86
|
+
end # module NRSER
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module NRSER
|
2
|
+
|
3
|
+
# Eigenclass (Singleton Class)
|
4
|
+
# ========================================================================
|
5
|
+
#
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# Create a new hash where all the values are the scalar "leaves" of the
|
9
|
+
# possibly nested `hash` param. Leaves are keyed by "key path" arrays
|
10
|
+
# representing the sequence of keys to dig that leaf out of the has param.
|
11
|
+
#
|
12
|
+
# In abstract, if `h` is the `hash` param and
|
13
|
+
#
|
14
|
+
# l = NRSER.leaves h
|
15
|
+
#
|
16
|
+
# then for each key `k` and corresponding value `v` in `l`
|
17
|
+
#
|
18
|
+
# h.dig( *k ) == v
|
19
|
+
#
|
20
|
+
# @example Simple "flat" hash
|
21
|
+
#
|
22
|
+
# NRSER.leaves( {a: 1, b: 2} )
|
23
|
+
# => {
|
24
|
+
# [:a] => 1,
|
25
|
+
# [:b] => 2,
|
26
|
+
# }
|
27
|
+
#
|
28
|
+
# @example Nested hash
|
29
|
+
#
|
30
|
+
# NRSER.leaves(
|
31
|
+
# 1 => {
|
32
|
+
# name: 'Neil',
|
33
|
+
# fav_color: 'blue',
|
34
|
+
# },
|
35
|
+
# 2 => {
|
36
|
+
# name: 'Mica',
|
37
|
+
# fav_color: 'red',
|
38
|
+
# }
|
39
|
+
# )
|
40
|
+
# # => {
|
41
|
+
# # [1, :name] => 'Neil',
|
42
|
+
# # [1, :fav_color] => 'blue',
|
43
|
+
# # [2, :name] => 'Mica',
|
44
|
+
# # [2, :fav_color] => 'red',
|
45
|
+
# # }
|
46
|
+
#
|
47
|
+
# @param [#each_pair | (#each_index & #each_with_index)] tree
|
48
|
+
#
|
49
|
+
# @return [Hash<Array, Object>]
|
50
|
+
#
|
51
|
+
def leaves tree
|
52
|
+
{}.tap { |results|
|
53
|
+
_internal_leaves tree, path: [], results: results
|
54
|
+
}
|
55
|
+
end # #leaves
|
56
|
+
|
57
|
+
|
58
|
+
private
|
59
|
+
# ========================================================================
|
60
|
+
|
61
|
+
# Internal recursive implementation for {NRSER.leaves}.
|
62
|
+
#
|
63
|
+
# @param [#each_pair | (#each_index & #each_with_index)] tree
|
64
|
+
# Tree to walk.
|
65
|
+
#
|
66
|
+
# @param [Array] path
|
67
|
+
# Key path down to `tree`.
|
68
|
+
#
|
69
|
+
# @param [Hash<Array, Object>] results
|
70
|
+
# New hash to stick results in.
|
71
|
+
#
|
72
|
+
# @return [nil]
|
73
|
+
#
|
74
|
+
def _internal_leaves tree, path:, results:
|
75
|
+
NRSER.each_branch( tree ) { |key, value|
|
76
|
+
new_path = [*path, key]
|
77
|
+
|
78
|
+
if NRSER::Types.tree.test value
|
79
|
+
_internal_leaves value, path: new_path, results: results
|
80
|
+
else
|
81
|
+
results[new_path] = value
|
82
|
+
end
|
83
|
+
}
|
84
|
+
|
85
|
+
nil
|
86
|
+
end # #_internal_leaves
|
87
|
+
|
88
|
+
# end private
|
89
|
+
|
90
|
+
end # class << self (Eigenclass)
|
91
|
+
|
92
|
+
end # module NRSER
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module NRSER
|
2
|
+
|
3
|
+
# Eigenclass (Singleton Class)
|
4
|
+
# ========================================================================
|
5
|
+
#
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def map_leaves tree, &block
|
9
|
+
NRSER::Types.tree.check tree
|
10
|
+
|
11
|
+
_internal_map_leaves tree, key_path: [], &block
|
12
|
+
end # #map_leaves
|
13
|
+
|
14
|
+
private
|
15
|
+
# ========================================================================
|
16
|
+
|
17
|
+
# Internal recursive implementation for {NRSER.leaves}.
|
18
|
+
#
|
19
|
+
# @param [#each_pair | (#each_index & #each_with_index)] tree
|
20
|
+
# Tree to walk.
|
21
|
+
#
|
22
|
+
# @param [Array] path
|
23
|
+
# Key path down to `tree`.
|
24
|
+
#
|
25
|
+
# @param [Hash<Array, Object>] results
|
26
|
+
# New hash to stick results in.
|
27
|
+
#
|
28
|
+
# @return [nil]
|
29
|
+
#
|
30
|
+
def _internal_map_leaves tree, key_path:, &block
|
31
|
+
NRSER::Types.match tree,
|
32
|
+
NRSER::Types.hash_like, ->( hash_like ) {
|
33
|
+
hash_like.map { |key, value|
|
34
|
+
new_key_path = [*key_path, key]
|
35
|
+
|
36
|
+
new_value = if NRSER::Types.tree.test( value )
|
37
|
+
_internal_map_leaves value, key_path: new_key_path, &block
|
38
|
+
else
|
39
|
+
block.call new_key_path, value
|
40
|
+
end
|
41
|
+
|
42
|
+
[key, new_value]
|
43
|
+
}.to_h
|
44
|
+
},
|
45
|
+
|
46
|
+
NRSER::Types.array_like, ->( array_like ) {
|
47
|
+
array_like.each_with_index.map { |value, index|
|
48
|
+
new_key_path = [*key_path, index]
|
49
|
+
|
50
|
+
if NRSER::Types.tree.test( value )
|
51
|
+
_internal_map_leaves value, key_path: new_key_path, &block
|
52
|
+
else
|
53
|
+
block.call new_key_path, value
|
54
|
+
end
|
55
|
+
}
|
56
|
+
}
|
57
|
+
end # #_internal_leaves
|
58
|
+
|
59
|
+
# end private
|
60
|
+
|
61
|
+
end # class << self (Eigenclass)
|
62
|
+
|
63
|
+
end # module NRSER
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# @todo Document transform method.
|
7
|
+
#
|
8
|
+
# @param [type] arg_name
|
9
|
+
# @todo Add name param description.
|
10
|
+
#
|
11
|
+
# @return [return_type]
|
12
|
+
# @todo Document return value.
|
13
|
+
#
|
14
|
+
def self.transform tree, source
|
15
|
+
each_branch( tree ).map { |pair|
|
16
|
+
pair.map { |value|
|
17
|
+
if NRSER::Types.tree.test value
|
18
|
+
transform value, source
|
19
|
+
else
|
20
|
+
if value.is_a? Proc
|
21
|
+
value.call source
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
}
|
27
|
+
}.to_h
|
28
|
+
end # .transform
|
29
|
+
|
30
|
+
end # module NRSER
|
data/lib/nrser/types.rb
CHANGED
@@ -30,6 +30,7 @@ require_relative './types/combinators'
|
|
30
30
|
require_relative './types/maybe'
|
31
31
|
require_relative './types/attrs'
|
32
32
|
require_relative './types/responds'
|
33
|
+
require_relative './types/in'
|
33
34
|
|
34
35
|
|
35
36
|
# Refinements
|
@@ -135,7 +136,7 @@ module NRSER::Types
|
|
135
136
|
|
136
137
|
to any of types
|
137
138
|
|
138
|
-
#{ enum.map {|type, expression| "\n #{ type.inspect }"} }
|
139
|
+
#{ enum.map {|type, expression| "\n #{ type.inspect }"}.join '' }
|
139
140
|
|
140
141
|
END
|
141
142
|
end # .match
|
@@ -168,7 +169,11 @@ require_relative './types/any'
|
|
168
169
|
require_relative './types/booleans'
|
169
170
|
require_relative './types/numbers'
|
170
171
|
require_relative './types/strings'
|
171
|
-
require_relative './types/
|
172
|
+
require_relative './types/symbols'
|
173
|
+
require_relative './types/labels'
|
172
174
|
require_relative './types/array'
|
173
|
-
require_relative './types/
|
174
|
-
require_relative './types/paths'
|
175
|
+
require_relative './types/hashes'
|
176
|
+
require_relative './types/paths'
|
177
|
+
require_relative './types/tuples'
|
178
|
+
require_relative './types/pairs'
|
179
|
+
require_relative './types/trees'
|
data/lib/nrser/types/any.rb
CHANGED
data/lib/nrser/types/array.rb
CHANGED
@@ -1,60 +1,202 @@
|
|
1
|
-
|
1
|
+
# Requirements
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
# Stdlib
|
5
|
+
# -----------------------------------------------------------------------
|
6
|
+
|
7
|
+
# Deps
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Project / Package
|
11
|
+
# -----------------------------------------------------------------------
|
2
12
|
require 'nrser/types/type'
|
3
13
|
require 'nrser/types/is_a'
|
4
14
|
|
15
|
+
|
16
|
+
# Refinements
|
17
|
+
# =======================================================================
|
18
|
+
|
19
|
+
require 'nrser/refinements'
|
5
20
|
using NRSER
|
21
|
+
|
22
|
+
|
23
|
+
# Declarations
|
24
|
+
# =======================================================================
|
25
|
+
|
26
|
+
module NRSER; end
|
27
|
+
|
28
|
+
|
29
|
+
# Definitions
|
30
|
+
# =======================================================================
|
6
31
|
|
7
32
|
module NRSER::Types
|
8
|
-
class
|
9
|
-
|
33
|
+
class ArrayType < IsA
|
34
|
+
# Default value to split strings with in {#from_s} if the string provided
|
35
|
+
# does is not recognized as an encoding format (as of writing, JSON is
|
36
|
+
# the only format we attempt to detect).
|
37
|
+
#
|
38
|
+
# Splits
|
39
|
+
DEFAULT_SPLIT_WITH = /\,\s*/m
|
10
40
|
|
11
41
|
attr_reader :item_type
|
12
42
|
|
13
|
-
def initialize
|
43
|
+
def initialize split_with: DEFAULT_SPLIT_WITH, **options
|
14
44
|
super ::Array, **options
|
15
|
-
@
|
45
|
+
@split_with = split_with
|
16
46
|
end
|
17
47
|
|
18
|
-
def test value
|
19
|
-
super(value) && if @item_type == NRSER::Types::ANY
|
20
|
-
true
|
21
|
-
else
|
22
|
-
value.all? {|v| @item_type.test v}
|
23
|
-
end
|
24
|
-
end
|
25
48
|
|
26
49
|
def default_name
|
27
|
-
|
50
|
+
self.class.short_name
|
28
51
|
end
|
29
52
|
|
30
|
-
|
31
|
-
|
53
|
+
|
54
|
+
# Called on an array of string items that have been split
|
55
|
+
# from a single string by {#from_s} to convert each individual item before
|
56
|
+
# {#check} is called on the value.
|
57
|
+
#
|
58
|
+
# {NRSER::Types::ArrayType} implementation is a no-op that just returns
|
59
|
+
# `items` - this method is in place for subclasses to override.
|
60
|
+
#
|
61
|
+
# @param [Array<String>] items
|
62
|
+
#
|
63
|
+
# @return [Array]
|
64
|
+
#
|
65
|
+
def items_from_strings items
|
66
|
+
items
|
32
67
|
end
|
33
68
|
|
69
|
+
|
34
70
|
def from_s s
|
35
|
-
#
|
36
|
-
|
71
|
+
# Use custom {@from_s} if we have one.
|
72
|
+
return check( @from_s.call s ) unless @from_s.nil?
|
73
|
+
|
74
|
+
# Does it looks like a JSON array?
|
75
|
+
if NRSER.looks_like_json_array? s
|
76
|
+
# It does! Load it
|
37
77
|
begin
|
38
|
-
|
78
|
+
array = JSON.load( s )
|
39
79
|
rescue
|
80
|
+
# pass - if we failed to load as JSON, it may just not be JSON, and
|
81
|
+
# we can try the split approach below.
|
82
|
+
else
|
83
|
+
# Check value and return. If we fail the check here let the error
|
84
|
+
# bubble up
|
85
|
+
return check array
|
40
86
|
end
|
41
87
|
end
|
42
88
|
|
43
|
-
|
89
|
+
# Split it with the splitter and check that
|
90
|
+
check items_from_strings( s.split( @split_with ) )
|
91
|
+
end
|
92
|
+
|
93
|
+
end # ArrayType
|
94
|
+
|
95
|
+
|
96
|
+
# Static instance that is satisfied by anything that is an {Array}.
|
97
|
+
ARRAY = ArrayType.new.freeze
|
98
|
+
|
99
|
+
|
100
|
+
# Type for arrays where every entry satisfies a specific type.
|
101
|
+
#
|
102
|
+
# Broken out from {ArrayType} so that {TupleType} can inherit from
|
103
|
+
# {ArrayType} and get share it's string handling functionality without
|
104
|
+
# receiving the entry type stuff (which it handles differently).
|
105
|
+
#
|
106
|
+
class ArrayOfType < ArrayType
|
107
|
+
|
108
|
+
# Attributes
|
109
|
+
# ======================================================================
|
110
|
+
|
111
|
+
# Type that all items must satisfy for an array to be a member of this
|
112
|
+
# type.
|
113
|
+
#
|
114
|
+
# @return [NRSER::Types::Type]
|
115
|
+
#
|
116
|
+
attr_reader :item_type
|
117
|
+
|
118
|
+
|
119
|
+
# Constructor
|
120
|
+
# ======================================================================
|
121
|
+
|
122
|
+
# Instantiate a new `ArrayOfType`.
|
123
|
+
def initialize item_type, **options
|
124
|
+
super **options
|
125
|
+
@item_type = NRSER::Types.make item_type
|
126
|
+
end # #initialize
|
127
|
+
|
128
|
+
|
129
|
+
# Instance Methods
|
130
|
+
# ======================================================================
|
131
|
+
|
132
|
+
def default_name
|
133
|
+
"#{ super() }<#{ @item_type }>"
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
def test value
|
138
|
+
# Check the super method first, which will test if `value` is an Array
|
139
|
+
# instance, and return `false` if it's not.
|
140
|
+
return false unless super( value )
|
141
|
+
|
142
|
+
# Otherwise test all the items
|
143
|
+
value.all? &@item_type.method( :test )
|
44
144
|
end
|
45
145
|
|
146
|
+
|
147
|
+
# {ArrayOfType} can convert values from strings if it's {#item_type}
|
148
|
+
# can convert values from strings.
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
#
|
152
|
+
def has_from_s?
|
153
|
+
@item_type.has_from_s?
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def items_from_strings items
|
158
|
+
items.map &@item_type.method( :from_s )
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# @todo
|
163
|
+
# I'm not even sure why this is implemented... was it used somewhere?
|
164
|
+
#
|
165
|
+
# It doesn't seems too well thought out... seems like the reality of
|
166
|
+
# comparing types is much more complicated?
|
167
|
+
#
|
46
168
|
def == other
|
47
169
|
equal?(other) || (
|
48
170
|
other.class == self.class && @item_type == other.item_type
|
49
171
|
)
|
50
172
|
end
|
51
|
-
|
173
|
+
|
174
|
+
end # class ArrayOfType
|
52
175
|
|
53
|
-
# array
|
54
|
-
def self.array *args
|
55
|
-
Array.new *args
|
56
|
-
end
|
57
176
|
|
58
|
-
|
177
|
+
# Eigenclass (Singleton Class)
|
178
|
+
# ========================================================================
|
179
|
+
#
|
180
|
+
class << self
|
181
|
+
|
182
|
+
# array
|
183
|
+
#
|
184
|
+
# @return [NRSER::Types::Type]
|
185
|
+
#
|
186
|
+
def array item_type = NRSER::NO_ARG, **options
|
187
|
+
if item_type == NRSER::NO_ARG
|
188
|
+
if options.empty?
|
189
|
+
ARRAY
|
190
|
+
else
|
191
|
+
ArrayType.new **options
|
192
|
+
end
|
193
|
+
else
|
194
|
+
ArrayOfType.new item_type, **options
|
195
|
+
end
|
196
|
+
end # #array
|
197
|
+
|
198
|
+
alias_method :list, :array
|
199
|
+
|
200
|
+
end # class << self (Eigenclass)
|
59
201
|
|
60
202
|
end # NRSER::Types
|