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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b85cef5b1becbc08cec24005e6e7c31b4e8156a
4
- data.tar.gz: 1a25c135dcb2d7784dedcb04cdee7e6633e841eb
3
+ metadata.gz: c4eb75d59a4fd999a789ee4fb811263dea86b493
4
+ data.tar.gz: 15a3e7ff204d3268112e41a565d6d441d60db873
5
5
  SHA512:
6
- metadata.gz: 2d54cd48ceef5cd706e662b40451f349595e88ecc7987ee54544110adff1a8493a11345e83d2ef659f825741029630d8d911b4be8325111ea28d7558fb2a7ad3
7
- data.tar.gz: 24dc1841b56c2b5304d13a8977e1123aabfe196ec87f912bc4251c575ffdb9d7cfd17703a9b10d933b03a335111350ff4aa6d70099c93eeebc893d7cbb4631f2
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.1)
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.2)
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.2.2)
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.2)
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.42.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.0)
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.2)
55
+ rake (~> 11.3)
56
56
  rspec (~> 3.5)
57
- rubocop (~> 0.42)
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.42"
39
- spec.add_development_dependency "rake", "~> 11.2"
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| receiver.key_to_env(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
- # When we have the keys (in priority order), try to see if the
100
- # environment yields something useful.
148
+ return env_keys
149
+ end
150
+
151
+ def override_value(variables, receiver, key)
101
152
  value = nil
102
- env_keys.each do |env_key|
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
- if not value.nil?
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 extended(base)
158
- enhance(base)
159
- end
188
+ def key_to_env(key)
189
+ # First, convert to upper case
190
+ env_key = key.upcase
160
191
 
161
- def prepended(base)
162
- enhance(base)
163
- end
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
- def enhance(base)
166
- # Make the capabilities of classes using EnvironmentOverride viral.
167
- base.extend(ViralCapabilities)
196
+ # Strip leading and trailing underscores.
197
+ env_key.gsub!(/^_*(.*?)_*$/, '\1')
168
198
 
169
- # Wrap read accessor functions to deal with paths
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
- # @return [String] the separator is the character or pattern splitting paths.
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
- # 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
- # 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
- next meth.call(*the_args, &block)
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
- KEYED_READ_METHODS.each do |method|
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
- KEYED_WRITE_METHODS.each do |method|
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
- # 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
198
-
199
- ##
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
204
-
205
- ##
206
- # Join path components with the `#separator`.
207
- def join_path(components)
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
- # 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)
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