tech(remaniement): extraction du passage d'une liste de champs a un simili arbre

This commit is contained in:
Martin 2023-04-11 13:53:59 +02:00 committed by mfo
parent ad77b9321b
commit 3b5d1bd55b
4 changed files with 186 additions and 13 deletions

View file

@ -21,6 +21,17 @@
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::HeaderSectionChamp < Champ 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 def search_terms
# The user cannot enter any information here so it doesnt make much sense to search # The user cannot enter any information here so it doesnt make much sense to search
end end

View file

@ -1,4 +1,4 @@
module Champs::Treeable module TreeableConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
MAX_DEPTH = 6 # deepest level for header_sections is 3. MAX_DEPTH = 6 # deepest level for header_sections is 3.
@ -16,27 +16,22 @@ module Champs::Treeable
# are added to the most_recent_subtree # are added to the most_recent_subtree
# given a root_depth at 0, we build a full tree # given a root_depth at 0, we build a full tree
# given a root_depth > 0, we build a partial tree (aka, a repetition) # given a root_depth > 0, we build a partial tree (aka, a repetition)
def to_tree(champs:, root_depth:, build_champs_subtree_component:) def to_tree(champs:, root_depth:)
root = build_champs_subtree_component(header_section: nil) root = []
depth_cache = Array.new(MAX_DEPTH) depth_cache = Array.new(MAX_DEPTH)
depth_cache[root_depth] = root depth_cache[root_depth] = root
most_recent_subtree = root most_recent_subtree = root
champs.each do |champ| champs.each do |champ|
if champ.header_section? if champ.header_section?
champs_subtree = build_champs_subtree_component(header_section: champ) champs_subtree = [champ]
depth_cache[champs_subtree.level - 1].add_node(champs_subtree) depth_cache[champ.level - 1].push(champs_subtree)
most_recent_subtree = depth_cache[champs_subtree.level] = champs_subtree most_recent_subtree = depth_cache[champ.level] = champs_subtree
else else
most_recent_subtree.add_node(champ) most_recent_subtree.push(champ)
end end
end end
root root
end end
# must be implemented to render subtree
def build_champs_subtree_component(header_section:)
raise NotImplementedError
end
end end
end end

View file

@ -413,9 +413,14 @@ class TypeDeChamp < ApplicationRecord
end end
def header_section_level_value def header_section_level_value
header_section_level.presence || 1 if header_section_level.presence
header_section_level.to_i
else
1
end
end end
def previous_section_level(upper_tdcs) def previous_section_level(upper_tdcs)
previous_header_section = upper_tdcs.reverse.find(&:header_section?) previous_header_section = upper_tdcs.reverse.find(&:header_section?)

View file

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