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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +53 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/Appraisals +10 -4
- data/Gemfile +8 -1
- data/README.md +28 -6
- data/Rakefile +17 -0
- data/bin/bump-version +67 -0
- data/gemfiles/rails_7.gemfile +15 -0
- data/gemfiles/rails_7.gemfile.lock +212 -0
- data/gemfiles/rails_8.gemfile +15 -0
- data/gemfiles/rails_8.gemfile.lock +213 -0
- data/gemfiles/rails_8_1.gemfile +15 -0
- data/gemfiles/rails_8_1.gemfile.lock +143 -0
- data/lib/mongo_trails/callback_propagation.rb +77 -0
- data/lib/mongo_trails/config.rb +7 -24
- data/lib/mongo_trails/events/base.rb +5 -308
- data/lib/mongo_trails/model_config.rb +1 -232
- data/lib/mongo_trails/mongo_support/config.rb +7 -1
- data/lib/mongo_trails/mongo_support/criteria.rb +7 -0
- data/lib/mongo_trails/mongo_support/version.rb +249 -19
- data/lib/mongo_trails/mongo_support/version_commit_wrap.rb +37 -0
- data/lib/mongo_trails/mongo_support/write_version_worker.rb +11 -0
- data/lib/mongo_trails/record_trail.rb +21 -262
- data/lib/mongo_trails/version.rb +5 -0
- data/lib/mongo_trails/version_concern.rb +4 -293
- data/lib/mongo_trails.rb +13 -149
- data/mongo_trails.gemspec +19 -20
- metadata +51 -85
- data/.travis.yml +0 -13
- data/Gemfile.lock +0 -62
- data/gemfiles/rails_5.gemfile +0 -9
- data/gemfiles/rails_5.gemfile.lock +0 -63
- data/gemfiles/rails_6.gemfile +0 -9
- data/gemfiles/rails_6.gemfile.lock +0 -63
- data/lib/mongo_trails/attribute_serializers/README.md +0 -10
- data/lib/mongo_trails/attribute_serializers/attribute_serializer_factory.rb +0 -27
- data/lib/mongo_trails/attribute_serializers/cast_attribute_serializer.rb +0 -51
- data/lib/mongo_trails/attribute_serializers/object_attribute.rb +0 -41
- data/lib/mongo_trails/attribute_serializers/object_changes_attribute.rb +0 -44
- data/lib/mongo_trails/cleaner.rb +0 -60
- data/lib/mongo_trails/compatibility.rb +0 -51
- data/lib/mongo_trails/events/create.rb +0 -32
- data/lib/mongo_trails/events/destroy.rb +0 -42
- data/lib/mongo_trails/events/update.rb +0 -60
- data/lib/mongo_trails/frameworks/cucumber.rb +0 -33
- data/lib/mongo_trails/frameworks/rails/controller.rb +0 -109
- data/lib/mongo_trails/frameworks/rails/engine.rb +0 -43
- data/lib/mongo_trails/frameworks/rails.rb +0 -4
- data/lib/mongo_trails/frameworks/rspec/helpers.rb +0 -29
- data/lib/mongo_trails/frameworks/rspec.rb +0 -43
- data/lib/mongo_trails/has_paper_trail.rb +0 -86
- data/lib/mongo_trails/queries/versions/where_object.rb +0 -65
- data/lib/mongo_trails/queries/versions/where_object_changes.rb +0 -75
- data/lib/mongo_trails/record_history.rb +0 -51
- data/lib/mongo_trails/reifier.rb +0 -130
- data/lib/mongo_trails/request.rb +0 -166
- data/lib/mongo_trails/serializers/json.rb +0 -46
- data/lib/mongo_trails/serializers/yaml.rb +0 -43
- data/lib/mongo_trails/type_serializers/postgres_array_serializer.rb +0 -48
- data/lib/mongo_trails/version_number.rb +0 -23
|
@@ -2,322 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module PaperTrail
|
|
4
4
|
module Events
|
|
5
|
-
# We refer to times in the lifecycle of a record as "events". There are
|
|
6
|
-
# three events:
|
|
7
|
-
#
|
|
8
|
-
# - create
|
|
9
|
-
# - `after_create` we call `RecordTrail#record_create`
|
|
10
|
-
# - update
|
|
11
|
-
# - `after_update` we call `RecordTrail#record_update`
|
|
12
|
-
# - `after_touch` we call `RecordTrail#record_update`
|
|
13
|
-
# - `RecordTrail#save_with_version` calls `RecordTrail#record_update`
|
|
14
|
-
# - `RecordTrail#update_columns` is also referred to as an update, though
|
|
15
|
-
# it uses `RecordTrail#record_update_columns` rather than
|
|
16
|
-
# `RecordTrail#record_update`
|
|
17
|
-
# - destroy
|
|
18
|
-
# - `before_destroy` or `after_destroy` we call `RecordTrail#record_destroy`
|
|
19
|
-
#
|
|
20
|
-
# The value inserted into the `event` column of the versions table can also
|
|
21
|
-
# be overridden by the user, with `paper_trail_event`.
|
|
22
|
-
#
|
|
23
|
-
# @api private
|
|
24
5
|
class Base
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def initialize(record, in_after_callback)
|
|
29
|
-
@record = record
|
|
30
|
-
@in_after_callback = in_after_callback
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Determines whether it is appropriate to generate a new version
|
|
34
|
-
# instance. A timestamp-only update (e.g. only `updated_at` changed) is
|
|
35
|
-
# considered notable unless an ignored attribute was also changed.
|
|
36
|
-
#
|
|
37
|
-
# @api private
|
|
38
|
-
def changed_notably?
|
|
39
|
-
if ignored_attr_has_changed?
|
|
40
|
-
timestamps = @record.send(:timestamp_attributes_for_update_in_model).map(&:to_s)
|
|
41
|
-
(notably_changed - timestamps).any?
|
|
42
|
-
else
|
|
43
|
-
notably_changed.any?
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
|
50
|
-
# https://github.com/paper-trail-gem/mongo_trails/pull/899
|
|
51
|
-
#
|
|
52
|
-
# @api private
|
|
53
|
-
def attribute_changed_in_latest_version?(attr_name)
|
|
54
|
-
if @in_after_callback && RAILS_GTE_5_1
|
|
55
|
-
@record.saved_change_to_attribute?(attr_name.to_s)
|
|
56
|
-
else
|
|
57
|
-
@record.attribute_changed?(attr_name.to_s)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# @api private
|
|
62
|
-
def nonskipped_attributes_before_change(is_touch)
|
|
63
|
-
cache_changed_attributes do
|
|
64
|
-
record_attributes = @record.attributes.except(*@record.paper_trail_options[:skip])
|
|
65
|
-
|
|
66
|
-
record_attributes.each_key do |k|
|
|
67
|
-
if @record.class.column_names.include?(k)
|
|
68
|
-
record_attributes[k] = attribute_in_previous_version(k, is_touch)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Rails 5.1 changed the API of `ActiveRecord::Dirty`.
|
|
75
|
-
# @api private
|
|
76
|
-
def cache_changed_attributes
|
|
77
|
-
if RAILS_GTE_5_1
|
|
78
|
-
# Everything works fine as it is
|
|
79
|
-
yield
|
|
80
|
-
else
|
|
81
|
-
# Any particular call to `changed_attributes` produces the huge memory allocation.
|
|
82
|
-
# Lets use the generic AR workaround for that.
|
|
83
|
-
@record.send(:cache_changed_attributes) { yield }
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
|
88
|
-
# https://github.com/paper-trail-gem/mongo_trails/pull/899
|
|
89
|
-
#
|
|
90
|
-
# Event can be any of the three (create, update, destroy).
|
|
91
|
-
#
|
|
92
|
-
# @api private
|
|
93
|
-
def attribute_in_previous_version(attr_name, is_touch)
|
|
94
|
-
if RAILS_GTE_5_1
|
|
95
|
-
if @in_after_callback && !is_touch
|
|
96
|
-
# For most events, we want the original value of the attribute, before
|
|
97
|
-
# the last save.
|
|
98
|
-
@record.attribute_before_last_save(attr_name.to_s)
|
|
99
|
-
else
|
|
100
|
-
# We are either performing a `record_destroy` or a
|
|
101
|
-
# `record_update(is_touch: true)`.
|
|
102
|
-
@record.attribute_in_database(attr_name.to_s)
|
|
103
|
-
end
|
|
104
|
-
else
|
|
105
|
-
@record.attribute_was(attr_name.to_s)
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# @api private
|
|
110
|
-
def calculated_ignored_array
|
|
111
|
-
ignore = @record.paper_trail_options[:ignore].dup
|
|
112
|
-
# Remove Hash arguments and then evaluate whether the attributes (the
|
|
113
|
-
# keys of the hash) should also get pushed into the collection.
|
|
114
|
-
ignore.delete_if do |obj|
|
|
115
|
-
obj.is_a?(Hash) &&
|
|
116
|
-
obj.each { |attr, condition|
|
|
117
|
-
ignore << attr if condition.respond_to?(:call) && condition.call(@record)
|
|
118
|
-
}
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# @api private
|
|
123
|
-
def changed_and_not_ignored
|
|
124
|
-
skip = @record.paper_trail_options[:skip]
|
|
125
|
-
(changed_in_latest_version - calculated_ignored_array) - skip
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# @api private
|
|
129
|
-
def changed_in_latest_version
|
|
130
|
-
# Memoized to reduce memory usage
|
|
131
|
-
@changed_in_latest_version ||= changes_in_latest_version.keys
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
|
135
|
-
# https://github.com/paper-trail-gem/mongo_trails/pull/899
|
|
136
|
-
#
|
|
137
|
-
# @api private
|
|
138
|
-
def changes_in_latest_version
|
|
139
|
-
# Memoized to reduce memory usage
|
|
140
|
-
@changes_in_latest_version ||= begin
|
|
141
|
-
if @in_after_callback && RAILS_GTE_5_1
|
|
6
|
+
def load_changes_in_latest_version
|
|
7
|
+
changes =
|
|
8
|
+
if @in_after_callback
|
|
142
9
|
@record.saved_changes
|
|
143
10
|
else
|
|
144
11
|
@record.changes
|
|
145
12
|
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
13
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
# changed.
|
|
152
|
-
#
|
|
153
|
-
# @api private
|
|
154
|
-
def ignored_attr_has_changed?
|
|
155
|
-
ignored = calculated_ignored_array + @record.paper_trail_options[:skip]
|
|
156
|
-
ignored.any? && (changed_in_latest_version & ignored).any?
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# PT 10 has a new optional column, `item_subtype`
|
|
160
|
-
#
|
|
161
|
-
# @api private
|
|
162
|
-
def merge_item_subtype_into(data)
|
|
163
|
-
if @record.class.paper_trail.version_class.fields.key?("item_subtype")
|
|
164
|
-
data.merge!(item_subtype: @record.class.name)
|
|
14
|
+
changes.delete_if do |_k, v|
|
|
15
|
+
v.is_a?(Array) && v.size > 1 && v.last.is_a?(Hash) && v.uniq.size == 1
|
|
165
16
|
end
|
|
166
17
|
end
|
|
167
|
-
|
|
168
|
-
# Updates `data` from the model's `meta` option and from `controller_info`.
|
|
169
|
-
# Metadata is always recorded; that means all three events (create, update,
|
|
170
|
-
# destroy) and `update_columns`.
|
|
171
|
-
#
|
|
172
|
-
# @api private
|
|
173
|
-
def merge_metadata_into(data)
|
|
174
|
-
merge_metadata_from_model_into(data)
|
|
175
|
-
merge_metadata_from_controller_into(data)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# Updates `data` from `controller_info`.
|
|
179
|
-
#
|
|
180
|
-
# @api private
|
|
181
|
-
def merge_metadata_from_controller_into(data)
|
|
182
|
-
data.merge(PaperTrail.request.controller_info || {})
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Updates `data` from the model's `meta` option.
|
|
186
|
-
#
|
|
187
|
-
# @api private
|
|
188
|
-
def merge_metadata_from_model_into(data)
|
|
189
|
-
@record.paper_trail_options[:meta].each do |k, v|
|
|
190
|
-
data[k] = model_metadatum(v, data[:event])
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# Given a `value` from the model's `meta` option, returns an object to be
|
|
195
|
-
# persisted. The `value` can be a simple scalar value, but it can also
|
|
196
|
-
# be a symbol that names a model method, or even a Proc.
|
|
197
|
-
#
|
|
198
|
-
# @api private
|
|
199
|
-
def model_metadatum(value, event)
|
|
200
|
-
if value.respond_to?(:call)
|
|
201
|
-
value.call(@record)
|
|
202
|
-
elsif value.is_a?(Symbol) && @record.respond_to?(value, true)
|
|
203
|
-
# If it is an attribute that is changing in an existing object,
|
|
204
|
-
# be sure to grab the current version.
|
|
205
|
-
if event != "create" &&
|
|
206
|
-
@record.has_attribute?(value) &&
|
|
207
|
-
attribute_changed_in_latest_version?(value)
|
|
208
|
-
attribute_in_previous_version(value, false)
|
|
209
|
-
else
|
|
210
|
-
@record.send(value)
|
|
211
|
-
end
|
|
212
|
-
else
|
|
213
|
-
value
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# @api private
|
|
218
|
-
def notable_changes
|
|
219
|
-
changes_in_latest_version.delete_if { |k, _v|
|
|
220
|
-
!notably_changed.include?(k)
|
|
221
|
-
}
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# @api private
|
|
225
|
-
def notably_changed
|
|
226
|
-
# Memoized to reduce memory usage
|
|
227
|
-
@notably_changed ||= begin
|
|
228
|
-
only = @record.paper_trail_options[:only].dup
|
|
229
|
-
# Remove Hash arguments and then evaluate whether the attributes (the
|
|
230
|
-
# keys of the hash) should also get pushed into the collection.
|
|
231
|
-
only.delete_if do |obj|
|
|
232
|
-
obj.is_a?(Hash) &&
|
|
233
|
-
obj.each { |attr, condition|
|
|
234
|
-
only << attr if condition.respond_to?(:call) && condition.call(@record)
|
|
235
|
-
}
|
|
236
|
-
end
|
|
237
|
-
only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
# Returns hash of attributes (with appropriate attributes serialized),
|
|
242
|
-
# omitting attributes to be skipped.
|
|
243
|
-
#
|
|
244
|
-
# @api private
|
|
245
|
-
def object_attrs_for_paper_trail(is_touch)
|
|
246
|
-
attrs = nonskipped_attributes_before_change(is_touch)
|
|
247
|
-
AttributeSerializers::ObjectAttribute.new(@record.class).serialize(attrs)
|
|
248
|
-
attrs
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
# @api private
|
|
252
|
-
def prepare_object_changes(changes)
|
|
253
|
-
changes = serialize_object_changes(changes)
|
|
254
|
-
recordable_object_changes(changes)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# Returns an object which can be assigned to the `object_changes`
|
|
258
|
-
# attribute of a nascent version record. If the `object_changes` column is
|
|
259
|
-
# a postgres `json` column, then a hash can be used in the assignment,
|
|
260
|
-
# otherwise the column is a `text` column, and we must perform the
|
|
261
|
-
# serialization here, using `PaperTrail.serializer`.
|
|
262
|
-
#
|
|
263
|
-
# @api private
|
|
264
|
-
# @param changes HashWithIndifferentAccess
|
|
265
|
-
def recordable_object_changes(changes)
|
|
266
|
-
if PaperTrail.config.object_changes_adapter&.respond_to?(:diff)
|
|
267
|
-
# We'd like to avoid the `to_hash` here, because it increases memory
|
|
268
|
-
# usage, but that would be a breaking change because
|
|
269
|
-
# `object_changes_adapter` expects a plain `Hash`, not a
|
|
270
|
-
# `HashWithIndifferentAccess`.
|
|
271
|
-
changes = PaperTrail.config.object_changes_adapter.diff(changes.to_hash)
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
if @record.class.paper_trail.version_class.object_changes_col_is_json?
|
|
275
|
-
changes
|
|
276
|
-
else
|
|
277
|
-
PaperTrail.serializer.dump(changes)
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# Returns a boolean indicating whether to store serialized version diffs
|
|
282
|
-
# in the `object_changes` column of the version record.
|
|
283
|
-
#
|
|
284
|
-
# @api private
|
|
285
|
-
def record_object_changes?
|
|
286
|
-
@record.class.paper_trail.version_class.fields.keys.include?("object_changes")
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# Returns a boolean indicating whether to store the original object during save.
|
|
290
|
-
#
|
|
291
|
-
# @api private
|
|
292
|
-
def record_object?
|
|
293
|
-
@record.class.paper_trail.version_class.fields.keys.include?("object")
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
# Returns an object which can be assigned to the `object` attribute of a
|
|
297
|
-
# nascent version record. If the `object` column is a postgres `json`
|
|
298
|
-
# column, then a hash can be used in the assignment, otherwise the column
|
|
299
|
-
# is a `text` column, and we must perform the serialization here, using
|
|
300
|
-
# `PaperTrail.serializer`.
|
|
301
|
-
#
|
|
302
|
-
# @api private
|
|
303
|
-
def recordable_object(is_touch)
|
|
304
|
-
if @record.class.paper_trail.version_class.object_col_is_json?
|
|
305
|
-
object_attrs_for_paper_trail(is_touch)
|
|
306
|
-
else
|
|
307
|
-
PaperTrail.serializer.dump(object_attrs_for_paper_trail(is_touch))
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# @api private
|
|
312
|
-
def serialize_object_changes(changes)
|
|
313
|
-
AttributeSerializers::ObjectChangesAttribute.
|
|
314
|
-
new(@record.class).
|
|
315
|
-
serialize(changes)
|
|
316
|
-
|
|
317
|
-
# We'd like to convert this `HashWithIndifferentAccess` to a plain
|
|
318
|
-
# `Hash`, but we don't, to save memory.
|
|
319
|
-
changes
|
|
320
|
-
end
|
|
321
18
|
end
|
|
322
19
|
end
|
|
323
20
|
end
|
|
@@ -1,178 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PaperTrail
|
|
4
|
-
# Configures an ActiveRecord model, mostly at application boot time, but also
|
|
5
|
-
# sometimes mid-request, with methods like enable/disable.
|
|
6
4
|
class ModelConfig
|
|
7
|
-
|
|
8
|
-
paper_trail.on_destroy(:after) is incompatible with ActiveRecord's
|
|
9
|
-
belongs_to_required_by_default. Use on_destroy(:before)
|
|
10
|
-
or disable belongs_to_required_by_default.
|
|
11
|
-
STR
|
|
12
|
-
E_HPT_ABSTRACT_CLASS = <<~STR.squish.freeze
|
|
13
|
-
An application model (%s) has been configured to use PaperTrail (via
|
|
14
|
-
`has_paper_trail`), but the version model it has been told to use (%s) is
|
|
15
|
-
an `abstract_class`. This could happen when an advanced feature called
|
|
16
|
-
Custom Version Classes (http://bit.ly/2G4ch0G) is misconfigured. When all
|
|
17
|
-
version classes are custom, PaperTrail::Version is configured to be an
|
|
18
|
-
`abstract_class`. This is fine, but all application models must be
|
|
19
|
-
configured to use concrete (not abstract) version models.
|
|
20
|
-
STR
|
|
21
|
-
E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE = <<~STR.squish.freeze
|
|
22
|
-
To use PaperTrail's per-model limit in your %s model, you must have an
|
|
23
|
-
item_subtype column in your versions table. See documentation sections
|
|
24
|
-
2.e.1 Per-model limit, and 4.b.1 The optional item_subtype column.
|
|
25
|
-
STR
|
|
26
|
-
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION = <<~STR.squish
|
|
27
|
-
Passing versions association name as `has_paper_trail versions: %{versions_name}`
|
|
28
|
-
is deprecated. Use `has_paper_trail versions: {name: %{versions_name}}` instead.
|
|
29
|
-
The hash you pass to `versions:` is now passed directly to `has_many`.
|
|
30
|
-
STR
|
|
31
|
-
DPR_CLASS_NAME_OPTION = <<~STR.squish
|
|
32
|
-
Passing Version class name as `has_paper_trail class_name: %{class_name}`
|
|
33
|
-
is deprecated. Use `has_paper_trail versions: {class_name: %{class_name}}`
|
|
34
|
-
instead. The hash you pass to `versions:` is now passed directly to `has_many`.
|
|
35
|
-
STR
|
|
36
|
-
|
|
37
|
-
def initialize(model_class)
|
|
38
|
-
@model_class = model_class
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Adds a callback that records a version after a "create" event.
|
|
42
|
-
#
|
|
43
|
-
# @api public
|
|
44
|
-
def on_create
|
|
45
|
-
@model_class.after_create { |r|
|
|
46
|
-
r.paper_trail.record_create if r.paper_trail.save_version?
|
|
47
|
-
}
|
|
48
|
-
return if @model_class.paper_trail_options[:on].include?(:create)
|
|
49
|
-
@model_class.paper_trail_options[:on] << :create
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Adds a callback that records a version before or after a "destroy" event.
|
|
53
|
-
#
|
|
54
|
-
# @api public
|
|
55
|
-
def on_destroy(recording_order = "before")
|
|
56
|
-
unless %w[after before].include?(recording_order.to_s)
|
|
57
|
-
raise ArgumentError, 'recording order can only be "after" or "before"'
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
if recording_order.to_s == "after" && cannot_record_after_destroy?
|
|
61
|
-
raise E_CANNOT_RECORD_AFTER_DESTROY
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
@model_class.send(
|
|
65
|
-
"#{recording_order}_destroy",
|
|
66
|
-
lambda do |r|
|
|
67
|
-
return unless r.paper_trail.save_version?
|
|
68
|
-
r.paper_trail.record_destroy(recording_order)
|
|
69
|
-
end
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
return if @model_class.paper_trail_options[:on].include?(:destroy)
|
|
73
|
-
@model_class.paper_trail_options[:on] << :destroy
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Adds a callback that records a version after an "update" event.
|
|
77
|
-
#
|
|
78
|
-
# @api public
|
|
79
|
-
def on_update
|
|
80
|
-
@model_class.before_save { |r|
|
|
81
|
-
r.paper_trail.reset_timestamp_attrs_for_update_if_needed
|
|
82
|
-
}
|
|
83
|
-
@model_class.after_update { |r|
|
|
84
|
-
if r.paper_trail.save_version?
|
|
85
|
-
r.paper_trail.record_update(
|
|
86
|
-
force: false,
|
|
87
|
-
in_after_callback: true,
|
|
88
|
-
is_touch: false
|
|
89
|
-
)
|
|
90
|
-
end
|
|
91
|
-
}
|
|
92
|
-
@model_class.after_update { |r|
|
|
93
|
-
r.paper_trail.clear_version_instance
|
|
94
|
-
}
|
|
95
|
-
return if @model_class.paper_trail_options[:on].include?(:update)
|
|
96
|
-
@model_class.paper_trail_options[:on] << :update
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Adds a callback that records a version after a "touch" event.
|
|
100
|
-
# @api public
|
|
101
|
-
def on_touch
|
|
102
|
-
@model_class.after_touch { |r|
|
|
103
|
-
r.paper_trail.record_update(
|
|
104
|
-
force: true,
|
|
105
|
-
in_after_callback: true,
|
|
106
|
-
is_touch: true
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Set up `@model_class` for PaperTrail. Installs callbacks, associations,
|
|
112
|
-
# "class attributes", instance methods, and more.
|
|
113
|
-
# @api private
|
|
114
|
-
def setup(options = {})
|
|
115
|
-
options[:on] ||= %i[create update destroy touch]
|
|
116
|
-
options[:on] = Array(options[:on]) # Support single symbol
|
|
117
|
-
@model_class.send :include, ::PaperTrail::Model::InstanceMethods
|
|
118
|
-
setup_options(options)
|
|
119
|
-
setup_associations(options)
|
|
120
|
-
check_presence_of_item_subtype_column(options)
|
|
121
|
-
@model_class.after_rollback { paper_trail.clear_rolled_back_versions }
|
|
122
|
-
setup_callbacks_from_options options[:on]
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def version_class
|
|
126
|
-
@_version_class ||= @model_class.version_class_name.constantize
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
private
|
|
130
|
-
|
|
131
|
-
# Raises an error if the provided class is an `abstract_class`.
|
|
132
|
-
# @api private
|
|
133
|
-
def assert_concrete_activerecord_class(class_name)
|
|
134
|
-
if class_name.constantize.abstract_class?
|
|
135
|
-
raise format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def cannot_record_after_destroy?
|
|
140
|
-
::ActiveRecord::Base.belongs_to_required_by_default
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Some options require the presence of the `item_subtype` column. Currently
|
|
144
|
-
# only `limit`, but in the future there may be others.
|
|
145
|
-
#
|
|
146
|
-
# @api private
|
|
147
|
-
def check_presence_of_item_subtype_column(options)
|
|
148
|
-
return unless options.key?(:limit)
|
|
149
|
-
return if version_class.item_subtype_column_present?
|
|
150
|
-
raise format(E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE, @model_class.name)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def check_version_class_name(options)
|
|
154
|
-
# @api private - `version_class_name`
|
|
155
|
-
@model_class.class_attribute :version_class_name
|
|
156
|
-
if options[:class_name]
|
|
157
|
-
::ActiveSupport::Deprecation.warn(
|
|
158
|
-
format(
|
|
159
|
-
DPR_CLASS_NAME_OPTION,
|
|
160
|
-
class_name: options[:class_name].inspect
|
|
161
|
-
),
|
|
162
|
-
caller(1)
|
|
163
|
-
)
|
|
164
|
-
options[:versions][:class_name] = options[:class_name]
|
|
165
|
-
end
|
|
166
|
-
@model_class.version_class_name = options[:versions][:class_name] || "PaperTrail::Version"
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def check_versions_association_name(options)
|
|
170
|
-
# @api private - versions_association_name
|
|
171
|
-
@model_class.class_attribute :versions_association_name
|
|
172
|
-
@model_class.versions_association_name = options[:versions][:name] || :versions
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def define_has_many_mongo_versions(options)
|
|
5
|
+
def define_has_many_versions(options)
|
|
176
6
|
options = ensure_versions_option_is_hash(options)
|
|
177
7
|
check_version_class_name(options)
|
|
178
8
|
check_versions_association_name(options)
|
|
@@ -184,66 +14,5 @@ module PaperTrail
|
|
|
184
14
|
end
|
|
185
15
|
RUBY
|
|
186
16
|
end
|
|
187
|
-
|
|
188
|
-
def ensure_versions_option_is_hash(options)
|
|
189
|
-
unless options[:versions].is_a?(Hash)
|
|
190
|
-
if options[:versions]
|
|
191
|
-
::ActiveSupport::Deprecation.warn(
|
|
192
|
-
format(
|
|
193
|
-
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION,
|
|
194
|
-
versions_name: options[:versions].inspect
|
|
195
|
-
),
|
|
196
|
-
caller(1)
|
|
197
|
-
)
|
|
198
|
-
end
|
|
199
|
-
options[:versions] = {
|
|
200
|
-
name: options[:versions]
|
|
201
|
-
}
|
|
202
|
-
end
|
|
203
|
-
options
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def get_versions_scope(options)
|
|
207
|
-
options[:versions][:scope] || -> { order(model.timestamp_sort_order) }
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def setup_associations(options)
|
|
211
|
-
# @api private - version_association_name
|
|
212
|
-
@model_class.class_attribute :version_association_name
|
|
213
|
-
@model_class.version_association_name = options[:version] || :version
|
|
214
|
-
|
|
215
|
-
# The version this instance was reified from.
|
|
216
|
-
# @api public
|
|
217
|
-
@model_class.send :attr_accessor, @model_class.version_association_name
|
|
218
|
-
|
|
219
|
-
# @api public - paper_trail_event
|
|
220
|
-
@model_class.send :attr_accessor, :paper_trail_event
|
|
221
|
-
|
|
222
|
-
define_has_many_mongo_versions(options)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def setup_callbacks_from_options(options_on = [])
|
|
226
|
-
options_on.each do |event|
|
|
227
|
-
public_send("on_#{event}")
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def setup_options(options)
|
|
232
|
-
# @api public - paper_trail_options - Let's encourage plugins to use eg.
|
|
233
|
-
# `paper_trail_options[:versions][:class_name]` rather than
|
|
234
|
-
# `version_class_name` because the former is documented and the latter is
|
|
235
|
-
# not.
|
|
236
|
-
@model_class.class_attribute :paper_trail_options
|
|
237
|
-
@model_class.paper_trail_options = options.dup
|
|
238
|
-
|
|
239
|
-
%i[ignore skip only].each do |k|
|
|
240
|
-
@model_class.paper_trail_options[k] = [@model_class.paper_trail_options[k]].
|
|
241
|
-
flatten.
|
|
242
|
-
compact.
|
|
243
|
-
map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
@model_class.paper_trail_options[:meta] ||= {}
|
|
247
|
-
end
|
|
248
17
|
end
|
|
249
18
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'mongoid'
|
|
2
4
|
|
|
3
5
|
Mongoid.configure do |config|
|
|
@@ -6,4 +8,8 @@ Mongoid.configure do |config|
|
|
|
6
8
|
config.log_level = :error
|
|
7
9
|
end
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
if defined?(Mongo::QueryCache)
|
|
12
|
+
Mongo::QueryCache.enabled = false
|
|
13
|
+
elsif defined?(Mongoid::QueryCache)
|
|
14
|
+
Mongoid::QueryCache.enabled = false
|
|
15
|
+
end
|