collapsium 0.4.1 → 0.5.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 +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
|