From fcb9b55bc4103d1f6842e92e3004c2908488f003 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Fri, 9 Sep 2022 15:36:50 +0200 Subject: [PATCH] feat(conditional): add include operator --- .../conditions_component.rb | 6 +++- app/models/logic.rb | 10 +++++-- app/models/logic/champ_value.rb | 8 ++++- app/models/logic/include_operator.rb | 29 +++++++++++++++++++ config/locales/models/logic/fr.yml | 1 + spec/models/logic/include_operator_spec.rb | 20 +++++++++++++ spec/models/logic_spec.rb | 8 +++++ 7 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 app/models/logic/include_operator.rb create mode 100644 spec/models/logic/include_operator_spec.rb diff --git a/app/components/types_de_champ_editor/conditions_component.rb b/app/components/types_de_champ_editor/conditions_component.rb index d728ad889..1d1cfd74a 100644 --- a/app/components/types_de_champ_editor/conditions_component.rb +++ b/app/components/types_de_champ_editor/conditions_component.rb @@ -119,6 +119,10 @@ class TypesDeChampEditor::ConditionsComponent < ApplicationComponent [t('is', scope: 'logic'), Eq.name], [t('is_not', scope: 'logic'), NotEq.name] ] + when ChampValue::CHAMP_VALUE_TYPE.fetch(:enums) + [ + [t(IncludeOperator.name, scope: 'logic.operators'), IncludeOperator.name] + ] when ChampValue::CHAMP_VALUE_TYPE.fetch(:number) [Eq, LessThan, GreaterThan, LessThanEq, GreaterThanEq] .map(&:name) @@ -151,7 +155,7 @@ class TypesDeChampEditor::ConditionsComponent < ApplicationComponent options_for_select(empty_target_for_select), id: input_id_for('value', row_index) ) - when :enum + when :enum, :enums enums_for_select = left.options if right_invalid diff --git a/app/models/logic.rb b/app/models/logic.rb index 8f2787754..e09e88988 100644 --- a/app/models/logic.rb +++ b/app/models/logic.rb @@ -8,7 +8,7 @@ module Logic end def self.class_from_name(name) - [ChampValue, Constant, Empty, LessThan, LessThanEq, Eq, NotEq, GreaterThanEq, GreaterThan, EmptyOperator, And, Or] + [ChampValue, Constant, Empty, LessThan, LessThanEq, Eq, NotEq, GreaterThanEq, GreaterThan, EmptyOperator, IncludeOperator, And, Or] .find { |c| c.name == name } end @@ -24,6 +24,8 @@ module Logic operator_class = EmptyOperator in [:enum, _] operator_class = Eq + in [:enums, _] + operator_class = IncludeOperator in [:number, EmptyOperator] operator_class = Eq in [:number, _] @@ -35,7 +37,7 @@ module Logic Constant.new(true) when :empty Empty.new - when :enum + when :enum, :enums Constant.new(left.options.first.second) when :number Constant.new(0) @@ -49,7 +51,7 @@ module Logic case [left.type, right.type] in [a, ^a] # syntax for same type true - in [:enum, :string] + in [:enum, :string] | [:enums, :string] left.options.map(&:second).include?(right.value) else false @@ -84,6 +86,8 @@ module Logic def less_than_eq(left, right) = Logic::LessThanEq.new(left, right) + def ds_include(left, right) = Logic::IncludeOperator.new(left, right) + def constant(value) = Logic::Constant.new(value) def champ_value(stable_id) = Logic::ChampValue.new(stable_id) diff --git a/app/models/logic/champ_value.rb b/app/models/logic/champ_value.rb index d6d2068fb..0658fdd30 100644 --- a/app/models/logic/champ_value.rb +++ b/app/models/logic/champ_value.rb @@ -4,13 +4,15 @@ class Logic::ChampValue < Logic::Term :checkbox, :integer_number, :decimal_number, - :drop_down_list + :drop_down_list, + :multiple_drop_down_list ) CHAMP_VALUE_TYPE = { boolean: :boolean, number: :number, enum: :enum, + enums: :enums, empty: :empty, unmanaged: :unmanaged } @@ -35,6 +37,8 @@ class Logic::ChampValue < Logic::Term targeted_champ.for_api when MANAGED_TYPE_DE_CHAMP.fetch(:drop_down_list) targeted_champ.selected + when MANAGED_TYPE_DE_CHAMP.fetch(:multiple_drop_down_list) + targeted_champ.selected_options end end @@ -49,6 +53,8 @@ class Logic::ChampValue < Logic::Term CHAMP_VALUE_TYPE.fetch(:number) when MANAGED_TYPE_DE_CHAMP.fetch(:drop_down_list) CHAMP_VALUE_TYPE.fetch(:enum) + when MANAGED_TYPE_DE_CHAMP.fetch(:multiple_drop_down_list) + CHAMP_VALUE_TYPE.fetch(:enums) else CHAMP_VALUE_TYPE.fetch(:unmanaged) end diff --git a/app/models/logic/include_operator.rb b/app/models/logic/include_operator.rb new file mode 100644 index 000000000..6ec906500 --- /dev/null +++ b/app/models/logic/include_operator.rb @@ -0,0 +1,29 @@ +class Logic::IncludeOperator < Logic::BinaryOperator + def operation = :include? + + def errors(stable_ids = []) + result = [] + + if left_not_a_list? + result << { type: :required_list } + elsif right_value_not_in_list? + result << { + type: :not_included, + stable_id: @left.stable_id, + right: @right + } + end + + result + @left.errors(stable_ids) + @right.errors(stable_ids) + end + + private + + def left_not_a_list? + @left.type != :enums + end + + def right_value_not_in_list? + !@left.options.map(&:second).include?(@right.value) + end +end diff --git a/config/locales/models/logic/fr.yml b/config/locales/models/logic/fr.yml index a4ca68dbc..c6f1f744d 100644 --- a/config/locales/models/logic/fr.yml +++ b/config/locales/models/logic/fr.yml @@ -12,3 +12,4 @@ fr: 'Logic::And': Et 'Logic::Or': Ou 'Logic::NotEq': N'est pas + 'Logic::IncludeOperator': Contient diff --git a/spec/models/logic/include_operator_spec.rb b/spec/models/logic/include_operator_spec.rb new file mode 100644 index 000000000..e104ac969 --- /dev/null +++ b/spec/models/logic/include_operator_spec.rb @@ -0,0 +1,20 @@ +describe Logic::IncludeOperator do + include Logic + + let(:champ) { create(:champ_multiple_drop_down_list, value: '["val1", "val2"]') } + + describe '#compute' do + it { expect(ds_include(champ_value(champ.stable_id), constant('val1')).compute([champ])).to be(true) } + it { expect(ds_include(champ_value(champ.stable_id), constant('something else')).compute([champ])).to be(false) } + end + + describe '#errors' do + it { expect(ds_include(champ_value(champ.stable_id), constant('val1')).errors).to be_empty } + it { expect(ds_include(champ_value(champ.stable_id), constant('something else')).errors).to eq(["« something else » ne fait pas partie de « #{champ.libelle} »"]) } + it { expect(ds_include(constant(1), constant('val1')).errors).to eq(["Lʼopérateur inclusion ne sʼapplique que sur une liste"]) } + end + + describe '#==' do + it { expect(ds_include(champ_value(champ.stable_id), constant('val1'))).to eq(ds_include(champ_value(champ.stable_id), constant('val1'))) } + end +end diff --git a/spec/models/logic_spec.rb b/spec/models/logic_spec.rb index c7ba00e4e..99f13c261 100644 --- a/spec/models/logic_spec.rb +++ b/spec/models/logic_spec.rb @@ -49,6 +49,14 @@ describe Logic do it { is_expected.to eq(ds_eq(champ_value(drop_down), constant(first_option))) } end + + context 'when multiple dropdown empty operator true' do + let(:multiple_drop_down) { create(:type_de_champ_multiple_drop_down_list) } + let(:first_option) { multiple_drop_down.drop_down_list_enabled_non_empty_options.first } + let(:condition) { empty_operator(champ_value(multiple_drop_down), constant(true)) } + + it { is_expected.to eq(ds_include(champ_value(multiple_drop_down), constant(first_option))) } + end end describe '.compatible_type?' do