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
@@ -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
|