collapsium 0.3.0 → 0.4.0
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/Gemfile.lock +6 -6
- data/collapsium.gemspec +1 -1
- data/lib/collapsium.rb +6 -1
- data/lib/collapsium/environment_override.rb +167 -0
- data/lib/collapsium/pathed_access.rb +173 -81
- data/lib/collapsium/recursive_dup.rb +8 -3
- data/lib/collapsium/recursive_merge.rb +8 -4
- data/lib/collapsium/recursive_sort.rb +6 -33
- data/lib/collapsium/support/hash_methods.rb +48 -0
- data/lib/collapsium/support/methods.rb +88 -0
- data/lib/collapsium/uber_hash.rb +7 -1
- data/lib/collapsium/version.rb +1 -1
- data/lib/collapsium/viral_capabilities.rb +151 -0
- data/spec/environment_override_spec.rb +154 -0
- data/spec/pathed_access_spec.rb +83 -0
- data/spec/recursive_merge_spec.rb +17 -0
- data/spec/support_methods_spec.rb +231 -0
- data/spec/uber_hash_spec.rb +16 -0
- data/spec/viral_capabilities_spec.rb +248 -0
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0565523a24719d83a3a81ec99005231cb86155df
|
4
|
+
data.tar.gz: eb817e05d8dec93788ecc51337579315a6719c70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3e20a3f37be0496fd1ac6c6f653e845aaf57d7dd8b149347bddef882ac2ddf7b414b656370e65c49070fb8a638c4beb9ba139fc18bd485765f7fe695ea738da
|
7
|
+
data.tar.gz: 45ed48ecbc31111ac87689f7ffc84cdb5769d6a19bde8368e6cf5e0f60902af1c1753894d011f059b286e989db398f50e65984b8cd336e132a9a09b4b0197ca3
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
collapsium (0.
|
4
|
+
collapsium (0.4.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -11,7 +11,7 @@ GEM
|
|
11
11
|
simplecov (>= 0.7.1, < 1.0.0)
|
12
12
|
diff-lcs (1.2.5)
|
13
13
|
docile (1.1.5)
|
14
|
-
json (2.0.
|
14
|
+
json (2.0.2)
|
15
15
|
parser (2.3.1.2)
|
16
16
|
ast (~> 2.2)
|
17
17
|
powerpack (0.1.1)
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
rspec-core (~> 3.5.0)
|
22
22
|
rspec-expectations (~> 3.5.0)
|
23
23
|
rspec-mocks (~> 3.5.0)
|
24
|
-
rspec-core (3.5.
|
24
|
+
rspec-core (3.5.2)
|
25
25
|
rspec-support (~> 3.5.0)
|
26
26
|
rspec-expectations (3.5.0)
|
27
27
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -30,7 +30,7 @@ GEM
|
|
30
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
31
31
|
rspec-support (~> 3.5.0)
|
32
32
|
rspec-support (3.5.0)
|
33
|
-
rubocop (0.
|
33
|
+
rubocop (0.42.0)
|
34
34
|
parser (>= 2.3.1.1, < 3.0)
|
35
35
|
powerpack (~> 0.1)
|
36
36
|
rainbow (>= 1.99.1, < 3.0)
|
@@ -43,7 +43,7 @@ GEM
|
|
43
43
|
simplecov-html (~> 0.10.0)
|
44
44
|
simplecov-html (0.10.0)
|
45
45
|
unicode-display_width (1.1.0)
|
46
|
-
yard (0.9.
|
46
|
+
yard (0.9.5)
|
47
47
|
|
48
48
|
PLATFORMS
|
49
49
|
ruby
|
@@ -54,7 +54,7 @@ DEPENDENCIES
|
|
54
54
|
collapsium!
|
55
55
|
rake (~> 11.2)
|
56
56
|
rspec (~> 3.5)
|
57
|
-
rubocop (~> 0.
|
57
|
+
rubocop (~> 0.42)
|
58
58
|
simplecov (~> 0.12)
|
59
59
|
yard (~> 0.9)
|
60
60
|
|
data/collapsium.gemspec
CHANGED
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.required_ruby_version = '>= 2.0'
|
36
36
|
|
37
37
|
spec.add_development_dependency "bundler", "~> 1.12"
|
38
|
-
spec.add_development_dependency "rubocop", "~> 0.
|
38
|
+
spec.add_development_dependency "rubocop", "~> 0.42"
|
39
39
|
spec.add_development_dependency "rake", "~> 11.2"
|
40
40
|
spec.add_development_dependency "rspec", "~> 3.5"
|
41
41
|
spec.add_development_dependency "simplecov", "~> 0.12"
|
data/lib/collapsium.rb
CHANGED
@@ -9,8 +9,13 @@
|
|
9
9
|
|
10
10
|
require 'collapsium/version'
|
11
11
|
|
12
|
-
require 'collapsium/
|
12
|
+
require 'collapsium/environment_override'
|
13
13
|
require 'collapsium/indifferent_access'
|
14
14
|
require 'collapsium/pathed_access'
|
15
|
+
require 'collapsium/prototype_match'
|
16
|
+
require 'collapsium/recursive_dup'
|
17
|
+
require 'collapsium/recursive_merge'
|
18
|
+
require 'collapsium/recursive_sort'
|
19
|
+
require 'collapsium/viral_capabilities'
|
15
20
|
|
16
21
|
require 'collapsium/uber_hash'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# collapsium
|
4
|
+
# https://github.com/jfinkhaeuser/collapsium
|
5
|
+
#
|
6
|
+
# Copyright (c) 2016 Jens Finkhaeuser and other collapsium contributors.
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'collapsium/support/hash_methods'
|
11
|
+
require 'collapsium/support/methods'
|
12
|
+
|
13
|
+
require 'collapsium/viral_capabilities'
|
14
|
+
|
15
|
+
module Collapsium
|
16
|
+
|
17
|
+
##
|
18
|
+
# The EnvironmentOverride module wraps read access methods to return
|
19
|
+
# the contents of environment variables derived from the key instead of
|
20
|
+
# the value contained in the Hash.
|
21
|
+
#
|
22
|
+
# The environment variable to use is derived from the key by replacing
|
23
|
+
# all consecutive occurrences of non-alphanumeric characters with an
|
24
|
+
# underscore (_), and converting the alphabetic characters to upper case.
|
25
|
+
# Leading and trailing underscores will be stripped.
|
26
|
+
#
|
27
|
+
# For example, the key "some!@email.org" will become "SOME_EMAIL_ORG".
|
28
|
+
#
|
29
|
+
# If PathedAccess is also used, the :path_prefix of nested hashes will
|
30
|
+
# be consulted after converting it in the same manner.
|
31
|
+
module EnvironmentOverride
|
32
|
+
##
|
33
|
+
# If the module is included, extended or prepended in a class, it'll
|
34
|
+
# wrap accessor methods.
|
35
|
+
class << self
|
36
|
+
include ::Collapsium::Support::HashMethods
|
37
|
+
include ::Collapsium::Support::Methods
|
38
|
+
|
39
|
+
##
|
40
|
+
# Returns a proc read access.
|
41
|
+
ENV_ACCESS_READER = proc do |super_method, *args, &block|
|
42
|
+
# If there are no arguments, there's nothing to do with paths. Just
|
43
|
+
# delegate to the hash.
|
44
|
+
if args.empty?
|
45
|
+
next super_method.call(*args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
# The method's receiver is encapsulated in the super_method; we'll
|
49
|
+
# use it a few times so let's reduce typing. This is essentially the
|
50
|
+
# equivalent of `self`.
|
51
|
+
receiver = super_method.receiver
|
52
|
+
|
53
|
+
# All KEYED_READ_METHODS have a key as the first argument.
|
54
|
+
key = args[0]
|
55
|
+
|
56
|
+
# Grab matching environment variable names. We consider first a pathed
|
57
|
+
# name, if pathed access is supported, followed by the unpathed name.
|
58
|
+
env_keys = [key.to_s]
|
59
|
+
if receiver.respond_to?(:path_prefix) and not
|
60
|
+
(receiver.path_prefix.nil? or receiver.path_prefix.empty?)
|
61
|
+
prefix_components = receiver.path_components(receiver.path_prefix)
|
62
|
+
|
63
|
+
if prefix_components.last == key
|
64
|
+
env_keys.unshift(receiver.path_prefix)
|
65
|
+
else
|
66
|
+
env_keys.unshift(receiver.path_prefix + receiver.separator + key.to_s)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
env_keys.map! { |k| receiver.key_to_env(k) }
|
70
|
+
env_keys.select! { |k| not k.empty? }
|
71
|
+
env_keys.uniq!
|
72
|
+
|
73
|
+
# When we have the keys (in priority order), try to see if the
|
74
|
+
# environment yields something useful.
|
75
|
+
value = nil
|
76
|
+
env_keys.each do |env_key|
|
77
|
+
# Grab the environment value; skip if there's nothing there.
|
78
|
+
env_value = ENV[env_key]
|
79
|
+
if env_value.nil?
|
80
|
+
next
|
81
|
+
end
|
82
|
+
|
83
|
+
# If the environment variable parses as JSON, that's great, we'll use
|
84
|
+
# the parsed result. Otherwise use it as a string.
|
85
|
+
require 'json'
|
86
|
+
# rubocop:disable Lint/HandleExceptions
|
87
|
+
begin
|
88
|
+
env_value = JSON.parse(env_value)
|
89
|
+
rescue JSON::ParserError
|
90
|
+
# Do nothing. We just use the env_value verbatim.
|
91
|
+
end
|
92
|
+
# rubocop:enable Lint/HandleExceptions
|
93
|
+
|
94
|
+
# For the given key, retrieve the current value. We'll need it later.
|
95
|
+
# Note that we deliberately do not use any of KEYED_READ_METHODS,
|
96
|
+
# because that would recurse infinitely.
|
97
|
+
old_value = nil
|
98
|
+
receiver.each do |k, v|
|
99
|
+
if k == key
|
100
|
+
old_value = v
|
101
|
+
break
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Note that we re-assign the value only if it's changed, but we
|
106
|
+
# break either way. If env_value exists, it must be used, but
|
107
|
+
# changing it always will lead to an infinite recursion.
|
108
|
+
# The double's super_method will never be called, but rather
|
109
|
+
# always this wrapper.
|
110
|
+
if env_value != old_value
|
111
|
+
value = env_value
|
112
|
+
end
|
113
|
+
break
|
114
|
+
end
|
115
|
+
if not value.nil?
|
116
|
+
# We can't just return the value, because that doesn't respect the
|
117
|
+
# method being called. We can deal with this by duplicating the
|
118
|
+
# receiver, writing the value we found into the appropriate key,
|
119
|
+
# and then sending the super_method to the duplicate.
|
120
|
+
double = receiver.dup
|
121
|
+
double[key] = value
|
122
|
+
meth = double.method(super_method.name)
|
123
|
+
next meth.call(*args, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Otherwise, fall back on the super method.
|
127
|
+
next super_method.call(*args, &block)
|
128
|
+
end.freeze # proc
|
129
|
+
|
130
|
+
def included(base)
|
131
|
+
enhance(base)
|
132
|
+
end
|
133
|
+
|
134
|
+
def extended(base)
|
135
|
+
enhance(base)
|
136
|
+
end
|
137
|
+
|
138
|
+
def prepended(base)
|
139
|
+
enhance(base)
|
140
|
+
end
|
141
|
+
|
142
|
+
def enhance(base)
|
143
|
+
# Make the capabilities of classes using PathedAccess viral.
|
144
|
+
base.extend(ViralCapabilities)
|
145
|
+
|
146
|
+
# Wrap read accessor functions to deal with paths
|
147
|
+
KEYED_READ_METHODS.each do |method|
|
148
|
+
wrap_method(base, method, &ENV_ACCESS_READER)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end # class << self
|
152
|
+
|
153
|
+
def key_to_env(key)
|
154
|
+
# First, convert to upper case
|
155
|
+
env_key = key.upcase
|
156
|
+
|
157
|
+
# Next, replace non-alphanumeric characters to underscore. This also
|
158
|
+
# collapses them into a single undescore.
|
159
|
+
env_key.gsub!(/[^[:alnum:]]+/, '_')
|
160
|
+
|
161
|
+
# Strip leading and trailing underscores.
|
162
|
+
env_key.gsub!(/^_*(.*?)_*$/, '\1')
|
163
|
+
|
164
|
+
return env_key
|
165
|
+
end
|
166
|
+
end # module EnvironmentOverride
|
167
|
+
end # module Collapsium
|
@@ -7,6 +7,11 @@
|
|
7
7
|
# All rights reserved.
|
8
8
|
#
|
9
9
|
|
10
|
+
require 'collapsium/support/hash_methods'
|
11
|
+
require 'collapsium/support/methods'
|
12
|
+
|
13
|
+
require 'collapsium/viral_capabilities'
|
14
|
+
|
10
15
|
module Collapsium
|
11
16
|
|
12
17
|
##
|
@@ -28,17 +33,16 @@ module Collapsium
|
|
28
33
|
return @separator
|
29
34
|
end
|
30
35
|
|
31
|
-
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
##
|
37
|
+
# Assume any pathed access has this prefix.
|
38
|
+
def path_prefix=(value)
|
39
|
+
@path_prefix = normalize_path(value)
|
40
|
+
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
].freeze
|
42
|
+
def path_prefix
|
43
|
+
@path_prefix ||= ''
|
44
|
+
return @path_prefix
|
45
|
+
end
|
42
46
|
|
43
47
|
# @api private
|
44
48
|
# Default path separator
|
@@ -50,95 +54,183 @@ module Collapsium
|
|
50
54
|
/(?<!\\)#{Regexp.escape(separator)}/
|
51
55
|
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
##
|
58
|
+
# If the module is included, extended or prepended in a class, it'll
|
59
|
+
# wrap accessor methods.
|
60
|
+
class << self
|
61
|
+
include ::Collapsium::Support::HashMethods
|
62
|
+
include ::Collapsium::Support::Methods
|
63
|
+
|
64
|
+
##
|
65
|
+
# Returns a proc for either read or write access. Procs for write
|
66
|
+
# access will create intermediary hashes when e.g. setting a value for
|
67
|
+
# `foo.bar.baz`, and the `bar` Hash doesn't exist yet.
|
68
|
+
def create_proc(write_access)
|
69
|
+
return proc do |super_method, *args, &block|
|
70
|
+
# If there are no arguments, there's nothing to do with paths. Just
|
71
|
+
# delegate to the hash.
|
72
|
+
if args.empty?
|
73
|
+
next super_method.call(*args, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
# The method's receiver is encapsulated in the super_method; we'll
|
77
|
+
# use it a few times so let's reduce typing. This is essentially the
|
78
|
+
# equivalent of `self`.
|
79
|
+
receiver = super_method.receiver
|
61
80
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
# With any of the dispatch methods, we know that the first argument has
|
82
|
+
# to be a key. We'll try to split it by the path separator.
|
83
|
+
components = receiver.path_components(args[0].to_s)
|
84
|
+
|
85
|
+
# If there are no components, return the receiver itself/the root
|
86
|
+
if components.empty?
|
87
|
+
next receiver
|
88
|
+
end
|
89
|
+
|
90
|
+
# Try to find the leaf, based on the given components.
|
91
|
+
leaf = recursive_fetch(components, receiver, [], create: write_access)
|
92
|
+
|
93
|
+
# The tricky part is what to do with the leaf.
|
94
|
+
meth = nil
|
95
|
+
if receiver.object_id == leaf.object_id
|
96
|
+
# a) if the leaf and the receiver are identical, then the receiver
|
97
|
+
# itself was requested, and we really just need to delegate to its
|
98
|
+
# super_method.
|
99
|
+
meth = super_method
|
100
|
+
else
|
101
|
+
# b) if the leaf is different from the receiver, we want to delegate
|
102
|
+
# to the leaf.
|
103
|
+
meth = leaf.method(super_method.name)
|
68
104
|
end
|
69
|
-
|
105
|
+
|
106
|
+
# If the first argument was a symbol key, we want to use it verbatim.
|
107
|
+
# Otherwise we had pathed access, and only want to pass the last
|
108
|
+
# component to whatever method we're calling.
|
109
|
+
the_args = args
|
110
|
+
if not args[0].is_a?(Symbol)
|
111
|
+
the_args = args.dup
|
112
|
+
the_args[0] = components.last
|
113
|
+
end
|
114
|
+
|
115
|
+
# Then we can continue with that method.
|
116
|
+
next meth.call(*the_args, &block)
|
117
|
+
end # proc
|
118
|
+
end # create_proc
|
119
|
+
|
120
|
+
# Create a reader and write proc, because we only know
|
121
|
+
PATHED_ACCESS_READER = PathedAccess.create_proc(false).freeze
|
122
|
+
PATHED_ACCESS_WRITER = PathedAccess.create_proc(true).freeze
|
123
|
+
|
124
|
+
def included(base)
|
125
|
+
enhance(base)
|
126
|
+
end
|
127
|
+
|
128
|
+
def extended(base)
|
129
|
+
enhance(base)
|
130
|
+
end
|
131
|
+
|
132
|
+
def prepended(base)
|
133
|
+
enhance(base)
|
134
|
+
end
|
135
|
+
|
136
|
+
def enhance(base)
|
137
|
+
# Make the capabilities of classes using PathedAccess viral.
|
138
|
+
base.extend(ViralCapabilities)
|
139
|
+
|
140
|
+
# Wrap all accessor functions to deal with paths
|
141
|
+
KEYED_READ_METHODS.each do |method|
|
142
|
+
wrap_method(base, method, &PATHED_ACCESS_READER)
|
143
|
+
end
|
144
|
+
KEYED_WRITE_METHODS.each do |method|
|
145
|
+
wrap_method(base, method, &PATHED_ACCESS_WRITER)
|
70
146
|
end
|
147
|
+
end
|
71
148
|
|
72
|
-
|
73
|
-
|
74
|
-
|
149
|
+
##
|
150
|
+
# Given the path components, recursively fetch any but the last key.
|
151
|
+
def recursive_fetch(path, data, current_path = [], options = {})
|
152
|
+
# Split path into head and tail; for the next iteration, we'll look use
|
153
|
+
# only head, and pass tail on recursively.
|
154
|
+
head = path[0]
|
155
|
+
current_path << head
|
156
|
+
tail = path.slice(1, path.length)
|
157
|
+
|
158
|
+
# We know that the data has the current path. We also know that thanks to
|
159
|
+
# virality, data will respond to :path_prefix. So we might as well set the
|
160
|
+
# path, as long as it is more specific than what was previously there.
|
161
|
+
current_normalized = data.normalize_path(current_path)
|
162
|
+
if current_normalized.length > data.path_prefix.length
|
163
|
+
data.path_prefix = current_normalized
|
75
164
|
end
|
76
165
|
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# which could mean looking fails.
|
82
|
-
# We can detect that by comparing copy[0] to a symbolized version of
|
83
|
-
# components[0].
|
84
|
-
copy = args.dup
|
85
|
-
if copy[0] != components[0].to_sym
|
86
|
-
copy[0] = components[0]
|
87
|
-
end
|
88
|
-
return super(*copy, &block)
|
166
|
+
# For the leaf element, we do nothing because that's where we want to
|
167
|
+
# dispatch to.
|
168
|
+
if path.length == 1
|
169
|
+
return data
|
89
170
|
end
|
90
171
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
172
|
+
# If we're a write function, then we need to create intermediary objects,
|
173
|
+
# i.e. what's at head if nothing is there.
|
174
|
+
if data[head].nil?
|
175
|
+
# If the head is nil, we can't recurse. In create mode that means we
|
176
|
+
# want to create hash children, but in read mode we're done recursing.
|
177
|
+
# By returning a hash here, we allow the caller to send methods on to
|
178
|
+
# this temporary, making a PathedAccess Hash act like any other Hash.
|
179
|
+
if not options[:create]
|
180
|
+
return {}
|
181
|
+
end
|
182
|
+
|
183
|
+
data[head] = {}
|
100
184
|
end
|
101
185
|
|
102
|
-
#
|
103
|
-
|
104
|
-
copy = args.dup
|
105
|
-
copy[0] = components.last
|
106
|
-
return leaf.send(method, *copy, &block)
|
186
|
+
# Ok, recurse.
|
187
|
+
return recursive_fetch(tail, data[head], current_path, options)
|
107
188
|
end
|
108
|
-
end
|
189
|
+
end # class << self
|
109
190
|
|
110
|
-
|
191
|
+
##
|
192
|
+
# Break path into components. Expects a String path separated by the
|
193
|
+
# `#separator`, and returns the path split into components (an Array of
|
194
|
+
# String).
|
195
|
+
def path_components(path)
|
196
|
+
return filter_components(path.split(split_pattern))
|
197
|
+
end
|
111
198
|
|
112
199
|
##
|
113
|
-
# Given
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
if path.length == 1
|
118
|
-
return data
|
119
|
-
end
|
200
|
+
# Given path components, filters out unnecessary ones.
|
201
|
+
def filter_components(components)
|
202
|
+
return components.select { |c| not c.nil? and not c.empty? }
|
203
|
+
end
|
120
204
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
# If we're a write function, then we need to create intermediary objects,
|
127
|
-
# i.e. what's at head if nothing is there.
|
128
|
-
if data[head].nil?
|
129
|
-
# If the head is nil, we can't recurse. In create mode that means we
|
130
|
-
# want to create hash children, but in read mode we're done recursing.
|
131
|
-
# By returning a hash here, we allow the caller to send methods on to
|
132
|
-
# this temporary, making a PathedAccess Hash act like any other Hash.
|
133
|
-
if not options[:create]
|
134
|
-
return {}
|
135
|
-
end
|
205
|
+
##
|
206
|
+
# Join path components with the `#separator`.
|
207
|
+
def join_path(components)
|
208
|
+
return components.join(separator)
|
209
|
+
end
|
136
210
|
|
137
|
-
|
211
|
+
##
|
212
|
+
# Normalizes a String path so that there are no empty components, and it
|
213
|
+
# starts with a separator.
|
214
|
+
def normalize_path(path)
|
215
|
+
components = []
|
216
|
+
if path.respond_to?(:split) # likely a String
|
217
|
+
components = path_components(path)
|
218
|
+
elsif path.respond_to?(:join) # likely an Array
|
219
|
+
components = filter_components(path)
|
138
220
|
end
|
221
|
+
return separator + join_path(components)
|
222
|
+
end
|
139
223
|
|
140
|
-
|
141
|
-
|
224
|
+
##
|
225
|
+
# Ensure that all values have their path_prefix set.
|
226
|
+
def virality(value)
|
227
|
+
# If a value was set via a nested Hash, it may not have got its
|
228
|
+
# path_prefix set during storing (i.e. x[key] = { nested: some_hash }
|
229
|
+
# In that case, we do always know that the value's path prefix is the same
|
230
|
+
# as the receiver.
|
231
|
+
value.path_prefix = path_prefix
|
232
|
+
return value
|
142
233
|
end
|
234
|
+
|
143
235
|
end # module PathedAccess
|
144
236
|
end # module Collapsium
|