tags: ensure aliased tag categories stay in sync.

* When a tag's category is changed, also change the category of any aliases pointing to it. For
  example, if "ff7" is aliased to "final_fantasy_vii", and "final_fantasy_vii" is changed to a
  copyright tag, then change the empty "ff7" tag to be a copyright tag too.

* Don't allow changing the category of an aliased tag. For example, if "ff7" is aliased to
  "final_fantasy_vii", then don't allow changing the "ff7" tag to be a non-copyright tag.

This ensures that the categories of aliased tags stay in sync with that of their parent tags. This
way aliased tags are colored correctly in wikis and other places.
This commit is contained in:
evazion
2022-11-22 21:54:22 -06:00
parent 1a9718250f
commit b234727832
4 changed files with 54 additions and 2 deletions

View File

@@ -23,8 +23,10 @@ class Tag < ApplicationRecord
validates :name, tag_name: true, uniqueness: true, on: :create
validates :name, tag_name: true, on: :name
validates :category, inclusion: { in: TagCategory.category_ids }
validate :validate_category, if: :category_changed?
before_create :create_character_tag_for_cosplay_tag, if: :is_cosplay_tag?
after_save :update_tag_alias_categories, if: :saved_change_to_category?
after_save :update_category_cache, if: :saved_change_to_category?
after_save :update_category_post_counts, if: :saved_change_to_category?
@@ -173,6 +175,21 @@ class Tag < ApplicationRecord
def update_category_cache
Cache.put("tc:#{Cache.hash(name)}", category, 3.hours)
end
# When a tag's category is changed, also change the categories of any aliases pointing to it.
def update_tag_alias_categories
consequent_aliases.each do |tag_alias|
if tag_alias.antecedent_tag.category != category
tag_alias.antecedent_tag.update!(category: category, updater: updater)
end
end
end
def validate_category
if is_aliased? && category != aliased_tag.category
errors.add(:base, "Can't change the category of an aliased tag")
end
end
end
concerning :NameMethods do

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env ruby
require_relative "base"
with_confirmation do
fix = ENV.fetch("FIX", "false").truthy?
aliases = TagAlias.active.joins(:antecedent_tag, :consequent_tag).where("tags.category != consequent_tags_tag_aliases.category")
aliases.find_each do |tag_alias|
tag_alias.antecedent_tag.assign_attributes(category: tag_alias.consequent_tag.category, updater: User.system)
puts ({ id: tag_alias.id, from: tag_alias.antecedent_tag.name, to: tag_alias.consequent_tag.name, changes: tag_alias.antecedent_tag.changes }).to_json
tag_alias.antecedent_tag.save!(touch: false) if fix
end
end

View File

@@ -587,13 +587,13 @@ class PostTest < ActiveSupport::TestCase
assert_equal(Tag.categories.character, tag.last_version.category)
end
should "change the category for an aliased tag" do
should "not change the category for an aliased tag" do
create(:tag_alias, antecedent_name: "hoge", consequent_name: "moge")
post = create(:post, tag_string: "char:hoge")
assert_equal(["moge"], post.tag_array)
assert_equal(Tag.categories.general, Tag.find_by_name("moge").category)
assert_equal(Tag.categories.character, Tag.find_by_name("hoge").category)
assert_equal(Tag.categories.general, Tag.find_by_name("hoge").category)
end
should "not raise an exception for an invalid tag name" do

View File

@@ -175,6 +175,17 @@ class TagTest < ActiveSupport::TestCase
assert_equal(0, tag.reload.category)
end
should "not change category if the tag is aliased" do
t1 = create(:tag, name: "ff7", category: Tag.categories.copyright)
t2 = create(:tag, name: "final_fantasy_vii", category: Tag.categories.copyright)
ta = create(:tag_alias, antecedent_name: "ff7", consequent_name: "final_fantasy_vii")
t1.reload.category = Tag.categories.character
assert_equal(false, t1.valid?)
assert_equal(["Can't change the category of an aliased tag"], t1.errors[:base])
end
should "update post tag counts when the category is changed" do
post = FactoryBot.create(:post, tag_string: "test")
assert_equal(1, post.tag_count_general)
@@ -186,6 +197,15 @@ class TagTest < ActiveSupport::TestCase
assert_equal(1, post.tag_count_character)
end
should "update aliased tags when the tag's category is changed" do
t1 = create(:tag, name: "ff7", category: Tag.categories.general)
t2 = create(:tag, name: "final_fantasy_vii", category: Tag.categories.general)
ta = create(:tag_alias, antecedent_name: "ff7", consequent_name: "final_fantasy_vii")
t2.update!(category: Tag.categories.copyright, updater: User.system)
assert_equal("Copyright", t1.reload.category_name)
end
should "be created when one doesn't exist" do
assert_difference("Tag.count", 1) do
tag = Tag.find_or_create_by_name("hoge")