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