ぼざクリタグ広場 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.
 
 
 
 
 

88 lines
2.3 KiB

  1. class VersionRecorder
  2. EVENT_TYPES = ['create', 'update', 'discard', 'restore'].freeze
  3. def initialize record:, event_type:, created_by_user:
  4. @record = record
  5. @event_type = event_type.to_s
  6. @created_by_user = created_by_user
  7. validate_event_type!
  8. end
  9. def record!
  10. raise "#{ record_class.name } must be persisted" unless @record.persisted?
  11. ApplicationRecord.transaction do
  12. @record = record_class.unscoped.lock.find(@record.id)
  13. latest = latest_version
  14. validate_version_sequence! latest
  15. attrs = snapshot_attributes
  16. if @event_type == 'update' && latest && same_snapshot?(latest, attrs)
  17. return latest
  18. end
  19. version = version_class.create!(
  20. base_attributes(latest).merge(record_key => @record).merge(attrs))
  21. update_record_version_no! version.version_no
  22. version
  23. end
  24. end
  25. private
  26. def latest_version = versions.order(version_no: :desc).first
  27. def versions = @record.public_send(version_association)
  28. def base_attributes latest
  29. { version_no: (latest&.version_no || 0) + 1,
  30. event_type: @event_type,
  31. created_at: Time.current,
  32. created_by_user: @created_by_user }
  33. end
  34. def update_record_version_no! version_no
  35. @record.update_columns version_no: version_no
  36. @record.version_no = version_no
  37. end
  38. def validate_version_sequence! latest
  39. if !(latest) && @event_type != 'create'
  40. raise "#{ version_class.name } first event must be create"
  41. end
  42. if @event_type == 'create' && latest
  43. raise "#{ version_class.name } create event already exists"
  44. end
  45. return unless latest
  46. if @record.version_no != latest.version_no
  47. raise ("#{ record_class.name }##{ @record.id } version_no is #{ @record.version_no }, " +
  48. "but latest #{ version_class.name } version_no is #{ latest.version_no }")
  49. end
  50. end
  51. def same_snapshot? version, attrs
  52. attrs.all? { |k, v| version.public_send(k) == v }
  53. end
  54. def validate_event_type!
  55. return if EVENT_TYPES.include?(@event_type)
  56. raise ArgumentError, "Invalid event_type: #{ @event_type }"
  57. end
  58. def version_class = raise NotImplementedError
  59. def version_association = raise NotImplementedError
  60. def record_key = raise NotImplementedError
  61. def snapshot_attributes = raise NotImplementedError
  62. def record_class = @record.class
  63. end