mongo_trails 10.3.1 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +53 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +19 -0
  5. data/.ruby-version +1 -0
  6. data/Appraisals +10 -4
  7. data/Gemfile +8 -1
  8. data/README.md +28 -6
  9. data/Rakefile +17 -0
  10. data/bin/bump-version +67 -0
  11. data/gemfiles/rails_7.gemfile +15 -0
  12. data/gemfiles/rails_7.gemfile.lock +212 -0
  13. data/gemfiles/rails_8.gemfile +15 -0
  14. data/gemfiles/rails_8.gemfile.lock +213 -0
  15. data/gemfiles/rails_8_1.gemfile +15 -0
  16. data/gemfiles/rails_8_1.gemfile.lock +143 -0
  17. data/lib/mongo_trails/callback_propagation.rb +77 -0
  18. data/lib/mongo_trails/config.rb +7 -24
  19. data/lib/mongo_trails/events/base.rb +5 -308
  20. data/lib/mongo_trails/model_config.rb +1 -232
  21. data/lib/mongo_trails/mongo_support/config.rb +7 -1
  22. data/lib/mongo_trails/mongo_support/criteria.rb +7 -0
  23. data/lib/mongo_trails/mongo_support/version.rb +249 -19
  24. data/lib/mongo_trails/mongo_support/version_commit_wrap.rb +37 -0
  25. data/lib/mongo_trails/mongo_support/write_version_worker.rb +11 -0
  26. data/lib/mongo_trails/record_trail.rb +21 -262
  27. data/lib/mongo_trails/version.rb +5 -0
  28. data/lib/mongo_trails/version_concern.rb +4 -293
  29. data/lib/mongo_trails.rb +13 -149
  30. data/mongo_trails.gemspec +19 -20
  31. metadata +51 -85
  32. data/.travis.yml +0 -13
  33. data/Gemfile.lock +0 -62
  34. data/gemfiles/rails_5.gemfile +0 -9
  35. data/gemfiles/rails_5.gemfile.lock +0 -63
  36. data/gemfiles/rails_6.gemfile +0 -9
  37. data/gemfiles/rails_6.gemfile.lock +0 -63
  38. data/lib/mongo_trails/attribute_serializers/README.md +0 -10
  39. data/lib/mongo_trails/attribute_serializers/attribute_serializer_factory.rb +0 -27
  40. data/lib/mongo_trails/attribute_serializers/cast_attribute_serializer.rb +0 -51
  41. data/lib/mongo_trails/attribute_serializers/object_attribute.rb +0 -41
  42. data/lib/mongo_trails/attribute_serializers/object_changes_attribute.rb +0 -44
  43. data/lib/mongo_trails/cleaner.rb +0 -60
  44. data/lib/mongo_trails/compatibility.rb +0 -51
  45. data/lib/mongo_trails/events/create.rb +0 -32
  46. data/lib/mongo_trails/events/destroy.rb +0 -42
  47. data/lib/mongo_trails/events/update.rb +0 -60
  48. data/lib/mongo_trails/frameworks/cucumber.rb +0 -33
  49. data/lib/mongo_trails/frameworks/rails/controller.rb +0 -109
  50. data/lib/mongo_trails/frameworks/rails/engine.rb +0 -43
  51. data/lib/mongo_trails/frameworks/rails.rb +0 -4
  52. data/lib/mongo_trails/frameworks/rspec/helpers.rb +0 -29
  53. data/lib/mongo_trails/frameworks/rspec.rb +0 -43
  54. data/lib/mongo_trails/has_paper_trail.rb +0 -86
  55. data/lib/mongo_trails/queries/versions/where_object.rb +0 -65
  56. data/lib/mongo_trails/queries/versions/where_object_changes.rb +0 -75
  57. data/lib/mongo_trails/record_history.rb +0 -51
  58. data/lib/mongo_trails/reifier.rb +0 -130
  59. data/lib/mongo_trails/request.rb +0 -166
  60. data/lib/mongo_trails/serializers/json.rb +0 -46
  61. data/lib/mongo_trails/serializers/yaml.rb +0 -43
  62. data/lib/mongo_trails/type_serializers/postgres_array_serializer.rb +0 -48
  63. data/lib/mongo_trails/version_number.rb +0 -23
@@ -1,20 +1,136 @@
1
- require "mongoid"
2
- require "autoinc"
1
+ # frozen_string_literal: true
2
+
3
+ require 'mongoid'
4
+ require 'time'
5
+ require 'mongo_trails/mongo_support/version_commit_wrap'
6
+
7
+ begin
8
+ require 'sidekiq'
9
+ require 'mongo_trails/mongo_support/write_version_worker'
10
+ require 'mongo_trails/mongo_support/criteria'
11
+ rescue LoadError
12
+ # Continue without Sidekiq
13
+ end
3
14
 
4
15
  class AutoIncrementCounters
5
16
  include Mongoid::Document
17
+
18
+ store_in collection: 'auto_increment_counters'
19
+
20
+ field :sequence, type: Integer, default: 0
6
21
  end
7
22
 
8
- module PaperTrail
23
+ PaperTrail.config.has_paper_trail_defaults = { versions: { class_name: 'MongoTrails::Version' } }
24
+
25
+ module MongoTrails
9
26
  class Version
27
+ class << self
28
+ def find(id)
29
+ find_by(integer_id: id)
30
+ end
31
+
32
+ def incrementing_fields
33
+ @incrementing_fields ||= {
34
+ integer_id: {
35
+ scope: -> { MongoTrails::Version.prefix_map },
36
+ step: 1
37
+ }
38
+ }
39
+ end
40
+
41
+ def prefix_map
42
+ (PaperTrail.config.mongo_prefix.is_a?(Proc) && PaperTrail.config.mongo_prefix.call) || 'paper_trail'
43
+ end
44
+
45
+ def next_integer_id
46
+ next_integer_ids(1).first
47
+ end
48
+
49
+ def next_integer_ids(count, scope: prefix_map)
50
+ return [] if count.to_i <= 0
51
+
52
+ counter_id = counter_key(scope)
53
+ ensure_counter_initialized(counter_id, scope)
54
+
55
+ counter = AutoIncrementCounters.collection.find(_id: counter_id).find_one_and_update(
56
+ { '$inc' => { sequence: count } },
57
+ upsert: true,
58
+ return_document: :after
59
+ )
60
+ max_integer_id = counter.fetch('sequence')
61
+
62
+ ((max_integer_id - count + 1)..max_integer_id).to_a
63
+ end
64
+
65
+ def bulk_insert_attributes(version)
66
+ version = version.is_a?(self) ? version : new(version)
67
+ attrs = version.attributes.dup
68
+ attrs.delete('_id') if attrs['_id'].nil?
69
+ attrs
70
+ end
71
+
72
+ def table_name; end
73
+
74
+ def abstract_class?
75
+ false
76
+ end
77
+
78
+ def columns_hash
79
+ fields
80
+ end
81
+
82
+ def column_names
83
+ fields.keys
84
+ end
85
+
86
+ def belongs_to(_name, _options = {}, &block); end
87
+
88
+ def validates_presence_of(_name); end
89
+
90
+ def after_create(_name); end
91
+
92
+ private
93
+
94
+ def counter_key(scope)
95
+ "#{name}:#{scope || prefix_map}"
96
+ end
97
+
98
+ def ensure_counter_initialized(counter_id, _scope)
99
+ existing_counter = AutoIncrementCounters.collection.find(_id: counter_id).first
100
+ return if existing_counter.present?
101
+
102
+ max_id = maximum_existing_integer_id
103
+ initialize_counter(counter_id, max_id)
104
+ end
105
+
106
+ def initialize_counter(counter_id, max_id)
107
+ AutoIncrementCounters.collection.find(_id: counter_id).find_one_and_update(
108
+ { '$setOnInsert' => { sequence: max_id } },
109
+ upsert: true,
110
+ return_document: :after
111
+ )
112
+ rescue Mongo::Error::OperationFailure => e
113
+ # DocumentDB can return E11000 when another writer creates this exact counter after the
114
+ # existence check. The caller can safely increment the counter created by that writer.
115
+ raise unless e.code == 11_000
116
+ end
117
+
118
+ def maximum_existing_integer_id
119
+ pipeline = [
120
+ { '$match' => {} },
121
+ { '$group' => { _id: nil, max_integer_id: { '$max' => '$integer_id' } } }
122
+ ]
123
+ Version.collection.aggregate(pipeline).first&.dig('max_integer_id').to_i
124
+ end
125
+ end
126
+
10
127
  include PaperTrail::VersionConcern
11
128
  include Mongoid::Document
12
- include Mongoid::Autoinc
13
129
 
14
- store_in collection: ->() { "#{PaperTrail::Version.prefix_map}_versions" }
130
+ store_in collection: -> { "#{MongoTrails::Version.prefix_map}_versions" }
15
131
 
16
132
  field :item_type, type: String
17
- field :item_id, type: Integer
133
+ field :item_id, type: String
18
134
  field :event, type: String
19
135
  field :whodunnit, type: String
20
136
  field :object, type: Hash
@@ -22,35 +138,149 @@ module PaperTrail
22
138
  field :created_at, type: DateTime
23
139
  field :integer_id, type: Integer
24
140
 
25
- increments :integer_id, scope: -> { PaperTrail::Version.prefix_map }
141
+ index({ item_type: -1, item_id: -1 }, { background: true })
26
142
 
27
- class << self
28
- def reset
29
- Mongoid::QueryCache.clear_cache
30
- end
143
+ before_create :assign_integer_id
31
144
 
32
- def find(id)
33
- find_by(integer_id: id)
34
- end
145
+ attr_accessor :mongo_trails_source_item
35
146
 
36
- def prefix_map
37
- (PaperTrail.config.mongo_prefix.is_a?(Proc) ? PaperTrail.config.mongo_prefix.call : 'paper_trail') || 'paper_trail'
38
- end
147
+ def save_version
148
+ schedule_version_persistence(bang: false)
149
+ end
150
+
151
+ def save_version!
152
+ schedule_version_persistence(bang: true)
39
153
  end
40
154
 
41
155
  def initialize(data)
156
+ data = data.to_h.deep_symbolize_keys
42
157
  item = data.delete(:item)
43
158
  if item.present?
44
159
  data[:item_type] = item.class.name
45
160
  data[:item_id] = item.id
46
161
  end
47
- data[:created_at] = Time.zone&.now || Time.now
162
+ data[:created_at] = normalize_created_at(data[:created_at])
48
163
 
49
164
  super
50
165
  end
51
166
 
52
167
  def item
53
- item_type.constantize.find(item_id)
168
+ item_type.constantize.find_by(id: item_id)
169
+ end
170
+
171
+ def object=(value)
172
+ super(escape_value(value))
173
+ end
174
+
175
+ def object
176
+ unescape_value(super)
177
+ end
178
+
179
+ def object_changes
180
+ unescape_value(super)
181
+ end
182
+
183
+ def object_changes=(value)
184
+ super(escape_value(value))
185
+ end
186
+
187
+ def unescape_value(value)
188
+ value&.deep_transform_keys { |key| parser.unescape(force_utf8(key)) }
189
+ end
190
+
191
+ def escape_value(value)
192
+ value&.deep_transform_keys { |key| parser.escape(force_utf8(key.to_s), /[$.]/) }
193
+ end
194
+
195
+ private
196
+
197
+ def schedule_version_persistence(bang:)
198
+ connection = transaction_connection
199
+ return persist_version(self, bang:) unless connection.transaction_open?
200
+ unless mongo_trails_source_item
201
+ return VersionCommitWrap.new { persist_version(self, bang:) }.add_to_transaction(connection)
202
+ end
203
+
204
+ if (propagation = callback_propagation)
205
+ merge_callback_propagation(propagation, connection:, bang:)
206
+ else
207
+ register_callback_propagation(connection:, bang:)
208
+ end
209
+ end
210
+
211
+ def transaction_connection
212
+ mongo_trails_source_item ? mongo_trails_source_item.class.connection : ActiveRecord::Base.connection
213
+ end
214
+
215
+ def callback_propagation
216
+ return callback_propagations[event] unless event == 'update'
217
+
218
+ callback_propagations[event] || callback_propagations['create']
219
+ end
220
+
221
+ def callback_propagations
222
+ mongo_trails_source_item.instance_variable_get(:@mongo_trails_callback_propagations) ||
223
+ mongo_trails_source_item.instance_variable_set(:@mongo_trails_callback_propagations, {})
224
+ end
225
+
226
+ def register_callback_propagation(connection:, bang:)
227
+ propagation = CallbackPropagation.new(mongo_trails_source_item, self, bang:)
228
+ callback_propagations[event] = propagation
229
+ VersionCommitWrap.new(rollback: -> { propagation.clear }) { propagation.persist }.add_to_transaction(connection)
230
+ end
231
+
232
+ def merge_callback_propagation(propagation, connection:, bang:)
233
+ snapshot = propagation.snapshot
234
+ VersionCommitWrap.new(rollback: -> { propagation.restore!(snapshot) }) {}.add_to_transaction(connection)
235
+ propagation.merge!(self, bang:)
236
+ end
237
+
238
+ def assign_integer_id
239
+ self.integer_id ||= self.class.next_integer_id
240
+ end
241
+
242
+ def normalize_created_at(value)
243
+ return value if value.is_a?(Time) || value.is_a?(DateTime)
244
+ return current_time if value.nil?
245
+
246
+ parse_time(value)
247
+ rescue ArgumentError
248
+ current_time
249
+ end
250
+
251
+ def persist_version(version, bang: false)
252
+ return version.send(:async_save!) if sidekiq_enabled?
253
+
254
+ bang ? version.save! : version.save
255
+ end
256
+
257
+ def parse_time(value)
258
+ time_zone&.parse(value.to_s) || Time.parse(value.to_s)
259
+ end
260
+
261
+ def current_time = time_zone&.now || Time.now
262
+ def time_zone = Time.zone
263
+
264
+ def sidekiq_enabled?
265
+ defined?(Sidekiq) && PaperTrail.config.enable_sidekiq
266
+ end
267
+
268
+ def force_utf8(value)
269
+ value.respond_to?(:force_encoding) ? value.dup.force_encoding('UTF-8') : value
270
+ end
271
+
272
+ def parser
273
+ @parser ||= URI::Parser.new
274
+ end
275
+
276
+ def async_save!
277
+ worker = defined?(PaperTrail.config.sidekiq_worker.queue) ? PaperTrail.config.sidekiq_worker : PaperTrail::WriteVersionWorker
278
+ args = attributes.as_json
279
+ if worker == PaperTrail::WriteVersionWorker
280
+ worker.set(PaperTrail.config.sidekiq_options).perform_async(args)
281
+ else
282
+ worker.perform_async(args)
283
+ end
54
284
  end
55
285
  end
56
286
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MongoTrails
4
+ class VersionCommitWrap
5
+ def initialize(rollback: nil, &callback)
6
+ @callback = callback
7
+ @rollback = rollback
8
+ @executed = false
9
+ end
10
+
11
+ def has_transactional_callbacks? # rubocop:disable Naming/PredicatePrefix
12
+ true
13
+ end
14
+
15
+ def trigger_transactional_callbacks?
16
+ true
17
+ end
18
+
19
+ def before_committed!; end
20
+
21
+ def rolledback!(*)
22
+ @executed = true
23
+ @rollback&.call
24
+ end
25
+
26
+ def committed!(**)
27
+ return if @executed
28
+
29
+ @executed = true
30
+ @callback.call
31
+ end
32
+
33
+ def add_to_transaction(connection = ActiveRecord::Base.connection)
34
+ connection.add_transaction_record(self)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ class WriteVersionWorker
5
+ include Sidekiq::Job
6
+
7
+ def perform(obj)
8
+ MongoTrails::Version.new(obj).save!
9
+ end
10
+ end
11
+ end
@@ -1,126 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mongo_trails/events/create"
4
- require "mongo_trails/events/destroy"
5
- require "mongo_trails/events/update"
6
-
7
3
  module PaperTrail
8
- # Represents the "paper trail" for a single record.
9
4
  class RecordTrail
10
- RAILS_GTE_5_1 = ::ActiveRecord.gem_version >= ::Gem::Version.new("5.1.0.beta1")
11
-
12
- def initialize(record)
13
- @record = record
14
- end
15
-
16
- # Invoked after rollbacks to ensure versions records are not created for
17
- # changes that never actually took place. Optimization: Use lazy `reset`
18
- # instead of eager `reload` because, in many use cases, the association will
19
- # not be used.
20
- def clear_rolled_back_versions
21
- versions_reset
22
- end
23
-
24
- def versions_reset
25
- @record.class.paper_trail.version_class.reset
26
- end
27
-
28
- # Invoked via`after_update` callback for when a previous version is
29
- # reified and then saved.
30
- def clear_version_instance
31
- @record.send("#{@record.class.version_association_name}=", nil)
32
- end
33
-
34
- # Is PT enabled for this particular record?
35
- # @api private
36
- def enabled?
37
- PaperTrail.enabled? &&
38
- PaperTrail.request.enabled? &&
39
- PaperTrail.request.enabled_for_model?(@record.class)
40
- end
41
-
42
- # Returns true if this instance is the current, live one;
43
- # returns false if this instance came from a previous version.
44
- def live?
45
- source_version.nil?
46
- end
47
-
48
- # Returns the object (not a Version) as it became next.
49
- # NOTE: if self (the item) was not reified from a version, i.e. it is the
50
- # "live" item, we return nil. Perhaps we should return self instead?
51
- def next_version
52
- subsequent_version = source_version.next
53
- subsequent_version ? subsequent_version.reify : @record.class.find(@record.id)
54
- rescue StandardError # TODO: Rescue something more specific
55
- nil
56
- end
57
-
58
- # Returns who put `@record` into its current state.
59
- #
60
- # @api public
61
- def originator
62
- (source_version || versions.last).try(:whodunnit)
63
- end
64
-
65
- # Returns the object (not a Version) as it was most recently.
66
- #
67
- # @api public
68
- def previous_version
69
- (source_version ? source_version.previous : versions.last).try(:reify)
70
- end
71
-
72
5
  def record_create
73
6
  return unless enabled?
74
7
 
75
- build_version_on_create(in_after_callback: true).tap do |version|
76
- version.save!
77
- # Because the version object was created using version_class.new instead
78
- # of versions_assoc.build?, the association cache is unaware. So, we
79
- # invalidate the `versions` association cache with `reset`.
80
- versions_reset
81
- end
82
- end
8
+ version = build_version_on_create(in_after_callback: true)
9
+ return if exceeds_record_size_limit?(version)
83
10
 
84
- # PT-AT extends this method to add its transaction id.
85
- #
86
- # @api private
87
- def data_for_create
88
- {}
11
+ version.mongo_trails_source_item = @record
12
+ version.save_version!
89
13
  end
90
14
 
91
- # `recording_order` is "after" or "before". See ModelConfig#on_destroy.
92
- #
93
- # @api private
94
- # @return - The created version object, so that plugins can use it, e.g.
95
- # paper_trail-association_tracking
96
15
  def record_destroy(recording_order)
97
16
  return unless enabled? && !@record.new_record?
98
- in_after_callback = recording_order == "after"
99
- event = Events::Destroy.new(@record, in_after_callback)
100
17
 
101
- # Merge data from `Event` with data from PT-AT. We no longer use
102
- # `data_for_destroy` but PT-AT still does.
18
+ in_after_callback = recording_order == 'after'
19
+ event = Events::Destroy.new(@record, in_after_callback)
103
20
  data = event.data.merge(data_for_destroy)
104
21
 
105
- version = @record.class.paper_trail.version_class.create(data)
106
- if version.errors.any?
107
- log_version_errors(version, :destroy)
108
- else
109
- assign_and_reset_version_association(version)
110
- version
111
- end
112
- end
22
+ version = @record.class.paper_trail.version_class.new(data)
23
+ return if exceeds_record_size_limit?(version)
113
24
 
114
- # PT-AT extends this method to add its transaction id.
115
- #
116
- # @api private
117
- def data_for_destroy
118
- {}
25
+ version.mongo_trails_source_item = @record
26
+ version.save_version
119
27
  end
120
28
 
121
- # @api private
122
- # @return - The created version object, so that plugins can use it, e.g.
123
- # paper_trail-association_tracking
124
29
  def record_update(force:, in_after_callback:, is_touch:)
125
30
  return unless enabled?
126
31
 
@@ -129,176 +34,30 @@ module PaperTrail
129
34
  in_after_callback: in_after_callback,
130
35
  is_touch: is_touch
131
36
  )
132
- return unless version
37
+ return unless version && !exceeds_record_size_limit?(version)
133
38
 
134
- if version.save
135
- # Because the version object was created using version_class.new instead
136
- # of versions_assoc.build?, the association cache is unaware. So, we
137
- # invalidate the `versions` association cache with `reset`.
138
- versions_reset
139
- version
140
- else
141
- log_version_errors(version, :update)
142
- end
39
+ version.mongo_trails_source_item = @record
40
+ version.save_version
143
41
  end
144
42
 
145
- # PT-AT extends this method to add its transaction id.
146
- #
147
- # @api private
148
- def data_for_update
149
- {}
150
- end
151
-
152
- # @api private
153
- # @return - The created version object, so that plugins can use it, e.g.
154
- # paper_trail-association_tracking
155
43
  def record_update_columns(changes)
156
44
  return unless enabled?
157
- event = Events::Update.new(@record, false, false, changes)
158
45
 
159
- # Merge data from `Event` with data from PT-AT. We no longer use
160
- # `data_for_update_columns` but PT-AT still does.
46
+ event = Events::Update.new(@record, false, false, changes)
161
47
  data = event.data.merge(data_for_update_columns)
162
-
163
48
  versions_assoc = @record.send(@record.class.versions_association_name)
164
- version = versions_assoc.create(data)
165
- if version.errors.any?
166
- log_version_errors(version, :update)
167
- else
168
- version
169
- end
170
- end
49
+ version = versions_assoc.new(data)
50
+ return if exceeds_record_size_limit?(version)
171
51
 
172
- # PT-AT extends this method to add its transaction id.
173
- #
174
- # @api private
175
- def data_for_update_columns
176
- {}
177
- end
178
-
179
- # Invoked via callback when a user attempts to persist a reified
180
- # `Version`.
181
- def reset_timestamp_attrs_for_update_if_needed
182
- return if live?
183
- @record.send(:timestamp_attributes_for_update_in_model).each do |column|
184
- @record.send("restore_#{column}!")
185
- end
186
- end
187
-
188
- # AR callback.
189
- # @api private
190
- def save_version?
191
- if_condition = @record.paper_trail_options[:if]
192
- unless_condition = @record.paper_trail_options[:unless]
193
- (if_condition.blank? || if_condition.call(@record)) && !unless_condition.try(:call, @record)
194
- end
195
-
196
- def source_version
197
- version
198
- end
199
-
200
- # Save, and create a version record regardless of options such as `:on`,
201
- # `:if`, or `:unless`.
202
- #
203
- # Arguments are passed to `save`.
204
- #
205
- # This is an "update" event. That is, we record the same data we would in
206
- # the case of a normal AR `update`.
207
- def save_with_version(*args)
208
- ::PaperTrail.request(enabled: false) do
209
- @record.save(*args)
210
- end
211
- record_update(force: true, in_after_callback: false, is_touch: false)
212
- end
213
-
214
- # Like the `update_column` method from `ActiveRecord::Persistence`, but also
215
- # creates a version to record those changes.
216
- # @api public
217
- def update_column(name, value)
218
- update_columns(name => value)
219
- end
220
-
221
- # Like the `update_columns` method from `ActiveRecord::Persistence`, but also
222
- # creates a version to record those changes.
223
- # @api public
224
- def update_columns(attributes)
225
- # `@record.update_columns` skips dirty-tracking, so we can't just use
226
- # `@record.changes` or @record.saved_changes` from `ActiveModel::Dirty`.
227
- # We need to build our own hash with the changes that will be made
228
- # directly to the database.
229
- changes = {}
230
- attributes.each do |k, v|
231
- changes[k] = [@record[k], v]
232
- end
233
- @record.update_columns(attributes)
234
- record_update_columns(changes)
235
- end
236
-
237
- # Returns the object (not a Version) as it was at the given timestamp.
238
- def version_at(timestamp, reify_options = {})
239
- # Because a version stores how its object looked *before* the change,
240
- # we need to look for the first version created *after* the timestamp.
241
- v = versions.subsequent(timestamp, true).first
242
- return v.reify(reify_options) if v
243
- @record unless @record.destroyed?
244
- end
245
-
246
- # Returns the objects (not Versions) as they were between the given times.
247
- def versions_between(start_time, end_time)
248
- versions = send(@record.class.versions_association_name).between(start_time, end_time)
249
- versions.collect { |version| version_at(version.created_at) }
52
+ version.mongo_trails_source_item = @record
53
+ version.save_version
250
54
  end
251
55
 
252
56
  private
253
57
 
254
- # @api private
255
- def assign_and_reset_version_association(version)
256
- @record.send("#{@record.class.version_association_name}=", version)
257
- @record.send(@record.class.versions_association_name).reset
258
- end
259
-
260
- # @api private
261
- def build_version_on_create(in_after_callback:)
262
- event = Events::Create.new(@record, in_after_callback)
263
-
264
- # Merge data from `Event` with data from PT-AT. We no longer use
265
- # `data_for_create` but PT-AT still does.
266
- data = event.data.merge!(data_for_create)
267
-
268
- # Pure `version_class.new` reduces memory usage compared to `versions_assoc.build`
269
- @record.class.paper_trail.version_class.new(data)
270
- end
271
-
272
- # @api private
273
- def build_version_on_update(force:, in_after_callback:, is_touch:)
274
- event = Events::Update.new(@record, in_after_callback, is_touch, nil)
275
- return unless force || event.changed_notably?
276
-
277
- # Merge data from `Event` with data from PT-AT. We no longer use
278
- # `data_for_update` but PT-AT still does. To save memory, we use `merge!`
279
- # instead of `merge`.
280
- data = event.data.merge!(data_for_update)
281
-
282
- # Using `version_class.new` reduces memory usage compared to
283
- # `versions_assoc.build`. It's a trade-off though. We have to clear
284
- # the association cache (see `versions.reset`) and that could cause an
285
- # additional query in certain applications.
286
- @record.class.paper_trail.version_class.new(data)
287
- end
288
-
289
- def log_version_errors(version, action)
290
- version.logger&.warn(
291
- "Unable to create version for #{action} of #{@record.class.name}" \
292
- "##{@record.id}: " + version.errors.full_messages.join(", ")
293
- )
294
- end
295
-
296
- def version
297
- @record.public_send(@record.class.version_association_name)
298
- end
299
-
300
- def versions
301
- @record.public_send(@record.class.versions_association_name)
58
+ def exceeds_record_size_limit?(version)
59
+ size_limit = PaperTrail.config.mongo_trails_config&.dig(:record_size_limit)
60
+ size_limit && size_limit.to_i < version.to_json.to_s.bytesize
302
61
  end
303
62
  end
304
63
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MongoTrails
4
+ VERSION = '14.0.1'
5
+ end