0ff7fdf78a
#317 #317 #317 #317 #317 #317 Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: #333
142 lines
3.9 KiB
Ruby
142 lines
3.9 KiB
Ruby
require 'digest'
|
||
|
||
|
||
module Wiki
|
||
class Commit
|
||
class Conflict < StandardError
|
||
;
|
||
end
|
||
|
||
def self.create_content! tag_name:, body:, created_by_user:, message: nil
|
||
normalised = normalise_body(body)
|
||
|
||
page = WikiPage.new(tag_name:,
|
||
body: normalised,
|
||
created_user: created_by_user,
|
||
updated_user: created_by_user)
|
||
|
||
if normalised.blank?
|
||
page.errors.add(:body, :blank)
|
||
raise ActiveRecord::RecordInvalid, page
|
||
end
|
||
|
||
ActiveRecord::Base.transaction do
|
||
page.save!
|
||
|
||
new(page:, created_user: created_by_user).content!(
|
||
body: normalised,
|
||
message:,
|
||
base_revision_id: nil)
|
||
|
||
page
|
||
end
|
||
end
|
||
|
||
def self.content! page:, body:, created_user:, message: nil, base_revision_id: nil
|
||
new(page:, created_user:).content!(body:, message:, base_revision_id:)
|
||
end
|
||
|
||
def self.redirect! page:, redirect_page:, created_user:, message: nil, base_revision_id: nil
|
||
new(page:, created_user:).redirect!(redirect_page:, message:, base_revision_id:)
|
||
end
|
||
|
||
def initialize page:, created_user:
|
||
@page = page
|
||
@created_user = created_user
|
||
end
|
||
|
||
def content! body:, message:, base_revision_id:
|
||
normalised = self.class.normalise_body(body)
|
||
if normalised.blank?
|
||
@page.errors.add(:body, :blank)
|
||
raise ActiveRecord::RecordInvalid, @page
|
||
end
|
||
|
||
lines = split_lines(normalised)
|
||
|
||
line_shas = lines.map { |line| Digest::SHA256.hexdigest(line) }
|
||
tree_sha = Digest::SHA256.hexdigest(line_shas.join(','))
|
||
|
||
line_id_by_sha = upsert_lines!(lines, line_shas)
|
||
line_ids = line_shas.map { |sha| line_id_by_sha.fetch(sha) }
|
||
|
||
ActiveRecord::Base.transaction do
|
||
@page.lock!
|
||
|
||
if base_revision_id.present?
|
||
current_id = @page.wiki_revisions.maximum(:id)
|
||
if current_id && current_id != base_revision_id.to_i
|
||
raise Conflict,
|
||
"競合が発生してゐます" +
|
||
"(現在の Id.:#{ current_id },ベース Id.:#{ base_revision_id })."
|
||
end
|
||
end
|
||
|
||
@page.update!(body: normalised)
|
||
|
||
WikiVersionRecorder.record!(
|
||
page: @page,
|
||
event_type: @page.wiki_versions.exists? ? :update : :create,
|
||
reason: message,
|
||
created_by_user: @created_user)
|
||
|
||
rev = WikiRevision.create!(
|
||
wiki_page: @page,
|
||
base_revision_id:,
|
||
created_user: @created_user,
|
||
kind: :content,
|
||
redirect_page_id: nil,
|
||
message:,
|
||
lines_count: lines.length,
|
||
tree_sha256: tree_sha)
|
||
|
||
rows = line_ids.each_with_index.map do |line_id, pos|
|
||
{ wiki_revision_id: rev.id, wiki_line_id: line_id, position: pos }
|
||
end
|
||
WikiRevisionLine.insert_all!(rows) if rows.any?
|
||
|
||
rev
|
||
end
|
||
end
|
||
|
||
def redirect!(redirect_page:, message:, base_revision_id:) = raise '廃止しました.'
|
||
|
||
def self.normalise_body body
|
||
s = body.to_s
|
||
s.gsub!(/\r\n?/, "\n")
|
||
s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕')
|
||
s.gsub(/\n+$/, '')
|
||
end
|
||
|
||
private
|
||
|
||
def split_lines(body) = body.split("\n")
|
||
|
||
def upsert_lines! lines, line_shas
|
||
now = Time.current
|
||
|
||
id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
|
||
|
||
missing_by_sha = { }
|
||
|
||
line_shas.each_with_index do |sha, i|
|
||
next if id_by_sha.key?(sha)
|
||
next if missing_by_sha.key?(sha)
|
||
|
||
missing_by_sha[sha] = {
|
||
sha256: sha,
|
||
body: lines[i],
|
||
created_at: now,
|
||
updated_at: now }
|
||
end
|
||
|
||
if missing_by_sha.any?
|
||
WikiLine.upsert_all(missing_by_sha.values)
|
||
id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
|
||
end
|
||
|
||
id_by_sha
|
||
end
|
||
end
|
||
end
|