フォームのバリデーションとニコ連携の画面変更 (#090) (#355)

Reviewed-on: #355
Co-authored-by: miteruzo <miteruzo@naver.com>
Co-committed-by: miteruzo <miteruzo@naver.com>
このコミットはPull リクエスト #355 でマージされました.
このコミットが含まれているのは:
2026-06-05 01:59:46 +09:00
committed by みてるぞ
コミット 750aa40e8e
66個のファイルの変更2624行の追加802行の削除
+48
ファイルの表示
@@ -1,4 +1,7 @@
class ApplicationController < ActionController::API
rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid
rescue_from ActiveRecord::RecordNotUnique, with: :render_record_not_unique
before_action :reject_banned_ip_address!
before_action :authenticate_user
before_action :reject_banned_user!
@@ -25,6 +28,27 @@ class ApplicationController < ActionController::API
end
end
def render_bad_request message = 'リクエストが不正です.'
render json: { type: 'bad_request',
message:,
errors: { },
base_errors: [message] },
status: :bad_request
end
def render_unprocessable_entity message = '入力を確認してください.', field: nil
render_validation_error(fields: field ? { field => [message] } : { },
base: field ? [] : [message])
end
def render_record_invalid error
render_validation_error error.record
end
def render_record_not_unique _error = nil
render_validation_error base: ['すでに存在してゐます.']
end
def reject_banned_ip_address!
ip_address = IpAddress.find_by(ip_address: IPAddr.new(request.remote_ip).hton)
return unless ip_address&.banned?
@@ -37,4 +61,28 @@ class ApplicationController < ActionController::API
head :forbidden
end
def render_validation_error record = nil, fields: { }, base: [], status: :unprocessable_entity
errors = { }
if record
record.errors.each do |error|
errors[error.attribute] ||= []
errors[error.attribute] << error.message
end
end
fields.each do |attr, messages|
errors[attr.to_sym] ||= []
errors[attr.to_sym].concat(Array(messages))
end
base_errors = Array(base) + Array(errors.delete(:base))
render json: { type: 'validation_error',
message: '入力内容を確認してください.',
errors:,
base_errors: },
status:
end
end
+7 -3
ファイルの表示
@@ -2,7 +2,8 @@ class DeerjikistsController < ApplicationController
def show
platform = params[:platform].to_s.strip
code = params[:code].to_s.strip
return head :bad_request if platform.blank? || code.blank?
return render_bad_request('platform は必須です.') if platform.blank?
return render_bad_request('code は必須です.') if code.blank?
deerjikist = Deerjikist
.joins(:tag)
@@ -22,7 +23,9 @@ class DeerjikistsController < ApplicationController
platform = params[:platform].to_s.strip
code = params[:code].to_s.strip
tag_id = params[:tag_id].to_i
return head :bad_request if platform.blank? || code.blank? || tag_id <= 0
return render_bad_request('platform は必須です.') if platform.blank?
return render_bad_request('code は必須です.') if code.blank?
return render_bad_request('tag_id が不正です.') if tag_id <= 0
deerjikist = Deerjikist.find_or_initialize_by(platform:, code:).tap do |d|
d.tag_id = tag_id
@@ -38,7 +41,8 @@ class DeerjikistsController < ApplicationController
platform = params[:platform].to_s.strip
code = params[:code].to_s.strip
return head :bad_request if platform.blank? || code.blank?
return render_bad_request('platform は必須です.') if platform.blank?
return render_bad_request('code は必須です.') if code.blank?
Deerjikist.find([platform, code]).destroy!
+12 -4
ファイルの表示
@@ -40,7 +40,11 @@ class MaterialsController < ApplicationController
tag_name_raw = params[:tag].to_s.strip
file = params[:file]
url = params[:url].to_s.strip.presence
return head :bad_request if tag_name_raw.blank? || (file.blank? && url.blank?)
return render_unprocessable_entity('タグは必須です.', field: :tag) if tag_name_raw.blank?
if file.blank? && url.blank?
return render_validation_error fields: { file: ['ファイルまたは URL は必須です.'],
url: ['ファイルまたは URL は必須です.'] }
end
tag_name = TagName.find_undiscard_or_create_by!(name: tag_name_raw)
tag = tag_name.tag
@@ -54,7 +58,7 @@ class MaterialsController < ApplicationController
if material.save
render json: MaterialRepr.base(material, host: request.base_url), status: :created
else
render json: { errors: material.errors.full_messages }, status: :unprocessable_entity
render_validation_error material
end
end
@@ -68,7 +72,11 @@ class MaterialsController < ApplicationController
tag_name_raw = params[:tag].to_s.strip
file = params[:file]
url = params[:url].to_s.strip.presence
return head :bad_request if tag_name_raw.blank? || (file.blank? && url.blank?)
return render_unprocessable_entity('タグは必須です.', field: :tag) if tag_name_raw.blank?
if file.blank? && url.blank?
return render_validation_error fields: { file: ['ファイルまたは URL は必須です.'],
url: ['ファイルまたは URL は必須です.'] }
end
tag_name = TagName.find_undiscard_or_create_by!(name: tag_name_raw)
tag = tag_name.tag
@@ -84,7 +92,7 @@ class MaterialsController < ApplicationController
if material.save
render json: MaterialRepr.base(material, host: request.base_url)
else
render json: { errors: material.errors.full_messages }, status: :unprocessable_entity
render_validation_error material
end
end
+82 -19
ファイルの表示
@@ -1,26 +1,69 @@
class NicoTagsController < ApplicationController
def index
limit = (params[:limit] || 20).to_i
cursor = params[:cursor].presence
name = params[:name].presence
linked_tag = params[:linked_tag].presence
link_status = params[:link_status].presence
order = params[:order].to_s.split(':', 2).map(&:strip)
order[0] = 'updated_at' unless order[0].in?(['name', 'created_at', 'updated_at'])
unless order[1].in?(['asc', 'desc'])
order[1] = order[0] == 'name' ? 'asc' : 'desc'
end
page = (params[:page].presence || 1).to_i
limit = (params[:limit].presence || 20).to_i
page = 1 if page < 1
limit = 1 if limit < 1
post_tag_max_sql =
PostTag
.select('tag_id, MAX(created_at) AS max_created_at')
.group('tag_id')
.to_sql
q = Tag.nico_tags
.joins(:tag_name)
.joins("LEFT JOIN (#{ post_tag_max_sql }) post_tag_max " \
'ON post_tag_max.tag_id = tags.id')
.includes(:tag_name, tag_name: :wiki_page, linked_tags: { tag_name: :wiki_page })
.order(updated_at: :desc)
q = q.where('tags.updated_at < ?', Time.iso8601(cursor)) if cursor
tags = q.limit(limit + 1).to_a
next_cursor = nil
if tags.size > limit
next_cursor = tags.last.updated_at.iso8601(6)
tags = tags.first(limit)
q = q.where('tag_names.name LIKE ?', "%#{ name }%") if name
if linked_tag
linked_tag_ids =
Tag
.joins(:tag_name)
.where('tag_names.name LIKE ?', "%#{ linked_tag }%")
.pluck(:id)
linked_nico_tag_ids = NicoTagRelation.where(tag_id: linked_tag_ids).pluck(:nico_tag_id)
q = q.where(id: linked_nico_tag_ids)
end
if link_status.in?(['linked', 'unlinked'])
exists_sql =
'EXISTS (SELECT 1 FROM nico_tag_relations ' \
'WHERE nico_tag_relations.nico_tag_id = tags.id)'
q = link_status == 'linked' ? q.where(exists_sql) : q.where("NOT #{ exists_sql }")
end
count = q.count
sort_sql =
case order[0]
when 'name'
'tag_names.name'
when 'updated_at'
'post_tag_max.max_created_at'
else
"tags.#{ order[0] }"
end
tags = q.reselect('tags.*',
Arel.sql('post_tag_max.max_created_at AS recent_post_tag_created_at'))
.order(Arel.sql("#{ sort_sql } #{ order[1] }, tags.id #{ order[1] }"))
.limit(limit)
.offset((page - 1) * limit)
.to_a
render json: { tags: tags.map { |tag|
TagRepr.base(tag).merge(linked_tags: tag.linked_tags.map { |lt|
TagRepr.base(lt)
})
}, next_cursor: }
TagRepr.base(tag).merge(
recent_post_tag_created_at: tag.recent_post_tag_created_at,
linked_tags: tag.linked_tags.map { |lt| TagRepr.base(lt) })
}, count: }
end
def update
@@ -30,14 +73,18 @@ class NicoTagsController < ApplicationController
id = params[:id].to_i
tag = Tag.find(id)
return head :bad_request unless tag.nico?
return render_bad_request('ニコニコ・タグを指定してください.') unless tag.nico?
linked_tag_names = params[:tags].to_s.split
linked_tags = Tag.normalise_tags!(linked_tag_names, with_tagme: false,
with_no_deerjikist: false)
return head :bad_request if linked_tags.any? { |t| t.nico? }
linked_tags = nil
ApplicationRecord.transaction do
linked_tags = Tag.normalise_tags!(linked_tag_names, with_tagme: false,
with_no_deerjikist: false)
if linked_tags.any? { |t| t.nico? }
raise Tag::NicoTagNormalisationError
end
TagVersioning.record_tag_snapshots!(linked_tags, created_by_user: current_user)
tag.linked_tags = linked_tags
@@ -47,5 +94,21 @@ class NicoTagsController < ApplicationController
end
render json: tag.linked_tags.map { |t| TagRepr.base(t) }, status: :ok
rescue Tag::NicoTagNormalisationError
render_validation_error fields: { tags: ['ニコニコ・タグ同士は連携できません.'] }
rescue ActiveRecord::RecordInvalid => e
render_nico_tag_form_record_invalid e.record
end
private
def render_nico_tag_form_record_invalid record
if record.is_a?(TagName) || record.is_a?(Tag)
render_validation_error fields: { tags: record.errors.full_messages.map { |message|
"タグ名 “#{ record.name }”: #{ message }"
} }
else
render_validation_error record
end
end
end
+57 -18
ファイルの表示
@@ -44,7 +44,7 @@ class PostsController < ApplicationController
filtered_posts
.joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id")
.reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all"))
.preload(tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
.preload(:uploaded_user, tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
.with_attached_thumbnail
q = q.where('posts.url LIKE ?', "%#{ url }%") if url
@@ -95,7 +95,9 @@ class PostsController < ApplicationController
end
def random
post = filtered_posts.preload(tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
post = filtered_posts.preload(:uploaded_user,
tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
.with_attached_thumbnail
.order('RAND()')
.first
return head :not_found unless post
@@ -104,12 +106,24 @@ class PostsController < ApplicationController
end
def show
post = Post.includes(tags: [:deerjikists, :materials, { tag_name: :wiki_page }]).find_by(id: params[:id])
post =
Post
.includes(:uploaded_user, tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
.with_attached_thumbnail
.find_by(id: params[:id])
return head :not_found unless post
render json: PostRepr.base(post, current_user)
.merge(tags: build_tag_tree_for(post.tags),
related: PostRepr.many(post.related(limit: 20)))
parent_posts = post.parents.with_attached_thumbnail.order(:id).to_a
child_posts = post.children.with_attached_thumbnail.order(:id).to_a
sibling_posts = sibling_posts_by_parent(parent_posts.map(&:id))
related = post.related(limit: 20).to_a
render json: PostRepr.detail(post, current_user,
parent_posts:,
child_posts:,
sibling_posts:,
related:)
.merge(tags: build_tag_tree_for(post.tags))
end
def create
@@ -148,11 +162,11 @@ class PostsController < ApplicationController
post.reload
render json: PostRepr.base(post), status: :created
rescue Tag::NicoTagNormalisationError
head :bad_request
render_validation_error fields: { tags: 'ニコニコ・タグは直接指定できません.' }
rescue ArgumentError => e
render json: { errors: [e.message] }, status: :unprocessable_entity
render_validation_error fields: { parent_post_ids: [e.message] }
rescue ActiveRecord::RecordInvalid => e
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
render_post_form_record_invalid e.record
end
def viewed
@@ -175,10 +189,10 @@ class PostsController < ApplicationController
force = bool?(:force)
merge = bool?(:merge)
return head :bad_request if force && merge
return render_bad_request('force と merge は同時に指定できません.') if force && merge
base_version_no = parse_base_version_no
return head :bad_request if !(force) && !(base_version_no)
return render_bad_request('base_version_no は必須です.') if !(force) && !(base_version_no)
title = params[:title].presence
tag_names = params[:tags].to_s.split
@@ -238,11 +252,11 @@ class PostsController < ApplicationController
json['tags'] = build_tag_tree_for(post.tags)
render json:, status: :ok
rescue Tag::NicoTagNormalisationError
head :bad_request
render_validation_error fields: { tags: ['ニコニコ・タグは直接指定できません.'] }
rescue ArgumentError => e
render json: { errors: [e.message] }, status: :unprocessable_entity
render_validation_error fields: { parent_post_ids: [e.message] }
rescue ActiveRecord::RecordInvalid => e
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
render_post_form_record_invalid e.record
end
def changes
@@ -385,7 +399,7 @@ class PostsController < ApplicationController
return nil unless tag
if path.include?(tag_id)
return TagRepr.base(tag).merge(children: [])
return TagRepr.inline(tag).merge(children: [])
end
if memo.key?(tag_id)
@@ -397,12 +411,26 @@ class PostsController < ApplicationController
children = child_ids.filter_map { |cid| build_node.(cid, new_path) }
memo[tag_id] = TagRepr.base(tag).merge(children:)
memo[tag_id] = TagRepr.inline(tag).merge(children:)
end
root_ids.filter_map { |id| build_node.call(id, []) }
end
def sibling_posts_by_parent parent_post_ids
return { } if parent_post_ids.blank?
implications =
PostImplication
.where(parent_post_id: parent_post_ids)
.includes(post: { thumbnail_attachment: :blob })
.order(:parent_post_id, :post_id)
implications.group_by(&:parent_post_id).transform_values { |items|
items.map(&:post)
}
end
def parse_parent_post_ids
raise ArgumentError, 'parent_post_ids は必須です.' unless params.key?(:parent_post_ids)
@@ -416,7 +444,7 @@ class PostsController < ApplicationController
def sync_parent_posts! post, parent_post_ids
if parent_post_ids.include?(post.id)
post.errors.add(:base, '自分自身を親投稿にはできません.')
post.errors.add :parent_post_ids, '自分自身を親投稿にはできません.'
raise ActiveRecord::RecordInvalid, post
end
@@ -424,7 +452,8 @@ class PostsController < ApplicationController
missing_ids = parent_post_ids - existing_ids
if missing_ids.present?
post.errors.add(:base, "存在しない親投稿 ID があります: #{ missing_ids.join(' ') }")
post.errors.add :parent_post_ids,
"存在しない親投稿 Id. があります: #{ missing_ids.join(' ') }"
raise ActiveRecord::RecordInvalid, post
end
@@ -640,4 +669,14 @@ class PostsController < ApplicationController
merged.uniq.sort
end
def render_post_form_record_invalid record
if record.is_a?(TagName) || record.is_a?(Tag)
render_validation_error fields: { tags: record.errors.full_messages.map { |message|
"タグ名 “#{ record.name }”: #{ message }"
} }
else
render_validation_error record
end
end
end
+8 -4
ファイルの表示
@@ -4,7 +4,7 @@ class PreviewController < ApplicationController
return head :unauthorized unless current_user
url = params[:url]
return head :bad_request unless url.present?
return render_bad_request('URL は必須です.') unless url.present?
unless url.start_with?(/http(s)?:\/\//)
url = 'http://' + url
@@ -16,7 +16,7 @@ class PreviewController < ApplicationController
render json: { title: title }
rescue => e
render json: { error: e.message }, status: :bad_request
render_bad_request(e.message)
end
def thumbnail
@@ -25,7 +25,7 @@ class PreviewController < ApplicationController
return head :unauthorized unless current_user
url = params[:url]
return head :bad_request if url.blank?
return render_bad_request('URL は必須です.') if url.blank?
unless url.start_with?(/http(s)?:\/\//)
url = 'http://' + url
@@ -40,7 +40,11 @@ class PreviewController < ApplicationController
File.delete(path) rescue nil
send_file image.path, type: 'image/png', disposition: 'inline'
else
render json: { error: 'Failed to generate thumbnail' }, status: :internal_server_error
render json: { type: 'internal_server_error',
message: 'サムネールを生成できませんでした.',
errors: { },
base_errors: ['サムネールを生成できませんでした.'] },
status: :internal_server_error
end
end
end
+6 -4
ファイルの表示
@@ -5,11 +5,12 @@ class TagChildrenController < ApplicationController
parent_id = params[:parent_id]
child_id = params[:child_id]
return head :bad_request if parent_id.blank? || child_id.blank?
return render_bad_request('parent_id は必須です.') if parent_id.blank?
return render_bad_request('child_id は必須です.') if child_id.blank?
parent = Tag.find(parent_id)
child = Tag.find(child_id)
return head :bad_request if parent.nico? || child.nico?
return render_bad_request('ニコニコ・タグの階層は変更できません.') if parent.nico? || child.nico?
ApplicationRecord.transaction do
TagVersioning.ensure_snapshot!(child, created_by_user: current_user)
@@ -27,11 +28,12 @@ class TagChildrenController < ApplicationController
parent_id = params[:parent_id]
child_id = params[:child_id]
return head :bad_request if parent_id.blank? || child_id.blank?
return render_bad_request('parent_id は必須です.') if parent_id.blank?
return render_bad_request('child_id は必須です.') if child_id.blank?
parent = Tag.find(parent_id)
child = Tag.find(child_id)
return head :bad_request if parent.nico? || child.nico?
return render_bad_request('ニコニコ・タグの階層は変更できません.') if parent.nico? || child.nico?
ApplicationRecord.transaction do
TagVersioning.ensure_snapshot!(child, created_by_user: current_user)
+34 -14
ファイルの表示
@@ -168,7 +168,7 @@ class TagsController < ApplicationController
def show_by_name
name = params[:name].to_s.strip
return head :bad_request if name.blank?
return render_bad_request('name は必須です.') if name.blank?
tag = Tag.joins(:tag_name)
.includes(:tag_name, :materials, tag_name: :wiki_page)
@@ -192,7 +192,7 @@ class TagsController < ApplicationController
def deerjikists_by_name
name = params[:name].to_s.strip
return head :bad_request if name.blank?
return render_bad_request('name は必須です.') if name.blank?
tag = Tag.joins(:tag_name)
.includes(:tag_name, tag_name: :wiki_page)
@@ -214,21 +214,24 @@ class TagsController < ApplicationController
ApplicationRecord.transaction do
tag.deerjikists = []
params[:_json].each do
platform = _1[:platform]
code = normalise_deerjikist_code(platform, _1[:code])
params[:_json].each.with_index do |item, i|
platform = item[:platform]
code = normalise_deerjikist_code(platform, item[:code])
deerjikist = Deerjikist.find_or_initialize_by(platform:, code:)
deerjikist.tag = tag
deerjikist.save!
render_deerjikist_form_record_invalid(deerjikist, i) unless deerjikist.save
raise ActiveRecord::Rollback if performed?
end
end
return if performed?
render json: DeerjikistRepr.many(tag.reload.deerjikists)
end
def materials_by_name
name = params[:name].to_s.strip
return head :bad_request if name.blank?
return render_bad_request('name は必須です.') if name.blank?
tag = Tag.joins(:tag_name)
.includes(:tag_name, :materials, tag_name: :wiki_page)
@@ -247,17 +250,16 @@ class TagsController < ApplicationController
name = params[:name].to_s.strip
category = params[:category].to_s.strip
return head :unprocessable_entity if name.blank? || category.blank?
return render_unprocessable_entity('名前は必須です.', field: :name) if name.blank?
return render_unprocessable_entity('カテゴリは必須です.', field: :category) if category.blank?
if name != tag.name &&
tag.in?([Tag.tagme, Tag.bot, Tag.no_deerjikist, Tag.video, Tag.niconico])
return render json: { error: 'システム・タグの名称は変更できません.' },
status: :unprocessable_entity
return render_unprocessable_entity('システム・タグの名称は変更できません.', field: :name)
end
if tag.nico? || category == 'nico'
return render json: { error: 'ニコタグは変更できません.' },
status: :unprocessable_entity
return render_unprocessable_entity('ニコタグは変更できません.', field: :category)
end
alias_names = params[:aliases].to_s.split.uniq
@@ -302,8 +304,7 @@ class TagsController < ApplicationController
tag = Tag.find(params[:id])
if tag.nico? || (category.present? && category == 'nico')
return render json: { error: 'ニコタグは変更できません.' },
status: :unprocessable_entity
return render_unprocessable_entity('ニコタグは変更できません.', field: :category)
end
ApplicationRecord.transaction do
@@ -437,4 +438,23 @@ class TagsController < ApplicationController
rescue
nil
end
def render_deerjikist_form_record_invalid deerjikist, index
fields = { }
deerjikist.errors.each do |error|
field =
case error.attribute
when :platform, :code
"deerjikists.#{ index }.#{ error.attribute }"
else
:deerjikists
end
fields[field] ||= []
fields[field] << error.full_message
end
render_validation_error fields:
end
end
+1 -1
ファイルの表示
@@ -15,7 +15,7 @@ class TheatreCommentsController < ApplicationController
return head :unauthorized unless current_user
content = params[:content]
return head :unprocessable_entity if content.blank?
return render_unprocessable_entity('本文は必須です.', field: :content) if content.blank?
theatre = Theatre.find_by(id: params[:theatre_id])
return head :not_found unless theatre
+2 -2
ファイルの表示
@@ -42,12 +42,12 @@ class UsersController < ApplicationController
return head :unauthorized if user&.id != params[:id].to_i
name = params[:name]
return head :bad_request if name.blank?
return render_unprocessable_entity('名前は必須です.', field: :name) if name.blank?
if user.update(name:)
render json: user.slice(:id, :name, :inheritance_code, :role), status: :ok
else
render json: user.errors, status: :unprocessable_entity
render_validation_error user
end
end
+10 -6
ファイルの表示
@@ -46,7 +46,7 @@ class WikiPagesController < ApplicationController
def diff
id = params[:id]
return head :bad_request if id.blank?
return render_bad_request('id は必須です.') if id.blank?
from = params[:from].presence
to = params[:to].presence
@@ -56,7 +56,7 @@ class WikiPagesController < ApplicationController
from_rev = from && page.wiki_revisions.find(from)
to_rev = to ? page.wiki_revisions.find(to) : page.current_revision
if ((from_rev && !(from_rev.content?)) || !(to_rev&.content?))
return head :unprocessable_entity
return render_unprocessable_entity('差分を表示できない版です.')
end
diffs = Diff::LCS.sdiff(from_rev&.body&.lines || [], to_rev.body.lines)
@@ -89,7 +89,8 @@ class WikiPagesController < ApplicationController
body = params[:body].to_s
message = params[:message].presence
return head :unprocessable_entity if title.blank? || body.blank?
return render_unprocessable_entity('タイトルは必須です.', field: :title) if title.blank?
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
tag_name = TagName.find_undiscard_or_create_by!(name: title)
@@ -101,8 +102,10 @@ class WikiPagesController < ApplicationController
message:)
render json: WikiPageRepr.base(page), status: :created
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
head :unprocessable_entity
rescue ActiveRecord::RecordInvalid => e
render_validation_error e.record
rescue ActiveRecord::RecordNotUnique
render_record_not_unique
end
def update
@@ -112,7 +115,8 @@ class WikiPagesController < ApplicationController
title = params[:title]&.strip
body = params[:body].to_s
return head :unprocessable_entity if title.blank? || body.blank?
return render_unprocessable_entity('タイトルは必須です.', field: :title) if title.blank?
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
page = WikiPage.find(params[:id])
base_revision_id = params[:base_revision_id].presence
+1 -1
ファイルの表示
@@ -94,7 +94,7 @@ class Post < ApplicationRecord
return if !(f) || !(b)
if f >= b
errors.add :original_created_before, 'オリジナルの作成日時の順番がをかしぃです.'
errors.add :original_created_at, 'オリジナルの作成日時の順番がをかしぃです.'
end
end
+51 -6
ファイルの表示
@@ -2,20 +2,65 @@
module PostRepr
BASE = { include: { tags: TagRepr::BASE, uploaded_user: UserRepr::BASE },
methods: [:parent_posts, :child_posts, :sibling_posts] }.freeze
BASE_FIELDS = [
:id,
:version_no,
:url,
:title,
:thumbnail_base,
:original_created_from,
:original_created_before,
:created_at,
:updated_at
].freeze
module_function
def base post, current_user = nil
json = post.as_json(BASE)
return json.merge(viewed: false) unless current_user
json = common(post)
json['tags'] = tag_json(post.tags)
json['uploaded_user'] = post.uploaded_user && UserRepr.base(post.uploaded_user)
json['viewed'] = current_user ? current_user.viewed?(post) : false
json
end
viewed = current_user.viewed?(post)
json.merge(viewed:)
def detail post, current_user = nil, parent_posts: [], child_posts: [],
sibling_posts: { }, related: []
base(post, current_user).merge(
'parent_posts' => cards(parent_posts),
'child_posts' => cards(child_posts),
'sibling_posts' => sibling_posts.transform_keys(&:to_s).transform_values { |posts|
cards(posts)
},
'related' => cards(related))
end
def card post
common(post).merge('parent_posts' => [], 'child_posts' => [])
end
def cards posts
posts.map { |post| card(post) }
end
def many posts, current_user = nil
posts.map { |p| base(p, current_user) }
end
def common post
BASE_FIELDS.to_h { |field| [field.to_s, post.public_send(field)] }
.merge('thumbnail' => thumbnail_url(post))
end
def tag_json tags
tags.map { |tag| TagRepr.inline(tag) }
end
def thumbnail_url post
return nil unless post.thumbnail.attached?
Rails.application.routes.url_helpers.rails_blob_url(post.thumbnail, only_path: false)
rescue
nil
end
end
+4
ファイルの表示
@@ -12,5 +12,9 @@ module TagRepr
parents: tag.parents.map { _1.as_json(BASE) })
end
def inline tag
tag.as_json(BASE).merge(aliases: [], parents: [])
end
def many(tags) = tags.map { |t| base(t) }
end