|
- require 'digest'
-
-
- module Wiki
- class Commit
- class Conflict < StandardError
- ;
- 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 = normalise_body(body)
- 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
-
- 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)
-
- rev
- end
- end
-
- def redirect! redirect_page:, message:, base_revision_id:
- 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
-
- WikiRevision.create!(
- wiki_page: @page,
- base_revision_id:,
- created_user: @created_user,
- kind: :redirect,
- redirect_page:,
- message:,
- lines_count: 0,
- tree_sha256: nil)
- end
- end
-
- private
-
- def normalise_body body
- s = body.to_s
- s.gsub!("\r\n", "\n")
- s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕')
- end
-
- def split_lines body
- body.split("\n")
- end
-
- def upsert_lines! lines, line_shas
- now = Time.current
-
- id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
-
- missing_rows = []
- line_shas.each_with_index do |sha, i|
- next if id_by_sha.key?(sha)
-
- missing_rows << { sha256: sha,
- body: lines[i],
- created_at: now,
- updated_at: now }
- end
-
- if missing_rows.any?
- WikiLine.upsert_all(missing_rows)
- id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
- end
-
- id_by_sha
- end
- end
- end
|