config_layers 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.gitreview +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +33 -0
- data/Gemfile +4 -0
- data/README.md +161 -0
- data/Rakefile +27 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/config_layers.gemspec +28 -0
- data/lib/config_layers/version.rb +4 -0
- data/lib/config_layers.rb +38 -0
- data/lib/prc_base_config.rb +309 -0
- data/lib/prc_core_config.rb +1218 -0
- data/lib/prc_section_config.rb +90 -0
- metadata +143 -0
@@ -0,0 +1,1218 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'yaml'
|
18
|
+
|
19
|
+
module PRC
|
20
|
+
# Internal CoreConfig functions
|
21
|
+
class CoreConfig
|
22
|
+
private
|
23
|
+
|
24
|
+
# Function to initialize a predefined layer
|
25
|
+
# Used internally by initialize_layers.
|
26
|
+
def _initialize_layer(layer)
|
27
|
+
newlayer = { :config => layer[:config], :name => layer[:name] }
|
28
|
+
newlayer[:set] = layer[:set].boolean? ? layer[:set] : true
|
29
|
+
newlayer[:load] = layer[:load].boolean? ? layer[:load] : false
|
30
|
+
newlayer[:save] = layer[:save].boolean? ? layer[:save] : false
|
31
|
+
newlayer[:file_set] = layer[:file_set].boolean? ? layer[:file_set] : false
|
32
|
+
newlayer[:init] = true
|
33
|
+
newlayer
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check and returns values of options required and optionnal.
|
37
|
+
#
|
38
|
+
# * *Args*
|
39
|
+
# - +options+ : options to extract information.
|
40
|
+
# - +required+ : Array of required option keys.
|
41
|
+
# - +optionnal+ : Array of optionnal option keys.
|
42
|
+
#
|
43
|
+
# * *Returns*
|
44
|
+
# - nil if at least one required keys doesn't exist
|
45
|
+
# - Array of combined required and optionnql values.
|
46
|
+
#
|
47
|
+
def _valid_options(options, required, optionnal = [])
|
48
|
+
return nil unless options.is_a?(Hash)
|
49
|
+
return nil unless required.is_a?(Array)
|
50
|
+
optionnal = [] unless optionnal.is_a?(Array)
|
51
|
+
|
52
|
+
result = [[], []]
|
53
|
+
|
54
|
+
required.each do |key|
|
55
|
+
return nil unless options.key?(key)
|
56
|
+
result[0] << options[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
optionnal.each { |key| result[1] << options[key] }
|
60
|
+
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
# Internal function to call _common_options_get
|
65
|
+
# It ensures and cleanup caller setting
|
66
|
+
# :names and :indexes as those must be single values instead of
|
67
|
+
# array. ie :name and :index is the only one supported.
|
68
|
+
#
|
69
|
+
def _nameindex_common_options_get(options, required = [], optionnal = [])
|
70
|
+
options = {} if options.nil?
|
71
|
+
|
72
|
+
# ensure :names or :indexes are not set. and clean it up if exists
|
73
|
+
options.delete(:names) if options.key?(:names)
|
74
|
+
options.delete(:indexes) if options.key?(:indexes)
|
75
|
+
|
76
|
+
_common_options_get(options, required, optionnal)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Take care of keys, indexes, names, index and name.
|
80
|
+
# If names is found, it will replace indexes.
|
81
|
+
# If name is found, it will replace index.
|
82
|
+
# If index is found, it will replace indexes.
|
83
|
+
def _common_options_get(options, required = [], optionnal = [])
|
84
|
+
return nil unless options.is_a?(Hash)
|
85
|
+
required = [] unless required.is_a?(Array)
|
86
|
+
optionnal = [] unless optionnal.is_a?(Array)
|
87
|
+
|
88
|
+
_convert_nameindex_to_arrays(options)
|
89
|
+
|
90
|
+
result = _valid_options(options, required,
|
91
|
+
[:indexes, :names,
|
92
|
+
:data_options].concat(optionnal))
|
93
|
+
return nil if result.nil?
|
94
|
+
# result Array is structured as:
|
95
|
+
# required [0] => [...]
|
96
|
+
# optional [1] => [indexes, names, data_options, ...]
|
97
|
+
|
98
|
+
# Following eliminates the optional :names (1) element
|
99
|
+
_set_indexes result
|
100
|
+
# required [0] => [...]
|
101
|
+
# optional [1] => [indexes, data_options, ...]
|
102
|
+
return nil if _keys_data_missing(options, required, result)
|
103
|
+
|
104
|
+
# Following eliminates the optional :indexes (0) element
|
105
|
+
# But add it as required in the Array, at pos 0
|
106
|
+
_build_layers(result)
|
107
|
+
# required [0] => [layers, ...]
|
108
|
+
# optional [1] => [data_options, ...]
|
109
|
+
|
110
|
+
# following eliminates the optional :data_options (0) element
|
111
|
+
# But data_options is added in required Array, at pos 1.
|
112
|
+
_set_data_opts(result)
|
113
|
+
# required [0] => [layers, data_options, ...]
|
114
|
+
# optional [1] => [...]
|
115
|
+
|
116
|
+
result
|
117
|
+
end
|
118
|
+
|
119
|
+
# This internal function identify :name and :index
|
120
|
+
# To replace to respectively to :names and :indexes
|
121
|
+
def _convert_nameindex_to_arrays(options)
|
122
|
+
options[:names] = [options.delete(:name)] if options.key?(:name)
|
123
|
+
options[:indexes] = [options.delete(:index)] if options.key?(:index)
|
124
|
+
end
|
125
|
+
|
126
|
+
# This internal function checks if the :keys is required, then
|
127
|
+
# The keys value HAVE to be an Array with at least one element.
|
128
|
+
# It returns false, if :keys is not the first required field
|
129
|
+
# or if the keys data (Array not empty) is confirmed.
|
130
|
+
def _keys_data_missing(options, required, result)
|
131
|
+
return false unless required[0] == :keys
|
132
|
+
|
133
|
+
return true unless options.key?(:keys)
|
134
|
+
return true unless result[0][0].is_a?(Array)
|
135
|
+
return true if result[0][0].length == 0
|
136
|
+
false
|
137
|
+
end
|
138
|
+
|
139
|
+
# Setting indexes from names or indexes.
|
140
|
+
def _set_indexes(result)
|
141
|
+
names_indexes = layer_indexes(result[1][1])
|
142
|
+
# replaced indexes by names indexes if exists.
|
143
|
+
result[1][0] = names_indexes if names_indexes
|
144
|
+
result[1].delete_at(1)
|
145
|
+
end
|
146
|
+
|
147
|
+
def _set_data_opts(result)
|
148
|
+
data_opts = []
|
149
|
+
|
150
|
+
result[0][0].each_index do |layer_index|
|
151
|
+
data_options = result[1][0][layer_index] if result[1][0].is_a?(Array)
|
152
|
+
data_options = {} unless data_options.is_a?(Hash)
|
153
|
+
data_opts << data_options
|
154
|
+
end
|
155
|
+
result[0].insert(1, data_opts)
|
156
|
+
|
157
|
+
# And removing the optionnal :data_options
|
158
|
+
result[1].delete_at(0)
|
159
|
+
end
|
160
|
+
|
161
|
+
def _build_layers(result)
|
162
|
+
# Setting layers at required [0]
|
163
|
+
if result[1][0].nil? || result[1][0].length == 0
|
164
|
+
config_layers = @config_layers
|
165
|
+
else
|
166
|
+
config_layers = []
|
167
|
+
result[1][0].each do |index|
|
168
|
+
config_layers << @config_layers[index] if index.is_a?(Fixnum)
|
169
|
+
end
|
170
|
+
config_layers = @config_layers if config_layers.length == 0
|
171
|
+
end
|
172
|
+
result[0].insert(0, config_layers)
|
173
|
+
|
174
|
+
# And removing the optionnal indexes
|
175
|
+
result[1].delete_at(0)
|
176
|
+
result
|
177
|
+
end
|
178
|
+
|
179
|
+
# Internal function to provide a result for #p_get.
|
180
|
+
# This function is typically used by a Child Class.
|
181
|
+
#
|
182
|
+
# * *Args*
|
183
|
+
# - +*keys+ : Array of keys.
|
184
|
+
# - +layers+ : Array of config layers to get data to merge.
|
185
|
+
# - +data_opts+ : Array of data options per layers
|
186
|
+
# - +data_options+ : common data options for all layers
|
187
|
+
# - +merge+ : True if need to get a merge result.
|
188
|
+
#
|
189
|
+
# * *Returns*
|
190
|
+
# - Value, Hash merged or nil.
|
191
|
+
#
|
192
|
+
def _get_from_layers(keys, layers, data_opts, data_options, merge)
|
193
|
+
result = {}
|
194
|
+
|
195
|
+
# In merge case, we need to build from bottom to top
|
196
|
+
if merge.is_a?(TrueClass)
|
197
|
+
layers = layers.reverse
|
198
|
+
data_opts = data_opts.reverse
|
199
|
+
end
|
200
|
+
|
201
|
+
layers.each_index do |layer_index|
|
202
|
+
layer = layers[layer_index]
|
203
|
+
|
204
|
+
data_options = data_options.merge(data_opts[layer_index])
|
205
|
+
|
206
|
+
layer[:config].data_options(data_options)
|
207
|
+
|
208
|
+
next unless layer[:config].exist?(*keys)
|
209
|
+
|
210
|
+
return layer[:config][*keys] unless merge.is_a?(TrueClass)
|
211
|
+
|
212
|
+
result = result.rh_merge(layer[:config][keys[0..-2]])
|
213
|
+
end
|
214
|
+
result[keys[-1]]
|
215
|
+
end
|
216
|
+
|
217
|
+
# Return true if at least the first key value found is of type Hash/Array,
|
218
|
+
# while be_exclusive is false
|
219
|
+
# return true if at least one key value is NOT Hash or Array,
|
220
|
+
# while be_exclusive is true
|
221
|
+
#
|
222
|
+
def _check_from_layers(keys, config_layers, data_opts, data_options,
|
223
|
+
be_exclusive)
|
224
|
+
found = false
|
225
|
+
found_class = nil
|
226
|
+
config_layers.each_index do |layer_index|
|
227
|
+
layer = config_layers[layer_index]
|
228
|
+
|
229
|
+
data_options = data_options.merge(data_opts[layer_index])
|
230
|
+
|
231
|
+
layer[:config].data_options(data_options)
|
232
|
+
|
233
|
+
next unless layer[:config].exist?(*keys)
|
234
|
+
|
235
|
+
found_class = layer[:config][*keys].class
|
236
|
+
found = [Hash, Array].include?(found_class)
|
237
|
+
if be_exclusive
|
238
|
+
return false unless found
|
239
|
+
unless found_class && layer[:config][*keys].is_a?(found_class)
|
240
|
+
return false
|
241
|
+
end
|
242
|
+
else
|
243
|
+
return found
|
244
|
+
end
|
245
|
+
end
|
246
|
+
true
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Internal core functions
|
251
|
+
class CoreConfig
|
252
|
+
# *****************************************************
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
# Del function called by default by del
|
257
|
+
# This function is typically used by a Child Class.
|
258
|
+
#
|
259
|
+
# * *Args*
|
260
|
+
# - +options+ : Hash of how to get the data
|
261
|
+
# - +:keys+ : Array of key path to found
|
262
|
+
# - +:name+ : layer to get data.
|
263
|
+
# - +:index+ : layer index to get data.
|
264
|
+
# If neither :name or :index is set, set will use the
|
265
|
+
# highest layer
|
266
|
+
# - +:data_opts+ : Array or Hash. Define data options per layer.
|
267
|
+
#
|
268
|
+
# * *Returns*
|
269
|
+
# - The value attached to the key deleted.
|
270
|
+
# OR
|
271
|
+
# - nil can be returned for several reasons:
|
272
|
+
# - value is nil
|
273
|
+
# - keys is not an array
|
274
|
+
# - keys array is empty.
|
275
|
+
#
|
276
|
+
# ex:
|
277
|
+
# value = CoreConfig.New
|
278
|
+
#
|
279
|
+
# value[:level1, :level2] = 'value'
|
280
|
+
# # => {:level1 => {:level2 => 'value'}}
|
281
|
+
#
|
282
|
+
# value.del(:keys => [:level1, :level2])
|
283
|
+
# # => {:level1 => {}}
|
284
|
+
def p_del(options) #:doc:
|
285
|
+
parameters = _nameindex_common_options_get(options, [:keys])
|
286
|
+
return nil if parameters.nil?
|
287
|
+
|
288
|
+
config_layers, data_opts, keys = parameters[0]
|
289
|
+
|
290
|
+
# get data options for level 0
|
291
|
+
data_options = options.clone.merge!(data_opts[0])
|
292
|
+
|
293
|
+
return nil if keys.length == 0
|
294
|
+
|
295
|
+
data_options.delete_if do |key|
|
296
|
+
[:keys, :names, :indexes, :name, :index].include?(key)
|
297
|
+
end
|
298
|
+
|
299
|
+
return nil unless @config_layers[0][:set]
|
300
|
+
|
301
|
+
config_layers[0][:config].data_options(data_options)
|
302
|
+
config_layers[0][:config].del(keys)
|
303
|
+
end
|
304
|
+
|
305
|
+
# p_file? Core file function called by default by #file.
|
306
|
+
# This function is typically used by a Child Class.
|
307
|
+
#
|
308
|
+
# This function can be used by child class to set one layer file name
|
309
|
+
#
|
310
|
+
# * *Args*
|
311
|
+
# - +options+ : Hash parameters
|
312
|
+
# - +:name+ : layer to get data.
|
313
|
+
# - +:index+ : Array layer indexes to get data.
|
314
|
+
# If neither :name or :index is set, level 0 is used.
|
315
|
+
#
|
316
|
+
# * *Returns*
|
317
|
+
# - filename : if updated.
|
318
|
+
# OR
|
319
|
+
# - false : if not updated.
|
320
|
+
# OR
|
321
|
+
# - nil : If something went wrong.
|
322
|
+
#
|
323
|
+
# ex:
|
324
|
+
# { :test => {:titi => 'found'}}
|
325
|
+
def p_file(filename = nil, options = {}) #:doc:
|
326
|
+
parameters = _nameindex_common_options_get(options)
|
327
|
+
|
328
|
+
return nil if parameters.nil?
|
329
|
+
|
330
|
+
config_layers = parameters[0][0]
|
331
|
+
|
332
|
+
layer = config_layers[0]
|
333
|
+
|
334
|
+
return layer[:config].filename unless filename.is_a?(String)
|
335
|
+
|
336
|
+
return false if _filename_unsetable(layer)
|
337
|
+
|
338
|
+
layer[:config].filename = filename
|
339
|
+
filename
|
340
|
+
end
|
341
|
+
|
342
|
+
def _filename_unsetable(layer)
|
343
|
+
return true if !layer[:load] && !layer[:save]
|
344
|
+
|
345
|
+
!layer[:config].filename.nil? && !layer[:file_set]
|
346
|
+
end
|
347
|
+
|
348
|
+
# p_exist? Core exist function called by default by #exist?.
|
349
|
+
# This function is typically used by a Child Class.
|
350
|
+
#
|
351
|
+
# * *Args*
|
352
|
+
# - +options+ : Hash parameters
|
353
|
+
# - +:keys+ : key tree to check existence in config layers
|
354
|
+
# - +:names+ : layer to get data.
|
355
|
+
# - +:indexes+ : Array layer indexes to get data.
|
356
|
+
# If neither :name or :index is set, get will search data
|
357
|
+
# per layers priority.
|
358
|
+
# - +:data_opts+ : Array or Hash. Define data options per layer.
|
359
|
+
#
|
360
|
+
# * *Returns*
|
361
|
+
# - boolean : true if the key path was found
|
362
|
+
#
|
363
|
+
# ex:
|
364
|
+
# # if one layer data is { :test => {:titi => 'found'}}
|
365
|
+
# p_exist?(:keys => [:test]) # => true
|
366
|
+
# p_exist?(:keys => [:test, :titi]) # => true
|
367
|
+
# p_exist?(:keys => [:test1]) # => false
|
368
|
+
#
|
369
|
+
def p_exist?(options) #:doc:
|
370
|
+
parameters = _common_options_get(options, [:keys])
|
371
|
+
return nil if parameters.nil?
|
372
|
+
|
373
|
+
config_layers, data_opts, keys = parameters[0]
|
374
|
+
|
375
|
+
return nil if keys.length == 0 || keys[0].nil? || config_layers[0].nil?
|
376
|
+
|
377
|
+
config_layers.each_index do |index|
|
378
|
+
config = config_layers[index][:config]
|
379
|
+
|
380
|
+
data_options = options.clone
|
381
|
+
data_options.delete_if do |key|
|
382
|
+
[:keys, :names, :indexes, :name, :index].include?(key)
|
383
|
+
end
|
384
|
+
data_options.merge!(data_opts[index])
|
385
|
+
|
386
|
+
config.data_options(data_options)
|
387
|
+
return true if config.exist?(*keys)
|
388
|
+
end
|
389
|
+
false
|
390
|
+
end
|
391
|
+
|
392
|
+
# p_where? called by default by #where
|
393
|
+
# This function is typically used by a Child Class.
|
394
|
+
#
|
395
|
+
# * *args*
|
396
|
+
# - +options+ : Hash parameters
|
397
|
+
# - +:keys+ : key tree to check existence in config layers
|
398
|
+
# - +:names+ : layer to get data.
|
399
|
+
# - +:indexes+ : Array layer indexes to get data.
|
400
|
+
# If neither :name or :index is set, get will search data
|
401
|
+
# per layers priority.
|
402
|
+
# - +:data_opts+ : Array or Hash. Define data options per layer.
|
403
|
+
#
|
404
|
+
# * *Returns*
|
405
|
+
# - array of config name : list of first layers where the key was found.
|
406
|
+
# OR
|
407
|
+
# - nil can be returned for several reasons:
|
408
|
+
# - keys is not an array
|
409
|
+
# - keys array is empty.
|
410
|
+
def p_where?(options) #:doc:
|
411
|
+
parameters = _common_options_get(options, [:keys])
|
412
|
+
return nil if parameters.nil?
|
413
|
+
|
414
|
+
config_layers, data_opts, keys = parameters[0]
|
415
|
+
|
416
|
+
return nil if keys.length == 0 || keys[0].nil? || config_layers[0].nil?
|
417
|
+
|
418
|
+
_do_where?(config_layers, keys, options, data_opts)
|
419
|
+
end
|
420
|
+
|
421
|
+
def _do_where?(config_layers, keys, options, data_opts)
|
422
|
+
layer_indexes = []
|
423
|
+
config_layers.each_index do |index|
|
424
|
+
config = config_layers[index][:config]
|
425
|
+
|
426
|
+
data_options = options.clone
|
427
|
+
data_options.delete_if do |key|
|
428
|
+
[:keys, :names, :indexes, :name, :index].include?(key)
|
429
|
+
end
|
430
|
+
data_options.merge!(data_opts[index]) if data_opts[index].is_a?(Hash)
|
431
|
+
|
432
|
+
config.data_options(data_options)
|
433
|
+
layer_indexes << config_layers[index][:name] if config.exist?(keys)
|
434
|
+
end
|
435
|
+
return layer_indexes if layer_indexes.length > 0
|
436
|
+
false
|
437
|
+
end
|
438
|
+
|
439
|
+
# Get function called by default by #[]
|
440
|
+
# This function is typically used by a Child Class.
|
441
|
+
#
|
442
|
+
# * *Args*
|
443
|
+
# - +options+ : Hash of how to get the data
|
444
|
+
# - +:keys+ : Array of key path to found
|
445
|
+
# - +:names+ : layer to get data.
|
446
|
+
# - +:indexes+ : Array layer indexes to get data.
|
447
|
+
# If neither :name or :index is set, get will search data
|
448
|
+
# per layers priority.
|
449
|
+
# - +:data_opts+ : Array or Hash. Define data options per layer.
|
450
|
+
# - +:merge+ : Provide a Merged result instead of first found
|
451
|
+
# returned.
|
452
|
+
# The merge result depends on deep layer data type found.
|
453
|
+
#
|
454
|
+
# Ex:
|
455
|
+
#
|
456
|
+
# with 2 config layers, like 'top' and 'bottom', usually a get will
|
457
|
+
# search in 'top', then 'bottom'. Merge will search in 'bottom'
|
458
|
+
# first. Then:
|
459
|
+
# - if 'bottom' value found is of type Hash(or Array),
|
460
|
+
# p_get will return a Hash(or Array), merged accross upper layers
|
461
|
+
# So, if 'top' is Hash(or Array), the result is the merge Hash
|
462
|
+
# (or Array). Otherwise, the 'top' will be ignored.
|
463
|
+
# - if 'bottom' value found is any kind of other types
|
464
|
+
# p_get won't merge, but get the highest non Array/Hash data found
|
465
|
+
# . So with a 'bottom' data of String, and 'top' as 'Fixnum', the
|
466
|
+
# result will be Fixnum. If there is any Hash/Array in between, it
|
467
|
+
# will be ignored.
|
468
|
+
#
|
469
|
+
# * *Returns*
|
470
|
+
# value found (or Hash merged) or nil.
|
471
|
+
#
|
472
|
+
# nil can be returned for several reasons:
|
473
|
+
# - keys is not an array
|
474
|
+
# - keys array is empty.
|
475
|
+
#
|
476
|
+
def p_get(options) #:doc:
|
477
|
+
parameters = _common_options_get(options, [:keys], [:merge])
|
478
|
+
return nil if parameters.nil?
|
479
|
+
|
480
|
+
# Required options : parameters[0]
|
481
|
+
config_layers, data_opts, keys = parameters[0]
|
482
|
+
# Optional options : parameters[1]
|
483
|
+
merge = parameters[1][0]
|
484
|
+
|
485
|
+
return nil if keys.length == 0 || keys[0].nil? || config_layers[0].nil?
|
486
|
+
|
487
|
+
data_options = options.clone
|
488
|
+
data_options.delete_if do |key|
|
489
|
+
[:keys, :names, :indexes, :name, :index, :merge].include?(key)
|
490
|
+
end
|
491
|
+
|
492
|
+
_get_from_layers(keys,
|
493
|
+
config_layers, data_opts, data_options,
|
494
|
+
merge)
|
495
|
+
end
|
496
|
+
|
497
|
+
# Set function called by default by #[]=
|
498
|
+
# This function is typically used by a Child Class.
|
499
|
+
#
|
500
|
+
# * *Args*
|
501
|
+
# - +options+ : Hash of how to get the data
|
502
|
+
# - +:value+: Value to set
|
503
|
+
# - +:keys+ : Array of key path to found
|
504
|
+
# - +:name+ : layer to get data.
|
505
|
+
# - +:index+: layer index to get data.
|
506
|
+
# If neither :name or :index is set, set will use the highest layer.
|
507
|
+
# - +:data_opts+ : Array or Hash. Define data options per layer.
|
508
|
+
#
|
509
|
+
# * *Returns*
|
510
|
+
# - The value set.
|
511
|
+
# OR
|
512
|
+
# - nil can be returned for several reasons:
|
513
|
+
# - layer options :set is false
|
514
|
+
# - options defines a :data_readonly to true.
|
515
|
+
# - value is nil
|
516
|
+
# - keys is not an array
|
517
|
+
# - keys array is empty.
|
518
|
+
#
|
519
|
+
# ex:
|
520
|
+
# value = CoreConfig.New
|
521
|
+
#
|
522
|
+
# value[:level1, :level2] = 'value'
|
523
|
+
# # => {:level1 => {:level2 => 'value'}}
|
524
|
+
def p_set(options) #:doc:
|
525
|
+
parameters = _nameindex_common_options_get(options, [:keys, :value])
|
526
|
+
return nil if parameters.nil?
|
527
|
+
|
528
|
+
config_layers, data_opts, keys, value = parameters[0]
|
529
|
+
|
530
|
+
# get data options for level 0
|
531
|
+
data_options = options.clone.merge!(data_opts[0])
|
532
|
+
|
533
|
+
return nil if keys.length == 0 || keys[0].nil? || config_layers[0].nil?
|
534
|
+
|
535
|
+
data_options.delete_if do |key|
|
536
|
+
[:keys, :names, :indexes, :name, :index, :value].include?(key)
|
537
|
+
end
|
538
|
+
|
539
|
+
return nil unless config_layers[0][:set]
|
540
|
+
|
541
|
+
config_layer = config_layers[0][:config]
|
542
|
+
config_layer.data_options(data_options)
|
543
|
+
config_layer[keys] = value
|
544
|
+
end
|
545
|
+
|
546
|
+
# Load from a file called by default by load.
|
547
|
+
# This function is typically used by a Child Class.
|
548
|
+
#
|
549
|
+
# * *Args* :
|
550
|
+
# - +options+ : Supported options for load
|
551
|
+
# - +:name+ : layer name to get data.
|
552
|
+
# - +:index+ : layer index to get data.
|
553
|
+
# If neither :name or :index is set, set will use the highest layer.
|
554
|
+
#
|
555
|
+
# * *Returns* :
|
556
|
+
# - true : loaded
|
557
|
+
# - false: not loaded. There are several possible reasons:
|
558
|
+
# - input/output issue (normally raised)
|
559
|
+
# - layer option :load is false.
|
560
|
+
def p_load(options = {}) #:doc:
|
561
|
+
options = {} if options.nil?
|
562
|
+
options[:names] = [options[:name]] if options.key?(:name)
|
563
|
+
options[:indexes] = [options[:index]] if options.key?(:index)
|
564
|
+
|
565
|
+
parameters = _nameindex_common_options_get(options)
|
566
|
+
return nil if parameters.nil?
|
567
|
+
|
568
|
+
config_layers = parameters[0][0]
|
569
|
+
|
570
|
+
return nil unless config_layers[0][:load]
|
571
|
+
|
572
|
+
config_layers[0][:config].load
|
573
|
+
end
|
574
|
+
|
575
|
+
# Save to a file called by default by save
|
576
|
+
# This function is typically used by a Child Class.
|
577
|
+
#
|
578
|
+
# * *Args* :
|
579
|
+
# - +options+ : Supported options for save
|
580
|
+
# - +:name+ : layer to get data.
|
581
|
+
# - +:index+: layer index to get data.
|
582
|
+
# If neither :name or :index is set, set will use the highest layer
|
583
|
+
#
|
584
|
+
# * *Returns* :
|
585
|
+
# - true : saved
|
586
|
+
# - false: not saved. There are several possible reasons:
|
587
|
+
# - options defines a :file_readonly to true.
|
588
|
+
# - input/output issue (normally raised)
|
589
|
+
# - layer option :save is false.
|
590
|
+
def p_save(options = {}) #:doc:
|
591
|
+
options[:names] = [options[:name]] if options.key?(:name)
|
592
|
+
options[:indexes] = [options[:index]] if options.key?(:index)
|
593
|
+
|
594
|
+
parameters = _nameindex_common_options_get(options)
|
595
|
+
return nil if parameters.nil?
|
596
|
+
|
597
|
+
config_layers = parameters[0][0]
|
598
|
+
|
599
|
+
return nil unless config_layers[0][:save]
|
600
|
+
|
601
|
+
config_layers[0][:config].save
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
# private functions usable by child classes
|
606
|
+
class CoreConfig
|
607
|
+
# initialize CoreConfig
|
608
|
+
#
|
609
|
+
# * *Args*
|
610
|
+
# - +config_layers+ : Array config layers configuration.
|
611
|
+
# Each layer options have those options:
|
612
|
+
# - :config : optional. See `Defining Config layer instance` for
|
613
|
+
# details
|
614
|
+
# - :name : required. String. Name of the config layer.
|
615
|
+
# Warning! unique name on layers is no tested.
|
616
|
+
# - :set : boolean. True if authorized. Default is True.
|
617
|
+
# - :load : boolean. True if authorized. Default is False.
|
618
|
+
# - :save : boolean. True if authorized. Default is False.
|
619
|
+
# - :file_set : boolean. True if authorized to update a filename.
|
620
|
+
# Default is False.
|
621
|
+
#
|
622
|
+
# each layers can defines some options for the layer to behave differently
|
623
|
+
# CoreConfig call a layer data_options to set some options, before
|
624
|
+
# exist?, get or [], set or []=, save and load functions.
|
625
|
+
# See BaseConfig::data_options for predefined options.
|
626
|
+
#
|
627
|
+
# Core config provides some private additionnal functions for
|
628
|
+
# child class functions:
|
629
|
+
# - #_set_data_options(layers, options) - To set data_options on one or
|
630
|
+
# more config layers
|
631
|
+
# - #p_get(options) - core get function
|
632
|
+
# - #p_set(options) - core set function
|
633
|
+
# - #p_save(options) core save function
|
634
|
+
# - #p_load(options) - core load function
|
635
|
+
#
|
636
|
+
# if +config_layers+ is not provided, CoreConfig will instanciate a runtime
|
637
|
+
# like system:
|
638
|
+
#
|
639
|
+
# config = CoreConfig.New
|
640
|
+
# # is equivalent to :
|
641
|
+
# config_layers = [{name: 'runtime',
|
642
|
+
# config: PRC::BaseConfig.new, set: true}]
|
643
|
+
# config = CoreConfig.New(config_layers)
|
644
|
+
#
|
645
|
+
# = Defining Config layer instance:
|
646
|
+
#
|
647
|
+
# :config value requires it to be of type 'BaseConfig'
|
648
|
+
# By default, it uses `:config => PRC::BaseConfig.new`
|
649
|
+
# Instead, you can set:
|
650
|
+
# * directly BaseConfig. `:config => PRC::BaseConfig.new`
|
651
|
+
# * a child based on BaseConfig. `:config => MyConfig.new`
|
652
|
+
# * some predefined enhanced BaseConfig:
|
653
|
+
# * PRC::SectionConfig. See prc_section_config.rb.
|
654
|
+
# `:config => PRC::SectionConfig.new`
|
655
|
+
#
|
656
|
+
def initialize(config_layers = nil)
|
657
|
+
if config_layers.nil?
|
658
|
+
config_layers = []
|
659
|
+
config_layers << CoreConfig.define_layer
|
660
|
+
end
|
661
|
+
initialize_layers(config_layers)
|
662
|
+
end
|
663
|
+
|
664
|
+
# This function add a config layer at runtime.
|
665
|
+
# The new layer added at runtime, can be removed at runtime
|
666
|
+
# with layer_remove
|
667
|
+
# The name MUST be different than other existing config layer names
|
668
|
+
#
|
669
|
+
# *Args*
|
670
|
+
# - +options+ : Hash data
|
671
|
+
# - :name : Required. Name of the layer to add
|
672
|
+
# - :index : Config position to use. 0 is the default. 0 is the first
|
673
|
+
# Config layer use by get.
|
674
|
+
# - :config : A Config instance of class type PRC::BaseConfig
|
675
|
+
# - :set : Boolean. True if is authorized to set a variable.
|
676
|
+
# - :load : Boolean. True if is authorized to load from a file.
|
677
|
+
# - :save : Boolean. True if is authorized to save to a file.
|
678
|
+
# - :file_set : Boolean. True if is authorized to change the file name.
|
679
|
+
#
|
680
|
+
# *returns*
|
681
|
+
# - true if layer is added.
|
682
|
+
# OR
|
683
|
+
# - nil : if layer name already exist
|
684
|
+
def layer_add(options)
|
685
|
+
layer = CoreConfig.define_layer(options)
|
686
|
+
|
687
|
+
layer[:init] = false # Runtime layer
|
688
|
+
|
689
|
+
index = 0
|
690
|
+
index = options[:index] if options[:index].is_a?(Fixnum)
|
691
|
+
names = []
|
692
|
+
@config_layers.each { |alayer| names << alayer[:name] }
|
693
|
+
|
694
|
+
return nil if names.include?(layer[:name])
|
695
|
+
@config_layers.insert(index, layer)
|
696
|
+
true
|
697
|
+
end
|
698
|
+
|
699
|
+
# Function to remove a runtime layer.
|
700
|
+
# You cannot remove a predefined layer, created during CoreConfig
|
701
|
+
# instanciation.
|
702
|
+
# *Args*
|
703
|
+
# - +options+ : Hash data
|
704
|
+
# - +:name+ : Name of the layer to remove.
|
705
|
+
# - +:index+: Index of the layer to remove.
|
706
|
+
#
|
707
|
+
# At least, :name or :index is required.
|
708
|
+
# If both; :name and :index are set, :name is used.
|
709
|
+
# *return*
|
710
|
+
# - true if layer name is removed.
|
711
|
+
# OR
|
712
|
+
# - nil : if not found or invalid.
|
713
|
+
def layer_remove(options)
|
714
|
+
index = layer_index(options[:name])
|
715
|
+
index = options[:index] if index.nil?
|
716
|
+
|
717
|
+
return nil if index.nil?
|
718
|
+
|
719
|
+
layer = @config_layers[index]
|
720
|
+
|
721
|
+
return nil if layer.nil? || layer[:init]
|
722
|
+
|
723
|
+
@config_layers.delete_at(index)
|
724
|
+
true
|
725
|
+
end
|
726
|
+
|
727
|
+
# Function to define layer options.
|
728
|
+
# By default, :set is true and :config is attached to a new PRC::BaseConfig
|
729
|
+
# instance.
|
730
|
+
#
|
731
|
+
# Supported options:
|
732
|
+
# - :config : optional. See `Defining Config layer instance` for details
|
733
|
+
# - :name : required. String. Name of the config layer.
|
734
|
+
# Warning! unique name on layers is no tested.
|
735
|
+
# - :set : boolean. True if authorized. Default is True.
|
736
|
+
# - :load : boolean. True if authorized. Default is False.
|
737
|
+
# - :save : boolean. True if authorized. Default is False.
|
738
|
+
# - :file_set : boolean. True if authorized to update a filename.
|
739
|
+
# Default is False.
|
740
|
+
def self.define_layer(options = {})
|
741
|
+
attributes = [:name, :config, :set, :load, :save, :file_set]
|
742
|
+
|
743
|
+
layer = {}
|
744
|
+
|
745
|
+
attributes.each do |attribute|
|
746
|
+
if options.key?(attribute)
|
747
|
+
layer[attribute] = options[attribute]
|
748
|
+
else
|
749
|
+
layer[attribute] = case attribute
|
750
|
+
when :name
|
751
|
+
'runtime'
|
752
|
+
when :config
|
753
|
+
PRC::BaseConfig.new
|
754
|
+
when :set
|
755
|
+
true
|
756
|
+
else
|
757
|
+
false
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
layer
|
762
|
+
end
|
763
|
+
|
764
|
+
# layer_indexes function
|
765
|
+
#
|
766
|
+
# * *Args*
|
767
|
+
# - +:name+ : layer to identify.
|
768
|
+
#
|
769
|
+
# * *Returns*
|
770
|
+
# first index found or nil.
|
771
|
+
#
|
772
|
+
def layer_indexes(names)
|
773
|
+
names = [names] if names.is_a?(String)
|
774
|
+
return nil unless names.is_a?(Array)
|
775
|
+
|
776
|
+
layers = []
|
777
|
+
|
778
|
+
names.each do |name|
|
779
|
+
index = layer_index(name)
|
780
|
+
layers << index unless index.nil?
|
781
|
+
end
|
782
|
+
return layers if layers.length > 0
|
783
|
+
nil
|
784
|
+
end
|
785
|
+
|
786
|
+
# layer_index function
|
787
|
+
#
|
788
|
+
# * *Args*
|
789
|
+
# - +:name+ : layer to identify.
|
790
|
+
#
|
791
|
+
# * *Returns*
|
792
|
+
# first index found or nil.
|
793
|
+
#
|
794
|
+
def layer_index(name)
|
795
|
+
return nil unless name.is_a?(String)
|
796
|
+
return nil if @config_layers.nil?
|
797
|
+
|
798
|
+
@config_layers.each_index do |index|
|
799
|
+
return index if @config_layers[index][:name] == name
|
800
|
+
end
|
801
|
+
nil
|
802
|
+
end
|
803
|
+
|
804
|
+
private
|
805
|
+
|
806
|
+
# Function to initialize Config layers.
|
807
|
+
# This function is typically used by a Child Class.
|
808
|
+
#
|
809
|
+
# * *Args*
|
810
|
+
# - +config_layers+ : Array of config layers.
|
811
|
+
# First layer is the deepest config object.
|
812
|
+
# Last layer is the first config object queried.
|
813
|
+
#
|
814
|
+
# Ex: If we define 2 config layers:
|
815
|
+
#
|
816
|
+
# class Test << PRC::CoreConfig
|
817
|
+
# def initialize
|
818
|
+
# local = PRC::BaseConfig.new(:test => :found_local)
|
819
|
+
# runtime = PRC::BaseConfig.new(:test => :found_runtime)
|
820
|
+
# layers = []
|
821
|
+
# layers << PRC::CoreConfig.define_layer(name: 'local',
|
822
|
+
# config: local )
|
823
|
+
# layers << PRC::CoreConfig.define_layer(name: 'runtime',
|
824
|
+
# config: runtime )
|
825
|
+
# initialize_layers(layers)
|
826
|
+
# end
|
827
|
+
# end
|
828
|
+
#
|
829
|
+
# config = Test.new
|
830
|
+
#
|
831
|
+
# p config[:test] # => :found_runtime
|
832
|
+
#
|
833
|
+
# config[:test] = "where?"
|
834
|
+
# p config.where?(:test) # => ["runtime", "local"]
|
835
|
+
#
|
836
|
+
# config.del(:test)
|
837
|
+
# p config.where?(:test) # => ["local"]
|
838
|
+
# p config[:test] # => :found_local
|
839
|
+
#
|
840
|
+
# config[:test] = "and now?"
|
841
|
+
# p config.where?(:test) # => ["runtime", "local"]
|
842
|
+
#
|
843
|
+
def initialize_layers(config_layers = nil) #:doc:
|
844
|
+
@config_layers = []
|
845
|
+
|
846
|
+
config_layers.each do |layer|
|
847
|
+
next unless layer.is_a?(Hash) && layer.key?(:config) &&
|
848
|
+
layer[:config].is_a?(BaseConfig)
|
849
|
+
next unless layer[:name].is_a?(String)
|
850
|
+
@config_layers << _initialize_layer(layer)
|
851
|
+
end
|
852
|
+
@config_layers.reverse!
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
# This class implement The CoreConfig system of lorj.
|
857
|
+
#
|
858
|
+
# * You can use it directly. See ::new.
|
859
|
+
# * you can enhance it with class heritage feature.
|
860
|
+
# See Class child discussion later in this class documentation.
|
861
|
+
#
|
862
|
+
# = Public functions implemented:
|
863
|
+
#
|
864
|
+
# It implements several layer of CoreConfig object type
|
865
|
+
# to provide several source of data in layers priorities.
|
866
|
+
# Ex: RunTime => LocalConfig => AppDefault
|
867
|
+
#
|
868
|
+
# It implements config features:
|
869
|
+
# * #[] - To get a value for a key or tree of keys
|
870
|
+
# * #[]= - To set a Config value in the highest config.
|
871
|
+
# * #del - To delete key or simply nil the value in highest config.
|
872
|
+
# * #exist? - To check the existence of a value in config levels.
|
873
|
+
# * #where? - To get the name of the level where the value was found.
|
874
|
+
# * #file - To get or set a filename to a config layer.
|
875
|
+
# * #save - To save one config data level in a yaml file.
|
876
|
+
# * #load - To load data from a yaml file to a config data layer.
|
877
|
+
# * #merge - To merge several layers data values. Values must be Hash.
|
878
|
+
#
|
879
|
+
# When the instance is initialized, it defines 3 Config layers (BaseConfig).
|
880
|
+
#
|
881
|
+
# If you need to define layers differently, consider to create your child
|
882
|
+
# class. You will be able to use SectionConfig or even any BaseConfig Child
|
883
|
+
# class as well.
|
884
|
+
#
|
885
|
+
# For details about a Config layers, See BaseConfig or SectionConfig.
|
886
|
+
#
|
887
|
+
# = Child Class implementation:
|
888
|
+
#
|
889
|
+
# This class can be enhanced with any kind of additional functionality.
|
890
|
+
#
|
891
|
+
# You can redefine following functions
|
892
|
+
# exist?, [], []=, file, save, load, del, merge.
|
893
|
+
#
|
894
|
+
# Each public functions calls pendant function, private, prefixed by _, with
|
895
|
+
# default options
|
896
|
+
#
|
897
|
+
# public => private
|
898
|
+
# * #exist? => #p_exist?
|
899
|
+
# * #[] => #p_get
|
900
|
+
# * #[]= => #p_set
|
901
|
+
# * #file => #p_file
|
902
|
+
# * #save => #p_save
|
903
|
+
# * #load => #p_load
|
904
|
+
# * #del => #p_del
|
905
|
+
# * #merge => #p_get(:merge => true).
|
906
|
+
#
|
907
|
+
# == Examples:
|
908
|
+
#
|
909
|
+
# * Your child class can limit or re-organize config layers to query.
|
910
|
+
# Use :indexes or :names options to select which layer you want to query
|
911
|
+
# and call the core function.
|
912
|
+
#
|
913
|
+
# Ex: If you have 4 config levels and want to limit to 2 top ones
|
914
|
+
#
|
915
|
+
# def [](*keys)
|
916
|
+
# options = { keys: keys}
|
917
|
+
# options[:indexes] = [0, 1]
|
918
|
+
# p_get(options)
|
919
|
+
# end
|
920
|
+
#
|
921
|
+
# Ex: If you have 4 config levels and want to limit to 2 names.
|
922
|
+
#
|
923
|
+
# def [](*keys)
|
924
|
+
# options = { keys: keys}
|
925
|
+
# options[:names] = ['local', 'default_app']
|
926
|
+
# p_get(options)
|
927
|
+
# end
|
928
|
+
#
|
929
|
+
# * Your child class can force some levels options or define some extra
|
930
|
+
# options.
|
931
|
+
#
|
932
|
+
# Use :data_options to define each of them
|
933
|
+
#
|
934
|
+
# # Ex: If your class has 4 levels. /:name is not updatable for level 1.
|
935
|
+
#
|
936
|
+
# def [](*keys)
|
937
|
+
# options = { keys: keys }
|
938
|
+
# # The following defines data_readonly for the config level 1
|
939
|
+
# if keys[0] == :name
|
940
|
+
# options[:data_options] = [nil, {data_readonly: true}]
|
941
|
+
# end
|
942
|
+
# p_get(options)
|
943
|
+
# end
|
944
|
+
#
|
945
|
+
# # Ex: if some layer takes care of option :section, and apply to each
|
946
|
+
# # layers.
|
947
|
+
# def [](section, *keys)
|
948
|
+
# options = { keys: keys, section: section }
|
949
|
+
# p_get(options)
|
950
|
+
# end
|
951
|
+
#
|
952
|
+
# # Ex: if some layer takes care of option :section, and to apply to some
|
953
|
+
# # layers, like layer 1 and 2. (Assume with 4 layers.)
|
954
|
+
#
|
955
|
+
# def [](section, *keys)
|
956
|
+
# options = { keys: keys }
|
957
|
+
# options[:data_options] = [nil, {section: section}, {section: section}]
|
958
|
+
# p_get(options)
|
959
|
+
# end
|
960
|
+
#
|
961
|
+
#
|
962
|
+
class CoreConfig
|
963
|
+
# exist?
|
964
|
+
#
|
965
|
+
# * *Args*
|
966
|
+
# - +keys+ : Array of key path to found
|
967
|
+
#
|
968
|
+
# * *Returns*
|
969
|
+
# - boolean : true if the key path was found
|
970
|
+
#
|
971
|
+
# Class child:
|
972
|
+
# A class child can redefine this function to increase default
|
973
|
+
# features.
|
974
|
+
#
|
975
|
+
def exist?(*keys)
|
976
|
+
p_exist?(:keys => keys)
|
977
|
+
end
|
978
|
+
|
979
|
+
# where?
|
980
|
+
#
|
981
|
+
# * *Args*
|
982
|
+
# - +keys+ : Array of key path to found
|
983
|
+
#
|
984
|
+
# * *Returns*
|
985
|
+
# - boolean : true if the key path was found
|
986
|
+
#
|
987
|
+
def where?(*keys)
|
988
|
+
p_where?(:keys => keys)
|
989
|
+
end
|
990
|
+
|
991
|
+
# Get function
|
992
|
+
#
|
993
|
+
# * *Args*
|
994
|
+
# - +keys+ : Array of key path to found
|
995
|
+
#
|
996
|
+
# * *Returns*
|
997
|
+
# value found or nil.
|
998
|
+
#
|
999
|
+
def [](*keys)
|
1000
|
+
p_get(:keys => keys)
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
# Merge function
|
1004
|
+
# Compare to get, merge will extract all values from each layers
|
1005
|
+
# If those values are found and are type of Hash, merge will merge
|
1006
|
+
# each layers values from the bottom to the top layer.
|
1007
|
+
# ie invert of CoreConfig.layers
|
1008
|
+
#
|
1009
|
+
# Note that if a layer contains a data, but not Hash, this layer
|
1010
|
+
# will be ignored.
|
1011
|
+
#
|
1012
|
+
# * *Args*
|
1013
|
+
# - +keys+ : Array of key path to found
|
1014
|
+
#
|
1015
|
+
# * *Returns*
|
1016
|
+
# value found merged or nil.
|
1017
|
+
#
|
1018
|
+
def merge(*keys)
|
1019
|
+
p_get(:keys => keys, :merge => true)
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# Set function
|
1023
|
+
#
|
1024
|
+
# * *Args*
|
1025
|
+
# - +keys+ : Array of key path to found
|
1026
|
+
# * *Returns*
|
1027
|
+
# - The value set or nil
|
1028
|
+
#
|
1029
|
+
# ex:
|
1030
|
+
# value = CoreConfig.New
|
1031
|
+
#
|
1032
|
+
# value[:level1, :level2] = 'value'
|
1033
|
+
# # => {:level1 => {:level2 => 'value'}}
|
1034
|
+
def []=(*keys, value)
|
1035
|
+
p_set(:keys => keys, :value => value)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# Del function
|
1039
|
+
#
|
1040
|
+
# * *Args*
|
1041
|
+
# - +keys+ : Array of key path to found and delete the last element.
|
1042
|
+
# * *Returns*
|
1043
|
+
# - The Hash updated.
|
1044
|
+
#
|
1045
|
+
# ex:
|
1046
|
+
# value = CoreConfig.New
|
1047
|
+
#
|
1048
|
+
# value[:level1, :level2] = 'value'
|
1049
|
+
# # => {:level1 => {:level2 => 'value'}}
|
1050
|
+
# {:level1 => {:level2 => 'value'}}.del(:level1, :level2)
|
1051
|
+
# # => {:level1 => {}}
|
1052
|
+
def del(*keys)
|
1053
|
+
p_del(:keys => keys)
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
# Load from a file to the highest layer or a specific layer.
|
1057
|
+
#
|
1058
|
+
# * *Args* :
|
1059
|
+
# - +options+ : Supported options for load
|
1060
|
+
# - +:name+ : layer to get data.
|
1061
|
+
# - +:index+: layer index to get data.
|
1062
|
+
# If neither :name or :index is set, set will use the highest
|
1063
|
+
# layer
|
1064
|
+
#
|
1065
|
+
# * *Returns* :
|
1066
|
+
# -
|
1067
|
+
# * *Raises* :
|
1068
|
+
# - ++ ->
|
1069
|
+
def load(options = {})
|
1070
|
+
p_load(options)
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
# Save to a file
|
1074
|
+
#
|
1075
|
+
# * *Args* :
|
1076
|
+
# - +options+ : Supported options for save
|
1077
|
+
# - +:name+ : layer to get data.
|
1078
|
+
# - +:index+: layer index to get data.
|
1079
|
+
# If neither :name or :index is set, set will use the highest
|
1080
|
+
# layer
|
1081
|
+
#
|
1082
|
+
# * *Returns* :
|
1083
|
+
# -
|
1084
|
+
def save(options = {})
|
1085
|
+
p_save(options)
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
# Get/Set the file name.
|
1089
|
+
#
|
1090
|
+
# * *Args*
|
1091
|
+
# - +:file+ : file name for the layer identified.
|
1092
|
+
# - +options+ : Supported options for save
|
1093
|
+
# - +:index+: layer index to get data.
|
1094
|
+
# - +:name+ : layer to get data.
|
1095
|
+
# If neither :name or :index is set, nil is returned.
|
1096
|
+
#
|
1097
|
+
# * *Returns*
|
1098
|
+
# - The file name.
|
1099
|
+
def file(filename = nil, options = {})
|
1100
|
+
p_file(filename, options)
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
# Function to check if merge can be used on a key.
|
1104
|
+
# merge can return data only if at least one key value accross layers
|
1105
|
+
# are of type Hash or Array.
|
1106
|
+
# * *Args*
|
1107
|
+
# - +options+ : Hash of how to get the data
|
1108
|
+
# - +:value+ : Value to set
|
1109
|
+
# - +:keys+ : Array of key path to found
|
1110
|
+
# - +:name+ : layer to get data.
|
1111
|
+
# - +:index+ : layer index to get data.
|
1112
|
+
# If neither :name or :index is set, set will use the highest layer.
|
1113
|
+
# - +:data_opts+ : Array or Hash. Define data options per layer.
|
1114
|
+
# - +:exclusive+ : true to ensure values found are exclusively Hash or
|
1115
|
+
# Array
|
1116
|
+
#
|
1117
|
+
def mergeable?(options)
|
1118
|
+
parameters = _common_options_get(options, [:keys], [:exclusive])
|
1119
|
+
return nil if parameters.nil?
|
1120
|
+
|
1121
|
+
# Required options : parameters[0]
|
1122
|
+
config_layers, data_opts, keys = parameters[0]
|
1123
|
+
# Optional options : parameters[1]
|
1124
|
+
be_exclusive = parameters[1][0]
|
1125
|
+
|
1126
|
+
# Merge is done in the reverse order. ie from deepest to top.
|
1127
|
+
config_layers = config_layers.reverse
|
1128
|
+
|
1129
|
+
return nil if keys.length == 0 || keys[0].nil? || config_layers[0].nil?
|
1130
|
+
|
1131
|
+
data_options = options.clone
|
1132
|
+
data_options.delete_if do |key|
|
1133
|
+
[:keys, :names, :indexes, :name, :index, :merge].include?(key)
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
_check_from_layers(keys, config_layers, data_opts, data_options,
|
1137
|
+
be_exclusive)
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
# Function to get the version of a config layer name.
|
1141
|
+
# * *Args*
|
1142
|
+
# - +:name+ : layer to get data.
|
1143
|
+
#
|
1144
|
+
def version(name)
|
1145
|
+
return nil unless name.is_a?(String)
|
1146
|
+
|
1147
|
+
index = layer_index(name)
|
1148
|
+
return nil if index.nil?
|
1149
|
+
|
1150
|
+
@config_layers[index][:config].version
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
# Function to set the version of a config layer name.
|
1154
|
+
# * *Args*
|
1155
|
+
# - +:name+ : layer to set data version.
|
1156
|
+
#
|
1157
|
+
def version_set(name, version)
|
1158
|
+
return nil unless name.is_a?(String) && version.is_a?(String)
|
1159
|
+
|
1160
|
+
index = layer_index(name)
|
1161
|
+
return nil if index.nil?
|
1162
|
+
|
1163
|
+
@config_layers[index][:config].version = version
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
def latest_version?(name)
|
1167
|
+
return nil unless name.is_a?(String)
|
1168
|
+
|
1169
|
+
index = layer_index(name)
|
1170
|
+
return nil if index.nil?
|
1171
|
+
|
1172
|
+
@config_layers[index][:config].latest_version?
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
# List all config layers defined in this instance.
|
1176
|
+
def layers
|
1177
|
+
result = []
|
1178
|
+
@config_layers.each { |layer| result << layer[:name] }
|
1179
|
+
result
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
# Display in human format.
|
1183
|
+
def to_s
|
1184
|
+
data = "Configs list ordered:\n"
|
1185
|
+
@config_layers.each do |layer|
|
1186
|
+
data += format("---- Config : %s ----\noptions: ", layer[:name])
|
1187
|
+
|
1188
|
+
data += 'predefined, ' if layer[:init].is_a?(TrueClass)
|
1189
|
+
if layer[:set]
|
1190
|
+
data += 'data RW '
|
1191
|
+
else
|
1192
|
+
data += 'data RO '
|
1193
|
+
end
|
1194
|
+
data += format(", %s\n", to_s_file_opts(layer))
|
1195
|
+
|
1196
|
+
data += format("%s\n", layer[:config].to_s)
|
1197
|
+
end
|
1198
|
+
data
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
private
|
1202
|
+
|
1203
|
+
def to_s_file_opts(layer)
|
1204
|
+
data = 'File '
|
1205
|
+
if layer[:load] &&
|
1206
|
+
if layer[:save]
|
1207
|
+
data += 'RW'
|
1208
|
+
else
|
1209
|
+
data += 'RO'
|
1210
|
+
end
|
1211
|
+
data += ', filename updatable' if layer[:file_set]
|
1212
|
+
else
|
1213
|
+
data += 'None'
|
1214
|
+
end
|
1215
|
+
data
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
end
|