lorj 1.0.10 → 1.0.11

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.
@@ -26,8 +26,8 @@ require 'erb'
26
26
  # this task to make it to work.
27
27
  module Lorj
28
28
  # Adding process core functions.
29
- class BaseDefinition
30
- # Load /:setup/:ask_step section of the defaults.yaml
29
+ class BaseDefinition # rubocop: disable ClassLength
30
+ # Load /:setup/:ask_step section of the data.yaml
31
31
  #
32
32
  # See lib/core/core_model.rb
33
33
  #
@@ -37,16 +37,63 @@ module Lorj
37
37
  # * *Raises* :
38
38
  #
39
39
  def _setup_load
40
+ setup_steps = {}
41
+
42
+ steps = Lorj.data.setup_data(:steps)
43
+ return setup_steps if steps.nil?
44
+
45
+ steps.each do |name, value|
46
+ setup_steps[name] = _setup_load_init(value)
47
+ end
48
+
49
+ ask_steps = Lorj.data.setup_data(:ask_step)
50
+ return setup_steps if ask_steps.nil?
51
+
52
+ ask_steps.each do |value|
53
+ name = value[:name]
54
+ name = ask_steps.index(value).to_s if name.nil?
55
+ if setup_steps.key?(name)
56
+ setup_steps[name].rh_merge(_setup_load_init(value))
57
+ next
58
+ end
59
+ setup_steps[name] = _setup_load_init(value)
60
+ end
61
+
62
+ setup_steps
63
+ end
64
+
65
+ def _setup_load_init(value)
66
+ value = {} if value.nil?
67
+
68
+ {
69
+ :desc => value[:desc],
70
+ :explanation => value[:explanation],
71
+ :pre_step_handler => value[:pre_step_function],
72
+ :order => [[]], # attributes in array of level/order
73
+ :post_step_handler => value[:post_step_function]
74
+ }
75
+ end
76
+
77
+ def _setup_step_definition
78
+ setup_steps = {}
79
+ steps = Lorj.data.setup_data(:steps)
80
+ return setup_steps if steps.nil?
81
+
82
+ steps.each do |name, value|
83
+ setup_steps[name] = value
84
+ end
85
+
40
86
  ask_steps = Lorj.data.setup_data(:ask_step)
41
- setup_steps = []
87
+ return setup_steps if ask_steps.nil?
88
+
42
89
  ask_steps.each do |value|
43
- setup_steps << {
44
- :desc => value[:desc],
45
- :explanation => value[:explanation],
46
- :pre_step_handler => value[:pre_step_function],
47
- :order => [[]], # attributes in array of level/order
48
- :post_step_handler => value[:post_step_function]
49
- }
90
+ name = value[:name]
91
+ name = ask_steps.index(value).to_s if name.nil?
92
+ if setup_steps.key?(name)
93
+ setup_steps[name].rh_merge(value)
94
+ next
95
+ end
96
+ setup_steps[name] = value
50
97
  end
51
98
  setup_steps
52
99
  end
@@ -63,83 +110,907 @@ module Lorj
63
110
  # * *Raises* :
64
111
  #
65
112
  def _setup_check_additional(setup_steps)
66
- setup_steps.each_index do |step|
67
- value = Lorj.data.setup_data(:ask_step)[step]
68
- next unless value.rh_exist?(:add)
113
+ setup_steps.each do |step|
114
+ step_name = step[:name]
115
+ value = _setup_step_definition
116
+ next unless value.is_a?(Hash) && value.rh_exist?(step_name, :add)
69
117
 
70
- datas_to_add = value.rh_get(:add)
118
+ datas_to_add = value.rh_get(step_name, :add)
71
119
  datas_to_add.each do |data_to_add|
72
- order_array = setup_steps[step][:order]
120
+ order_array = step[:order]
73
121
  next if _setup_attr_already_added?(order_array, data_to_add)
74
122
 
75
- _setup_data_insert(setup_steps, data_to_add, step, order_array.length)
123
+ _setup_data_insert(step, data_to_add)
76
124
  end
77
125
  end
78
126
  end
79
127
 
80
- # Loop on object dependencies to determine the list of attributes to setup.
128
+ # Function to build a step/order structure of Attributes.
129
+ # step/order is an array of array.
130
+ # the first array represents the steps
131
+ # the second array represents the list of attributes in a specific order.
132
+ #
133
+ # This structure is used by setup to ask the list of attributes in a
134
+ # specific order.
135
+ #
136
+ # It loops on object dependencies, then data definition to determine the
137
+ # list of attributes and their required order.
138
+ #
139
+ # A new step is created when an attribute, during setup time requires
140
+ # to query an object, which may requires some additional attributes,
141
+ # considered as dependent attributes.
142
+ # The previous step must contains at least the dependent attributes.
81
143
  #
144
+ # Process data definition can influence those detected steps, by assigning
145
+ # an :ask_step to the attribute.
146
+ # Then the default attribute step may be splitted to more steps.
147
+ #
148
+ # You cannot set an attribute step lower then the detected step.
149
+ # Process data definition can influence the attributes order, by assigning
150
+ # an :ask_order to the attribute.
151
+ #
152
+ # :setup section of the process data definition (data.yaml) can set the step
153
+ # description or add some extra attributes (not detected by default)
82
154
  #
83
155
  # * *Args* :
156
+ # - sObjectType : Symbol/String. Object type to analyze for list of
157
+ # attributes to setup
84
158
  # - setup_steps : Hash. setup data structure to update.
85
159
  # It will update setup_steps:/:order 2 dimensions array
86
160
  #
87
161
  # * *Returns*:
88
- # - Nothing. But setup_steps is updated.
162
+ # - setup_steps: Hash updated
89
163
  #
90
164
  # * *Raises* :
91
165
  #
92
166
  def _setup_identify(sObjectType, setup_steps)
93
- objs_to_inspect = [sObjectType]
94
- inspected_objects = []
95
-
96
- while objs_to_inspect.length > 0
97
- # Identify data to ask
98
- # A data to ask is a data needs from an object type
99
- # which is declared in section of defaults.yaml
100
- # and is declared :account to true (in defaults.yaml or in process
101
- # declaration - define_data)
102
-
103
- object_type = objs_to_inspect.pop
104
-
105
- Lorj.debug(1, "Checking '%s'", object_type)
106
- attributes = PrcLib.model.meta_obj.rh_get(object_type,
107
- :params, :keys)
108
- if attributes.nil?
109
- Lorj.debug(1, "Warning! Object '%s' has no data/object needs. Check"\
110
- ' the process', object_type)
111
- next
112
- end
113
- attributes.each do |attr_path, attr_params|
114
- attr_name = KeyPath.new(attr_path).key
115
- _setup_identify_obj_params(setup_steps,
116
- inspected_objects, objs_to_inspect,
117
- attr_name, attr_params)
118
- end
167
+ # There is 3 sources of data used by setup to build the list of data to
168
+ # setup:
169
+ # - Lorj.data (Lorj::MetaConfig): Process data definition.
170
+ # - config (Lorj::Account) : Configuration files/runtime data.
171
+ # - PrcLib.model.meta_obj(Hash) : Object definition / mapping
172
+
173
+ dependencies = { :objects => {}, :attributes => {} }
174
+ _setup_identify_dep_init(dependencies, sObjectType)
175
+
176
+ # Build list of attributes which will be needed for an object and
177
+ # its dependencies
178
+
179
+ Lorj.debug(2, '- Checking objects dependencies -')
180
+ _setup_identify_deps(dependencies, sObjectType)
181
+
182
+ # Build list of additional attributes and build
183
+ # attribute dependencies thanks to attribute queries.
184
+ Lorj.debug(2, '- Checking attributes dependencies -')
185
+ orders = _setup_build_steps_from(dependencies)
186
+
187
+ # Reorganize each required steps thanks to :ask_order
188
+ Lorj.debug(2, '- Re-organizing attributes steps/order -')
189
+ attrs = _setup_reorganize_steps_order(dependencies[:attributes], orders)
190
+
191
+ # Apply additionnal steps as described by Lorj.data
192
+ Lorj.debug(2, '- Add extra steps -')
193
+ _setup_reorganize_steps(attrs, orders, setup_steps)
194
+ end
195
+
196
+ # Internal setup function to parse objects/attributes dependency list
197
+ # and build objects list and required attributes list.
198
+ def _setup_identify_deps(dependencies, object_type, path = [])
199
+ model_object = dependencies.rh_get(:objects, object_type)
200
+ objects_list = PrcLib.model.meta_obj.rh_get(object_type, :params, :keys)
201
+ if objects_list.nil?
202
+ PrcLib.warning("'%s' as object type is not valid. Not declared.")
203
+ return [[], [], []]
119
204
  end
205
+
206
+ objects = []
207
+ attrs = []
208
+ deps_attrs = []
209
+ deps_objects = []
210
+ new_path = path + [object_type]
211
+
212
+ objects_list.each do |attr_path, attr_params|
213
+ a, d_a, o, d_o = _setup_id_each(dependencies, model_object, new_path,
214
+ attr_path, attr_params)
215
+ attrs += a
216
+ deps_attrs += d_a
217
+ objects += o
218
+ deps_objects += d_o
219
+ end
220
+ attrs.uniq!
221
+ deps_attrs.uniq!
222
+ objects.uniq!
223
+ deps_objects.uniq!
224
+
225
+ _sid_show_debug(3, format("'%s' has ", object_type) + '%s',
226
+ attrs, objects)
227
+ _sid_show_debug(4, format("'%s' has ", object_type) + 'also indirect %s',
228
+ deps_attrs, deps_objects)
229
+
230
+ [(objects + deps_objects).uniq, attrs, deps_attrs]
120
231
  end
121
232
 
122
- # Internal setup function to identify data to ask
123
- # Navigate through objects dependencies to determine the need.
124
- def _setup_identify_obj_params(setup_steps,
125
- inspected_objects, objs_to_inspect,
126
- attr_name, attr_params)
233
+ def _setup_id_each(dependencies, model_object, new_path,
234
+ attr_path, attr_params)
235
+ objects = []
236
+ attrs = []
237
+ deps_attrs = []
238
+ deps_objects = []
127
239
 
240
+ attr_name = KeyPath.new(attr_path).key_tree
128
241
  attr_type = attr_params[:type]
129
242
 
130
243
  case attr_type
131
244
  when :data
132
- return unless _setup_obj_param_is_data(setup_steps,
133
- inspected_objects, attr_name)
134
- inspected_objects << attr_name
135
- return
245
+ attr_to_add = _setup_id_init_data_deps(dependencies, attr_name)
246
+ if attr_to_add
247
+ model_object[:attrs] << attr_to_add
248
+ attrs << attr_to_add
249
+ end
136
250
  when :CloudObject
137
- return if objs_to_inspect.include?(attr_name) ||
138
- inspected_objects.include?(attr_name)
139
- # New object to inspect
140
- objs_to_inspect << attr_name
251
+ if _setup_identify_dep_init(dependencies, attr_name, new_path)
252
+ found_obj,
253
+ found_attrs,
254
+ found_deps_attrs = _setup_identify_deps(dependencies,
255
+ attr_name, new_path)
256
+ else
257
+ found_obj = dependencies.rh_get(:objects, attr_name, :objects)
258
+ found_attrs = dependencies.rh_get(:objects, attr_name, :attrs)
259
+ found_deps_attrs = []
260
+ end
261
+ deps_objects = (deps_objects + found_obj).uniq
262
+ deps_attrs = (deps_attrs + found_attrs + found_deps_attrs).uniq
263
+ model_object[:objects] << attr_name
264
+ objects << attr_name
265
+ end
266
+ [attrs, deps_attrs, objects, deps_objects]
267
+ end
268
+
269
+ def _setup_id_init_data_deps(dependencies, attr_name)
270
+ return if dependencies[:attributes].key?(attr_name)
271
+
272
+ dependencies.rh_set({}, :attributes, attr_name)
273
+ attr_name
274
+ end
275
+
276
+ # Internal setup function to display debug info related to
277
+ # _setup_identify_deps
278
+ def _sid_show_debug(level, str, attrs, objects)
279
+ data = []
280
+ data << format("'%s' attributes", attrs.join(', ')) if attrs.length > 0
281
+
282
+ data << format("'%s' objects", objects.join(', ')) if objects.length > 0
283
+
284
+ Lorj.debug(level, str, data.join(' and ')) if data.length > 0
285
+ end
286
+
287
+ # Internal setup function to build Attribute dependency array
288
+ # based on :depends_on
289
+ #
290
+ # def _setup_identify_attr_depends_on(_attr_name)
291
+ # attrs_dep = []
292
+ # num = 0
293
+
294
+ # dependencies[:attributes].each do |attr_name, dep|
295
+ # next unless dep.nil?
296
+ # attr_def = Lorj.data.auto_section_data(attr_name)
297
+
298
+ # dependency = []
299
+ # if attr_def && attr_def[:depends_on].is_a?(Array)
300
+ # dependency = attr_def[:depends_on].clone
301
+ # end
302
+ # attrs_dep[num] = dependency
303
+ # num += 1
304
+ # end
305
+
306
+ # attrs_dep
307
+ # end
308
+
309
+ # Internal setup function to Reorganize attributes order thanks following
310
+ # data definition:
311
+ #
312
+ # - :sections/<Section>/<Attribute>/:ask_sort: (FixNum)
313
+ # Defines the list of attributes that needs to be setup before.
314
+ #
315
+ # This function will first of all determine the order thanks
316
+ # to :ask_sort in the current step group.
317
+ def _setup_reorganize_steps_order(attrs, attr_groups)
318
+ attr_groups.collect do |attr_group|
319
+ index = attr_groups.index(attr_group)
320
+ Lorj.debug(3, "Step '%s' analyzing re-organisation", index)
321
+ res = _setup_reorganize_so_sort(attr_group)
322
+ res = _setup_reorganize_so_befaft(res)
323
+
324
+ old = attr_group.map { |attr| attr.keys[0] }
325
+ new = res.map { |attr| attr.keys[0] }
326
+ Lorj.debug(3, "Step '%s' reorganized from '%s' to '%s'",
327
+ index, old, new) unless old == new
328
+ attr_groups[index] = res
329
+ end
330
+
331
+ attrs_list = []
332
+ attrs.each do |k, v|
333
+ attrs_list << { k => v }
334
+ end
335
+ res = _setup_reorganize_so_sort(attrs_list)
336
+ res = _setup_reorganize_so_befaft(res)
337
+
338
+ old = attrs_list.map { |attr| attr.keys[0] }
339
+ new = res.map { |attr| attr.keys[0] }
340
+ Lorj.debug(3, "unordered reorganized from '%s' to '%s'",
341
+ old, new) unless old == new
342
+ res
343
+ end
344
+
345
+ # Internal setup function to re-organize thanks to :before and :after.
346
+ def _setup_reorganize_so_befaft(attr_group)
347
+ attrs = attr_group.map { |a| a.keys[0] }
348
+
349
+ attrs.clone.each do |attr_name|
350
+ meta = _get_meta_data(attr_name)
351
+ next if meta.nil?
352
+ next unless meta.rh_exist?(:after) || meta.rh_exist?(:before)
353
+
354
+ if _setup_reorganize_so_befaft_move?(:after, meta, attrs, attr_name)
355
+ _setup_reorganize_so_befaft_move(:after, meta, attrs, attr_group,
356
+ attr_name)
357
+ end
358
+
359
+ next unless _setup_reorganize_so_befaft_move?(:before, meta, attrs,
360
+ attr_name)
361
+
362
+ _setup_reorganize_so_befaft_move(:before, meta, attrs, attr_group,
363
+ attr_name)
364
+ end
365
+ attr_group
366
+ end
367
+
368
+ # return true if there is a need to move the element before/after
369
+ def _setup_reorganize_so_befaft_move?(where, meta, attrs, attr_name)
370
+ element = meta[where]
371
+ element = nil unless (attrs - [attr_name]).include?(element)
372
+
373
+ cur = attrs.index(attr_name)
374
+ return (element && cur < attrs.index(element)) if where == :after
375
+ (element && cur > attrs.index(element))
376
+ end
377
+
378
+ # Do the move
379
+ def _setup_reorganize_so_befaft_move(where, meta, attrs, attr_group,
380
+ attr_name)
381
+ old = attrs.index(attr_name)
382
+ ref = meta[where]
383
+ new = attrs.index(ref)
384
+
385
+ # Must be inserted after the attribute => + 1
386
+ new += 1 if where == :after
387
+
388
+ Lorj.debug(5, ":%s: Move '%s' %s '%s'"\
389
+ " - pos '%s' => pos '%s'",
390
+ where, attr_name, where, ref, old, new)
391
+
392
+ attrs.insert(new, attrs.delete(attr_name))
393
+ attr_group.insert(new, attr_group.delete(attr_group[old]))
394
+ end
395
+
396
+ # Internal setup function to re-organize thanks to :ask_sort
397
+ def _setup_reorganize_so_sort(attr_group)
398
+ attr_subgroups = []
399
+ attr_noorder = []
400
+
401
+ attr_group.each do |attr|
402
+ attr_def = attr[attr.keys[0]]
403
+ if attr_def[:ask_sort].is_a?(Fixnum)
404
+ Lorj.debug(4, "'%s' position requested is '%s'",
405
+ attr.key(attr_def), attr_def[:ask_sort])
406
+ if attr_subgroups[attr_def[:ask_sort]].nil?
407
+ attr_subgroups[attr_def[:ask_sort]] = [attr]
408
+ else
409
+ attr_subgroups[attr_def[:ask_sort]] << attr
410
+ end
411
+ else
412
+ attr_noorder << attr
413
+ end
414
+ end
415
+
416
+ attr_subgroups.flatten!
417
+
418
+ attr_subgroups + attr_noorder
419
+ end
420
+
421
+ # Internal setup function to re-organize steps as described by
422
+ # :setup/:steps section and data property :step
423
+ #
424
+ def _setup_reorganize_steps(steps_unordered, steps, setup_steps)
425
+ steps << steps_unordered
426
+
427
+ # build the steps attributes
428
+ build_steps = [{ :order => {} }]
429
+ build_step = 0
430
+ step_name = nil
431
+ # cur_step = build_steps[build_step]
432
+ Lorj.debug(3, "Building setup step position '%s'", build_step)
433
+
434
+ steps.each_index do |step_index|
435
+ steps[step_index].each do |attr|
436
+ attr_name = attr.keys[0]
437
+ attr_def = attr[attr_name]
438
+
439
+ unless attr_def[:setup]
440
+ Lorj.debug(2, "'%s' is ignored. configured with :account = '%s'",
441
+ attr_name, attr_def[:setup])
442
+ next
443
+ end
444
+
445
+ step_name = _setup_ros_get_step_name(attr_def)
446
+
447
+ next if _setup_ros_same_step_name(build_steps[build_step],
448
+ step_index, step_name, attr_name)
449
+
450
+ build_step = _setup_ros_set_step(:build_steps => build_steps,
451
+ :build_step => build_step,
452
+ :setup_steps => setup_steps,
453
+ :step_index => step_index,
454
+ :step_name => step_name,
455
+ :attr_name => attr_name)
456
+
457
+ _setup_ros_add_attribute(build_steps[build_step][:order],
458
+ step_index, attr_name)
459
+ end
460
+ end
461
+
462
+ if build_steps.last[:name].nil?
463
+ _setup_ros_set_step(:build_steps => build_steps,
464
+ :setup_steps => setup_steps,
465
+ :step_index => steps.length - 1,
466
+ :attr_name => :default)
467
+ end
468
+
469
+ build_steps
470
+ end
471
+
472
+ # Internal function to get the step name to use
473
+ def _setup_ros_get_step_name(attr_def)
474
+ return nil if attr_def[:ask_step].nil?
475
+
476
+ step_name = attr_def[:ask_step]
477
+ step_name = step_name.to_s if step_name.is_a?(Fixnum)
478
+ step_name
479
+ end
480
+
481
+ # Internal function checking if the step is already assigned
482
+ def _setup_ros_same_step_name(build_step, stepo_index, step_name, attr_name)
483
+ return false unless step_name.nil? || build_step[:name] == step_name
484
+
485
+ _setup_ros_add_attribute(build_step[:order], stepo_index, attr_name)
486
+ true
487
+ end
488
+
489
+ # Internal function to add an attribute to the step_order structure.
490
+ def _setup_ros_add_attribute(step_order, stepo_index, attr_name)
491
+ step_order[stepo_index] = [] if step_order[stepo_index].nil?
492
+ step_order[stepo_index] << attr_name
493
+ end
494
+
495
+ # attr_steps.reject! { |group| group.length == 0 }
496
+
497
+ # Set the step data to the current order built.
498
+ #
499
+ # If the step has already been set, a warning is printed.
500
+ #
501
+ # * *returns*:
502
+ # - +Return the
503
+ #
504
+ def _setup_ros_set_step(params)
505
+ build_steps = params[:build_steps]
506
+ build_step = params[:build_step]
507
+ step_index = params[:step_index]
508
+ step_name = params[:step_name]
509
+ attr_name = 'of latest step'
510
+ attr_name = params[:attr_name] unless params[:attr_name].nil?
511
+
512
+ setup_step = params[:setup_steps][step_name]
513
+ if setup_step.nil?
514
+ PrcLib.warning("Attribute '%s': Setup step '%s' is not defined.",
515
+ attr_name, step_name)
516
+ return build_step
517
+ end
518
+
519
+ step_found = _setup_ros_find_buildstep(build_steps, step_index,
520
+ step_name)
521
+
522
+ return build_step if step_found.nil?
523
+
524
+ if step_found == build_steps.length
525
+ step = { :order => { step_index => [] } }
526
+ Lorj.debug(3, "Building setup step position '%s'", step_found)
527
+ build_steps << step
528
+ end
529
+
530
+ build_step = step_found
531
+ step = build_steps[build_step]
532
+
533
+ return build_step if step[:name] == step_name
534
+
535
+ step[:name] = step_name
536
+
537
+ step[:desc] = setup_step[:desc]
538
+ step[:explanation] = setup_step[:explanation]
539
+ step[:pre_step_handler] = setup_step[:pre_step_function]
540
+ step[:post_step_handler] = setup_step[:post_step_function]
541
+
542
+ Lorj.debug(3, "Requested by '%s' attribute, setup step position '%s'"\
543
+ " is assigned to '%s'", attr_name,
544
+ build_steps.length - 1, step_name)
545
+ build_step
546
+ end
547
+
548
+ # Internal Function searching in build_steps a step_name.
549
+ # If found, the index is kept.
550
+ #
551
+ # If found, it compares the index with the current step_index
552
+ # It will be considered valid if the step index has a
553
+ # step_index in :orders or if the step_index - 1 is the found
554
+ # in the last known step.
555
+ #
556
+ # If the step is not found, the index returned will be a new index
557
+ # to create in build_steps.
558
+ #
559
+ # * *args*:
560
+ # - +build_steps+ : List of build_steps already identified.
561
+ # - +step_order_index+ : current step order index to add an attribute to.
562
+ # - +step_name+ : Step name to search.
563
+ #
564
+ # * *return*:
565
+ # - +index+ : It returns a correct build_step to use.
566
+ # - *nil* : nil if there is no valid index to return.
567
+ #
568
+ def _setup_ros_find_buildstep(build_steps, step_order_index,
569
+ searched_step_name)
570
+ return 0 if step_order_index == 0
571
+
572
+ step_found = nil
573
+ last_step_order_found = nil
574
+
575
+ build_steps.each_index do |i|
576
+ step_found = i if build_steps[i][:name] == searched_step_name
577
+ if build_steps[i][:order].include?(step_order_index - 1)
578
+ last_step_order_found = i
579
+ end
580
+ end
581
+
582
+ return build_steps.length if step_found.nil?
583
+
584
+ if step_found
585
+ if build_steps[step_found][:order].include?(step_order_index)
586
+ return step_found
587
+ end
588
+
589
+ return step_found if step_found >= last_step_order_found
590
+ end
591
+
592
+ PrcLib.warning("Unable to set step '%s' at position %s. "\
593
+ "This step is already at position '%s'. "\
594
+ "Due to attribute dependencies, attribute '%' cannot be"\
595
+ ' asked at that step. Please correct the process'\
596
+ ' definition. ',
597
+ step_name, build_steps.index(e),
598
+ build_steps.length - 1, attr_name)
599
+ nil
600
+ end
601
+ # Internal setup function to complete the list of attributes
602
+ # and organize attributes by step as requested by attr dependencies
603
+ # It loops on a list of unanalyzed attributes to determine
604
+ # new objects/attributes
605
+ #
606
+ # The analyze is based on attributes having setup obj (and group of deps)
607
+ # required
608
+ #
609
+ # A new level is added:
610
+ # - when a new group of deps is found.
611
+ # Then each attributes required decrease the level by 1.
612
+ # - when a group already exist, nothing is done
613
+ #
614
+ # In case of depends_on, the attribute level is set to 0
615
+ # and depends_on attributes level are decreased.
616
+ # each attributes parsed get a level 0 if requires a group is requires
617
+ # otherwise level is set to nil.
618
+ def _setup_build_steps_from(dependencies)
619
+ count = dependencies[:attributes].length
620
+ attrs_done = {}
621
+ level = 0
622
+ loop do
623
+ attrs = dependencies[:attributes].clone
624
+ attrs.each_key do |attr_name|
625
+ next if attrs_done.key?(attr_name)
626
+
627
+ element = _setup_bs_objs(attrs_done, dependencies, attr_name)
628
+
629
+ attrs_done[attr_name] = element
630
+ dependencies.rh_set(element, :attributes, attr_name)
631
+
632
+ if element.rh_exist?(:group, :level)
633
+ level = [level, element[:group][:level]].min
634
+ end
635
+
636
+ list_attrs = (element[:attrs] + element.rh_get(:group, :attrs)).uniq
637
+
638
+ next if list_attrs.length == 0
639
+ Lorj.debug(2, "setup: '%s' attribute dependency found '%s'",
640
+ attr_name, list_attrs.join(', '))
641
+ end
642
+ break if dependencies[:attributes].length == count
643
+ count = dependencies[:attributes].length
644
+ end
645
+
646
+ # Thanks to attributes/group deps, set levels
647
+ attrs = dependencies[:attributes]
648
+ _setup_bs_levels(attrs, level)
649
+ end
650
+
651
+ # Uses attributes level detected to initialize the list of steps/orders.
652
+ def _setup_bs_levels(attrs, level)
653
+ steps = [[]]
654
+ pos = 0
655
+
656
+ loop do
657
+ attrs.clone.each do |k, v|
658
+ cur_level = v.rh_get(:group, :level)
659
+ next unless cur_level == level
660
+
661
+ attrs_to_add = v[:attrs] + v.rh_get(:group, :attrs).uniq
662
+ attrs.reject! do |attr, _|
663
+ res = attrs_to_add.include?(attr)
664
+ steps[pos] << { attr => attrs[attr] } if res
665
+ res
666
+ end
667
+
668
+ steps[pos + 1] = [] if steps[pos + 1].nil?
669
+
670
+ steps[pos + 1] << { k => attrs[k] }
671
+ attrs.reject! { |attr, _| attr == k }
672
+ end
673
+ level += 1
674
+ break if level == 0
675
+ pos += 1
676
+ end
677
+ steps
678
+ end
679
+
680
+ # Internal setup function to build the list of attribute deps
681
+ #
682
+ def _setup_bs_objs(attrs_done, dependencies, attr_name)
683
+ # Check if this attr requires an object query.
684
+ Lorj.debug(2, "-- Checking '%s' attribute --", attr_name)
685
+ data = Lorj.data.auto_section_data(attr_name)
686
+
687
+ element = _setup_bs_objs_init(data)
688
+
689
+ return element if data.nil?
690
+
691
+ return element unless data.rh_exist?(:list_values, :object) ||
692
+ data[:depends_on].is_a?(Array)
693
+
694
+ _setup_bs_list_query(dependencies, attr_name, data, element)
695
+
696
+ # ensure attribute dependency is dynamically added to the list of
697
+ # object deps.
698
+ _setup_bs_objs_new_dep(dependencies, attr_name, element)
699
+
700
+ element[:depends_on] = []
701
+ element[:depends_on] = data[:depends_on] if data[:depends_on].is_a?(Array)
702
+
703
+ group = element[:group]
704
+ if group[:objs].length > 0
705
+ _setup_set_group_level(dependencies, attrs_done, attr_name,
706
+ group, group[:objs].sort)
707
+ else
708
+ _setup_set_group_level(dependencies, attrs_done, attr_name,
709
+ group, [element[:obj]])
710
+ end
711
+
712
+ _setup_attrs_depends_on(dependencies, attr_name,
713
+ element[:depends_on])
714
+
715
+ element
716
+ end
717
+
718
+ # Initialize attribute element.
719
+ def _setup_bs_objs_init(data)
720
+ element = { :obj => nil, :setup => false, :ask_sort => nil,
721
+ :group => { :objs => [], :attrs => [] },
722
+ :attrs => [], :ask_step => nil }
723
+
724
+ return element if data.nil?
725
+
726
+ element[:setup] = data[:account] if data.rh_get(:account).boolean?
727
+ if data.rh_get(:ask_sort).is_a?(Fixnum)
728
+ element[:ask_sort] = data[:ask_sort]
729
+ end
730
+
731
+ element[:ask_step] = data[:ask_step]
732
+
733
+ element
734
+ end
735
+
736
+ # Internal function to verify if any attributes adds an object dependency
737
+ #
738
+ def _setup_bs_objs_new_dep(dependencies, parent_attr_name, element)
739
+ attrs = (element[:attrs] + element[:group][:attrs]).uniq
740
+ return if attrs.length == 0
741
+
742
+ found = false
743
+
744
+ attrs.each do |attr_name|
745
+ obj_found = dependencies.rh_get(:attributes, attr_name, :obj)
746
+ next if obj_found.nil?
747
+
748
+ objs = element.rh_get(:group, :objs)
749
+ next if objs.include?(obj_found)
750
+
751
+ dep = dependencies.rh_get(:attributes, attr_name)
752
+ # Updating list of deps objs
753
+ objs << obj_found
754
+ objs = (objs + dep[:group][:objs]).uniq
755
+ element.rh_set(objs, :group, :objs)
756
+
757
+ # Undating list of deps attributes
758
+ attrs = element.rh_get(:group, :attrs)
759
+ attrs_found = (attrs + dep[:attrs] + dep[:group][:attrs]).uniq
760
+ element.rh_set(attrs_found, :group, :attrs)
761
+
762
+ Lorj.debug(4, "attr setup '%s': '%s' dependent attribute adds a new"\
763
+ " object dependency '%s'",
764
+ parent_attr_name, attr_name, obj_found)
765
+ found = true
766
+ end
767
+ return unless found
768
+
769
+ Lorj.debug(5, "attr setup '%s': query '%s' (+ %s) and "\
770
+ "requires '%s' (+ %s)", parent_attr_name,
771
+ element[:obj], element[:group][:objs],
772
+ element[:attrs], element[:group][:attrs] - element[:attrs])
773
+ end
774
+
775
+ def _setup_bs_list_query(dependencies, attr_name, data, element)
776
+ return unless data.rh_exist?(:list_values, :object)
777
+
778
+ element[:obj] = data.rh_get(:list_values, :object)
779
+ element[:group] = _setup_bs_objs_deps(dependencies, element[:obj])
780
+
781
+ _object_params_event(element[:obj], :query_e, :data).each do |attr_obj|
782
+ element[:attrs] << attr_obj.key_tree
783
+ end
784
+ Lorj.debug(5, "attr setup '%s': query '%s' (+ %s) and "\
785
+ "requires '%s' (+ %s)", attr_name,
786
+ element[:obj], element[:group][:objs],
787
+ element[:attrs], element[:group][:attrs] - element[:attrs])
788
+ end
789
+
790
+ # Function to initialize the level to 0 for the current attribute.
791
+ # def _setup_set_level(dependencies, attr_name)
792
+ # level = dependencies.rh_get(:attributes, attr_name, :level)
793
+ # if level.nil?
794
+ # level = 0
795
+ # else
796
+ # level += -1
797
+ # end
798
+ # level
799
+ # end
800
+
801
+ # Function to increase level (-1) for the list of attributes.
802
+ # def _setup_increase_level(dependencies, attrs)
803
+ # attrs.each do |a|
804
+ # unless dependencies.rh_exist?(:attributes, a, :level)
805
+ # dependencies.rh_set(0, :attributes, a, :level)
806
+ # end
807
+ # dependencies[:attributes][a][:level] += -1
808
+ # end
809
+ # end
810
+
811
+ # Function parsing the attrs_done to found equivalent group or subgroup.
812
+ #
813
+ # - 1. search for equivalent group
814
+ # copy the equivalent group level to the group tested.
815
+ # return if updated
816
+ #
817
+ # - 2. search for any subgroup already treated.
818
+ # The group will get the highest level
819
+ # each subgroup (part of the new group) will be decreased.
820
+ # return if updated
821
+ #
822
+ # - 3. loop in groups if the tested group is a subgroup of an existing group
823
+ # The group tested will get the lowest group level - 1
824
+ # return if updated
825
+ #
826
+ # - 4. the group level is set with -1
827
+ #
828
+ def _setup_set_group_level(dependencies, attrs_done, attr_name, group, objs)
829
+ return unless _setup_set_group_case1(attrs_done, attr_name, group, objs)
830
+ return unless _setup_set_group_case2(dependencies, attrs_done,
831
+ attr_name, group, objs)
832
+
833
+ _setup_set_group_case34(attrs_done, attr_name, group, objs)
834
+ end
835
+
836
+ # Case 1 - equivalent group?
837
+ def _setup_set_group_case1(attrs_done, attr_name, group, objs)
838
+ attrs_done.each do |k, v|
839
+ next unless v.rh_get(:group, :objs) == objs ||
840
+ (v.rh_get(:group, :objs).length == 0 && v[:obj] == objs[0])
841
+
842
+ group[:level] = v.rh_get(:group, :level)
843
+ Lorj.debug(5, "attr setup '%s': Equivalent: '%s' group level set to %s",
844
+ attr_name, k, group[:level])
845
+ return false
846
+ end
847
+ true
848
+ end
849
+
850
+ # case 2 - existing group found as subgroup?
851
+ def _setup_set_group_case2(dependencies, attrs_done, attr_name, group, objs)
852
+ attr_subgroups = []
853
+ level = nil
854
+ attrs_done.each do |k, v|
855
+ group_to_check = v.rh_get(:group, :objs)
856
+ next if objs - [v[:obj]] == objs && objs - group_to_check == objs
857
+
858
+ attr_subgroups << k
859
+ if level.nil?
860
+ level = v.rh_get(:group, :level)
861
+ else
862
+ level = [v.rh_get(:group, :level), level].max
863
+ end
864
+ end
865
+
866
+ if level
867
+ group[:level] = level
868
+ Lorj.debug(5, "attr setup '%s': group level set to %s",
869
+ attr_name, group[:level])
870
+
871
+ attr_subgroups.each do |v|
872
+ group = dependencies.rh_get(:attributes, v, :group)
873
+ group[:level] += -1
874
+ Lorj.debug(5, "attr setup '%s': attribute subgroup '%s' level"\
875
+ ' decreased: group level set to %s',
876
+ attr_name, v, group[:level])
877
+ end
878
+ return false
879
+ end
880
+ true
881
+ end
882
+
883
+ # case 3 - Is a subgroup of existing group?
884
+ def _setup_set_group_case34(attrs_done, attr_name, group, objs)
885
+ group[:level] = -1 # default is case 4 - new group!
886
+
887
+ attrs_done.each_value do |v|
888
+ group_to_check = v.rh_get(:group, :objs)
889
+ next if group_to_check - objs == group_to_check
890
+
891
+ group[:level] = [group[:level], v.rh_get(:group, :level) - 1].min
892
+ end
893
+ Lorj.debug(5, "attr setup '%s': group level set to '%s'",
894
+ attr_name, group[:level])
895
+ end
896
+
897
+ # Internal setup function - Extract object data information.
898
+ def _setup_bs_objs_deps(dependencies, object_type)
899
+ group = { :objs => nil, :attrs => nil }
900
+ if dependencies.rh_exist?(:objects, object_type)
901
+ group[:objs], group[:attrs] = _setup_objects_attr_needs(dependencies,
902
+ object_type)
903
+ return group
904
+ end
905
+ _setup_identify_dep_init(dependencies, object_type)
906
+ group[:objs], _, group[:attrs] = _setup_identify_deps(dependencies,
907
+ object_type)
908
+ group
909
+ end
910
+
911
+ # Internal setup function - Build list of ALL attributes required for an
912
+ # object
913
+ #
914
+ # It navigates on loaded dependencies to build the list of attributes.
915
+ def _setup_objects_attr_needs(dependencies, object_type)
916
+ attrs = []
917
+
918
+ objects = dependencies.rh_get(:objects, object_type, :objects)
919
+ deps_objects = []
920
+ objects.each do |o|
921
+ attrs += dependencies.rh_get(:objects, o, :attrs)
922
+ found_obj, deps_attrs = _setup_objects_attr_needs(dependencies, o)
923
+ attrs += deps_attrs
924
+ deps_objects += found_obj
925
+ end
926
+ [(objects + deps_objects).uniq, attrs.uniq]
927
+ end
928
+
929
+ # Internal setup function - Identify a list of attributes from depends_on
930
+ #
931
+ # If the attribute has a object dependency, attributes attached are added.
932
+ # If the object has depends_on
933
+ def _setup_attrs_depends_on(dependencies, attr_name, attrs)
934
+ return [] unless attrs.is_a?(Array) && attrs.length > 0
935
+
936
+ result = []
937
+
938
+ Lorj.debug(3, "%s: depends on added '%s'", attr_name, attrs.join(', '))
939
+
940
+ attrs.each do |a|
941
+ data = Lorj.data.auto_section_data(a)
942
+
943
+ if data.rh_exist?(:list_values, :object)
944
+ element = _setup_bs_objs_deps(dependencies,
945
+ data.rh_get(:list_values, :object))
946
+ element[:attrs] += dependencies.rh_get(:object, element[:obj], :attrs)
947
+ else
948
+ element = { :attrs => [] }
949
+ end
950
+
951
+ if data[:depends_on].is_a?(Array)
952
+ element[:depends_on] = _setup_attrs_depends_on(dependencies, a,
953
+ data[:depends_on])
954
+ else
955
+ element[:depends_on] = []
956
+ end
957
+
958
+ attrs_list = element[:attrs] + element[:depends_on]
959
+ attrs_list.uniq!
960
+ result += attrs_list
141
961
  end
962
+ result.uniq
142
963
  end
964
+ # rubocop: enable Metrics/CyclomaticComplexity
965
+
966
+ # Internal setup function - set/get dep level for an object
967
+ # def _setup_build_so_obj_level(obj_data_level, name, order_index)
968
+ # return 0 if name.nil?
969
+
970
+ # return obj_data_level[name] if obj_data_level.key?(name)
971
+
972
+ # obj_data_level[name] = order_index
973
+ # end
974
+
975
+ # Internal setup function initializing a model object
976
+ #
977
+ def _setup_identify_dep_init(dependencies, object_type, path = [])
978
+ if path.include?(object_type)
979
+ PrcLib.warning('Loop detection: Be careful! a loop is detected with'\
980
+ " the dependency from '%s' to '%s'. "\
981
+ 'Dependency ignored.', path.join('/'), object_type)
982
+ return false
983
+ end
984
+
985
+ return false if dependencies.rh_exist?(:objects, object_type)
986
+
987
+ model_object = { :objects => [], :objects_attrs => [], :attrs => [] }
988
+
989
+ dependencies.rh_set(model_object, :objects, object_type)
990
+ true
991
+ end
992
+
993
+ # Internal setup function to identify data to ask
994
+ # Navigate through objects dependencies to determine the need.
995
+ # def _setup_identify_obj_params(setup_steps,
996
+ # inspected_objects, objs_to_inspect,
997
+ # attr_name, attr_params)
998
+
999
+ # attr_type = attr_params[:type]
1000
+
1001
+ # case attr_type
1002
+ # when :data
1003
+ # return unless _setup_obj_param_is_data(setup_steps,
1004
+ # inspected_objects, attr_name)
1005
+ # inspected_objects << attr_name
1006
+ # return
1007
+ # when :CloudObject
1008
+ # return if objs_to_inspect.include?(attr_name) ||
1009
+ # inspected_objects.include?(attr_name)
1010
+ # # New object to inspect
1011
+ # objs_to_inspect << attr_name
1012
+ # end
1013
+ # end
143
1014
 
144
1015
  def _setup_display_step(setup_step, step)
145
1016
  Lorj.debug(2, 'Ask step %s:', step)
@@ -211,7 +1082,7 @@ module Lorj
211
1082
 
212
1083
  order_array = setup_steps[iStep][:order]
213
1084
 
214
- order_array.each_index do |iIndex|
1085
+ order_array.each_key do |iIndex|
215
1086
  Lorj.debug(2, 'Ask order %s:', iIndex)
216
1087
  order_array[iIndex].each do |data|
217
1088
  options = _get_meta_data(data)