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