155 行
5.3 KiB
Ruby
155 行
5.3 KiB
Ruby
class PostCreator
|
|
class VideoMsParseError < ArgumentError; end
|
|
|
|
attr_reader :field_warnings
|
|
|
|
def initialize actor:, attributes:
|
|
@actor = actor
|
|
@attributes = attributes.symbolize_keys
|
|
@field_warnings = { }
|
|
end
|
|
|
|
def create!
|
|
thumbnail_attachment = prepare_thumbnail_attachment
|
|
post = Post.new(title: @attributes[:title].presence,
|
|
url: @attributes[:url],
|
|
thumbnail_base: @attributes[:thumbnail_base].presence,
|
|
uploaded_user: @actor,
|
|
original_created_from: @attributes[:original_created_from].presence,
|
|
original_created_before: @attributes[:original_created_before].presence)
|
|
|
|
ApplicationRecord.transaction do
|
|
post.save!
|
|
post.thumbnail.attach(thumbnail_attachment) if thumbnail_attachment.present?
|
|
snapshot_tags = planned_snapshot_tags
|
|
post_tags = planned_post_tags
|
|
sections = planned_sections
|
|
TagVersioning.record_tag_snapshots!(snapshot_tags, created_by_user: @actor)
|
|
post.video_ms = planned_video_ms
|
|
post.save!
|
|
sync_post_tags!(post, post_tags, sections)
|
|
sync_parent_posts!(post, planned_parent_post_ids)
|
|
PostVersionRecorder.record!(post:, event_type: :create, created_by_user: @actor)
|
|
end
|
|
post
|
|
rescue StandardError
|
|
post&.thumbnail&.purge if post&.thumbnail&.attached?
|
|
raise
|
|
end
|
|
|
|
private
|
|
|
|
def prepare_thumbnail_attachment
|
|
PostThumbnailAttachmentBuilder.build(
|
|
thumbnail: @attributes[:thumbnail],
|
|
thumbnail_base: @attributes[:thumbnail_base].presence)
|
|
end
|
|
|
|
def planned_snapshot_tags = planned_create_attributes[:snapshot_tags]
|
|
|
|
def planned_post_tags = planned_create_attributes[:post_tags]
|
|
|
|
def planned_sections = planned_create_attributes[:tag_sections]
|
|
|
|
def planned_parent_post_ids = planned_create_attributes[:normalised_parent_post_ids]
|
|
|
|
def planned_video_ms
|
|
planned_create_attributes[:video_ms]
|
|
end
|
|
|
|
def planned_create_attributes
|
|
@planned_create_attributes ||= begin
|
|
if @attributes.key?(:snapshot_tag_specs)
|
|
snapshot_tags = materialise_tags(@attributes[:snapshot_tag_specs] || [])
|
|
post_tags = materialise_tags(@attributes[:post_tag_specs] || [])
|
|
{
|
|
snapshot_tags: snapshot_tags,
|
|
post_tags: post_tags,
|
|
tag_sections: materialise_sections(
|
|
@attributes[:tag_sections] || { },
|
|
snapshot_tags,
|
|
post_tags),
|
|
normalised_parent_post_ids: @attributes[:normalised_parent_post_ids] || [],
|
|
video_ms: @attributes[:video_ms] }
|
|
else
|
|
build_materialised_plan
|
|
end
|
|
end
|
|
end
|
|
|
|
def build_materialised_plan
|
|
plan = PostCreatePlan.new(attributes: @attributes).build!
|
|
snapshot_tags = materialise_tags(plan[:snapshot_tag_specs] || [])
|
|
post_tags = materialise_tags(plan[:post_tag_specs] || [])
|
|
{
|
|
snapshot_tags: snapshot_tags,
|
|
post_tags: post_tags,
|
|
tag_sections: materialise_sections(
|
|
plan[:tag_sections] || { },
|
|
snapshot_tags,
|
|
post_tags),
|
|
normalised_parent_post_ids: plan[:normalised_parent_post_ids] || [],
|
|
video_ms: plan[:video_ms] }
|
|
end
|
|
|
|
def materialise_tags specs
|
|
Array(specs).each_with_object({ }) do |spec, tags|
|
|
name = spec[:name] || spec['name']
|
|
category = spec[:category] || spec['category']
|
|
next if name.blank? || category.blank?
|
|
|
|
tag = Tag.find_or_create_by_tag_name!(name, category:)
|
|
tag.update!(category:) if tag.category.to_sym != category.to_sym
|
|
tags[name] ||= tag
|
|
end.values
|
|
end
|
|
|
|
def materialise_sections sections_by_name, snapshot_tags, post_tags
|
|
tags_by_name = post_tags.index_by(&:name)
|
|
snapshot_tags.each do |tag|
|
|
tags_by_name[tag.name] ||= tag
|
|
end
|
|
|
|
sections_by_name.each_with_object({ }) do |(tag_name, ranges), sections|
|
|
tag = tags_by_name[tag_name.to_s]
|
|
next if tag.nil?
|
|
|
|
sections[tag.id] = Array(ranges).map { |range| [range[0], range[1]] }
|
|
end
|
|
end
|
|
|
|
def sync_post_tags! post, desired_tags, sections
|
|
desired_ids = desired_tags.map(&:id).to_set
|
|
current_ids = post.tags.pluck(:id).to_set
|
|
Tag.where(id: desired_ids - current_ids).find_each do |tag|
|
|
PostTag.create_or_find_by!(post:, tag:, created_user: @actor)
|
|
end
|
|
PostTagSection.where(post_id: post.id).destroy_all
|
|
sections.each do |tag_id, ranges|
|
|
ranges.each do |begin_ms, end_ms|
|
|
PostTagSection.create!(post_id: post.id,
|
|
tag_id:,
|
|
begin_ms:,
|
|
end_ms:)
|
|
end
|
|
end
|
|
PostTag.where(post_id: post.id,
|
|
tag_id: (current_ids - desired_ids).to_a).kept.find_each do |post_tag|
|
|
post_tag.discard_by!(@actor)
|
|
end
|
|
end
|
|
|
|
def sync_parent_posts! post, ids
|
|
if ids.include?(post.id)
|
|
post.errors.add :parent_post_ids, '自分自身を親投稿にはできません.'
|
|
raise ActiveRecord::RecordInvalid, post
|
|
end
|
|
missing = ids - Post.where(id: ids).pluck(:id)
|
|
if missing.present?
|
|
post.errors.add :parent_post_ids, "存在しない親投稿 Id. があります: #{ missing.join(' ') }"
|
|
raise ActiveRecord::RecordInvalid, post
|
|
end
|
|
ids.each { |parent_post_id| PostImplication.create_or_find_by!(post:, parent_post_id:) }
|
|
end
|
|
end
|