タグの合併処理追加(#282) (#284)

#282

#282

#282

#282

#282

#282

Co-authored-by: miteruzo <miteruzo@naver.com>
Reviewed-on: #284
This commit was merged in pull request #284.
This commit is contained in:
2026-03-08 15:46:05 +09:00
parent 7885f6dfb9
commit 16e9b8ca49
13 changed files with 138 additions and 51 deletions
+40 -10
View File
@@ -23,6 +23,8 @@ class Tag < ApplicationRecord
has_many :parents, through: :reversed_tag_implications, source: :parent_tag
has_many :tag_similarities, dependent: :delete_all
has_many :tag_similarities_as_target,
class_name: 'TagSimilarity', foreign_key: :target_tag_id, dependent: :delete_all
has_many :deerjikists, dependent: :delete_all
@@ -46,7 +48,7 @@ class Tag < ApplicationRecord
validate :tag_name_must_be_canonical
validate :category_must_be_deerjikist_with_deerjikists
scope :nico_tags, -> { where(category: :nico) }
scope :nico_tags, -> { nico }
CATEGORY_PREFIXES = {
'general:' => :general,
@@ -64,9 +66,7 @@ class Tag < ApplicationRecord
(self.tag_name ||= build_tag_name).name = val
end
def has_wiki
wiki_page.present?
end
def has_wiki = wiki_page.present?
def self.tagme
@tagme ||= find_or_create_by_tag_name!('タグ希望', category: :meta)
@@ -97,14 +97,12 @@ class Tag < ApplicationRecord
pf, cat = CATEGORY_PREFIXES.find { |p, _| name.downcase.start_with?(p) } || ['', nil]
name = TagName.canonicalise(name.sub(/\A#{ pf }/i, '')).first
find_or_create_by_tag_name!(name, category: (cat || :general)).tap do |tag|
if cat && tag.category != cat
tag.update!(category: cat)
end
tag.update!(category: cat) if cat && tag.category != cat
end
end
tags << Tag.tagme if with_tagme && tags.size < 10 && tags.none?(Tag.tagme)
tags << Tag.no_deerjikist if tags.all? { |t| t.category != 'deerjikist' }
tags << Tag.no_deerjikist if tags.all? { |t| !(t.deerjikist?) }
tags.uniq(&:id)
end
@@ -142,12 +140,44 @@ class Tag < ApplicationRecord
retry
end
def self.merge_tags! target_tag, source_tags
target_tag => Tag
Tag.transaction do
Array(source_tags).compact.uniq.each do |st|
st => Tag
next if st == target_tag
st.post_tags.find_each do |pt|
if PostTag.kept.exists?(post_id: pt.post_id, tag_id: target_tag.id)
pt.discard_by!(nil)
# discard 後の update! は禁止なので DB を直に更新
pt.update_columns(tag_id: target_tag.id, updated_at: Time.current)
else
pt.update!(tag: target_tag)
end
end
tag_name = st.tag_name
st.destroy!
tag_name.reload
tag_name.update!(canonical: target_tag.tag_name)
end
# 投稿件数を再集計
target_tag.update_columns(post_count: PostTag.kept.where(tag: target_tag).count)
end
target_tag.reload
end
private
def nico_tag_name_must_start_with_nico
n = name.to_s
if ((category == 'nico' && !(n.downcase.start_with?('nico:'))) ||
(category != 'nico' && n.downcase.start_with?('nico:')))
if ((nico? && !(n.downcase.start_with?('nico:'))) ||
(!(nico?) && n.downcase.start_with?('nico:')))
errors.add :name, 'ニコニコ・タグの命名規則に反してゐます.'
end
end
+9 -15
View File
@@ -1,29 +1,23 @@
class User < ApplicationRecord
enum :role, { guest: 'guest', member: 'member', admin: 'admin' }
enum :role, guest: 'guest', member: 'member', admin: 'admin'
validates :name, length: { maximum: 255 }
validates :inheritance_code, presence: true, length: { maximum: 64 }
validates :role, presence: true, inclusion: { in: roles.keys }
validates :banned, inclusion: { in: [true, false] }
has_many :posts
has_many :created_posts,
class_name: 'Post', foreign_key: :uploaded_user_id, dependent: :nullify
has_many :settings
has_many :user_ips, dependent: :destroy
has_many :ip_addresses, through: :user_ips
has_many :user_post_views, dependent: :destroy
has_many :viewed_posts, through: :user_post_views, source: :post
has_many :created_wiki_pages, class_name: 'WikiPage', foreign_key: 'created_user_id', dependent: :nullify
has_many :updated_wiki_pages, class_name: 'WikiPage', foreign_key: 'updated_user_id', dependent: :nullify
has_many :created_wiki_pages,
class_name: 'WikiPage', foreign_key: :created_user_id, dependent: :nullify
has_many :updated_wiki_pages,
class_name: 'WikiPage', foreign_key: :updated_user_id, dependent: :nullify
def viewed? post
user_post_views.exists? post_id: post.id
end
def member?
['member', 'admin'].include?(role)
end
def admin?
role == 'admin'
end
def viewed?(post) = user_post_views.exists?(post_id: post.id)
def gte_member? = member? || admin?
end
+1 -1
View File
@@ -8,7 +8,7 @@ class WikiLine < ApplicationRecord
sha = Digest::SHA256.hexdigest(body)
now = Time.current
upsert({ sha256: sha, body:, created_at: now, updated_at: now })
upsert(sha256: sha, body:, created_at: now, updated_at: now)
find_by!(sha256: sha)
end
+6 -13
View File
@@ -14,17 +14,13 @@ class WikiPage < ApplicationRecord
belongs_to :tag_name
validates :tag_name, presence: true
def title
tag_name.name
end
def title = tag_name.name
def title= val
(self.tag_name ||= build_tag_name).name = val
end
def current_revision
wiki_revisions.order(id: :desc).first
end
def current_revision = wiki_revisions.order(id: :desc).first
def body
rev = current_revision
@@ -49,11 +45,8 @@ class WikiPage < ApplicationRecord
page
end
def pred_revision_id revision_id
wiki_revisions.where('id < ?', revision_id).order(id: :desc).limit(1).pick(:id)
end
def succ_revision_id revision_id
wiki_revisions.where('id > ?', revision_id).order(id: :asc).limit(1).pick(:id)
end
def pred_revision_id(revision_id) =
wiki_revisions.where('id < ?', revision_id).order(id: :desc).limit(1).pick(:id)
def succ_revision_id(revision_id) =
wiki_revisions.where('id > ?', revision_id).order(id: :asc).limit(1).pick(:id)
end
+1 -1
View File
@@ -7,7 +7,7 @@ class WikiRevision < ApplicationRecord
has_many :wiki_revision_lines, dependent: :delete_all
has_many :wiki_lines, through: :wiki_revision_lines
enum :kind, { content: 0, redirect: 1 }
enum :kind, content: 0, redirect: 1
validates :kind, presence: true
validates :lines_count, numericality: { only_integer: true, greater_than_or_equal_to: 0 }