From e6342b7b6fc71874fa25dd53ace2a60a4b0b7d5c Mon Sep 17 00:00:00 2001 From: elviabth Date: Thu, 4 Jun 2026 13:01:51 +0200 Subject: [PATCH 1/4] fix bug --- .rubocop.yml | 1 + lib/decidim/term_customizer.rb | 8 ++- lib/decidim/term_customizer/i18n_backend.rb | 1 - .../term_customizer_loader_spec.rb | 72 +++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 2e0537dc..48302228 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,3 +10,4 @@ RSpec/DescribeClass: Exclude: - spec/i18n_spec.rb - "spec/system/**/*" + - "spec/lib/**/*" diff --git a/lib/decidim/term_customizer.rb b/lib/decidim/term_customizer.rb index 7e3f2349..b1ff98d3 100644 --- a/lib/decidim/term_customizer.rb +++ b/lib/decidim/term_customizer.rb @@ -37,7 +37,13 @@ module TermCustomizer end class << self - attr_accessor :loader + def loader + Thread.current.thread_variable_get(:term_customizer_loader) + end + + def loader=(loader) + Thread.current.thread_variable_set(:term_customizer_loader, loader) + end end end end diff --git a/lib/decidim/term_customizer/i18n_backend.rb b/lib/decidim/term_customizer/i18n_backend.rb index 38dfd8ee..30cb6218 100644 --- a/lib/decidim/term_customizer/i18n_backend.rb +++ b/lib/decidim/term_customizer/i18n_backend.rb @@ -30,7 +30,6 @@ def reload! end def translations - return @translations if @translations return {} unless TermCustomizer.loader @translations = TermCustomizer.loader.translations_hash diff --git a/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb b/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb new file mode 100644 index 00000000..316d7fb8 --- /dev/null +++ b/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Decidim::TermCustomizer.loader" do + describe ".loader" do + it "is isolated per thread" do + loader_org1 = double("loader_org1") + loader_org2 = double("loader_org2") + + thread1_loader = nil + thread2_loader = nil + + begin + Concurrent::CyclicBarrier.new(2) + rescue StandardError + nil + end + + t1 = Thread.new do + Decidim::TermCustomizer.loader = loader_org1 + sleep 0.05 + thread1_loader = Decidim::TermCustomizer.loader + end + + t2 = Thread.new do + sleep 0.02 + Decidim::TermCustomizer.loader = loader_org2 + thread2_loader = Decidim::TermCustomizer.loader + end + + t1.join + t2.join + + expect(thread1_loader).to eq(loader_org1) + expect(thread2_loader).to eq(loader_org2) + end + end + + describe "Decidim::TermCustomizer::I18nBackend" do + let(:backend) { Decidim::TermCustomizer::I18nBackend.new } + + let(:translations_org1) { { en: { decidim: { test_key: "Org 1 translation" } } } } + let(:translations_org2) { { en: { decidim: { test_key: "Org 2 translation" } } } } + + it "does not bleed translations between concurrent threads" do + loader_org1 = double("loader_org1", translations_hash: translations_org1) + loader_org2 = double("loader_org2", translations_hash: translations_org2) + + result_thread1 = nil + result_thread2 = nil + + t1 = Thread.new do + Decidim::TermCustomizer.loader = loader_org1 + sleep 0.05 + result_thread1 = backend.translations + end + + t2 = Thread.new do + sleep 0.02 + Decidim::TermCustomizer.loader = loader_org2 + result_thread2 = backend.translations + end + + t1.join + t2.join + + expect(result_thread1).to eq(translations_org1) + expect(result_thread2).to eq(translations_org2) + end + end +end From 1fe3163d02a30cbc56927bd9545997fafcdcce0d Mon Sep 17 00:00:00 2001 From: elviabth Date: Thu, 4 Jun 2026 16:17:26 +0200 Subject: [PATCH 2/4] add loader cleanup --- lib/decidim/term_customizer/engine.rb | 8 ++++++++ lib/decidim/term_customizer/i18n_backend.rb | 1 + .../term_customizer/term_customizer_loader_spec.rb | 6 ------ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/decidim/term_customizer/engine.rb b/lib/decidim/term_customizer/engine.rb index 82f44ce8..25e1a888 100644 --- a/lib/decidim/term_customizer/engine.rb +++ b/lib/decidim/term_customizer/engine.rb @@ -33,6 +33,10 @@ class Engine < ::Rails::Engine customizer_backend.reload! end + ActiveSupport::Notifications.subscribe "process_action.action_controller" do + TermCustomizer.loader = nil + end + # The jobs are generally run in different context than the controllers # which causes the term customizations not to be active. During the # jobs, only the organization and global context translations are loaded @@ -62,6 +66,10 @@ class Engine < ::Rails::Engine # Force the backend to reload the translations for the job customizer_backend.reload! end + + ActiveSupport::Notifications.subscribe "perform.active_job" do + TermCustomizer.loader = nil + end end end end diff --git a/lib/decidim/term_customizer/i18n_backend.rb b/lib/decidim/term_customizer/i18n_backend.rb index 30cb6218..38dfd8ee 100644 --- a/lib/decidim/term_customizer/i18n_backend.rb +++ b/lib/decidim/term_customizer/i18n_backend.rb @@ -30,6 +30,7 @@ def reload! end def translations + return @translations if @translations return {} unless TermCustomizer.loader @translations = TermCustomizer.loader.translations_hash diff --git a/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb b/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb index 316d7fb8..21afd0e6 100644 --- a/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb +++ b/spec/lib/decidim/term_customizer/term_customizer_loader_spec.rb @@ -11,12 +11,6 @@ thread1_loader = nil thread2_loader = nil - begin - Concurrent::CyclicBarrier.new(2) - rescue StandardError - nil - end - t1 = Thread.new do Decidim::TermCustomizer.loader = loader_org1 sleep 0.05 From 202a01f2b721244809769714b4828ff855971cab Mon Sep 17 00:00:00 2001 From: elviabth Date: Thu, 4 Jun 2026 16:42:51 +0200 Subject: [PATCH 3/4] fix spec --- lib/decidim/term_customizer/i18n_backend.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/decidim/term_customizer/i18n_backend.rb b/lib/decidim/term_customizer/i18n_backend.rb index 38dfd8ee..6831ed5b 100644 --- a/lib/decidim/term_customizer/i18n_backend.rb +++ b/lib/decidim/term_customizer/i18n_backend.rb @@ -20,17 +20,10 @@ def available_locales end def initialized? - !@translations.nil? - end - - # Clean up translations hash on reload! - def reload! - @translations = nil - super + !TermCustomizer.loader.nil? end def translations - return @translations if @translations return {} unless TermCustomizer.loader @translations = TermCustomizer.loader.translations_hash From 208193525c55159e50a341b5c7d010c283345abd Mon Sep 17 00:00:00 2001 From: elviabth Date: Thu, 4 Jun 2026 19:07:39 +0200 Subject: [PATCH 4/4] fix spec --- lib/decidim/term_customizer/i18n_backend.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/decidim/term_customizer/i18n_backend.rb b/lib/decidim/term_customizer/i18n_backend.rb index 6831ed5b..30cb6218 100644 --- a/lib/decidim/term_customizer/i18n_backend.rb +++ b/lib/decidim/term_customizer/i18n_backend.rb @@ -20,7 +20,13 @@ def available_locales end def initialized? - !TermCustomizer.loader.nil? + !@translations.nil? + end + + # Clean up translations hash on reload! + def reload! + @translations = nil + super end def translations