lorj 1.0.3 → 1.0.4

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.
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