diff --git a/lib/decidim/term_customizer/context/job_context.rb b/lib/decidim/term_customizer/context/job_context.rb index 5108778b..6491f8c8 100644 --- a/lib/decidim/term_customizer/context/job_context.rb +++ b/lib/decidim/term_customizer/context/job_context.rb @@ -5,7 +5,6 @@ module TermCustomizer module Context class JobContext < Base # rubocop:disable Metrics/CyclomaticComplexity - # rubocop:disable Metrics/PerceivedComplexity def resolve! # Figure out the organization and user through the job arguments if # passed for the job. @@ -14,7 +13,7 @@ def resolve! @organization ||= organization_from_argument(arg) @space ||= space_from_argument(arg) @component ||= component_from_argument(arg) - user ||= arg if arg.is_a?(Decidim::User) + user ||= find_object_by_class(arg, Decidim::User) end # In case a component was found, define the space as the component @@ -30,26 +29,83 @@ def resolve! @organization ||= user.organization if user end # rubocop:enable Metrics/CyclomaticComplexity - # rubocop:enable Metrics/PerceivedComplexity protected def organization_from_argument(arg) - return arg if arg.is_a?(Decidim::Organization) + org = find_object_by_class(arg, Decidim::Organization) - arg.organization if arg.respond_to?(:organization) + org || find_value_by_method(arg, :organization) end def space_from_argument(arg) - return arg if arg.is_a?(Decidim::Participable) + space = find_object_by_class(arg, Decidim::Participable) - arg.participatory_space if arg.respond_to?(:participatory_space) + space || find_value_by_method(arg, :participatory_space) end def component_from_argument(arg) - return arg if arg.is_a?(Decidim::Component) + component = find_object_by_class(arg, Decidim::Component) + return component if component - arg.component if arg.respond_to?(:component) + found = find_value_by_method(arg, :component) + return found if found.is_a?(Decidim::Component) + + if defined?(Decidim::Forms::Questionnaire) + component = find_questionnaire_component(arg) + return component if component + end + + nil + end + + def find_questionnaire_component(arg) + questionnaire_component = find_object_by_class(arg, Decidim::Forms::Questionnaire)&.questionnaire_for + return questionnaire_component if questionnaire_component.is_a?(Decidim::Component) + return questionnaire_component.component if questionnaire_component.respond_to?(:component) + + nil + end + + def find_object_by_class(obj, klass, seen = {}) + return obj if obj.is_a?(klass) + return nil if obj.nil? + return nil if seen[obj.__id__] + + seen[obj.__id__] = true + + values_from_iterable(obj).each do |item| + found = find_object_by_class(item, klass, seen) + return found if found + end + + nil + end + + def find_value_by_method(obj, method, seen = {}) + return nil if obj.nil? + return nil if seen[obj.__id__] + + seen[obj.__id__] = true + return obj.send(method) if obj.respond_to?(method) + + values_from_iterable(obj).each do |item| + found = find_value_by_method(item, method, seen) + return found if found + end + + nil + end + + def values_from_iterable(obj) + case obj + when Hash + obj.values + when Array + obj + else + [] + end end end end diff --git a/spec/lib/decidim/tasks/decidim_term_customizer_combine_db_spec.rb b/spec/lib/decidim/tasks/decidim_term_customizer_combine_db_spec.rb index eab4a77d..969b8ecc 100644 --- a/spec/lib/decidim/tasks/decidim_term_customizer_combine_db_spec.rb +++ b/spec/lib/decidim/tasks/decidim_term_customizer_combine_db_spec.rb @@ -44,8 +44,9 @@ it "passes" do task.reenable - expect { task.invoke("te") }.to output("Combined 1 file(s) into 'config/locales/te.yml'\nTranslations combined successfully!\n").to_stdout - check_no_errors_have_been_printed + expect { task.invoke("te") } + .to output("Combined 1 file(s) into 'config/locales/te.yml'\nTranslations combined successfully!\n").to_stdout + .and(not_output.to_stderr) end it "writes file correctly" do diff --git a/spec/lib/decidim/tasks/decidim_term_customizer_combine_file_spec.rb b/spec/lib/decidim/tasks/decidim_term_customizer_combine_file_spec.rb index c18372ba..046c9cc8 100644 --- a/spec/lib/decidim/tasks/decidim_term_customizer_combine_file_spec.rb +++ b/spec/lib/decidim/tasks/decidim_term_customizer_combine_file_spec.rb @@ -68,8 +68,9 @@ it "passes" do task.reenable - expect { task.invoke("te", "file:#{file_path}") }.to output("Combined 1 file(s) into 'config/locales/te.yml'\nTranslations combined successfully!\n").to_stdout - check_no_errors_have_been_printed + expect { task.invoke("te", "file:#{file_path}") } + .to output("Combined 1 file(s) into 'config/locales/te.yml'\nTranslations combined successfully!\n").to_stdout + .and(not_output.to_stderr) end it "writes file correctly" do diff --git a/spec/lib/decidim/term_customizer/context/job_context_spec.rb b/spec/lib/decidim/term_customizer/context/job_context_spec.rb index 4ba69d8c..a94b880b 100644 --- a/spec/lib/decidim/term_customizer/context/job_context_spec.rb +++ b/spec/lib/decidim/term_customizer/context/job_context_spec.rb @@ -49,6 +49,39 @@ end end + context "with user passed inside an array in the arguments" do + let(:user) { create(:user) } + let(:arguments) { [[user]] } + + it "resolves the user's organization" do + expect(subject.organization).to be(user.organization) + expect(subject.space).to be_nil + expect(subject.component).to be_nil + end + end + + context "with user passed inside a hash in the arguments" do + let(:user) { create(:user) } + let(:arguments) { [{ arg: user }] } + + it "resolves the user's organization" do + expect(subject.organization).to be(user.organization) + expect(subject.space).to be_nil + expect(subject.component).to be_nil + end + end + + context "with user passed inside a sub-hash in the arguments" do + let(:user) { create(:user) } + let(:arguments) { [{ job: { arg: user } }] } + + it "resolves the user's organization" do + expect(subject.organization).to be(user.organization) + expect(subject.space).to be_nil + expect(subject.component).to be_nil + end + end + context "with participatory process passed in the arguments" do let(:space) { create(:participatory_process, organization:) } let(:arguments) { [organization, space] } @@ -105,6 +138,32 @@ end end + context "with a questionnaire passed in the arguments" do + let(:space) { create(:participatory_process, organization:) } + let(:questionnaire) { create(:questionnaire, questionnaire_for: component) } + let(:component) do + create(:component, manifest_name: :meetings, participatory_space: space) + end + let(:arguments) { [[questionnaire]] } + + it "resolves the participatory space based on the questionnaire" do + expect(subject.organization).to be(organization) + expect(subject.space).to be(space) + expect(subject.component).to be(component) + end + + context "and questionnaire is for an intermediary object" do + let(:questionnaire) { create(:questionnaire, questionnaire_for: meeting) } + let(:meeting) { create(:meeting, component: component) } + + it "resolves the participatory space based on the questionnaire" do + expect(subject.organization).to be(organization) + expect(subject.space).to be(space) + expect(subject.component).to be(component) + end + end + end + context "with organization and space passed in the arguments" do let(:other_organization) { create(:organization) } let(:space) { create(:participatory_process, organization:) } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0b80de01..5bd040b4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,6 +9,8 @@ require "decidim/dev/test/base_spec_helper" +RSpec::Matchers.define_negated_matcher :not_output, :output + RSpec.configure do |config| # Add extra traslation load path for the tests I18n.load_path << File.join(__dir__, "fixtures", "locales", "en.yml")