From 3b5d1bd55b48f36a6c607ca69b1b5752b9d7cbc1 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 11 Apr 2023 13:53:59 +0200 Subject: [PATCH] tech(remaniement): extraction du passage d'une liste de champs a un simili arbre --- app/models/champs/header_section_champ.rb | 11 ++ .../concerns/treeable_concern.rb} | 19 +- app/models/type_de_champ.rb | 7 +- spec/models/concern/treeable_concern_spec.rb | 162 ++++++++++++++++++ 4 files changed, 186 insertions(+), 13 deletions(-) rename app/{components/champs/treeable.rb => models/concerns/treeable_concern.rb} (63%) create mode 100644 spec/models/concern/treeable_concern_spec.rb diff --git a/app/models/champs/header_section_champ.rb b/app/models/champs/header_section_champ.rb index 4185db524..59e4b5755 100644 --- a/app/models/champs/header_section_champ.rb +++ b/app/models/champs/header_section_champ.rb @@ -21,6 +21,17 @@ # type_de_champ_id :integer # class Champs::HeaderSectionChamp < Champ + + def level + if parent.present? + header_section_level_value.to_i + parent.current_section_level + elsif header_section_level_value + header_section_level_value.to_i + else + 0 + end + end + def search_terms # The user cannot enter any information here so it doesn’t make much sense to search end diff --git a/app/components/champs/treeable.rb b/app/models/concerns/treeable_concern.rb similarity index 63% rename from app/components/champs/treeable.rb rename to app/models/concerns/treeable_concern.rb index dc47e7c6d..3f269e11e 100644 --- a/app/components/champs/treeable.rb +++ b/app/models/concerns/treeable_concern.rb @@ -1,4 +1,4 @@ -module Champs::Treeable +module TreeableConcern extend ActiveSupport::Concern MAX_DEPTH = 6 # deepest level for header_sections is 3. @@ -16,27 +16,22 @@ module Champs::Treeable # are added to the most_recent_subtree # given a root_depth at 0, we build a full tree # given a root_depth > 0, we build a partial tree (aka, a repetition) - def to_tree(champs:, root_depth:, build_champs_subtree_component:) - root = build_champs_subtree_component(header_section: nil) + def to_tree(champs:, root_depth:) + root = [] depth_cache = Array.new(MAX_DEPTH) depth_cache[root_depth] = root most_recent_subtree = root champs.each do |champ| if champ.header_section? - champs_subtree = build_champs_subtree_component(header_section: champ) - depth_cache[champs_subtree.level - 1].add_node(champs_subtree) - most_recent_subtree = depth_cache[champs_subtree.level] = champs_subtree + champs_subtree = [champ] + depth_cache[champ.level - 1].push(champs_subtree) + most_recent_subtree = depth_cache[champ.level] = champs_subtree else - most_recent_subtree.add_node(champ) + most_recent_subtree.push(champ) end end root end - - # must be implemented to render subtree - def build_champs_subtree_component(header_section:) - raise NotImplementedError - end end end diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 0d36dece3..e7c521de5 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -413,9 +413,14 @@ class TypeDeChamp < ApplicationRecord end def header_section_level_value - header_section_level.presence || 1 + if header_section_level.presence + header_section_level.to_i + else + 1 + end end + def previous_section_level(upper_tdcs) previous_header_section = upper_tdcs.reverse.find(&:header_section?) diff --git a/spec/models/concern/treeable_concern_spec.rb b/spec/models/concern/treeable_concern_spec.rb new file mode 100644 index 000000000..c2757a3a2 --- /dev/null +++ b/spec/models/concern/treeable_concern_spec.rb @@ -0,0 +1,162 @@ +describe TreeableConcern do + class ChampsToTree + include TreeableConcern + attr_reader :root + def initialize(champs:, root_depth:) + @root = to_tree(champs:, root_depth:) + end + end + + subject { ChampsToTree.new(champs: champs, root_depth: 0).root } + describe "to_tree" do + let(:header_1) { build(:champ_header_section_level_1) } + let(:header_1_2) { build(:champ_header_section_level_2) } + let(:header_2) { build(:champ_header_section_level_1) } + let(:champ_text) { build(:champ_text) } + let(:champ_textarea) { build(:champ_textarea) } + let(:champ_explication) { build(:champ_explication) } + let(:champ_communes) { build(:champ_communes) } + + context 'without section' do + let(:champs) do + [ + champ_text, champ_textarea + ] + end + it 'inlines champs at root level' do + expect(subject.size).to eq(champs.size) + expect(subject).to eq(champs) + end + end + + context 'with header_section and champs' do + let(:champs) do + [ + header_1, + champ_explication, + champ_text, + header_2, + champ_textarea + ] + end + + it 'wraps champs within preview header section' do + expect(subject.size).to eq(2) + expect(subject).to eq([ + [ header_1, champ_explication, champ_text ], + [ header_2, champ_textarea ] + ]) + end + end + + context 'leading champs, and in between sections only' do + let(:champs) do + [ + champ_text, + champ_textarea, + header_1, + champ_explication, + champ_communes, + header_2, + champ_textarea + ] + end + it 'chunk by uniq champs' do + expect(subject.size).to eq(4) + expect(subject).to eq([ + champ_text, + champ_textarea, + [header_1, champ_explication, champ_communes], + [header_2, champ_textarea] + ]) + end + end + + context 'with one sub sections' do + let(:champs) do + [ + header_1, + champ_explication, + header_1_2, + champ_communes, + header_2, + champ_textarea + ] + end + it 'chunk by uniq champs' do + expect(subject.size).to eq(2) + expect(subject).to eq([ + [header_1, champ_explication, [header_1_2, champ_communes]], + [header_2, champ_textarea] + ]) + end + end + + context 'with consecutive subsection' do + let(:header_1) { build(:champ_header_section_level_1) } + let(:header_1_2_1) { build(:champ_header_section_level_2) } + let(:header_1_2_2) { build(:champ_header_section_level_2) } + let(:header_1_2_3) { build(:champ_header_section_level_2) } + let(:champs) do + [ + header_1, + header_1_2_1, + champ_text, + header_1_2_2, + champ_textarea, + header_1_2_3, + champ_communes + ] + end + it 'chunk by uniq champs' do + expect(subject.size).to eq(1) + expect(subject).to eq([ + [ + header_1, + [header_1_2_1, champ_text], + [header_1_2_2, champ_textarea], + [header_1_2_3, champ_communes] + ] + ]) + end + end + + context 'with one sub sections and one subsub section' do + let(:header_1_2_3) { build(:champ_header_section_level_3) } + + let(:champs) do + [ + header_1, + champ_explication, + header_1_2, + champ_communes, + header_1_2_3, + champ_text, + header_2, + champ_textarea + ] + end + + it 'chunk by uniq champs' do + expect(subject.size).to eq(2) + expect(subject).to eq([ + [ + header_1, + champ_explication, + [ + header_1_2, + champ_communes, + [ + header_1_2_3, champ_text + ] + ] + ], + [ + header_2, + champ_textarea + ] + ]) + end + end + end +end