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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5695621d94d38882e9dcce612c022ac723febf5e
|
4
|
+
data.tar.gz: 825fe26548e53f4f8a72138fabb9554832392c8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35da0260954c54d63cb02aadd26d110810bdcf776bfc9e7dfe5966512411d9b42b6a2d49474e4db3025891b976b9d8c72f140c440f96a402931b5141811b2fd6
|
7
|
+
data.tar.gz: 17a5658d7d56c4a93a8f1eda5fa8782f41452fbc64a4fb195daeca9e1b22fe5e34f6329b595db6e73a23651e61f2b3ab47cec66e74e90a20a2d3d37819ada1b5
|
data/README.md
CHANGED
@@ -1,18 +1,22 @@
|
|
1
|
-
|
1
|
+
NRSER Ruby lib
|
2
|
+
========================================================================
|
2
3
|
|
3
|
-
|
4
|
+
Basic Ruby utilities I use in a lot of stuff.
|
4
5
|
|
5
|
-
|
6
|
+
These tools are hastily written or copied from other sources.
|
6
7
|
|
7
|
-
|
8
|
+
They are not fast.
|
8
9
|
|
9
|
-
|
10
|
+
They are not optimized.
|
10
11
|
|
11
|
-
|
12
|
+
They are largely untested. Though this *is* slowly getting better.
|
12
13
|
|
13
|
-
|
14
|
+
Proceed with caution.
|
14
15
|
|
15
|
-
|
16
|
+
|
17
|
+
------------------------------------------------------------------------
|
18
|
+
Installation
|
19
|
+
------------------------------------------------------------------------
|
16
20
|
|
17
21
|
Add this line to your application's Gemfile:
|
18
22
|
|
@@ -27,6 +31,30 @@ Or install it yourself as:
|
|
27
31
|
$ gem install nrser
|
28
32
|
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
------------------------------------------------------------------------
|
35
|
+
Design
|
36
|
+
------------------------------------------------------------------------
|
37
|
+
|
38
|
+
1. No monkey-patching (`core_ext` kinda stuff).
|
39
|
+
|
40
|
+
All language extension is done with refinements.
|
41
|
+
|
42
|
+
Refinements are funky in a few ways, but they make me feel better.
|
43
|
+
|
44
|
+
2. As a corollary, (pretty-much) all refinement methods are implemented functionally so they can be used in older Rubies (2.0 and before I think?).
|
45
|
+
|
46
|
+
This is pretty much only an issue with the current system Ruby version on macOS, which is `2.0.0`.
|
47
|
+
|
48
|
+
I don't run into it much, but it's nice to have it when you need it, and has panned-out to be a decent design philosophy.
|
49
|
+
|
50
|
+
3. This means that code in the `NRSER` gem itself can't use the refinements it provides.
|
51
|
+
|
52
|
+
Which sucks, but I can live with it, because the times that I need some Ruby when I've only got the macOS system installation available I really wants it.
|
53
|
+
|
54
|
+
- The exception is the types system, which uses the refinements, and will thus be unavailable in old-ass rubies.
|
55
|
+
|
56
|
+
4. Basically all these methods are defined directly on the `NRSER` module for the sake of brevity and connivence, though their definitions are split up across many files and directories by subject.
|
57
|
+
|
58
|
+
I like this because I like small files. They make it easier to see what's changing in commits at a glance, and I find them easier to work with in the GUI editors that I use.
|
59
|
+
|
60
|
+
The fact that Ruby does not tie source file location to API path is one of my favorite parts of the language.
|
data/lib/nrser.rb
CHANGED
@@ -6,8 +6,10 @@ end
|
|
6
6
|
|
7
7
|
require_relative './nrser/version'
|
8
8
|
require_relative './nrser/no_arg'
|
9
|
+
require_relative './nrser/message'
|
10
|
+
require_relative './nrser/proc'
|
9
11
|
require_relative './nrser/collection'
|
10
|
-
require_relative './nrser/
|
12
|
+
require_relative './nrser/object'
|
11
13
|
require_relative './nrser/string'
|
12
14
|
require_relative './nrser/binding'
|
13
15
|
require_relative './nrser/exception'
|
@@ -17,3 +19,5 @@ require_relative './nrser/array'
|
|
17
19
|
require_relative './nrser/types'
|
18
20
|
require_relative './nrser/meta'
|
19
21
|
require_relative './nrser/open_struct'
|
22
|
+
require_relative './nrser/merge_by'
|
23
|
+
require_relative './nrser/tree'
|
data/lib/nrser/array.rb
CHANGED
@@ -1,58 +1,15 @@
|
|
1
1
|
module NRSER
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Functional implementation of "rest" for arrays. Used when refining `#rest`
|
4
|
+
# into {Array}.
|
5
5
|
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# 3. If `value` responds to `#to_a`, try calling it. If it succeeds, return
|
15
|
-
# that.
|
16
|
-
#
|
17
|
-
# 4. Return an array with `value` as it's only item.
|
18
|
-
#
|
19
|
-
# Refinement
|
20
|
-
# ----------
|
21
|
-
#
|
22
|
-
# Added to `Object` in `nrser/refinements`.
|
23
|
-
#
|
24
|
-
# @param [Object] value
|
25
|
-
#
|
26
|
-
# @return [Array]
|
27
|
-
#
|
28
|
-
def as_array value
|
29
|
-
return value if value.is_a? Array
|
30
|
-
return [] if value.nil?
|
31
|
-
|
32
|
-
if value.respond_to? :to_a
|
33
|
-
begin
|
34
|
-
return value.to_a
|
35
|
-
rescue
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
[value]
|
40
|
-
end # #as_array
|
41
|
-
|
42
|
-
|
43
|
-
# Functional implementation of "rest" for arrays. Used when refining `#rest`
|
44
|
-
# into {Array}.
|
45
|
-
#
|
46
|
-
# @param [Array] array
|
47
|
-
#
|
48
|
-
# @return [return_type]
|
49
|
-
# New array consisting of all elements after the first.
|
50
|
-
#
|
51
|
-
def rest array
|
52
|
-
array[1..-1]
|
53
|
-
end # #rest
|
54
|
-
|
55
|
-
|
56
|
-
end # class << self (Eigenclass)
|
6
|
+
# @param [Array] array
|
7
|
+
#
|
8
|
+
# @return [return_type]
|
9
|
+
# New array consisting of all elements after the first.
|
10
|
+
#
|
11
|
+
def self.rest array
|
12
|
+
array[1..-1]
|
13
|
+
end # .rest
|
57
14
|
|
58
15
|
end # module NRSER
|
data/lib/nrser/enumerable.rb
CHANGED
@@ -107,6 +107,27 @@ module NRSER
|
|
107
107
|
end # #find_only
|
108
108
|
|
109
109
|
|
110
|
+
|
111
|
+
# @todo Document only method.
|
112
|
+
#
|
113
|
+
# @param [type] arg_name
|
114
|
+
# @todo Add name param description.
|
115
|
+
#
|
116
|
+
# @return [return_type]
|
117
|
+
# @todo Document return value.
|
118
|
+
#
|
119
|
+
def only! enum
|
120
|
+
unless enum.length == 1
|
121
|
+
raise TypeError.new squish <<-END
|
122
|
+
Expected enumerable #{ enum.inspect } to have exactly one entry.
|
123
|
+
END
|
124
|
+
end
|
125
|
+
|
126
|
+
enum.first
|
127
|
+
end # #only
|
128
|
+
|
129
|
+
|
130
|
+
|
110
131
|
# @todo Document to_h_by method.
|
111
132
|
#
|
112
133
|
# @param [type] arg_name
|
data/lib/nrser/hash.rb
CHANGED
@@ -1,476 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
results
|
16
|
-
end # .leaves
|
17
|
-
|
18
|
-
|
19
|
-
# Treat the value as the value for `key` in a hash if it's not already a
|
20
|
-
# hash and can't be converted to one:
|
21
|
-
#
|
22
|
-
# 1. If the value is a `Hash`, return it.
|
23
|
-
#
|
24
|
-
# 2. If `value` is `nil`, return `{}`.
|
25
|
-
#
|
26
|
-
# 3. If the value responds to `#to_h` and `#to_h` succeeds, return the
|
27
|
-
# resulting hash.
|
28
|
-
#
|
29
|
-
# 4. Otherwise, return a new hash where `key` points to the value.
|
30
|
-
# **`key` MUST be provided in this case.**
|
31
|
-
#
|
32
|
-
# Useful in method overloading and similar situations where you expect a
|
33
|
-
# hash that may specify a host of options, but want to allow the method
|
34
|
-
# to be called with a single value that corresponds to a default key in that
|
35
|
-
# option hash.
|
36
|
-
#
|
37
|
-
# Refinement
|
38
|
-
# ----------
|
39
|
-
#
|
40
|
-
# Added to `Object` in `nrser/refinements`.
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# Example Time!
|
44
|
-
# -------------
|
45
|
-
#
|
46
|
-
# Say you have a method `m` that handles a hash of HTML options that can
|
47
|
-
# look something like
|
48
|
-
#
|
49
|
-
# {class: 'address', data: {confirm: 'Really?'}}
|
50
|
-
#
|
51
|
-
# And can call `m` like
|
52
|
-
#
|
53
|
-
# m({class: 'address', data: {confirm: 'Really?'}})
|
54
|
-
#
|
55
|
-
# but often you are just dealing with the `:class` option. You can use
|
56
|
-
# {NRSER.as_hash} to accept a string and treat it as the `:class` key:
|
57
|
-
#
|
58
|
-
# using NRSER
|
59
|
-
#
|
60
|
-
# def m opts
|
61
|
-
# opts = opts.as_hash :class
|
62
|
-
# # ...
|
63
|
-
# end
|
64
|
-
#
|
65
|
-
# If you pass a hash, everything works normally, but if you pass a string
|
66
|
-
# `'address'` it will be converted to `{class: 'address'}`.
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# About `#to_h` Support
|
70
|
-
# ---------------------
|
71
|
-
#
|
72
|
-
# Right now, {.as_hash} also tests if `value` responds to `#to_h`, and will
|
73
|
-
# try to call it, using the result if it doesn't raise. This lets it deal
|
74
|
-
# with Ruby's "I used to be a Hash until someone mapped me" values like
|
75
|
-
# `[[:class, 'address']]`. I'm not sure if this is the best approach, but
|
76
|
-
# I'm going to try it for now and see how it pans out in actual usage.
|
77
|
-
#
|
78
|
-
# @todo
|
79
|
-
# It might be nice to have a `check` option that ensures the resulting
|
80
|
-
# hash has a value for `key`.
|
81
|
-
#
|
82
|
-
# @param [Object] value
|
83
|
-
# The value that we want to be a hash.
|
84
|
-
#
|
85
|
-
# @param [Object] key [default nil]
|
86
|
-
# The key that `value` will be stored under in the result if `value` is
|
87
|
-
# not a hash or can't be turned into one via `#to_h`. If this happens
|
88
|
-
# this value can **NOT** be `nil` or an `ArgumentError` is raised.
|
89
|
-
#
|
90
|
-
# @return [Hash]
|
91
|
-
#
|
92
|
-
# @raise [ArgumentError]
|
93
|
-
# If it comes to constructing a new Hash with `value` as a value and no
|
94
|
-
# argument was provided
|
95
|
-
#
|
96
|
-
def self.as_hash value, key = nil
|
97
|
-
return value if value.is_a? Hash
|
98
|
-
return {} if value.nil?
|
99
|
-
|
100
|
-
if value.respond_to? :to_h
|
101
|
-
begin
|
102
|
-
return value.to_h
|
103
|
-
rescue
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# at this point we need a key argument
|
108
|
-
if key.nil?
|
109
|
-
raise ArgumentError,
|
110
|
-
"Need key to construct hash with value #{ value.inspect }, " +
|
111
|
-
"found nil."
|
112
|
-
end
|
113
|
-
|
114
|
-
{key => value}
|
115
|
-
end # .as_hash
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
# Lifted from ActiveSupport
|
120
|
-
# =====================================================================
|
121
|
-
#
|
122
|
-
# Not sure *why* I didn't want to depend on ActiveSupport in the first place,
|
123
|
-
# but I'm guessing it's many other things depending on it and the potential
|
124
|
-
# for dependency hell, but anyways, I didn't, and I'm going to keep it that
|
125
|
-
# way for the moment.
|
126
|
-
#
|
127
|
-
# However, I do want some of that functionality, and I think it makes sense
|
128
|
-
# to keep the names and behaviors the same since ActiveSupport is so wide
|
129
|
-
# spread.
|
130
|
-
#
|
131
|
-
# The methods are modified to operate functionally since we use refinements
|
132
|
-
# instead of global monkey-patching, and Ruby versions before 2.1 (I think)
|
133
|
-
# don't support refinements, so these are useful in environments where you
|
134
|
-
# don't want to mess with the global built-ins and you don't have
|
135
|
-
# refinements available.
|
136
|
-
#
|
137
|
-
|
138
|
-
# Removes the given keys from hash and returns it.
|
139
|
-
#
|
140
|
-
# Lifted from ActiveSupport.
|
141
|
-
#
|
142
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:except!
|
143
|
-
#
|
144
|
-
# @param [Hash] hash
|
145
|
-
# Hash to mutate.
|
146
|
-
#
|
147
|
-
# @return [Hash]
|
148
|
-
#
|
149
|
-
def self.except_keys! hash, *keys
|
150
|
-
keys.each { |key| hash.delete(key) }
|
151
|
-
hash
|
152
|
-
end
|
153
|
-
|
154
|
-
singleton_class.send :alias_method, :omit_keys!, :except_keys!
|
155
|
-
|
156
|
-
|
157
|
-
# Returns a new hash without `keys`.
|
158
|
-
#
|
159
|
-
# Lifted from ActiveSupport.
|
160
|
-
#
|
161
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:except
|
162
|
-
#
|
163
|
-
# @param [Hash] hash
|
164
|
-
# Source hash.
|
165
|
-
#
|
166
|
-
# @return [Hash]
|
167
|
-
#
|
168
|
-
def self.except_keys hash, *keys
|
169
|
-
except_keys! hash.dup, *keys
|
170
|
-
end
|
171
|
-
|
172
|
-
singleton_class.send :alias_method, :omit_keys, :except_keys
|
173
|
-
|
174
|
-
|
175
|
-
# Lifted from ActiveSupport.
|
176
|
-
#
|
177
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:transform_keys!
|
178
|
-
#
|
179
|
-
# @param [Hash] hash
|
180
|
-
# Hash to mutate keys.
|
181
|
-
#
|
182
|
-
# @return [Hash]
|
183
|
-
# The mutated hash.
|
184
|
-
#
|
185
|
-
def self.transform_keys! hash
|
186
|
-
# File 'lib/active_support/core_ext/hash/keys.rb', line 23
|
187
|
-
hash.keys.each do |key|
|
188
|
-
hash[yield(key)] = hash.delete(key)
|
189
|
-
end
|
190
|
-
hash
|
191
|
-
end
|
192
|
-
|
193
|
-
|
194
|
-
# Returns a new hash with each key transformed by the provided block.
|
195
|
-
#
|
196
|
-
# Lifted from ActiveSupport.
|
197
|
-
#
|
198
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:transform_keys
|
199
|
-
#
|
200
|
-
# @param [Hash] hash
|
201
|
-
#
|
202
|
-
# @return [Hash]
|
203
|
-
# New hash with transformed keys.
|
204
|
-
#
|
205
|
-
def self.transform_keys hash, &block
|
206
|
-
# File 'lib/active_support/core_ext/hash/keys.rb', line 12
|
207
|
-
result = {}
|
208
|
-
hash.each_key do |key|
|
209
|
-
result[yield(key)] = hash[key]
|
210
|
-
end
|
211
|
-
result
|
212
|
-
end
|
213
|
-
|
214
|
-
# My-style name
|
215
|
-
singleton_class.send :alias_method, :map_keys, :transform_keys
|
216
|
-
|
217
|
-
|
218
|
-
# Mutates `hash` by converting all keys that respond to `#to_sym` to symbols.
|
219
|
-
#
|
220
|
-
# Lifted from ActiveSupport.
|
221
|
-
#
|
222
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:symbolize_keys!
|
223
|
-
#
|
224
|
-
# @param [Hash] hash
|
225
|
-
#
|
226
|
-
# @return [Hash]
|
227
|
-
#
|
228
|
-
def self.symbolize_keys! hash
|
229
|
-
transform_keys!(hash) { |key| key.to_sym rescue key }
|
230
|
-
end # .symbolize_keys!
|
231
|
-
|
232
|
-
|
233
|
-
# Returns a new hash with all keys that respond to `#to_sym` converted to
|
234
|
-
# symbols.
|
235
|
-
#
|
236
|
-
# Lifted from ActiveSupport.
|
237
|
-
#
|
238
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:symbolize_keys
|
239
|
-
#
|
240
|
-
# @param [Hash] hash
|
241
|
-
#
|
242
|
-
# @return [Hash]
|
243
|
-
#
|
244
|
-
def self.symbolize_keys hash
|
245
|
-
# File 'lib/active_support/core_ext/hash/keys.rb', line 54
|
246
|
-
transform_keys(hash) { |key| key.to_sym rescue key }
|
247
|
-
end
|
248
|
-
|
249
|
-
|
250
|
-
# Converts all keys into strings by calling `#to_s` on them. **Mutates the
|
251
|
-
# hash.**
|
252
|
-
#
|
253
|
-
# Lifted from ActiveSupport.
|
254
|
-
#
|
255
|
-
# @param [Hash] hash
|
256
|
-
#
|
257
|
-
# @return [Hash<String, *>]
|
258
|
-
#
|
259
|
-
def self.stringify_keys! hash
|
260
|
-
transform_keys! hash, &:to_s
|
261
|
-
end
|
262
|
-
|
263
|
-
|
264
|
-
# Returns a new hash with all keys transformed to strings by calling `#to_s`
|
265
|
-
# on them.
|
266
|
-
#
|
267
|
-
# Lifted from ActiveSupport.
|
268
|
-
#
|
269
|
-
# @param [Hash] hash
|
270
|
-
#
|
271
|
-
# @return [Hash<String, *>]
|
272
|
-
#
|
273
|
-
def self.stringify_keys hash
|
274
|
-
transform_keys hash, &:to_s
|
275
|
-
end
|
276
|
-
|
277
|
-
|
278
|
-
# Lifted from ActiveSupport.
|
279
|
-
#
|
280
|
-
# @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:slice
|
281
|
-
#
|
282
|
-
#
|
283
|
-
def self.slice_keys hash, *keys
|
284
|
-
# We're not using this, but, whatever, leave it in...
|
285
|
-
if hash.respond_to?(:convert_key, true)
|
286
|
-
keys.map! { |key| hash.send :convert_key, key }
|
287
|
-
end
|
288
|
-
|
289
|
-
keys.each_with_object(hash.class.new) { |k, new_hash|
|
290
|
-
new_hash[k] = hash[k] if hash.has_key?(k)
|
291
|
-
}
|
292
|
-
end
|
293
|
-
|
294
|
-
|
295
|
-
# Eigenclass (Singleton Class)
|
296
|
-
# ========================================================================
|
297
|
-
#
|
298
|
-
class << self
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
# @todo Document select_map method.
|
303
|
-
#
|
304
|
-
# @param [type] arg_name
|
305
|
-
# @todo Add name param description.
|
306
|
-
#
|
307
|
-
# @return [return_type]
|
308
|
-
# @todo Document return value.
|
309
|
-
#
|
310
|
-
def select_map arg_name
|
311
|
-
# method body...
|
312
|
-
end # #select_map
|
313
|
-
|
314
|
-
|
315
|
-
# Guess which type of "name" key - strings or symbols - a hash (or other
|
316
|
-
# object that responds to `#keys` and `#empty`) uses.
|
317
|
-
#
|
318
|
-
# @param [#keys & #empty] keyed
|
319
|
-
# Hash or similar object that responds to `#keys` and `#empty` to guess
|
320
|
-
# about.
|
321
|
-
#
|
322
|
-
# @return [nil]
|
323
|
-
# If we can't determine the type of name keys used.
|
324
|
-
#
|
325
|
-
# @return [Class]
|
326
|
-
# If we can determine that {String} or {Symbol} keys are exclusively
|
327
|
-
# used returns that class.
|
328
|
-
#
|
329
|
-
def guess_name_type keyed
|
330
|
-
# We can't tell shit if the hash is empty
|
331
|
-
return nil if keyed.empty?
|
332
|
-
|
333
|
-
name_types = keyed.
|
334
|
-
keys.
|
335
|
-
map( &:class ).
|
336
|
-
select { |klass| klass == String || klass == Symbol }.
|
337
|
-
uniq
|
338
|
-
|
339
|
-
return name_types[0] if name_types.length == 1
|
340
|
-
|
341
|
-
# There are both string and symbol keys present, we can't guess
|
342
|
-
nil
|
343
|
-
end # #guess_key_type
|
344
|
-
|
345
|
-
|
346
|
-
# @todo Document bury method.
|
347
|
-
#
|
348
|
-
# @param [Hash] hash
|
349
|
-
# Hash to bury the value in.
|
350
|
-
#
|
351
|
-
# @param [Array | #to_s] key_path
|
352
|
-
# - When an {Array}, each entry is used exactly as-is for each key.
|
353
|
-
#
|
354
|
-
# - Otherwise, the `key_path` is converted to a string and split by
|
355
|
-
# `.` to produce the key array, and the actual keys used depend on
|
356
|
-
# the `parsed_key_type` option.
|
357
|
-
#
|
358
|
-
# @param [Object] value
|
359
|
-
# The value to set at the end of the path.
|
360
|
-
#
|
361
|
-
# @param [Class | :guess] parsed_key_type:
|
362
|
-
# How to handle parsed key path segments:
|
363
|
-
#
|
364
|
-
# - `String` - use the strings that naturally split from a parsed
|
365
|
-
# key path.
|
366
|
-
#
|
367
|
-
# Note that this is the *String class itself, **not** a value that
|
368
|
-
# is a String*.
|
369
|
-
#
|
370
|
-
# - `Symbol` - convert the strings that are split from the key path
|
371
|
-
# to symbols.
|
372
|
-
#
|
373
|
-
# Note that this is the *Symbol class itself, **not** a value that
|
374
|
-
# is a Symbol*.``
|
375
|
-
#
|
376
|
-
# - `:guess` (default) -
|
377
|
-
#
|
378
|
-
# @return [return_type]
|
379
|
-
# @todo Document return value.
|
380
|
-
#
|
381
|
-
def bury! hash,
|
382
|
-
key_path,
|
383
|
-
value,
|
384
|
-
parsed_key_type: :guess,
|
385
|
-
clobber: false
|
386
|
-
|
387
|
-
# Parse the key if it's not an array
|
388
|
-
unless key_path.is_a?( Array )
|
389
|
-
key_path = key_path.to_s.split '.'
|
390
|
-
|
391
|
-
# Convert the keys to symbols now if that's what we want to use
|
392
|
-
if parsed_key_type == Symbol
|
393
|
-
key_path.map! &:to_sym
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
_internal_bury! \
|
398
|
-
hash,
|
399
|
-
key_path,
|
400
|
-
value,
|
401
|
-
guess_key_type: ( parsed_key_type == :guess ),
|
402
|
-
clobber: clobber
|
403
|
-
end # #bury
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
private
|
408
|
-
# ========================================================================
|
409
|
-
|
410
|
-
|
411
|
-
# @todo Document _internal_bury! method.
|
412
|
-
#
|
413
|
-
# @param [type] arg_name
|
414
|
-
# @todo Add name param description.
|
415
|
-
#
|
416
|
-
# @return [return_type]
|
417
|
-
# @todo Document return value.
|
418
|
-
#
|
419
|
-
def _internal_bury! hash,
|
420
|
-
key_path,
|
421
|
-
value,
|
422
|
-
guess_key_type:,
|
423
|
-
clobber:
|
424
|
-
|
425
|
-
# Split the key path into the current key and the rest of the keys
|
426
|
-
key, *rest = key_path
|
427
|
-
|
428
|
-
# If we are guessing the key type and the hash uses some {Symbol}
|
429
|
-
# (and no {String}) keys then convert the key to a symbol.
|
430
|
-
if guess_key_type && guess_name_type( hash ) == Symbol
|
431
|
-
key = key.to_sym
|
432
|
-
end
|
433
|
-
|
434
|
-
# Terminating case: we're at the last segment
|
435
|
-
if rest.empty?
|
436
|
-
# Set the value
|
437
|
-
hash[key] = value
|
438
|
-
|
439
|
-
else
|
440
|
-
# Go deeper...
|
441
|
-
|
442
|
-
# See if there is a hash in place
|
443
|
-
unless hash[key].is_a?( Hash )
|
444
|
-
# There is not... so we need to do some figurin'
|
445
|
-
|
446
|
-
# If we're clobbering or the hash has no value, we're good:
|
447
|
-
# assign a new hash to set in
|
448
|
-
if clobber || ! hash.key?( key )
|
449
|
-
hash[key] = {}
|
450
|
-
|
451
|
-
else
|
452
|
-
# We've got an intractable state conflict; raise
|
453
|
-
raise NRSER::ConflictError.new squish <<-END
|
454
|
-
can not set key #{ key.inspect } due to conflicting value
|
455
|
-
#{ hash[key].inspect } in hash #{ hash.inspect } (:clobber
|
456
|
-
option not set)
|
457
|
-
END
|
458
|
-
|
459
|
-
end
|
460
|
-
end # unless hash[key].is_a?( Hash )
|
461
|
-
|
462
|
-
# Dive in...
|
463
|
-
bury! hash[key], rest, value
|
464
|
-
|
465
|
-
end # if rest.empty? / else
|
466
|
-
end # #_internal_bury!
|
467
|
-
|
468
|
-
|
469
|
-
# end private
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
end # class << self (Eigenclass)
|
474
|
-
|
475
|
-
|
476
|
-
end # module NRSER
|
1
|
+
# Requirements
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
# Project / Package
|
5
|
+
# -----------------------------------------------------------------------
|
6
|
+
require_relative './hash/except_keys'
|
7
|
+
require_relative './hash/transform_keys'
|
8
|
+
require_relative './hash/symbolize_keys'
|
9
|
+
require_relative './hash/stringify_keys'
|
10
|
+
require_relative './hash/slice_keys'
|
11
|
+
require_relative './hash/guess_label_key_type'
|
12
|
+
require_relative './hash/bury'
|
13
|
+
require_relative './hash/deep_merge'
|