ぼざクリタグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

143 lines
4.1 KiB

  1. require 'digest'
  2. module Wiki
  3. class Commit
  4. class Conflict < StandardError
  5. ;
  6. end
  7. def self.create_content! tag_name:, body:, created_by_user:, message: nil
  8. normalised = normalise_body(body)
  9. page = WikiPage.new(tag_name:,
  10. body: normalised,
  11. created_user: created_by_user,
  12. updated_user: created_by_user)
  13. if normalised.blank?
  14. page.errors.add(:body, :blank)
  15. raise ActiveRecord::RecordInvalid, page
  16. end
  17. ActiveRecord::Base.transaction do
  18. page.save!
  19. new(page:, created_user: created_by_user).content!(
  20. body: normalised,
  21. message:,
  22. base_revision_id: nil)
  23. page
  24. end
  25. end
  26. def self.content! page:, body:, created_user:, message: nil, base_revision_id: nil
  27. new(page:, created_user:).content!(body:, message:, base_revision_id:)
  28. end
  29. def self.redirect! page:, redirect_page:, created_user:, message: nil, base_revision_id: nil
  30. new(page:, created_user:).redirect!(redirect_page:, message:, base_revision_id:)
  31. end
  32. def initialize page:, created_user:
  33. @page = page
  34. @created_user = created_user
  35. end
  36. def content! body:, message:, base_revision_id:
  37. normalised = self.class.normalise_body(body)
  38. if normalised.blank?
  39. @page.errors.add(:body, :blank)
  40. raise ActiveRecord::RecordInvalid, @page
  41. end
  42. lines = split_lines(normalised)
  43. line_shas = lines.map { |line| Digest::SHA256.hexdigest(line) }
  44. tree_sha = Digest::SHA256.hexdigest(line_shas.join(','))
  45. line_id_by_sha = upsert_lines!(lines, line_shas)
  46. line_ids = line_shas.map { |sha| line_id_by_sha.fetch(sha) }
  47. ActiveRecord::Base.transaction do
  48. @page.lock!
  49. if base_revision_id.present?
  50. current_id = @page.wiki_revisions.maximum(:id)
  51. if current_id && current_id != base_revision_id.to_i
  52. raise Conflict,
  53. "競合が発生してゐます" +
  54. "(現在の Id.:#{ current_id },ベース Id.:#{ base_revision_id })."
  55. end
  56. end
  57. @page.update!(body: normalised)
  58. WikiVersionRecorder.record!(
  59. page: @page,
  60. event_type: @page.wiki_versions.exists? ? :update : :create,
  61. reason: message,
  62. created_by_user: @created_user)
  63. tag = @page.tag_name.tag
  64. if tag&.tag_versions&.exists?
  65. TagVersionRecorder.record!(tag:, event_type: :update, created_by_user: @created_user)
  66. end
  67. rev = WikiRevision.create!(
  68. wiki_page: @page,
  69. base_revision_id:,
  70. created_user: @created_user,
  71. kind: :content,
  72. redirect_page_id: nil,
  73. message:,
  74. lines_count: lines.length,
  75. tree_sha256: tree_sha)
  76. rows = line_ids.each_with_index.map do |line_id, pos|
  77. { wiki_revision_id: rev.id, wiki_line_id: line_id, position: pos }
  78. end
  79. WikiRevisionLine.insert_all!(rows) if rows.any?
  80. rev
  81. end
  82. end
  83. def redirect!(redirect_page:, message:, base_revision_id:) = raise '廃止しました.'
  84. def self.normalise_body body
  85. s = body.to_s
  86. s.gsub!(/\r\n?/, "\n")
  87. s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕')
  88. s.gsub(/\n+$/, '')
  89. end
  90. private
  91. def split_lines(body) = body.split("\n")
  92. def upsert_lines! lines, line_shas
  93. now = Time.current
  94. id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
  95. missing_rows = []
  96. line_shas.each_with_index do |sha, i|
  97. next if id_by_sha.key?(sha)
  98. missing_rows << { sha256: sha,
  99. body: lines[i],
  100. created_at: now,
  101. updated_at: now }
  102. end
  103. if missing_rows.any?
  104. WikiLine.upsert_all(missing_rows)
  105. id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
  106. end
  107. id_by_sha
  108. end
  109. end
  110. end