diff --git a/app/models/category.rb b/app/models/category.rb index 8022f580463..75686ccc39c 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1091,11 +1091,13 @@ class Category < ActiveRecord::Base .find_each { |category| category.create_category_definition } end - def slug_path + def slug_path(parent_ids = Set.new) if self.parent_category_id.present? - slug_path = self.parent_category.slug_path - slug_path.push(self.slug_for_url) - slug_path + if parent_ids.add?(self.parent_category_id) + self.parent_category.slug_path(parent_ids) << self.slug_for_url + else + [] + end else [self.slug_for_url] end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index cdd1b4372de..006bf280595 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -47,8 +47,7 @@ RSpec.describe Category do it "should delete associated sidebar_section_links when category is destroyed" do category_sidebar_section_link = Fabricate(:category_sidebar_section_link) - category_sidebar_section_link_2 = - Fabricate(:category_sidebar_section_link, linkable: category_sidebar_section_link.linkable) + Fabricate(:category_sidebar_section_link, linkable: category_sidebar_section_link.linkable) tag_sidebar_section_link = Fabricate(:tag_sidebar_section_link) expect { category_sidebar_section_link.linkable.destroy! }.to change { @@ -996,7 +995,7 @@ RSpec.describe Category do ) category.clear_auto_bump_cache! - post1 = create_post(category: category, created_at: 15.seconds.ago) + create_post(category: category, created_at: 15.seconds.ago) # no limits on post creation or category creation please RateLimiter.enable @@ -1419,7 +1418,7 @@ RSpec.describe Category do it 'returns nil when input is ["category:invalid-slug:sub-subcategory"] and maximum category nesting is 3' do SiteSetting.max_category_nesting = 3 - sub_subcategory = Fabricate(:category, parent_category: subcategory, slug: "sub-subcategory") + Fabricate(:category, parent_category: subcategory, slug: "sub-subcategory") expect(Category.ids_from_slugs(%w[category:invalid-slug:sub-subcategory])).to eq([]) end @@ -1464,6 +1463,34 @@ RSpec.describe Category do end end + describe "#slug_path" do + before { SiteSetting.max_category_nesting = 3 } + + fab!(:grandparent) { Fabricate(:category, slug: "foo") } + fab!(:parent) { Fabricate(:category, parent_category: grandparent, slug: "bar") } + let(:child) { Fabricate(:category, parent_category: parent, slug: "boo") } + + it "returns the slug for categories without parents" do + expect(grandparent.slug_path).to eq [grandparent.slug] + end + + it "returns the slug for categories with parent" do + expect(parent.slug_path).to eq [grandparent.slug, parent.slug] + end + + it "returns the slug for categories with grand-parent" do + expect(child.slug_path).to eq [grandparent.slug, parent.slug, child.slug] + end + + it "avoids infinite loops with circular references" do + grandparent.parent_category = parent + grandparent.save!(validate: false) + + expect(grandparent.slug_path).to eq [parent.slug, grandparent.slug] + expect(parent.slug_path).to eq [grandparent.slug, parent.slug] + end + end + describe "#slug_ref" do fab!(:category) { Fabricate(:category, slug: "foo") }