collapsium 0.4.1 → 0.5.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 +8 -8
- data/collapsium.gemspec +2 -2
- data/lib/collapsium/environment_override.rb +65 -52
- data/lib/collapsium/pathed_access.rb +54 -89
- data/lib/collapsium/support/array_methods.rb +48 -0
- data/lib/collapsium/support/methods.rb +69 -5
- data/lib/collapsium/support/path_components.rb +95 -0
- data/lib/collapsium/version.rb +1 -1
- data/lib/collapsium/viral_capabilities.rb +206 -40
- data/spec/environment_override_spec.rb +43 -18
- data/spec/pathed_access_spec.rb +96 -31
- data/spec/support_methods_spec.rb +247 -5
- data/spec/support_path_components.rb +85 -0
- data/spec/viral_capabilities_spec.rb +42 -15
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4eb75d59a4fd999a789ee4fb811263dea86b493
|
4
|
+
data.tar.gz: 15a3e7ff204d3268112e41a565d6d441d60db873
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31d5e290424e9e75eda116508351e2ae7ff3873ba977257140bf1112532278f4eae00590e9974b351d84afa89b1e1943d7ed4b4f74705c2ae54d6538b948b71f
|
7
|
+
data.tar.gz: 395b15971c754dea2eea60d7ebd601691f02214e7eeb97fa25281626b84cdaf6b4ecbb40585ebbfd8daa3914aa770f28856c81de8d7543a3e43ce15b8d4bc5a2
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
collapsium (0.
|
4
|
+
collapsium (0.5.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -12,16 +12,16 @@ GEM
|
|
12
12
|
diff-lcs (1.2.5)
|
13
13
|
docile (1.1.5)
|
14
14
|
json (2.0.2)
|
15
|
-
parser (2.3.1.
|
15
|
+
parser (2.3.1.4)
|
16
16
|
ast (~> 2.2)
|
17
17
|
powerpack (0.1.1)
|
18
18
|
rainbow (2.1.0)
|
19
|
-
rake (11.
|
19
|
+
rake (11.3.0)
|
20
20
|
rspec (3.5.0)
|
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.4)
|
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.44.1)
|
34
34
|
parser (>= 2.3.1.1, < 3.0)
|
35
35
|
powerpack (~> 0.1)
|
36
36
|
rainbow (>= 1.99.1, < 3.0)
|
@@ -42,7 +42,7 @@ GEM
|
|
42
42
|
json (>= 1.8, < 3)
|
43
43
|
simplecov-html (~> 0.10.0)
|
44
44
|
simplecov-html (0.10.0)
|
45
|
-
unicode-display_width (1.1.
|
45
|
+
unicode-display_width (1.1.1)
|
46
46
|
yard (0.9.5)
|
47
47
|
|
48
48
|
PLATFORMS
|
@@ -52,9 +52,9 @@ DEPENDENCIES
|
|
52
52
|
bundler (~> 1.12)
|
53
53
|
codeclimate-test-reporter
|
54
54
|
collapsium!
|
55
|
-
rake (~> 11.
|
55
|
+
rake (~> 11.3)
|
56
56
|
rspec (~> 3.5)
|
57
|
-
rubocop (~> 0.
|
57
|
+
rubocop (~> 0.44)
|
58
58
|
simplecov (~> 0.12)
|
59
59
|
yard (~> 0.9)
|
60
60
|
|
data/collapsium.gemspec
CHANGED
@@ -35,8 +35,8 @@ 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.
|
39
|
-
spec.add_development_dependency "rake", "~> 11.
|
38
|
+
spec.add_development_dependency "rubocop", "~> 0.44"
|
39
|
+
spec.add_development_dependency "rake", "~> 11.3"
|
40
40
|
spec.add_development_dependency "rspec", "~> 3.5"
|
41
41
|
spec.add_development_dependency "simplecov", "~> 0.12"
|
42
42
|
spec.add_development_dependency "yard", "~> 0.9"
|
@@ -39,6 +39,7 @@ module Collapsium
|
|
39
39
|
# wrap accessor methods.
|
40
40
|
class << self
|
41
41
|
include ::Collapsium::Support::HashMethods
|
42
|
+
include ::Collapsium::Support::ArrayMethods
|
42
43
|
include ::Collapsium::Support::Methods
|
43
44
|
|
44
45
|
##
|
@@ -62,6 +63,54 @@ module Collapsium
|
|
62
63
|
# supported, we'll try environment variables of the path, starting
|
63
64
|
# with the full qualified path and ending with just the last key
|
64
65
|
# component.
|
66
|
+
env_keys = EnvironmentOverride.environment_variables_for_key(receiver, key)
|
67
|
+
|
68
|
+
# When we have the keys (in priority order), try to see if the
|
69
|
+
# environment yields something useful.
|
70
|
+
value = EnvironmentOverride.override_value(env_keys, receiver, key)
|
71
|
+
|
72
|
+
if not value.nil?
|
73
|
+
# We can't just return the value, because that doesn't respect the
|
74
|
+
# method being called. We also can't store the value, because that
|
75
|
+
# would not reset the Hash after the environment variable was
|
76
|
+
# cleared.
|
77
|
+
#
|
78
|
+
# We can deal with this by duplicating the receiver, writing the value
|
79
|
+
# we found into the appropriate key, and then sending the
|
80
|
+
# wrapped_method to the duplicate.
|
81
|
+
double = receiver.recursive_dup
|
82
|
+
double[key] = value
|
83
|
+
meth = double.method(wrapped_method.name)
|
84
|
+
next meth.call(*args, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Otherwise, fall back on the super method.
|
88
|
+
next wrapped_method.call(*args, &block)
|
89
|
+
end.freeze # proc
|
90
|
+
|
91
|
+
def included(base)
|
92
|
+
enhance(base)
|
93
|
+
end
|
94
|
+
|
95
|
+
def extended(base)
|
96
|
+
enhance(base)
|
97
|
+
end
|
98
|
+
|
99
|
+
def prepended(base)
|
100
|
+
enhance(base)
|
101
|
+
end
|
102
|
+
|
103
|
+
def enhance(base)
|
104
|
+
# Make the capabilities of classes using EnvironmentOverride viral.
|
105
|
+
base.extend(ViralCapabilities)
|
106
|
+
|
107
|
+
# Wrap read accessor functions to deal with paths
|
108
|
+
(INDEXED_READ_METHODS + KEYED_READ_METHODS).each do |method|
|
109
|
+
wrap_method(base, method, raise_on_missing: false, &ENV_ACCESS_READER)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def environment_variables_for_key(receiver, key)
|
65
114
|
env_keys = []
|
66
115
|
if receiver.respond_to?(:path_prefix)
|
67
116
|
|
@@ -92,14 +141,16 @@ module Collapsium
|
|
92
141
|
else
|
93
142
|
env_keys = [key.to_s]
|
94
143
|
end
|
95
|
-
env_keys.map! { |k|
|
144
|
+
env_keys.map! { |k| key_to_env(k) }
|
96
145
|
env_keys.select! { |k| not k.empty? }
|
97
146
|
env_keys.uniq!
|
98
147
|
|
99
|
-
|
100
|
-
|
148
|
+
return env_keys
|
149
|
+
end
|
150
|
+
|
151
|
+
def override_value(variables, receiver, key)
|
101
152
|
value = nil
|
102
|
-
|
153
|
+
variables.each do |env_key|
|
103
154
|
# Grab the environment value; skip if there's nothing there.
|
104
155
|
env_value = ENV[env_key]
|
105
156
|
if env_value.nil?
|
@@ -131,60 +182,22 @@ module Collapsium
|
|
131
182
|
break
|
132
183
|
end
|
133
184
|
|
134
|
-
|
135
|
-
# We can't just return the value, because that doesn't respect the
|
136
|
-
# method being called. We also can't store the value, because that
|
137
|
-
# would not reset the Hash after the environment variable was
|
138
|
-
# cleared.
|
139
|
-
#
|
140
|
-
# We can deal with this by duplicating the receiver, writing the value
|
141
|
-
# we found into the appropriate key, and then sending the
|
142
|
-
# wrapped_method to the duplicate.
|
143
|
-
double = receiver.recursive_dup
|
144
|
-
double[key] = value
|
145
|
-
meth = double.method(wrapped_method.name)
|
146
|
-
next meth.call(*args, &block)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Otherwise, fall back on the super method.
|
150
|
-
next wrapped_method.call(*args, &block)
|
151
|
-
end.freeze # proc
|
152
|
-
|
153
|
-
def included(base)
|
154
|
-
enhance(base)
|
185
|
+
return value
|
155
186
|
end
|
156
187
|
|
157
|
-
def
|
158
|
-
|
159
|
-
|
188
|
+
def key_to_env(key)
|
189
|
+
# First, convert to upper case
|
190
|
+
env_key = key.upcase
|
160
191
|
|
161
|
-
|
162
|
-
|
163
|
-
|
192
|
+
# Next, replace non-alphanumeric characters to underscore. This also
|
193
|
+
# collapses them into a single undescore.
|
194
|
+
env_key.gsub!(/[^[:alnum:]]+/, '_')
|
164
195
|
|
165
|
-
|
166
|
-
|
167
|
-
base.extend(ViralCapabilities)
|
196
|
+
# Strip leading and trailing underscores.
|
197
|
+
env_key.gsub!(/^_*(.*?)_*$/, '\1')
|
168
198
|
|
169
|
-
|
170
|
-
KEYED_READ_METHODS.each do |method|
|
171
|
-
wrap_method(base, method, &ENV_ACCESS_READER)
|
172
|
-
end
|
199
|
+
return env_key
|
173
200
|
end
|
174
201
|
end # class << self
|
175
|
-
|
176
|
-
def key_to_env(key)
|
177
|
-
# First, convert to upper case
|
178
|
-
env_key = key.upcase
|
179
|
-
|
180
|
-
# Next, replace non-alphanumeric characters to underscore. This also
|
181
|
-
# collapses them into a single undescore.
|
182
|
-
env_key.gsub!(/[^[:alnum:]]+/, '_')
|
183
|
-
|
184
|
-
# Strip leading and trailing underscores.
|
185
|
-
env_key.gsub!(/^_*(.*?)_*$/, '\1')
|
186
|
-
|
187
|
-
return env_key
|
188
|
-
end
|
189
202
|
end # module EnvironmentOverride
|
190
203
|
end # module Collapsium
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
require 'collapsium/support/hash_methods'
|
11
11
|
require 'collapsium/support/methods'
|
12
|
+
require 'collapsium/support/path_components'
|
12
13
|
|
13
14
|
require 'collapsium/viral_capabilities'
|
14
15
|
|
@@ -27,40 +28,24 @@ module Collapsium
|
|
27
28
|
# for a path.
|
28
29
|
module PathedAccess
|
29
30
|
|
30
|
-
|
31
|
-
def separator
|
32
|
-
@separator ||= DEFAULT_SEPARATOR
|
33
|
-
return @separator
|
34
|
-
end
|
35
|
-
|
36
|
-
##
|
37
|
-
# Assume any pathed access has this prefix.
|
38
|
-
def path_prefix=(value)
|
39
|
-
@path_prefix = normalize_path(value)
|
40
|
-
end
|
41
|
-
|
42
|
-
def path_prefix
|
43
|
-
@path_prefix ||= ''
|
44
|
-
return @path_prefix
|
45
|
-
end
|
46
|
-
|
47
|
-
# @api private
|
48
|
-
# Default path separator
|
49
|
-
DEFAULT_SEPARATOR = '.'.freeze
|
50
|
-
|
51
|
-
##
|
52
|
-
# @return [RegExp] the pattern to split paths at; based on `separator`
|
53
|
-
def split_pattern
|
54
|
-
/(?<!\\)#{Regexp.escape(separator)}/
|
55
|
-
end
|
31
|
+
include ::Collapsium::Support::PathComponents
|
56
32
|
|
57
33
|
##
|
58
34
|
# If the module is included, extended or prepended in a class, it'll
|
59
35
|
# wrap accessor methods.
|
60
36
|
class << self
|
61
|
-
include ::Collapsium::Support::HashMethods
|
62
37
|
include ::Collapsium::Support::Methods
|
63
38
|
|
39
|
+
# We want to wrap methods for Arrays and Hashes alike
|
40
|
+
READ_METHODS = (
|
41
|
+
::Collapsium::Support::HashMethods::KEYED_READ_METHODS \
|
42
|
+
+ ::Collapsium::Support::ArrayMethods::INDEXED_READ_METHODS
|
43
|
+
).uniq.freeze
|
44
|
+
WRITE_METHODS = (
|
45
|
+
::Collapsium::Support::HashMethods::KEYED_WRITE_METHODS \
|
46
|
+
+ ::Collapsium::Support::ArrayMethods::INDEXED_WRITE_METHODS
|
47
|
+
).uniq.freeze
|
48
|
+
|
64
49
|
##
|
65
50
|
# Returns a proc for either read or write access. Procs for write
|
66
51
|
# access will create intermediary hashes when e.g. setting a value for
|
@@ -90,18 +75,10 @@ module Collapsium
|
|
90
75
|
# Try to find the leaf, based on the given components.
|
91
76
|
leaf = recursive_fetch(components, receiver, [], create: write_access)
|
92
77
|
|
93
|
-
#
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
# itself was requested, and we really just need to delegate to its
|
98
|
-
# wrapped_method.
|
99
|
-
meth = wrapped_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(wrapped_method.name)
|
104
|
-
end
|
78
|
+
# Since Methods already contains loop prevention and we may want to
|
79
|
+
# call wrapped methods, let's just find the method to call from the
|
80
|
+
# leaf by name.
|
81
|
+
meth = leaf.method(wrapped_method.name)
|
105
82
|
|
106
83
|
# If the first argument was a symbol key, we want to use it verbatim.
|
107
84
|
# Otherwise we had pathed access, and only want to pass the last
|
@@ -112,8 +89,21 @@ module Collapsium
|
|
112
89
|
the_args[0] = components.last
|
113
90
|
end
|
114
91
|
|
92
|
+
# Array methods we're modifying here are indexed, so the first argument
|
93
|
+
# must be an integer. Let's make it so :)
|
94
|
+
if leaf.is_a? Array
|
95
|
+
the_args[0] = the_args[0].to_i
|
96
|
+
end
|
97
|
+
|
115
98
|
# Then we can continue with that method.
|
116
|
-
|
99
|
+
result = meth.call(*the_args, &block)
|
100
|
+
|
101
|
+
# Sadly, we can't just return the result and be done with it.
|
102
|
+
# We need to tell the virality function (below) what we know about the
|
103
|
+
# result's path prefix, so we enhance the result value explicitly here.
|
104
|
+
result_path = receiver.path_components(receiver.path_prefix)
|
105
|
+
result_path += components
|
106
|
+
next ViralCapabilities.enhance_value(leaf, result, result_path)
|
117
107
|
end # proc
|
118
108
|
end # create_proc
|
119
109
|
|
@@ -138,11 +128,11 @@ module Collapsium
|
|
138
128
|
base.extend(ViralCapabilities)
|
139
129
|
|
140
130
|
# Wrap all accessor functions to deal with paths
|
141
|
-
|
142
|
-
wrap_method(base, method, &PATHED_ACCESS_READER)
|
131
|
+
READ_METHODS.each do |method|
|
132
|
+
wrap_method(base, method, raise_on_missing: false, &PATHED_ACCESS_READER)
|
143
133
|
end
|
144
|
-
|
145
|
-
wrap_method(base, method, &PATHED_ACCESS_WRITER)
|
134
|
+
WRITE_METHODS.each do |method|
|
135
|
+
wrap_method(base, method, raise_on_missing: false, &PATHED_ACCESS_WRITER)
|
146
136
|
end
|
147
137
|
end
|
148
138
|
|
@@ -155,14 +145,6 @@ module Collapsium
|
|
155
145
|
current_path << head
|
156
146
|
tail = path.slice(1, path.length)
|
157
147
|
|
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
|
164
|
-
end
|
165
|
-
|
166
148
|
# For the leaf element, we do nothing because that's where we want to
|
167
149
|
# dispatch to.
|
168
150
|
if path.length == 1
|
@@ -189,46 +171,29 @@ module Collapsium
|
|
189
171
|
end # class << self
|
190
172
|
|
191
173
|
##
|
192
|
-
#
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
return components.join(separator)
|
209
|
-
end
|
174
|
+
# Ensure that all values have their path_prefix set.
|
175
|
+
def virality(value, *args)
|
176
|
+
# Figure out what path prefix to set on the value, if any.
|
177
|
+
# Candidates for the prefix are:
|
178
|
+
explicit = args[0] || []
|
179
|
+
from_self = path_components(path_prefix)
|
180
|
+
from_value = path_components(value.path_prefix)
|
181
|
+
|
182
|
+
prefix = []
|
183
|
+
if not explicit.empty?
|
184
|
+
# If we got explicit information, we most likely want to use that.
|
185
|
+
prefix = explicit
|
186
|
+
elsif not from_self.empty?
|
187
|
+
# If we got information from self, that's the next best candidate.
|
188
|
+
prefix = from_self
|
189
|
+
end
|
210
190
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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)
|
191
|
+
# However, if the value already has a better path prefix than either
|
192
|
+
# of the above, we want to keep that.
|
193
|
+
if prefix.length > from_value.length
|
194
|
+
value.path_prefix = normalize_path(prefix)
|
220
195
|
end
|
221
|
-
return separator + join_path(components)
|
222
|
-
end
|
223
196
|
|
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
197
|
return value
|
233
198
|
end
|
234
199
|
|
@@ -0,0 +1,48 @@
|
|
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
|
+
module Collapsium
|
11
|
+
|
12
|
+
##
|
13
|
+
# Support functionality for Collapsium
|
14
|
+
module Support
|
15
|
+
|
16
|
+
##
|
17
|
+
# @api private
|
18
|
+
# Defines which read and write functions we expect Array to have.
|
19
|
+
module ArrayMethods
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
# Read access methods with index parameter
|
23
|
+
INDEXED_READ_METHODS = [
|
24
|
+
:[], :at, :fetch, :include?,
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
# All read access methods
|
29
|
+
READ_METHODS = INDEXED_READ_METHODS + [
|
30
|
+
:dup, :first, :last, :take, :drop,
|
31
|
+
].freeze
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
# Write access methods with index parameter
|
35
|
+
INDEXED_WRITE_METHODS = [
|
36
|
+
:[]=, :insert, :compact,
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
# All write access methods
|
40
|
+
WRITE_METHODS = INDEXED_WRITE_METHODS + [
|
41
|
+
:unshift, :pop, :shift,
|
42
|
+
].freeze
|
43
|
+
|
44
|
+
end # module ArrayMethods
|
45
|
+
|
46
|
+
end # module Support
|
47
|
+
|
48
|
+
end # module Collapsium
|