From 927ce90adc0ec5bec4ab539ac9e45adf54dd0f5f Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sat, 3 Oct 2026 01:30:26 +0900 Subject: [PATCH] #55 --- .../controllers/tag_versions_controller.rb | 13 ++++----- backend/app/controllers/tags_controller.rb | 14 +++++++--- backend/app/models/language.rb | 2 +- backend/app/models/region.rb | 3 ++ backend/app/models/script.rb | 4 +-- backend/app/models/tag.rb | 28 ++++++++++--------- backend/app/models/tag_name.rb | 9 +++++- backend/app/services/tag_version_recorder.rb | 26 +++++++++-------- backend/spec/tasks/nico_sync_spec.rb | 23 +++++++++------ 9 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 backend/app/models/region.rb diff --git a/backend/app/controllers/tag_versions_controller.rb b/backend/app/controllers/tag_versions_controller.rb index 2aab1db..f45d042 100644 --- a/backend/app/controllers/tag_versions_controller.rb +++ b/backend/app/controllers/tag_versions_controller.rb @@ -41,8 +41,8 @@ class TagVersionsController < ApplicationController rows.map do |row| current_names = names_for_locale(row.names, language_code) previous_names = names_for_locale(row.attributes['prev_names'], language_code) - cur_aliases = current_names.reject { _1['primary_flg'] }.map { _1['name'] } - prev_aliases = previous_names.reject { _1['primary_flg'] }.map { _1['name'] } + cur_aliases = current_names.fetch('aliases', []).map { _1.fetch('name') } + prev_aliases = previous_names.fetch('aliases', []).map { _1.fetch('name') } cur_parent_tag_ids = split_parent_tag_ids(row.parent_tag_ids) prev_parent_tag_ids = split_parent_tag_ids(row.attributes['prev_parent_tag_ids']) @@ -66,8 +66,7 @@ class TagVersionsController < ApplicationController version_no: row.version_no, event_type: row.event_type, names: row.names, - name: { current: current_names.find { _1['primary_flg'] }&.fetch('name'), - prev: previous_names.find { _1['primary_flg'] }&.fetch('name') }, + name: { current: current_names['name'], prev: previous_names['name'] }, category: { current: row.category, prev: row.attributes['prev_category'] }, deprecated_at: { current: row.deprecated_at&.iso8601, prev: row.attributes['prev_deprecated_at']&.iso8601 }, @@ -98,10 +97,10 @@ class TagVersionsController < ApplicationController def split_values(values) = values.to_s.split(/\s+/).reject(&:blank?) def names_for_locale names, language_code - values = names.is_a?(String) ? JSON.parse(names) : Array(names) - values.select { _1['language_code'] == language_code } + values = names.is_a?(String) ? JSON.parse(names) : names + values&.fetch(language_code, {}) || {} rescue JSON::ParserError - [] + {} end def split_parent_tag_ids(values) = split_values(values).map(&:to_i) diff --git a/backend/app/controllers/tags_controller.rb b/backend/app/controllers/tags_controller.rb index 58df873..9be0365 100644 --- a/backend/app/controllers/tags_controller.rb +++ b/backend/app/controllers/tags_controller.rb @@ -764,7 +764,7 @@ class TagsController < ApplicationController unless target_tag_name if current_tag_name - current_tag_name.update!(name:) + current_tag_name.update!(name:, auto_generated: false) else current_tag_name = TagName.create!( tag:, @@ -790,8 +790,9 @@ class TagsController < ApplicationController current_tag_name.wiki_page&.update!(tag_name_id: promoted_tag_name.id) current_tag_name.association(:wiki_page).reset - current_tag_name.update!(canonical_id: promoted_tag_name.id, primary_flg: false) - promoted_tag_name.update!(tag:, canonical_id: nil, primary_flg: true) + current_tag_name.update!(canonical_id: promoted_tag_name.id, + primary_flg: false, auto_generated: false) + promoted_tag_name.update!(tag:, canonical_id: nil, primary_flg: true, auto_generated: false) TagName.where(tag:, language_code: current_tag_name.language_code, @@ -832,6 +833,10 @@ class TagsController < ApplicationController alias_names.each do |alias_name| alias_tag_name = TagName.find_by(language_code: locale.language_code, name: alias_name) + if alias_tag_name&.primary? && alias_tag_name.tag_id && alias_tag_name.tag_id != tag.id + alias_tag_name.errors.add(:name, 'その名前は既に使はれてゐます.') + raise ActiveRecord::RecordInvalid, alias_tag_name + end affected_tags << alias_tag_name&.tag end @@ -851,7 +856,8 @@ class TagsController < ApplicationController language_code: locale.language_code, name: alias_name) alias_tag_name.script_code = locale.script_code - alias_tag_name.update!(tag:, primary_flg: false, canonical_id: primary_tag_name.id) + alias_tag_name.update!(tag:, primary_flg: false, auto_generated: false, + canonical_id: primary_tag_name.id) end affected_tags.compact.uniq.each do |affected_tag| diff --git a/backend/app/models/language.rb b/backend/app/models/language.rb index 87f86d8..fb2727f 100644 --- a/backend/app/models/language.rb +++ b/backend/app/models/language.rb @@ -1,5 +1,5 @@ class Language < ApplicationRecord has_many :locales, foreign_key: :language_code, primary_key: :code - enum :code, ja: 'ja', en: 'en', fr: 'fr' + enum :code, ja: 'ja' end diff --git a/backend/app/models/region.rb b/backend/app/models/region.rb new file mode 100644 index 0000000..85476d6 --- /dev/null +++ b/backend/app/models/region.rb @@ -0,0 +1,3 @@ +class Region < ApplicationRecord + ; +end diff --git a/backend/app/models/script.rb b/backend/app/models/script.rb index d87f262..bbc23f1 100644 --- a/backend/app/models/script.rb +++ b/backend/app/models/script.rb @@ -1,5 +1,5 @@ class Script < ApplicationRecord - has_many :locales, foreign_key: :language_code, primary_key: :code + has_many :locales, foreign_key: :script_code, primary_key: :code - enum :code, Jpan: 'Jpan', Latn: 'Latn' + enum :code, Jpan: 'Jpan' end diff --git a/backend/app/models/tag.rb b/backend/app/models/tag.rb index ff758ff..7891029 100644 --- a/backend/app/models/tag.rb +++ b/backend/app/models/tag.rb @@ -250,20 +250,22 @@ class Tag < ApplicationRecord language_code = locale.language_code name = name.to_s.strip - tn = TagName.find_or_create_by!(language_code:, name:) do - _1.script_code = locale.script_code - _1.primary_flg = true + ApplicationRecord.transaction do + tn = TagName.find_or_create_by!(language_code:, name:) do + _1.script_code = locale.script_code + _1.primary_flg = true + end + + tag = tn.tag + next tag if tag + + tag = Tag.create!(tag_name_id: tn.id, category:) + tn.update!(tag:, primary_flg: true, canonical_id: nil, auto_generated: false) + generate_tag_names!(locale, tag, tn) + tag.association(:tag_names).reset + + tag end - - tag = tn.tag - return tag if tag - - tag = Tag.create!(tag_name_id: tn.id, category:) - tn.update!(tag:) - generate_tag_names!(locale, tag, tn) - tag.association(:tag_names).reset - - tag rescue ActiveRecord::RecordNotUnique retry end diff --git a/backend/app/models/tag_name.rb b/backend/app/models/tag_name.rb index 0ce8223..6c119ab 100644 --- a/backend/app/models/tag_name.rb +++ b/backend/app/models/tag_name.rb @@ -11,6 +11,7 @@ class TagName < ApplicationRecord validate :alias_must_not_have_wiki_page validate :name_must_be_sanitised validate :name_mustnt_start_with_nico + validate :generated_name_must_be_reserved def primary? = primary_flg @@ -22,7 +23,7 @@ class TagName < ApplicationRecord names.map { |name| tag_name = tns[name] - if !tag_name || tag_name.primary? + if !tag_name || tag_name.primary? || tag_name.tag_id.nil? name else TagName.find_by(language_code: locale.language_code, @@ -40,6 +41,12 @@ class TagName < ApplicationRecord private + def generated_name_must_be_reserved + if !auto_generated && name.to_s.start_with?('Tag_#') + errors.add :name, 'タグの命名規則に反してゐます.' + end + end + def alias_name_must_not_have_prefix if !(primary?) && name.to_s.include?(':') errors.add :name, 'エーリアス名にプレフィクスを含むことはできません.' diff --git a/backend/app/services/tag_version_recorder.rb b/backend/app/services/tag_version_recorder.rb index 025c3d4..e14d497 100644 --- a/backend/app/services/tag_version_recorder.rb +++ b/backend/app/services/tag_version_recorder.rb @@ -15,24 +15,28 @@ class TagVersionRecorder < VersionRecorder def snapshot_attributes names = snapshot_names - japanese_names = names.select { _1['language_code'] == 'ja' } - compatibility_names = japanese_names.presence || names + compatibility_names = names.fetch('ja') - { name: compatibility_names.find { _1['primary_flg'] }['name'], + { name: compatibility_names.fetch('name'), category: @record.category, deprecated_at: @record.deprecated_at, - aliases: compatibility_names.reject { _1['primary_flg'] }.map { _1['name'] }.join(' '), + aliases: compatibility_names.fetch('aliases').map { _1.fetch('name') }.join(' '), names:, parent_tag_ids: @record.snapshot_parent_tag_ids.join(' ') } end def snapshot_names - @record.tag_names.order(:language_code, primary_flg: :desc).order(:name).map { |tag_name| - { 'language_code' => tag_name.language_code, - 'name' => tag_name.name, - 'script_code' => tag_name.script_code, - 'primary_flg' => tag_name.primary_flg, - 'auto_generated' => tag_name.auto_generated } - } + @record.tag_names.order(:language_code, :name).group_by(&:language_code) + .transform_values do |tag_names| + primary = tag_names.find(&:primary?) + aliases = tag_names.reject(&:primary?).sort_by(&:name).map { |tag_name| + { 'name' => tag_name.name, 'script_code' => tag_name.script_code } + } + + { 'name' => primary.name, + 'script_code' => primary.script_code, + 'auto_generated' => primary.auto_generated, + 'aliases' => aliases } + end end end diff --git a/backend/spec/tasks/nico_sync_spec.rb b/backend/spec/tasks/nico_sync_spec.rb index 6ae8afa..db6d55a 100644 --- a/backend/spec/tasks/nico_sync_spec.rb +++ b/backend/spec/tasks/nico_sync_spec.rb @@ -16,17 +16,22 @@ RSpec.describe 'nico:sync' do end def create_nico_sanitisation_rules! - TagNameSanitisationRule.create!(priority: 20, - source_pattern: '\\?', - replacement: '_') + rules = [ + [20, '\\?', '_'], + [40, '_$', ''], + [45, '^([^:]+\\:)?_', '\\1']] - TagNameSanitisationRule.create!(priority: 40, - source_pattern: '_$', - replacement: '') + priorities = rules.map(&:first) + source_patterns = rules.map { _1[1] } - TagNameSanitisationRule.create!(priority: 45, - source_pattern: '^([^:]+\\:)?_', - replacement: '\\1') + scope = TagNameSanitisationRule.unscoped + scope.where(priority: priorities) + .or(scope.where(source_pattern: source_patterns)) + .delete_all + + rules.each do |priority, source_pattern, replacement| + TagNameSanitisationRule.create!(priority:, source_pattern:, replacement:) + end end it '既存 post を見つけて、nico tag と linked tag を追加し、差分が出たら bot を付ける' do