Class: PaperTrail::RecordTrail
- Inherits:
-
Object
- Object
- PaperTrail::RecordTrail
- Defined in:
- lib/mongo_trails/record_trail.rb
Overview
Represents the “paper trail” for a single record.
Constant Summary collapse
- RAILS_GTE_5_1 =
::ActiveRecord.gem_version >= ::Gem::Version.new("5.1.0.beta1")
Instance Method Summary collapse
-
#clear_rolled_back_versions ⇒ Object
Invoked after rollbacks to ensure versions records are not created for changes that never actually took place.
-
#clear_version_instance ⇒ Object
Invoked via`after_update` callback for when a previous version is reified and then saved.
-
#data_for_create ⇒ Object
private
PT-AT extends this method to add its transaction id.
-
#data_for_destroy ⇒ Object
private
PT-AT extends this method to add its transaction id.
-
#data_for_update ⇒ Object
private
PT-AT extends this method to add its transaction id.
-
#data_for_update_columns ⇒ Object
private
PT-AT extends this method to add its transaction id.
-
#enabled? ⇒ Boolean
private
Is PT enabled for this particular record?.
-
#initialize(record) ⇒ RecordTrail
constructor
A new instance of RecordTrail.
-
#live? ⇒ Boolean
Returns true if this instance is the current, live one; returns false if this instance came from a previous version.
-
#next_version ⇒ Object
Returns the object (not a Version) as it became next.
-
#originator ⇒ Object
Returns who put ‘@record` into its current state.
-
#previous_version ⇒ Object
Returns the object (not a Version) as it was most recently.
- #record_create ⇒ Object
-
#record_destroy(recording_order) ⇒ Object
private
‘recording_order` is “after” or “before”.
-
#record_update(force:, in_after_callback:, is_touch:) ⇒ Object
private
paper_trail-association_tracking.
-
#record_update_columns(changes) ⇒ Object
private
paper_trail-association_tracking.
-
#reset_timestamp_attrs_for_update_if_needed ⇒ Object
Invoked via callback when a user attempts to persist a reified ‘Version`.
-
#save_version? ⇒ Boolean
private
AR callback.
-
#save_with_version(*args) ⇒ Object
Save, and create a version record regardless of options such as ‘:on`, `:if`, or `:unless`.
- #source_version ⇒ Object
-
#update_column(name, value) ⇒ Object
Like the ‘update_column` method from `ActiveRecord::Persistence`, but also creates a version to record those changes.
-
#update_columns(attributes) ⇒ Object
Like the ‘update_columns` method from `ActiveRecord::Persistence`, but also creates a version to record those changes.
-
#version_at(timestamp, reify_options = {}) ⇒ Object
Returns the object (not a Version) as it was at the given timestamp.
-
#versions_between(start_time, end_time) ⇒ Object
Returns the objects (not Versions) as they were between the given times.
- #versions_reset ⇒ Object
Constructor Details
#initialize(record) ⇒ RecordTrail
Returns a new instance of RecordTrail.
12 13 14 |
# File 'lib/mongo_trails/record_trail.rb', line 12 def initialize(record) @record = record end |
Instance Method Details
#clear_rolled_back_versions ⇒ Object
Invoked after rollbacks to ensure versions records are not created for changes that never actually took place. Optimization: Use lazy ‘reset` instead of eager `reload` because, in many use cases, the association will not be used.
20 21 22 |
# File 'lib/mongo_trails/record_trail.rb', line 20 def clear_rolled_back_versions versions_reset end |
#clear_version_instance ⇒ Object
Invoked via`after_update` callback for when a previous version is reified and then saved.
30 31 32 |
# File 'lib/mongo_trails/record_trail.rb', line 30 def clear_version_instance @record.send("#{@record.class.version_association_name}=", nil) end |
#data_for_create ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
PT-AT extends this method to add its transaction id.
87 88 89 |
# File 'lib/mongo_trails/record_trail.rb', line 87 def data_for_create {} end |
#data_for_destroy ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
PT-AT extends this method to add its transaction id.
117 118 119 |
# File 'lib/mongo_trails/record_trail.rb', line 117 def data_for_destroy {} end |
#data_for_update ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
PT-AT extends this method to add its transaction id.
148 149 150 |
# File 'lib/mongo_trails/record_trail.rb', line 148 def data_for_update {} end |
#data_for_update_columns ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
PT-AT extends this method to add its transaction id.
175 176 177 |
# File 'lib/mongo_trails/record_trail.rb', line 175 def data_for_update_columns {} end |
#enabled? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Is PT enabled for this particular record?
36 37 38 39 40 |
# File 'lib/mongo_trails/record_trail.rb', line 36 def enabled? PaperTrail.enabled? && PaperTrail.request.enabled? && PaperTrail.request.enabled_for_model?(@record.class) end |
#live? ⇒ Boolean
Returns true if this instance is the current, live one; returns false if this instance came from a previous version.
44 45 46 |
# File 'lib/mongo_trails/record_trail.rb', line 44 def live? source_version.nil? end |
#next_version ⇒ Object
Returns the object (not a Version) as it became next. NOTE: if self (the item) was not reified from a version, i.e. it is the
"live" item, we return nil. Perhaps we should return self instead?
51 52 53 54 55 56 |
# File 'lib/mongo_trails/record_trail.rb', line 51 def next_version subsequent_version = source_version.next subsequent_version ? subsequent_version.reify : @record.class.find(@record.id) rescue StandardError # TODO: Rescue something more specific nil end |
#originator ⇒ Object
Returns who put ‘@record` into its current state.
61 62 63 |
# File 'lib/mongo_trails/record_trail.rb', line 61 def originator (source_version || versions.last).try(:whodunnit) end |
#previous_version ⇒ Object
Returns the object (not a Version) as it was most recently.
68 69 70 |
# File 'lib/mongo_trails/record_trail.rb', line 68 def previous_version (source_version ? source_version.previous : versions.last).try(:reify) end |
#record_create ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/mongo_trails/record_trail.rb', line 72 def record_create return unless enabled? build_version_on_create(in_after_callback: true).tap do |version| version.save! # Because the version object was created using version_class.new instead # of versions_assoc.build?, the association cache is unaware. So, we # invalidate the `versions` association cache with `reset`. versions_reset end end |
#record_destroy(recording_order) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
‘recording_order` is “after” or “before”. See ModelConfig#on_destroy.
paper_trail-association_tracking
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/mongo_trails/record_trail.rb', line 96 def record_destroy(recording_order) return unless enabled? && !@record.new_record? in_after_callback = recording_order == "after" event = Events::Destroy.new(@record, in_after_callback) # Merge data from `Event` with data from PT-AT. We no longer use # `data_for_destroy` but PT-AT still does. data = event.data.merge(data_for_destroy) version = @record.class.paper_trail.version_class.create(data) if version.errors.any? log_version_errors(version, :destroy) else assign_and_reset_version_association(version) version end end |
#record_update(force:, in_after_callback:, is_touch:) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
paper_trail-association_tracking
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/mongo_trails/record_trail.rb', line 124 def record_update(force:, in_after_callback:, is_touch:) return unless enabled? version = build_version_on_update( force: force, in_after_callback: in_after_callback, is_touch: is_touch ) return unless version if version.save # Because the version object was created using version_class.new instead # of versions_assoc.build?, the association cache is unaware. So, we # invalidate the `versions` association cache with `reset`. versions_reset version else log_version_errors(version, :update) end end |
#record_update_columns(changes) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
paper_trail-association_tracking
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/mongo_trails/record_trail.rb', line 155 def record_update_columns(changes) return unless enabled? event = Events::Update.new(@record, false, false, changes) # Merge data from `Event` with data from PT-AT. We no longer use # `data_for_update_columns` but PT-AT still does. data = event.data.merge(data_for_update_columns) versions_assoc = @record.send(@record.class.versions_association_name) version = versions_assoc.create(data) if version.errors.any? log_version_errors(version, :update) else version end end |
#reset_timestamp_attrs_for_update_if_needed ⇒ Object
Invoked via callback when a user attempts to persist a reified ‘Version`.
181 182 183 184 185 186 |
# File 'lib/mongo_trails/record_trail.rb', line 181 def return if live? @record.send(:timestamp_attributes_for_update_in_model).each do |column| @record.send("restore_#{column}!") end end |
#save_version? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
AR callback.
190 191 192 193 194 |
# File 'lib/mongo_trails/record_trail.rb', line 190 def save_version? if_condition = @record.[:if] unless_condition = @record.[:unless] (if_condition.blank? || if_condition.call(@record)) && !unless_condition.try(:call, @record) end |
#save_with_version(*args) ⇒ Object
Save, and create a version record regardless of options such as ‘:on`, `:if`, or `:unless`.
Arguments are passed to ‘save`.
This is an “update” event. That is, we record the same data we would in the case of a normal AR ‘update`.
207 208 209 210 211 212 |
# File 'lib/mongo_trails/record_trail.rb', line 207 def save_with_version(*args) ::PaperTrail.request(enabled: false) do @record.save(*args) end record_update(force: true, in_after_callback: false, is_touch: false) end |
#source_version ⇒ Object
196 197 198 |
# File 'lib/mongo_trails/record_trail.rb', line 196 def source_version version end |
#update_column(name, value) ⇒ Object
Like the ‘update_column` method from `ActiveRecord::Persistence`, but also creates a version to record those changes.
217 218 219 |
# File 'lib/mongo_trails/record_trail.rb', line 217 def update_column(name, value) update_columns(name => value) end |
#update_columns(attributes) ⇒ Object
Like the ‘update_columns` method from `ActiveRecord::Persistence`, but also creates a version to record those changes.
224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/mongo_trails/record_trail.rb', line 224 def update_columns(attributes) # `@record.update_columns` skips dirty-tracking, so we can't just use # `@record.changes` or @record.saved_changes` from `ActiveModel::Dirty`. # We need to build our own hash with the changes that will be made # directly to the database. changes = {} attributes.each do |k, v| changes[k] = [@record[k], v] end @record.update_columns(attributes) record_update_columns(changes) end |
#version_at(timestamp, reify_options = {}) ⇒ Object
Returns the object (not a Version) as it was at the given timestamp.
238 239 240 241 242 243 244 |
# File 'lib/mongo_trails/record_trail.rb', line 238 def version_at(, = {}) # Because a version stores how its object looked *before* the change, # we need to look for the first version created *after* the timestamp. v = versions.subsequent(, true).first return v.reify() if v @record unless @record.destroyed? end |
#versions_between(start_time, end_time) ⇒ Object
Returns the objects (not Versions) as they were between the given times.
247 248 249 250 |
# File 'lib/mongo_trails/record_trail.rb', line 247 def versions_between(start_time, end_time) versions = send(@record.class.versions_association_name).between(start_time, end_time) versions.collect { |version| version_at(version.created_at) } end |
#versions_reset ⇒ Object
24 25 26 |
# File 'lib/mongo_trails/record_trail.rb', line 24 def versions_reset @record.class.paper_trail.version_class.reset end |