lorj 1.0.10 → 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)