diff options
| author | Robert Dober | 2017-10-09 16:57:57 +0200 |
|---|---|---|
| committer | GitHub | 2017-10-09 16:57:57 +0200 |
| commit | 0e0f0c802f30f073a8bfa05952f071af424f8d37 (patch) | |
| tree | 03c89d7f230c59f0cc2c6170dd9c4890fe88a151 /lib/compliance_control_set_copier.rb | |
| parent | e94f6f64a1c75cee5155b86c3d0fa51ac26ac96e (diff) | |
| parent | a8f97544fd048d50ef63dae1c74cff2de077152d (diff) | |
| download | chouette-core-0e0f0c802f30f073a8bfa05952f071af424f8d37.tar.bz2 | |
Merge pull request #84 from af83/4629-comp-check-set-creation
4629 comp check set creation *Mergeable*
Diffstat (limited to 'lib/compliance_control_set_copier.rb')
| -rw-r--r-- | lib/compliance_control_set_copier.rb | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/lib/compliance_control_set_copier.rb b/lib/compliance_control_set_copier.rb new file mode 100644 index 000000000..b015735c2 --- /dev/null +++ b/lib/compliance_control_set_copier.rb @@ -0,0 +1,126 @@ +# We use a class instead of a singleton object because we will use +# a cache to avoid copying instancs of ComplianceControl twice as they +# might be reachable from CCSet **and** its descendent CCBlock. +# More generally spoken, we copy a DAG, not a tree. +class ComplianceControlSetCopier + + # Naming Convention: As we are in a domain with quite long names we + # abbreviate compliance_control to cc and + # compliance_check to cck iff used as prefixes. + + attr_reader :cc_set_id, :referential_id + + def copy cc_set_id, referential_id + @cc_set_id = cc_set_id + @referential_id = referential_id + check_organisation_coherence! + copy_dag + end + + + private + + + # Workers + # ------- + def check_organisation_coherence! + return true if cc_set.organisation_id == referential.organisation_id + raise ArgumentError, "Incoherent organisation of referential" + end + + def copy_dag + # Assure cck_set's existance, just in case cc_set is _empty_. + cck_set + make_cck_blocks + make_ccks_from_ccs + end + + def make_all_cck_block_children cc_block, cck_block + cc_block + .compliance_controls + .each{ |compliance_control| make_compliance_check(compliance_control, cck_block.id) } + end + def make_cck_block cc_block + cck_block = + cck_set.compliance_check_blocks.create( + name: name_with_refid(cc_block.name)) + + make_all_cck_block_children cc_block, cck_block + end + def make_cck_blocks + cc_set.compliance_control_blocks.each(&method(:make_cck_block)) + end + + def make_ccks_from_ccs + cc_set.compliance_controls.each(&method(:make_compliance_check)) + end + + def make_compliance_check(compliance_control, cck_block_id = nil) + already_there = get_from_cache compliance_control.id + # We do not want to impose traversal order of the DAG, that would + # make the code more resistant to change... + # So we check if we need to update the compliance_check_block_id + # of a control found in the cache. + # N.B. By traversing the indirect descendents from a set first, + # or IOW, traversing the control_blocks before the controls, + # this check could go away and we could return from the + # method in case of a cache hit. + if already_there + # Purely defensive: + if already_there.compliance_check_block_id.nil? && cck_block_id + already_there.update compliance_check_block_id: cck_block_id + end + return + end + make_compliance_check!(compliance_control, cck_block_id) + end + def make_compliance_check!(compliance_control, cck_block_id) + add_to_cache( + compliance_control.id, + cck_set.compliance_checks.create( + criticity: compliance_control.criticity, + name: name_with_refid(compliance_control.name), + code: compliance_control.code, + origin_code: compliance_control.origin_code, + compliance_check_block_id: cck_block_id + )) + end + + def name_with_refid name + [name, referential.name].join('-') + end + + # Lazy Values + # ----------- + def cc_set + @__cc_set__ ||= ComplianceControlSet.find(cc_set_id) + end + def cck_set + @__cck_set__ ||= ComplianceCheckSet.create!( + compliance_control_set_id: cc_set_id, + referential_id: referential_id, + workbench_id: referential.workbench_id, + name: name_with_refid(cc_set.name), + status: 'new' + ) + end + def referential + @__referential__ ||= Referential.find(referential_id) + end + + # Copy Cache + # ---------- + def add_to_cache key, obj + # Right now we map key -> obj, in case memory consumption becomes too important + # we can map key -> obj.id and fetch the object from the database in get_from_cache + # (time vs. space tradeoff) + copy_cache.merge!(key => obj) + end + def get_from_cache key + copy_cache[key].tap do | ele | + end + end + def copy_cache + @__copy_cache__ ||= Hash.new + end +end |
