From bb6c02852245ee973d4301aa66fc2d45e611c637 Mon Sep 17 00:00:00 2001 From: Elias Date: Tue, 7 Jul 2026 11:27:16 +0300 Subject: [PATCH] feat(clone): gate category cloning behind CLONE_CATEGORIES_ENABLED Add an env gate around the taxonomy and taxon duplicators in StoreCloneRunner. When CLONE_CATEGORIES_ENABLED=false the clone skips copying the template store's taxonomies/taxons, so newly cloned shops are born without the inherited category clutter (Categories, Collections, Categories 2) that appears in nav and footer. Defaults to enabled: when the variable is absent the clone behaves exactly as before, so the change is inert until deliberately switched off in an environment. The taxon duplicator is still constructed so its taxons_cache stays an empty hash when gated; the downstream product duplicator reads that cache null-safely, so no product->taxon links are orphaned. --- .../olitt/clone_store/store_clone_runner.rb | 11 ++- .../clone_store/store_clone_runner_spec.rb | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 spec/services/spree/olitt/clone_store/store_clone_runner_spec.rb diff --git a/app/services/spree/olitt/clone_store/store_clone_runner.rb b/app/services/spree/olitt/clone_store/store_clone_runner.rb index 6061dda..94d1a78 100644 --- a/app/services/spree/olitt/clone_store/store_clone_runner.rb +++ b/app/services/spree/olitt/clone_store/store_clone_runner.rb @@ -53,8 +53,6 @@ def call new_store: @new_store, vendor: @vendor ) - run_section('taxonomies', taxonomies_duplicator) { taxonomies_duplicator.handle_clone_taxonomies } - taxon_duplicator = Duplicators::TaxonsDuplicator.new( old_store: @old_store, new_store: @new_store, @@ -62,7 +60,10 @@ def call taxonomies_cache: taxonomies_duplicator.taxonomies_cache, root_taxons: taxonomies_duplicator.root_taxons ) - run_section('taxons', taxon_duplicator) { taxon_duplicator.handle_clone_taxons } + if clone_categories_enabled? + run_section('taxonomies', taxonomies_duplicator) { taxonomies_duplicator.handle_clone_taxonomies } + run_section('taxons', taxon_duplicator) { taxon_duplicator.handle_clone_taxons } + end linked_resource.taxons_cache = taxon_duplicator.taxons_cache page_duplicator = Duplicators::PagesDuplicator.new( @@ -174,6 +175,10 @@ def attach_store_images @new_store.save! end + def clone_categories_enabled? + ENV.fetch('CLONE_CATEGORIES_ENABLED', 'true') != 'false' + end + def run_section(section_name, duplicator = nil) yield errors.concat(normalize_errors(duplicator.errors)) if duplicator&.errors_are_present? diff --git a/spec/services/spree/olitt/clone_store/store_clone_runner_spec.rb b/spec/services/spree/olitt/clone_store/store_clone_runner_spec.rb new file mode 100644 index 0000000..3ba1577 --- /dev/null +++ b/spec/services/spree/olitt/clone_store/store_clone_runner_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +module Spree + module Olitt + module CloneStore + describe StoreCloneRunner do + describe '#call category cloning gate' do + let(:old_store) { instance_double('Spree::Store') } + let(:new_store) { instance_double('Spree::Store') } + let(:vendor) { instance_double('Spree::Vendor') } + let(:taxonomies_duplicator) { instance_double(Duplicators::TaxonomiesDuplicator).as_null_object } + let(:taxons_duplicator) { instance_double(Duplicators::TaxonsDuplicator).as_null_object } + + before do + allow(Duplicators::LinkedResourceDuplicator).to receive(:new).and_return(instance_double(Duplicators::LinkedResourceDuplicator).as_null_object) + allow(Duplicators::StockLocationsDuplicator).to receive(:new).and_return(instance_double(Duplicators::StockLocationsDuplicator).as_null_object) + allow(Duplicators::VendorAddressesDuplicator).to receive(:new).and_return(instance_double(Duplicators::VendorAddressesDuplicator).as_null_object) + allow(Duplicators::TaxonomiesDuplicator).to receive(:new).and_return(taxonomies_duplicator) + allow(Duplicators::TaxonsDuplicator).to receive(:new).and_return(taxons_duplicator) + allow(Duplicators::PagesDuplicator).to receive(:new).and_return(instance_double(Duplicators::PagesDuplicator).as_null_object) + allow(Duplicators::ShippingCategoriesDuplicator).to receive(:new).and_return(instance_double(Duplicators::ShippingCategoriesDuplicator).as_null_object) + allow(Duplicators::OptionTypesDuplicator).to receive(:new).and_return(instance_double(Duplicators::OptionTypesDuplicator).as_null_object) + allow(Duplicators::ProductsDuplicator).to receive(:new).and_return(instance_double(Duplicators::ProductsDuplicator).as_null_object) + allow(Duplicators::StockItemsDuplicator).to receive(:new).and_return(instance_double(Duplicators::StockItemsDuplicator).as_null_object) + allow(Duplicators::SectionsDuplicator).to receive(:new).and_return(instance_double(Duplicators::SectionsDuplicator).as_null_object) + allow(Duplicators::MenusDuplicator).to receive(:new).and_return(instance_double(Duplicators::MenusDuplicator).as_null_object) + allow(Duplicators::MenuItemsDuplicator).to receive(:new).and_return(instance_double(Duplicators::MenuItemsDuplicator).as_null_object) + allow(Duplicators::PaymentMethodsDuplicator).to receive(:new).and_return(instance_double(Duplicators::PaymentMethodsDuplicator).as_null_object) + allow(Duplicators::ShippingMethodsDuplicator).to receive(:new).and_return(instance_double(Duplicators::ShippingMethodsDuplicator).as_null_object) + allow(ZoneResolver).to receive(:new).and_return(instance_double(ZoneResolver)) + end + + def run + runner = described_class.new(old_store: old_store, new_store: new_store, vendor: vendor) + allow(runner).to receive(:attach_store_images) + runner.call + end + + context 'when CLONE_CATEGORIES_ENABLED is unset (default)' do + before { allow(ENV).to receive(:fetch).and_call_original } + + it 'clones taxonomies and taxons' do + run + + expect(taxonomies_duplicator).to have_received(:handle_clone_taxonomies) + expect(taxons_duplicator).to have_received(:handle_clone_taxons) + end + end + + context "when CLONE_CATEGORIES_ENABLED is 'false'" do + before do + allow(ENV).to receive(:fetch).and_call_original + allow(ENV).to receive(:fetch).with('CLONE_CATEGORIES_ENABLED', 'true').and_return('false') + end + + it 'skips taxonomy and taxon cloning' do + run + + expect(taxonomies_duplicator).not_to have_received(:handle_clone_taxonomies) + expect(taxons_duplicator).not_to have_received(:handle_clone_taxons) + end + end + end + end + end + end +end