lorj 1.0.3 → 1.0.4

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