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
@@ -0,0 +1,154 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# Eigenclass (Singleton Class)
|
7
|
+
# ========================================================================
|
8
|
+
#
|
9
|
+
class << self
|
10
|
+
|
11
|
+
# The opposite of `#dig` - set a value at a deep key path, creating
|
12
|
+
# necessary structures along the way and optionally clobbering whatever's
|
13
|
+
# in the way to achieve success.
|
14
|
+
#
|
15
|
+
# @param [Hash] hash
|
16
|
+
# Hash to bury the value in.
|
17
|
+
#
|
18
|
+
# @param [Array | #to_s] key_path
|
19
|
+
# - When an {Array}, each entry is used exactly as-is for each key.
|
20
|
+
#
|
21
|
+
# - Otherwise, the `key_path` is converted to a string and split by
|
22
|
+
# `.` to produce the key array, and the actual keys used depend on
|
23
|
+
# the `parsed_key_type` option.
|
24
|
+
#
|
25
|
+
# @param [Object] value
|
26
|
+
# The value to set at the end of the path.
|
27
|
+
#
|
28
|
+
# @param [Class | :guess] parsed_key_type:
|
29
|
+
# How to handle parsed key path segments:
|
30
|
+
#
|
31
|
+
# - `String` - use the strings that naturally split from a parsed
|
32
|
+
# key path.
|
33
|
+
#
|
34
|
+
# Note that this is the *String class itself, **not** a value that
|
35
|
+
# is a String*.
|
36
|
+
#
|
37
|
+
# - `Symbol` - convert the strings that are split from the key path
|
38
|
+
# to symbols.
|
39
|
+
#
|
40
|
+
# Note that this is the *Symbol class itself, **not** a value that
|
41
|
+
# is a Symbol*.``
|
42
|
+
#
|
43
|
+
# - `:guess` (default) -
|
44
|
+
#
|
45
|
+
# @return [return_type]
|
46
|
+
# @todo Document return value.
|
47
|
+
#
|
48
|
+
def bury! hash,
|
49
|
+
key_path,
|
50
|
+
value,
|
51
|
+
parsed_key_type: :guess,
|
52
|
+
clobber: false,
|
53
|
+
create_arrays_for_unsigned_keys: false
|
54
|
+
|
55
|
+
# Parse the key if it's not an array
|
56
|
+
unless key_path.is_a?( Array )
|
57
|
+
key_path = key_path.to_s.split '.'
|
58
|
+
|
59
|
+
# Convert the keys to symbols now if that's what we want to use
|
60
|
+
if parsed_key_type == Symbol
|
61
|
+
key_path.map! &:to_sym
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
_internal_bury! \
|
66
|
+
hash,
|
67
|
+
key_path,
|
68
|
+
value,
|
69
|
+
guess_key_type: ( parsed_key_type == :guess ),
|
70
|
+
clobber: clobber,
|
71
|
+
create_arrays_for_unsigned_keys: create_arrays_for_unsigned_keys
|
72
|
+
end # #bury
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
# ========================================================================
|
77
|
+
|
78
|
+
|
79
|
+
# @todo Document _internal_bury! method.
|
80
|
+
#
|
81
|
+
# @param [type] arg_name
|
82
|
+
# @todo Add name param description.
|
83
|
+
#
|
84
|
+
# @return [return_type]
|
85
|
+
# @todo Document return value.
|
86
|
+
#
|
87
|
+
def _internal_bury! tree,
|
88
|
+
key_path,
|
89
|
+
value,
|
90
|
+
guess_key_type:,
|
91
|
+
clobber:,
|
92
|
+
create_arrays_for_unsigned_keys:
|
93
|
+
|
94
|
+
# Split the key path into the current key and the rest of the keys
|
95
|
+
key, *rest = key_path
|
96
|
+
|
97
|
+
# If we are
|
98
|
+
#
|
99
|
+
# - Guessing the key type
|
100
|
+
# - The tree is keyed
|
101
|
+
# - The tree uses some {Symbol} (and no {String}) keys
|
102
|
+
#
|
103
|
+
# then convert the key to a symbol.
|
104
|
+
#
|
105
|
+
if guess_key_type &&
|
106
|
+
tree.respond_to?( :keys ) &&
|
107
|
+
guess_label_key_type( tree ) == Symbol
|
108
|
+
key = key.to_sym
|
109
|
+
end
|
110
|
+
|
111
|
+
# Terminating case: we're at the last segment
|
112
|
+
if rest.empty?
|
113
|
+
# Set the value
|
114
|
+
tree[key] = value
|
115
|
+
|
116
|
+
else
|
117
|
+
# Go deeper...
|
118
|
+
|
119
|
+
# See if there is a hash in place
|
120
|
+
unless NRSER::Types.tree.test tree[key]
|
121
|
+
# There is not... so we need to do some figurin'
|
122
|
+
|
123
|
+
# If we're clobbering or the hash has no value, we're good:
|
124
|
+
# assign a new hash to set in
|
125
|
+
if clobber || tree[key].nil?
|
126
|
+
if create_arrays_for_unsigned_keys &&
|
127
|
+
NRSER::Types.unsigned.test( key )
|
128
|
+
tree[key] = []
|
129
|
+
else
|
130
|
+
tree[key] = {}
|
131
|
+
end
|
132
|
+
|
133
|
+
else
|
134
|
+
# We've got an intractable state conflict; raise
|
135
|
+
raise NRSER::ConflictError.new squish <<-END
|
136
|
+
can not set key #{ key.inspect } due to conflicting value
|
137
|
+
#{ tree[key].inspect } in tree #{ tree.inspect } (:clobber
|
138
|
+
option not set)
|
139
|
+
END
|
140
|
+
|
141
|
+
end
|
142
|
+
end # unless hash[key].is_a?( Hash )
|
143
|
+
|
144
|
+
# Dive in...
|
145
|
+
bury! tree[key], rest, value
|
146
|
+
|
147
|
+
end # if rest.empty? / else
|
148
|
+
end # #_internal_bury!
|
149
|
+
|
150
|
+
# end private
|
151
|
+
|
152
|
+
end # class << self (Eigenclass)
|
153
|
+
|
154
|
+
end # module NRSER
|
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
# Definitions
|
3
|
+
# =======================================================================
|
4
|
+
|
5
|
+
module NRSER
|
6
|
+
|
7
|
+
# Returns a new hash created by recursively merging `other_hash` on top of
|
8
|
+
# `base_hash`.
|
9
|
+
#
|
10
|
+
# Adapted from ActiveSupport.
|
11
|
+
#
|
12
|
+
# @see https://github.com/rails/rails/blob/23c8f6918d4e6b9a823aa7a91377c6e3b5d60e13/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
|
13
|
+
#
|
14
|
+
# @param [Hash] base_hash
|
15
|
+
# Base hash - it's values will be overwritten by any key paths shared with
|
16
|
+
# the other hash.
|
17
|
+
#
|
18
|
+
# @param [Hash] other_hash
|
19
|
+
# "Update" hash - it's values will overwrite values at the same key path
|
20
|
+
# in the base hash.
|
21
|
+
#
|
22
|
+
# I don't love the name; just went with what ActiveSupport used.
|
23
|
+
#
|
24
|
+
# @return [Hash]
|
25
|
+
# New merged hash.
|
26
|
+
#
|
27
|
+
def self.deep_merge base_hash, other_hash, &block
|
28
|
+
deep_merge! base_hash.dup, other_hash, &block
|
29
|
+
end # .deep_merge
|
30
|
+
|
31
|
+
|
32
|
+
# Same as {.deep_merge}, but modifies `base_hash`.
|
33
|
+
#
|
34
|
+
# @return [Hash]
|
35
|
+
# The mutated base hash.
|
36
|
+
#
|
37
|
+
def self.deep_merge! base_hash, other_hash, &block
|
38
|
+
other_hash.each_pair do |current_key, other_value|
|
39
|
+
this_value = base_hash[current_key]
|
40
|
+
|
41
|
+
base_hash[current_key] = if this_value.is_a?(Hash) &&
|
42
|
+
other_value.is_a?(Hash)
|
43
|
+
deep_merge this_value, other_value, &block
|
44
|
+
else
|
45
|
+
if block_given? && key?(current_key)
|
46
|
+
block.call(current_key, this_value, other_value)
|
47
|
+
else
|
48
|
+
other_value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
base_hash
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
end # module NRSER
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# Removes the given keys from hash and returns it.
|
7
|
+
#
|
8
|
+
# Lifted from ActiveSupport.
|
9
|
+
#
|
10
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:except!
|
11
|
+
#
|
12
|
+
# @param [Hash] hash
|
13
|
+
# Hash to mutate.
|
14
|
+
#
|
15
|
+
# @return [Hash]
|
16
|
+
#
|
17
|
+
def self.except_keys! hash, *keys
|
18
|
+
keys.each { |key| hash.delete(key) }
|
19
|
+
hash
|
20
|
+
end
|
21
|
+
|
22
|
+
singleton_class.send :alias_method, :omit_keys!, :except_keys!
|
23
|
+
|
24
|
+
|
25
|
+
# Returns a new hash without `keys`.
|
26
|
+
#
|
27
|
+
# Lifted from ActiveSupport.
|
28
|
+
#
|
29
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:except
|
30
|
+
#
|
31
|
+
# @param [Hash] hash
|
32
|
+
# Source hash.
|
33
|
+
#
|
34
|
+
# @return [Hash]
|
35
|
+
#
|
36
|
+
def self.except_keys hash, *keys
|
37
|
+
except_keys! hash.dup, *keys
|
38
|
+
end
|
39
|
+
|
40
|
+
singleton_class.send :alias_method, :omit_keys, :except_keys
|
41
|
+
|
42
|
+
end # module NRSER
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# Guess which type of "label" key - strings or symbols - a hash (or other
|
7
|
+
# object that responds to `#keys` and `#empty`) uses.
|
8
|
+
#
|
9
|
+
# @param [#keys & #empty] keyed
|
10
|
+
# Hash or similar object that responds to `#keys` and `#empty` to guess
|
11
|
+
# about.
|
12
|
+
#
|
13
|
+
# @return [nil]
|
14
|
+
# If we can't determine the type of "label" keys are used (there aren't
|
15
|
+
# any or there is a mix).
|
16
|
+
#
|
17
|
+
# @return [Class]
|
18
|
+
# If we can determine that {String} or {Symbol} keys are exclusively
|
19
|
+
# used returns that class.
|
20
|
+
#
|
21
|
+
def self.guess_label_key_type keyed
|
22
|
+
# We can't tell shit if the hash is empty
|
23
|
+
return nil if keyed.empty?
|
24
|
+
|
25
|
+
name_types = keyed.
|
26
|
+
keys.
|
27
|
+
map( &:class ).
|
28
|
+
select { |klass| klass == String || klass == Symbol }.
|
29
|
+
uniq
|
30
|
+
|
31
|
+
return name_types[0] if name_types.length == 1
|
32
|
+
|
33
|
+
# There are both string and symbol keys present, we can't guess
|
34
|
+
nil
|
35
|
+
end # .guess_label_key_type
|
36
|
+
|
37
|
+
end # module NRSER
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# Lifted from ActiveSupport.
|
7
|
+
#
|
8
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:slice
|
9
|
+
#
|
10
|
+
#
|
11
|
+
def self.slice_keys hash, *keys
|
12
|
+
# We're not using this, but, whatever, leave it in...
|
13
|
+
if hash.respond_to?(:convert_key, true)
|
14
|
+
keys.map! { |key| hash.send :convert_key, key }
|
15
|
+
end
|
16
|
+
|
17
|
+
keys.each_with_object(hash.class.new) { |k, new_hash|
|
18
|
+
new_hash[k] = hash[k] if hash.has_key?(k)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Meant to be a drop-in replacement for the ActiveSupport version, though
|
24
|
+
# I've changed the implementation a bit... because honestly I didn't
|
25
|
+
# understand why they were doing it the way they do :/
|
26
|
+
#
|
27
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:slice!
|
28
|
+
#
|
29
|
+
#
|
30
|
+
def self.slice_keys! hash, *keys
|
31
|
+
# We're not using this, but, whatever, leave it in...
|
32
|
+
if hash.respond_to?(:convert_key, true)
|
33
|
+
keys.map! { |key| hash.send :convert_key, key }
|
34
|
+
end
|
35
|
+
|
36
|
+
slice_keys( hash, *keys ).tap { |slice|
|
37
|
+
except_keys! hash, *keys
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
end # module NRSER
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# Converts all keys into strings by calling `#to_s` on them. **Mutates the
|
7
|
+
# hash.**
|
8
|
+
#
|
9
|
+
# Lifted from ActiveSupport.
|
10
|
+
#
|
11
|
+
# @param [Hash] hash
|
12
|
+
#
|
13
|
+
# @return [Hash<String, *>]
|
14
|
+
#
|
15
|
+
def self.stringify_keys! hash
|
16
|
+
transform_keys! hash, &:to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
singleton_class.send :alias_method, :str_keys!, :stringify_keys!
|
20
|
+
|
21
|
+
|
22
|
+
# Returns a new hash with all keys transformed to strings by calling `#to_s`
|
23
|
+
# on them.
|
24
|
+
#
|
25
|
+
# Lifted from ActiveSupport.
|
26
|
+
#
|
27
|
+
# @param [Hash] hash
|
28
|
+
#
|
29
|
+
# @return [Hash<String, *>]
|
30
|
+
#
|
31
|
+
def self.stringify_keys hash
|
32
|
+
transform_keys hash, &:to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
singleton_class.send :alias_method, :str_keys, :stringify_keys
|
36
|
+
|
37
|
+
end # module NRSER
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Definitions
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
module NRSER
|
5
|
+
|
6
|
+
# Mutates `hash` by converting all keys that respond to `#to_sym` to symbols.
|
7
|
+
#
|
8
|
+
# Lifted from ActiveSupport.
|
9
|
+
#
|
10
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:symbolize_keys!
|
11
|
+
#
|
12
|
+
# @param [Hash] hash
|
13
|
+
#
|
14
|
+
# @return [Hash]
|
15
|
+
#
|
16
|
+
def self.symbolize_keys! hash
|
17
|
+
transform_keys!(hash) { |key| key.to_sym rescue key }
|
18
|
+
end # .symbolize_keys!
|
19
|
+
|
20
|
+
singleton_class.send :alias_method, :sym_keys!, :symbolize_keys!
|
21
|
+
|
22
|
+
|
23
|
+
# Returns a new hash with all keys that respond to `#to_sym` converted to
|
24
|
+
# symbols.
|
25
|
+
#
|
26
|
+
# Lifted from ActiveSupport.
|
27
|
+
#
|
28
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:symbolize_keys
|
29
|
+
#
|
30
|
+
# @param [Hash] hash
|
31
|
+
#
|
32
|
+
# @return [Hash]
|
33
|
+
#
|
34
|
+
def self.symbolize_keys hash
|
35
|
+
# File 'lib/active_support/core_ext/hash/keys.rb', line 54
|
36
|
+
transform_keys(hash) { |key| key.to_sym rescue key }
|
37
|
+
end
|
38
|
+
|
39
|
+
singleton_class.send :alias_method, :sym_keys, :symbolize_keys
|
40
|
+
|
41
|
+
end # module NRSER
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module NRSER
|
2
|
+
|
3
|
+
# Lifted from ActiveSupport.
|
4
|
+
#
|
5
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:transform_keys!
|
6
|
+
#
|
7
|
+
# @param [Hash] hash
|
8
|
+
# Hash to mutate keys.
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
11
|
+
# The mutated hash.
|
12
|
+
#
|
13
|
+
def self.transform_keys! hash
|
14
|
+
# File 'lib/active_support/core_ext/hash/keys.rb', line 23
|
15
|
+
hash.keys.each do |key|
|
16
|
+
hash[yield(key)] = hash.delete(key)
|
17
|
+
end
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Returns a new hash with each key transformed by the provided block.
|
23
|
+
#
|
24
|
+
# Lifted from ActiveSupport.
|
25
|
+
#
|
26
|
+
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:transform_keys
|
27
|
+
#
|
28
|
+
# @param [Hash] hash
|
29
|
+
#
|
30
|
+
# @return [Hash]
|
31
|
+
# New hash with transformed keys.
|
32
|
+
#
|
33
|
+
def self.transform_keys hash, &block
|
34
|
+
# File 'lib/active_support/core_ext/hash/keys.rb', line 12
|
35
|
+
result = {}
|
36
|
+
hash.each_key do |key|
|
37
|
+
result[yield(key)] = hash[key]
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
# My-style name
|
43
|
+
singleton_class.send :alias_method, :map_keys, :transform_keys
|
44
|
+
|
45
|
+
end # module NRSER
|