このコミットが含まれているのは:
2026-10-15 23:49:38 +09:00
コミット 6ba87beef6
8個のファイルの変更、176行の追加、134行の削除
+12 -7
ファイルの表示
@@ -1,14 +1,18 @@
class TagDescriptionsController < ApplicationController
def show
description = TagDescription.find_by(tag_id: params[:id], language_code: 'ja')
locale = resolve_locale!
description = TagDescription.find_by(tag_id: params[:id], language_code: locale.language_code)
return head :not_found unless description
render json: representation(description)
end
def show_by_title
tag_name = TagName.find_by(language_code: 'ja', name: params[:name].to_s.strip)
description = tag_name&.tag && TagDescription.find_by(tag: tag_name.tag, language_code: 'ja')
locale = resolve_locale!
tag_name = TagName.find_by(
language_code: locale.language_code, name: params[:name].to_s.strip)
description = tag_name&.tag && TagDescription.find_by(
tag: tag_name.tag, language_code: locale.language_code)
return head :not_found unless description
render json: representation(description)
@@ -21,18 +25,19 @@ class TagDescriptionsController < ApplicationController
body = params[:body].to_s
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
locale = resolve_locale!
tag = Tag.find_by(id: params[:id])
return head :not_found unless tag
description = TagDescription.find_by(tag:, language_code: 'ja')
description = TagDescription.find_by(tag:, language_code: locale.language_code)
if description
TagDescriptionOperation.update!(tag:, body:, updated_by_user: current_user)
TagDescriptionOperation.update!(locale, tag:, body:, updated_by_user: current_user)
description.reload
render json: representation(description)
else
TagDescriptionOperation.create!(tag:, body:, created_by_user: current_user)
description = TagDescription.find_by!(tag:, language_code: 'ja')
TagDescriptionOperation.create!(locale, tag:, body:, created_by_user: current_user)
description = TagDescription.find_by!(tag:, language_code: locale.language_code)
render json: representation(description), status: :created
end
rescue ActiveRecord::RecordInvalid => e
+10 -6
ファイルの表示
@@ -1,33 +1,37 @@
class TagGroupsController < ApplicationController
def index
locale = resolve_locale!
groups = TagGroup.includes(:created_by_user, :updated_by_user).order(:id)
render json: TagGroupRepr.many(groups.to_a)
render json: TagGroupRepr.many(locale, groups.to_a)
end
def show
locale = resolve_locale!
group = TagGroup.includes(:created_by_user, :updated_by_user).find_by(id: params[:id])
return head :not_found unless group
render json: TagGroupRepr.one(group)
render json: TagGroupRepr.one(locale, group)
end
def create
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
group = TagGroupOperation.create!(**write_attributes, actor: current_user)
render json: TagGroupRepr.one(group), status: :created
locale = resolve_locale!
group = TagGroupOperation.create!(locale, **write_attributes, actor: current_user)
render json: TagGroupRepr.one(locale, group), status: :created
end
def update
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
locale = resolve_locale!
group = TagGroup.find_by(id: params[:id])
return head :not_found unless group
TagGroupOperation.update!(group:, **write_attributes, actor: current_user)
render json: TagGroupRepr.one(group.reload)
TagGroupOperation.update!(locale, group:, **write_attributes, actor: current_user)
render json: TagGroupRepr.one(locale, group.reload)
end
private
+74 -53
ファイルの表示
@@ -4,9 +4,12 @@ class WikiPagesController < ApplicationController
rescue_from TagDescriptionOperation::Conflict, with: :render_wiki_conflict
def index
locale = resolve_locale!
title = params[:title].to_s.strip
descriptions =
TagDescription.where(language_code: 'ja')
TagDescription
.where(language_code: locale.language_code)
.includes(tag: :tag_names)
descriptions = descriptions.order(updated_at: :desc, id: :asc)
@@ -15,13 +18,14 @@ class WikiPagesController < ApplicationController
pattern = "%#{ ApplicationRecord.sanitize_sql_like(title) }%"
descriptions =
descriptions.joins(tag: :tag_names)
.where(tag_names: { language_code: 'ja', primary_flg: true })
.where(tag_names: { language_code: locale.language_code, primary_flg: true })
.where('tag_names.name LIKE ?', pattern).limit(20)
tag_groups = tag_groups.where(
'CONCAT(?, name) LIKE ?', TagGroupRepr::PREFIX, pattern).limit(20)
'CONCAT(?, name) LIKE ? OR CONCAT(?, name) LIKE ?',
TagGroupRepr::PREFIX, pattern, 'tag_group:', pattern).limit(20)
end
json = descriptions.map { |description| WikiPageRepr.tag_description(description) }
json = descriptions.map { |description| WikiPageRepr.tag_description(locale, description) }
json.concat(tag_groups.map { WikiPageRepr.tag_group(_1) })
json.sort_by! do |row|
[-row['updated_at'].to_f, row.fetch('resource_type'), row.fetch('id')]
@@ -31,47 +35,53 @@ class WikiPagesController < ApplicationController
end
def show
tag_description = TagDescription.find_by(id: params[:id])
locale = resolve_locale!
tag_description = TagDescription.find_by(id: params[:id], language_code: locale.language_code)
return render_wiki_page_by_tag_description tag_description if tag_description
return render_wiki_page_by_tag_description locale, tag_description if tag_description
tag_group = TagGroup.find_by(id: params[:id])
return render_tag_group tag_group if tag_group
return render_tag_group locale, tag_group if tag_group
head :not_found
end
def show_by_title
locale = resolve_locale!
title = params[:title].to_s.strip
if title.start_with?(TagGroupRepr::PREFIX)
name = title.delete_prefix(TagGroupRepr::PREFIX)
if (name = TagGroupRepr.name_from_title(title))
tag_group = TagGroup.find_by(name:)
return render_tag_group tag_group if tag_group
return render_tag_group locale, tag_group if tag_group
return head :not_found
end
tag_description = tag_description_by_title(title)
tag_description = tag_description_by_title(locale, title)
return render_wiki_page_by_tag_description tag_description if tag_description
return render_wiki_page_by_tag_description locale, tag_description if tag_description
head :not_found
end
def exists
return head :no_content if TagDescription.exists?(id: params[:id])
locale = resolve_locale!
if TagDescription.exists?(id: params[:id], language_code: locale.language_code)
return head :no_content
end
return head :no_content if TagGroup.exists?(id: params[:id])
head :not_found
end
def exists_by_title
locale = resolve_locale!
title = params[:title].to_s.strip
if title.start_with?(TagGroupRepr::PREFIX)
exists = TagGroup.exists?(name: title.delete_prefix(TagGroupRepr::PREFIX))
if (name = TagGroupRepr.name_from_title(title))
exists = TagGroup.exists?(name:)
return head exists ? :no_content : :not_found
end
return head :no_content if tag_description_by_title(title)
return head :no_content if tag_description_by_title(locale, title)
head :not_found
end
@@ -80,15 +90,17 @@ class WikiPagesController < ApplicationController
id = params[:id]
return render_bad_request('id は必須です.') if id.blank?
locale = resolve_locale!
from = params[:from].presence
to = params[:to].presence
description = TagDescription.find_by(id:)
description = TagDescription.find_by(id:, language_code: locale.language_code)
if description
versions = description_versions(description)
from_rev = from && versions.find(from)
to_rev = to ? versions.find(to) : versions.find_by!(version_no: description.version_no)
resource = WikiPageRepr.tag_description(description)
resource = WikiPageRepr.tag_description(locale, description)
else
tag_group = TagGroup.find_by(id:)
if tag_group
@@ -96,8 +108,8 @@ class WikiPagesController < ApplicationController
to_rev = to ? group_version(tag_group, to) : current_group_version(tag_group)
return head :not_found if (from && !from_rev) || !to_rev
from_body = from_rev && render_group_version(from_rev)
to_body = render_group_version(to_rev)
from_body = from_rev && render_group_version(locale, from_rev)
to_body = render_group_version(locale, to_rev)
resource = { 'id' => tag_group.id,
'title' => "#{ TagGroupRepr::PREFIX }#{ tag_group.name }",
'deprecated_at' => nil }
@@ -131,14 +143,15 @@ class WikiPagesController < ApplicationController
return render_unprocessable_entity('タイトルは必須です.', field: :title) if title.blank?
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
if title.start_with?(TagGroupRepr::PREFIX)
name = title.delete_prefix(TagGroupRepr::PREFIX)
parsed = TagGroupMarkdownParser.parse(body:, group: nil)
locale = resolve_locale!
if (name = TagGroupRepr.name_from_title(title))
parsed = TagGroupMarkdownParser.parse(locale, body:, group: nil)
group = TagGroupOperation.create!(
name:, **parsed.except(:compatibility_body), actor: current_user,
locale, name:, **parsed.except(:compatibility_body), actor: current_user,
require_compatibility: true)
version = current_group_version(group)
return render_group_representation(TagGroupRepr.one(group), version,
return render_group_representation(TagGroupRepr.one(locale, group), version,
status: :created)
end
@@ -146,14 +159,15 @@ class WikiPagesController < ApplicationController
if title.include?(':')
return render_unprocessable_entity field: :title
else
tag_name = TagName.find_by(language_code: 'ja', name: title)
tag_name = TagName.find_by(language_code: locale.language_code, name: title)
return render_unprocessable_entity field: :title unless tag_name&.tag
TagDescriptionOperation.create!(
locale,
tag: tag_name.tag, body:, created_by_user: current_user, message:)
end
render json: WikiPageRepr.tag_description(description), status: :created
render json: WikiPageRepr.tag_description(locale, description), status: :created
rescue ActiveRecord::RecordInvalid => e
render_validation_error e.record
rescue ActiveRecord::RecordNotUnique
@@ -172,26 +186,28 @@ class WikiPagesController < ApplicationController
return render_unprocessable_entity('タイトルは必須です.', field: :title) if title.blank?
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
locale = resolve_locale!
group = TagGroup.find_by(id: params[:id])
if title.start_with?(TagGroupRepr::PREFIX)
if (name = TagGroupRepr.name_from_title(title))
return head :not_found unless group
name = title.delete_prefix(TagGroupRepr::PREFIX)
parsed = TagGroupMarkdownParser.parse(body:, group:)
parsed = TagGroupMarkdownParser.parse(locale, body:, group:)
TagGroupOperation.update!(
group:, name:, **parsed.except(:compatibility_body), actor: current_user,
locale, group:, name:, **parsed.except(:compatibility_body), actor: current_user,
require_compatibility: true)
return head :ok
end
return render_unprocessable_entity field: :title if title.include?(':')
description = TagDescription.find_by(id: params[:id])
description = TagDescription.find_by(id: params[:id], language_code: locale.language_code)
unless description
return render_unprocessable_entity field: :title if group
return head :not_found
end
tag = description.tag
tag_name = tag.tag_name('ja')
tag_name = tag.tag_name(locale.language_code)
return render_unprocessable_entity field: :title unless tag_name
return render_unprocessable_entity field: :title if tag_name.name.include?(':')
@@ -207,7 +223,7 @@ class WikiPagesController < ApplicationController
tag_name.update!(name: title) if title != old_title
message = params[:message].presence
TagDescriptionOperation.update!(tag:, body:, updated_by_user: current_user,
TagDescriptionOperation.update!(locale, tag:, body:, updated_by_user: current_user,
message:, base_revision_id:)
if title != old_title
@@ -226,10 +242,12 @@ class WikiPagesController < ApplicationController
end
def changes
locale = resolve_locale!
id = params[:id].presence
description = id && TagDescription.find_by(id:)
description = id && TagDescription.find_by(id:, language_code: locale.language_code)
if description
return render json: description_changes(TagDescription.where(id: description.id))
return render json: description_changes(locale, TagDescription.where(id: description.id))
end
tag_group = id && TagGroup.find_by(id:)
@@ -240,7 +258,8 @@ class WikiPagesController < ApplicationController
json = []
unless id
json.concat(tag_group_changes(TagGroupVersion.all))
json.concat(description_changes(TagDescription.where(language_code: 'ja')))
json.concat(description_changes(locale,
TagDescription.where(language_code: locale.language_code)))
json = json.each_with_index.sort_by do |row, index|
[-row.fetch(:timestamp).to_f, index]
end.map(&:first)
@@ -250,7 +269,7 @@ class WikiPagesController < ApplicationController
private
def render_tag_group group
def render_tag_group locale, group
version = if params[:version].present?
group_version(group, params[:version])
else
@@ -258,16 +277,17 @@ class WikiPagesController < ApplicationController
end
return head :not_found unless version
representation = if params[:version].present?
TagGroupRepr.historical(version)
else
TagGroupRepr.one(group)
end
representation =
if params[:version].present?
TagGroupRepr.historical(locale, version)
else
TagGroupRepr.one(locale, group)
end
render_group_representation(representation, version)
end
def render_group_version version
representation = TagGroupRepr.historical(version)
def render_group_version locale, version
representation = TagGroupRepr.historical(locale, version)
TagGroupMarkdownRenderer.render(representation)
end
@@ -336,7 +356,7 @@ class WikiPagesController < ApplicationController
end
end
def render_wiki_page_by_tag_description description
def render_wiki_page_by_tag_description locale, description
versions = description_versions(description)
historical = params[:version].present?
rev = historical ? versions.find_by(id: params[:version]) :
@@ -347,7 +367,7 @@ class WikiPagesController < ApplicationController
succ = versions.find_by(version_no: rev.version_no + 1)&.id
body = historical ? rev.body : description.body
updated_at = historical ? rev.created_at : description.updated_at
render json: WikiPageRepr.tag_description(description).merge(
render json: WikiPageRepr.tag_description(locale, description).merge(
body:, revision_id: rev.id, pred:, succ:, updated_at:)
end
@@ -356,13 +376,14 @@ class WikiPagesController < ApplicationController
language_code: description.language_code)
end
def tag_description_by_title title
def tag_description_by_title locale, title
TagDescription.joins(tag: :tag_names)
.find_by(language_code: 'ja', tag_names: { language_code: 'ja', name: title })
.find_by(language_code: locale.language_code,
tag_names: { language_code: locale.language_code, name: title })
end
def description_changes descriptions
versions = TagDescriptionVersion.where(language_code: 'ja',
def description_changes locale, descriptions
versions = TagDescriptionVersion.where(language_code: locale.language_code,
tag_id: descriptions.select(:tag_id))
.order(id: :desc).limit(200).to_a
return [] if versions.empty?
@@ -371,7 +392,7 @@ class WikiPagesController < ApplicationController
.includes(tag: :tag_names).index_by(&:tag_id)
users = User.where(id: versions.map(&:created_by_user_id)).index_by(&:id)
predecessors = TagDescriptionVersion.where(
language_code: 'ja', tag_id: by_tag.keys,
language_code: locale.language_code, tag_id: by_tag.keys,
version_no: versions.map { |version| version.version_no - 1 })
.pluck(:tag_id, :version_no, :id)
.to_h { |tag_id, version_no, id| [[tag_id, version_no], id] }
@@ -381,7 +402,7 @@ class WikiPagesController < ApplicationController
{ revision_id: version.id,
pred: predecessors[[version.tag_id, version.version_no - 1]],
succ: nil,
wiki_page: { id: description.id, title: description.tag.name('ja'),
wiki_page: { id: description.id, title: description.tag.name(locale.language_code),
deprecated_at: description.tag.deprecated_at },
user: user && { id: user.id, name: user.name },
kind: 'content',
+32 -21
ファイルの表示
@@ -3,7 +3,12 @@ module TagGroupRepr
module_function
def many groups
def name_from_title title
prefix = [PREFIX, 'tag_group:'].find { title.downcase.start_with?(_1) }
title[prefix.length..] if prefix
end
def many locale, groups
ids = groups.map(&:id)
return [] if ids.empty?
@@ -14,14 +19,20 @@ module TagGroupRepr
placements = TagGroupPlacement.where(tag_group_id: ids).order(:position)
.group_by(&:tag_group_id)
target_ids = tag_members.values.flatten.map(&:tag_id) +
group_members.values.flatten.map(&:child_tag_group_id) +
target_ids =
tag_members.values.flatten.map(&:tag_id) +
placements.values.flatten.filter_map(&:tag_id)
child_ids = group_members.values.flatten.map(&:child_tag_group_id) +
child_ids =
group_members.values.flatten.map(&:child_tag_group_id) +
placements.values.flatten.filter_map(&:child_tag_group_id)
tags = Tag.select(:id, :category).where(id: target_ids.uniq).index_by(&:id)
names = TagName.where(tag_id: target_ids.uniq, language_code: 'ja', primary_flg: true)
.pluck(:tag_id, :name).to_h
names =
TagName
.where(tag_id: target_ids.uniq,
language_code: locale.language_code,
primary_flg: true)
.pluck(:tag_id, :name)
.to_h
child_groups = TagGroup.where(id: child_ids.uniq).index_by(&:id)
groups.map do |group|
@@ -32,8 +43,8 @@ module TagGroupRepr
end
end
def one group
many([group]).first
def one locale, group
many(locale, [group]).first
end
def tag_group wiki_page
@@ -48,7 +59,7 @@ module TagGroupRepr
TagGroupVersion.find_by(tag_group_id: group.id, version_no: group.version_no)
end
def historical version
def historical locale, version
members = Array(version.members)
placements = Array(version.placements)
tag_references = members.filter_map { |member| member['tag_id'] } +
@@ -68,10 +79,10 @@ module TagGroupRepr
[member.fetch('tag_group_id'), member.fetch('version_no')]
end.to_h
historical_members = members.map do |member|
historical_member(member, tag_versions, group_versions)
historical_member(locale, member, tag_versions, group_versions)
end
historical_placements = placements.map do |node|
historical_placement(node, tag_versions, group_versions,
historical_placement(locale, node, tag_versions, group_versions,
tag_version_numbers, group_version_numbers)
end
{ 'id' => version.tag_group_id,
@@ -89,7 +100,7 @@ module TagGroupRepr
def group_json group, tag_members, group_members, rows, tags, names, child_groups
tree = placement_tree(rows, tags, names, child_groups)
members = tag_members.sort_by(&:tag_id).map do |member|
{ 'tag' => tag_json(tags.fetch(member.tag_id), names.fetch(member.tag_id)) }
{ 'tag' => tag_json(tags.fetch(member.tag_id), names[member.tag_id]) }
end
members.concat(group_members.sort_by(&:child_tag_group_id).map do |member|
{ 'tag_group' => group_json_reference(child_groups.fetch(member.child_tag_group_id)) }
@@ -115,7 +126,7 @@ module TagGroupRepr
build = lambda do |parent|
by_parent.fetch(parent, []).sort_by(&:position).map do |row|
node = if row.tag_id
{ 'tag' => tag_json(tags.fetch(row.tag_id), names.fetch(row.tag_id)) }
{ 'tag' => tag_json(tags.fetch(row.tag_id), names[row.tag_id]) }
elsif row.child_tag_group_id
{ 'tag_group' => group_json_reference(child_groups.fetch(row.child_tag_group_id)) }
else
@@ -153,11 +164,11 @@ module TagGroupRepr
end
end
def historical_member member, tag_versions, group_versions
def historical_member locale, member, tag_versions, group_versions
if member['tag_id']
id = member.fetch('tag_id')
version = tag_versions.fetch([id, member.fetch('version_no')])
{ 'tag' => { 'id' => id, 'name' => japanese_version_name(version),
{ 'tag' => { 'id' => id, 'name' => version_name(locale, version),
'category' => version.category } }
else
id = member.fetch('tag_group_id')
@@ -166,13 +177,13 @@ module TagGroupRepr
end
end
def historical_placement(node, tag_versions, group_versions,
tag_version_numbers, group_version_numbers)
def historical_placement locale, node, tag_versions, group_versions,
tag_version_numbers, group_version_numbers
result = if node['tag_id']
id = node.fetch('tag_id')
version_no = tag_version_numbers.fetch(id)
version = tag_versions.fetch([id, version_no])
{ 'tag' => { 'id' => id, 'name' => japanese_version_name(version),
{ 'tag' => { 'id' => id, 'name' => version_name(locale, version),
'category' => version.category } }
elsif node['tag_group_id']
id = node.fetch('tag_group_id')
@@ -183,12 +194,12 @@ module TagGroupRepr
{ 'section_name' => node.fetch('section_name'), 'description' => node['description'] }
end
result.merge('children' => Array(node['children']).map do |child|
historical_placement(child, tag_versions, group_versions,
historical_placement(locale, child, tag_versions, group_versions,
tag_version_numbers, group_version_numbers)
end)
end
def japanese_version_name version
version.names.fetch('ja').fetch('name')
def version_name locale, version
version.names[locale.language_code]&.fetch('name')
end
end
+4 -3
ファイルの表示
@@ -4,11 +4,12 @@
module WikiPageRepr
module_function
def tag_description description
def tag_description locale, description
tag = description.tag
{ 'id' => description.id,
'resource_type' => 'tag_description',
'tag_name_id' => tag.tag_name('ja')&.id,
'tag_name_id' => tag.tag_name(locale.language_code)&.id,
'body' => description.body,
'created_user_id' => description.created_by_user_id,
'updated_user_id' => description.updated_by_user_id,
@@ -16,7 +17,7 @@ module WikiPageRepr
'updated_at' => description.updated_at,
'discarded_at' => nil,
'version_no' => description.version_no,
'title' => tag.name('ja'),
'title' => tag.name(locale.language_code),
'deprecated_at' => tag.deprecated_at }
end
+7 -9
ファイルの表示
@@ -1,32 +1,30 @@
class TagDescriptionOperation
LANGUAGE_CODE = 'ja'
class Conflict < StandardError
;
end
def self.create!(tag:, body:, created_by_user:, message: nil)
def self.create! locale, tag:, body:, created_by_user:, message: nil
ApplicationRecord.transaction do
tag.lock!
raise ActiveRecord::RecordNotUnique if TagDescription.exists?(
tag_id: tag.id, language_code: LANGUAGE_CODE)
tag_id: tag.id, language_code: locale.language_code)
description = TagDescription.new(tag:, language_code: LANGUAGE_CODE)
description = TagDescription.new(tag:, language_code: locale.language_code)
body = normalise_body!(description, body)
record_version!(description:, body:, user: created_by_user)
description
end
end
def self.update!(tag:, body:, updated_by_user:, message: nil,
base_revision_id: nil)
def self.update! locale, tag:, body:, updated_by_user:, message: nil, base_revision_id: nil
ApplicationRecord.transaction do
tag.lock!
description = TagDescription.lock.find_by!(
tag_id: tag.id, language_code: LANGUAGE_CODE)
tag_id: tag.id, language_code: locale.language_code)
body = normalise_body!(description, body)
latest = TagDescriptionVersion.find_by(
tag_id: tag.id, language_code: LANGUAGE_CODE,
tag_id: tag.id, language_code: locale.language_code,
version_no: description.version_no)
if base_revision_id.present? && latest&.id != base_revision_id.to_i
raise Conflict, '競合が発生してゐます.'
+22 -21
ファイルの表示
@@ -7,8 +7,8 @@ class TagGroupMarkdownParser
;
end
def self.parse(body:, group: nil)
new(body:, group:).parse
def self.parse(locale, body:, group: nil)
new(body:, group:).parse(locale)
end
def initialize(body:, group:)
@@ -16,7 +16,7 @@ class TagGroupMarkdownParser
@group = group
end
def parse
def parse locale
parse_body = @body.gsub(/([^\n])\n([\x23]{1,6}[ \t]+\S)/, "\\1\n\n\\2")
root = Kramdown::Document.new(parse_body, input: 'GFM', auto_ids: false).root
placements = []
@@ -51,7 +51,7 @@ class TagGroupMarkdownParser
when :ul
invalid!('未対応のリスト項目があります.') if node.attr.any?
target = headings.any? ? headings.last.fetch(:node).fetch('children') : placements
target.concat(node.children.map { parse_list_item(_1) })
target.concat(node.children.map { parse_list_item(locale, _1) })
else
invalid!("未対応の Markdown 構文 #{ node.type.inspect } です.")
end
@@ -66,7 +66,7 @@ class TagGroupMarkdownParser
private
def parse_list_item(item)
def parse_list_item locale, item
invalid!('リスト項目ではありません.') unless item.type == :li
invalid!('未対応のリスト項目があります.') if item.attr.any?
@@ -81,29 +81,30 @@ class TagGroupMarkdownParser
label = own.map { inline_text(_1) }.join(' ').strip
invalid!('空のリスト項目は使用できません.') if label.blank?
node = if label.start_with?(PREFIX)
group_name = label.delete_prefix(PREFIX).strip
child_group = TagGroup.find_by(name: group_name)
invalid!("Tag Group #{ group_name.inspect } を解決できません.") unless child_group
invalid!('自分自身を含めることはできません.') if child_group.id == @group&.id
{ 'tag_group_id' => child_group.id }
elsif label.end_with?(':', ':')
section_name = label.sub(/[::]\z/, '').strip
invalid!('Section 名は必須です.') if section_name.blank?
{ 'section_name' => section_name, 'description' => nil }
else
{ 'tag_id' => resolve_tag(label) }
end
node =
if (group_name = TagGroupRepr.name_from_title(label))
group_name = group_name.strip
child_group = TagGroup.find_by(name: group_name)
invalid!("Tag Group #{ group_name.inspect } を解決できません.") unless child_group
invalid!('自分自身を含めることはできません.') if child_group.id == @group&.id
{ 'tag_group_id' => child_group.id }
elsif label.end_with?(':', ':')
section_name = label.sub(/[::]\z/, '').strip
invalid!('Section 名は必須です.') if section_name.blank?
{ 'section_name' => section_name, 'description' => nil }
else
{ 'tag_id' => resolve_tag(locale, label) }
end
node['children'] = nested.flat_map do |list|
invalid!('未対応の入れ子リストです.') unless list.type == :ul
list.children.map { parse_list_item(_1) }
list.children.map { parse_list_item(locale, _1) }
end
node
end
def resolve_tag(name)
names = TagName.where(language_code: 'ja', name:).to_a
def resolve_tag locale, name
names = TagName.where(language_code: locale.language_code, name:).to_a
tag_ids = names.filter_map(&:tag_id).uniq
invalid!("Tag #{ name.inspect } を解決できません.") if tag_ids.empty?
invalid!("Tag 名 #{ name.inspect } が曖昧です.") if tag_ids.many?
+15 -14
ファイルの表示
@@ -1,29 +1,29 @@
class TagGroupOperation
MAX_PLACEMENT_DEPTH = 5
def self.create!(name:, description:, members:, placements:, actor:,
def self.create!(locale, name:, description:, members:, placements:, actor:,
require_compatibility: false)
ApplicationRecord.transaction do
group = TagGroup.new(version_no: 1, created_by_user: actor,
updated_by_user: actor)
attributes = prepare!(group:, name:, description:, members:, placements:)
attributes = prepare!(locale:, group:, name:, description:, members:, placements:)
group.assign_attributes(name: attributes.fetch(:name),
description: attributes.fetch(:description))
group.save!
persist_structure!(group:, attributes:, actor:)
validate_compatibility!(group) if require_compatibility
validate_compatibility!(locale, group) if require_compatibility
persist_version!(group:, attributes:, actor:, event_type: 'create')
group
end
end
def self.update!(group:, name:, description:, members:, placements:, actor:,
def self.update!(locale, group:, name:, description:, members:, placements:, actor:,
require_compatibility: false)
ApplicationRecord.transaction do
group.lock!
attributes = prepare!(group:, name:, description:, members:, placements:)
attributes = prepare!(locale:, group:, name:, description:, members:, placements:)
if unchanged?(group, attributes)
validate_compatibility!(group) if require_compatibility
validate_compatibility!(locale, group) if require_compatibility
next group
end
@@ -32,20 +32,20 @@ class TagGroupOperation
version_no: group.version_no + 1,
updated_by_user: actor)
persist_structure!(group:, attributes:, actor:)
validate_compatibility!(group) if require_compatibility
validate_compatibility!(locale, group) if require_compatibility
persist_version!(group:, attributes:, actor:, event_type: 'update')
group
end
end
def self.validate_compatibility! group
TagGroupMarkdownRenderer.render(TagGroupRepr.one(group))
def self.validate_compatibility! locale, group
TagGroupMarkdownRenderer.render(TagGroupRepr.one(locale, group))
rescue TagGroupMarkdownRenderer::UnsupportedStructure => error
invalid!(group, :base, error.message)
end
private_class_method :validate_compatibility!
def self.prepare!(group:, name:, description:, members:, placements:)
def self.prepare!(locale:, group:, name:, description:, members:, placements:)
record = group
normalized_name = name.to_s.strip
invalid!(record, :name, 'グループ名は必須です.') if normalized_name.blank?
@@ -63,7 +63,7 @@ class TagGroupOperation
member_rows = normalize_members(record, members)
placement_tree = normalize_placements(record, placements)
validate_references!(record, member_rows, placement_tree)
validate_references!(locale, record, member_rows, placement_tree)
validate_containment!(record, member_rows)
{ name: normalized_name, description:, members: member_rows,
placements: placement_tree }
@@ -187,7 +187,7 @@ class TagGroupOperation
end
private_class_method :normalize_placement_nodes
def self.validate_references!(record, members, placements)
def self.validate_references!(locale, record, members, placements)
membership = members.to_set
placed = placement_references(placements).uniq
missing = placed - membership.to_a
@@ -206,13 +206,14 @@ class TagGroupOperation
tag_versions = TagVersion.where(tag_id: tag_ids).pluck(:tag_id, :version_no).to_set
group_versions = TagGroupVersion.where(tag_group_id: group_ids)
.pluck(:tag_group_id, :version_no).to_set
tag_names = TagName.where(tag_id: tag_ids, language_code: 'ja', primary_flg: true)
tag_names = TagName.where(
tag_id: tag_ids, language_code: locale.language_code, primary_flg: true)
.pluck(:tag_id).uniq
members.each do |member|
if member.key?('tag_id')
id = member.fetch('tag_id')
invalid!(record, :members, "Tag ##{ id } に日本語の正規名がありません.") unless
invalid!(record, :members, :invalid) unless
tag_names.include?(id)
version_no = tags.fetch(id).version_no
unless tag_versions.include?([id, version_no])