This commit is contained in:
2026-05-10 05:03:27 +09:00
parent de86879e79
commit 5b50642756
12 changed files with 185 additions and 122 deletions
+51 -56
View File
@@ -173,8 +173,8 @@ class PostsController < ApplicationController
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
force = truthy_param?(params[:force])
merge = truthy_param?(params[:merge])
force = bool?(:force)
merge = bool?(:merge)
return head :bad_request if force && merge
base_version_no = nil
@@ -201,15 +201,14 @@ class PostsController < ApplicationController
base_snapshot = post_snapshot_from_version(base_version)
current_snapshot = post_snapshot_from_record(post)
end
incoming_snapshot = post_incoming_snapshot(post,
title:,
incoming_snapshot = post_incoming_snapshot(title:,
original_created_from:,
original_created_before:,
tag_names:,
parent_post_ids:)
snapshot_to_apply =
if post.version_no == base_version_no || force
if force || post.version_no == base_version_no || current_snapshot == base_snapshot
incoming_snapshot
else
changes = post_snapshot_changes(base_snapshot, current_snapshot, incoming_snapshot)
@@ -448,30 +447,36 @@ class PostsController < ApplicationController
version_no
end
def truthy_param?(value) = ActiveModel::Type::Boolean.new.cast(value)
def post_snapshot_from_version version
{ title: version.title,
original_created_from: snapshot_time(version.original_created_from),
original_created_before: snapshot_time(version.original_created_before),
tag_names: version.tags.to_s.split.filter { !(_1.start_with?('nico:')) }.sort,
tag_names: editable_tag_names_from_version(version),
parent_post_ids: snapshot_parent_post_ids_from_version(version) }
end
def editable_tag_names_from_version version
version.tags.to_s.split.reject { |name| name.downcase.start_with?('nico:') }.sort
end
def post_snapshot_from_record post
{ title: post.title,
original_created_from: snapshot_time(post.original_created_from),
original_created_before: snapshot_time(post.original_created_before),
tag_names: post.tags.joins(:tag_name).order('tag_names.name').pluck('tag_names.name'),
tag_names: editable_tag_names_from_post(post),
parent_post_ids: post.parent_posts.order(:id).pluck(:id) }
end
def post_incoming_snapshot post, title:, original_created_from:, original_created_before:,
tag_names:, parent_post_ids:
def editable_tag_names_from_post post
post.tags.not_nico.joins(:tag_name).order('tag_names.name').pluck('tag_names.name')
end
def post_incoming_snapshot title:, original_created_from:, original_created_before:,
tag_names:, parent_post_ids:
{ title:,
original_created_from: snapshot_time(original_created_from),
original_created_before: snapshot_time(original_created_before),
tag_names: incoming_tag_names_for_snapshot(post, tag_names),
tag_names: incoming_tag_names_for_snapshot(tag_names),
parent_post_ids: parent_post_ids.sort }
end
@@ -494,44 +499,10 @@ class PostsController < ApplicationController
value.to_s
end
def incoming_tag_names_for_snapshot post, raw_tag_names
manual_names = normalised_manual_tag_names_for_snapshot(raw_tag_names)
def incoming_tag_names_for_snapshot raw_tag_names
tags = Tag.normalise_tags!(raw_tag_names, with_tagme: false)
existing_tags =
Tag
.joins(:tag_name)
.where(tag_names: { name: manual_names })
.to_a
expanded_names = Tag.expand_parent_tags(existing_tags).map(&:name)
(manual_names + expanded_names).uniq.sort
end
def normalised_manual_tag_names_for_snapshot raw_tag_names
if raw_tag_names.any? { |name| name.downcase.start_with?('nico:') }
raise Tag::NicoTagNormalisationError
end
pairs = raw_tag_names.map do |raw_name|
prefix, category =
Tag::CATEGORY_PREFIXES.find { |p, _| raw_name.downcase.start_with?(p) } || ['', nil]
name = TagName.canonicalise(raw_name.sub(/\A#{ Regexp.escape(prefix) }/i, '')).first
[name, category]
end
names = pairs.map(&:first)
has_deerjikist = pairs.any? do |name, category|
category == :deerjikist ||
Tag.joins(:tag_name).where(category: :deerjikist, tag_names: { name: }).exists?
end
names << Tag.no_deerjikist.name unless has_deerjikist
names.uniq.sort
Tag.expand_parent_tags(tags).map(&:name).uniq.sort
end
def post_conflict_json post:, base_version_no:, base_snapshot:,
@@ -618,29 +589,53 @@ class PostsController < ApplicationController
original_created_from: snapshot[:original_created_from],
original_created_before: snapshot[:original_created_before])
tags = Tag.normalise_tags!(snapshot[:tag_names], with_tagme: false)
TagVersioning.record_tag_snapshots!(tags, created_by_user: current_user)
editable_tags = Tag.normalise_tags!(snapshot[:tag_names], with_tagme: false)
TagVersioning.record_tag_snapshots!(editable_tags, created_by_user: current_user)
readonly_tags = post.tags.nico.to_a
tags = readonly_tags + editable_tags
tags = Tag.expand_parent_tags(tags)
sync_post_tags!(post, tags)
sync_post_tags!(post, tags)
sync_parent_posts!(post, snapshot[:parent_post_ids])
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: current_user)
end
def merge_post_snapshots base_snapshot, current_snapshot, incoming_snapshot
[:title, :original_created_from, :original_created_before, :tag_names, :parent_post_ids].map {
[_1, merge_scaler_snapshot_value(base_snapshot[_1],
[:title, :original_created_from, :original_created_before].map {
[_1, merge_scalar_snapshot_value(base_snapshot[_1],
current_snapshot[_1],
incoming_snapshot[_1])]
}.to_h
}.to_h.merge([:tag_names, :parent_post_ids].map {
[_1, merge_set_snapshot_value(base_snapshot[_1],
current_snapshot[_1],
incoming_snapshot[_1])]
}.to_h)
end
def merge_scaler_snapshot_value base, current, mine
def merge_scalar_snapshot_value base, current, mine
return mine if current == base
return current if mine == base || current == mine
raise ArgumentError, '競合してゐる項目はマージできません.'
end
def merge_set_snapshot_value base, current, mine
base = base.to_a
current = current.to_a
mine = mine.to_a
added_by_current = current - base
removed_by_current = base - current
added_by_me = mine - base
removed_by_me = base - mine
merged = base + added_by_current + added_by_me
merged -= removed_by_current
merged -= removed_by_me
merged.uniq.sort
end
end
+2
View File
@@ -28,6 +28,8 @@ class Post < ApplicationRecord
has_one_attached :thumbnail
attribute :version_no, :integer, default: 1
before_validation :normalise_url
validates :url, presence: true, uniqueness: true
+2 -1
View File
@@ -40,6 +40,8 @@ class Tag < ApplicationRecord
belongs_to :tag_name
delegate :wiki_page, to: :tag_name
attribute :version_no, :integer, default: 1
delegate :name, to: :tag_name, allow_nil: true
validates :tag_name, presence: true
@@ -136,7 +138,6 @@ class Tag < ApplicationRecord
tn = tn.canonical if tn.canonical_id?
Tag.find_undiscard_or_create_by!(tag_name_id: tn.id) do |t|
t.version_no = TagVersion.where(tag_id: t.id).order(version_no: :desc).first || 1
t.category = category
end
rescue ActiveRecord::RecordNotUnique
+2
View File
@@ -15,6 +15,8 @@ class WikiPage < ApplicationRecord
has_many :wiki_versions
attribute :version_no, :integer, default: 1
belongs_to :tag_name
validates :tag_name, presence: true
validates :body, presence: true