paper_trail 9.2.0 → 12.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +20 -0
- data/lib/generators/paper_trail/install/USAGE +3 -0
- data/lib/generators/paper_trail/{install_generator.rb → install/install_generator.rb} +15 -38
- data/lib/generators/paper_trail/{templates → install/templates}/add_object_changes_to_versions.rb.erb +0 -0
- data/lib/generators/paper_trail/{templates → install/templates}/create_versions.rb.erb +5 -3
- data/lib/generators/paper_trail/migration_generator.rb +38 -0
- data/lib/generators/paper_trail/update_item_subtype/USAGE +4 -0
- data/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb +85 -0
- data/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +19 -0
- data/lib/paper_trail/attribute_serializers/attribute_serializer_factory.rb +24 -10
- data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +14 -46
- data/lib/paper_trail/compatibility.rb +51 -0
- data/lib/paper_trail/config.rb +9 -2
- data/lib/paper_trail/errors.rb +33 -0
- data/lib/paper_trail/events/base.rb +320 -0
- data/lib/paper_trail/events/create.rb +32 -0
- data/lib/paper_trail/events/destroy.rb +42 -0
- data/lib/paper_trail/events/update.rb +65 -0
- data/lib/paper_trail/frameworks/active_record.rb +9 -2
- data/lib/paper_trail/frameworks/rails/controller.rb +1 -9
- data/lib/paper_trail/frameworks/rails/railtie.rb +30 -0
- data/lib/paper_trail/frameworks/rails.rb +1 -2
- data/lib/paper_trail/has_paper_trail.rb +20 -17
- data/lib/paper_trail/model_config.rb +127 -87
- data/lib/paper_trail/queries/versions/where_attribute_changes.rb +50 -0
- data/lib/paper_trail/queries/versions/where_object.rb +4 -1
- data/lib/paper_trail/queries/versions/where_object_changes.rb +8 -13
- data/lib/paper_trail/queries/versions/where_object_changes_from.rb +57 -0
- data/lib/paper_trail/queries/versions/where_object_changes_to.rb +57 -0
- data/lib/paper_trail/record_trail.rb +94 -411
- data/lib/paper_trail/reifier.rb +41 -25
- data/lib/paper_trail/request.rb +0 -3
- data/lib/paper_trail/serializers/json.rb +0 -10
- data/lib/paper_trail/serializers/yaml.rb +6 -13
- data/lib/paper_trail/type_serializers/postgres_array_serializer.rb +1 -15
- data/lib/paper_trail/version_concern.rb +142 -61
- data/lib/paper_trail/version_number.rb +1 -1
- data/lib/paper_trail.rb +18 -123
- metadata +147 -56
- data/lib/generators/paper_trail/USAGE +0 -2
- data/lib/paper_trail/frameworks/rails/engine.rb +0 -14
@@ -0,0 +1,320 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
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
|
+
class Base
|
25
|
+
# @api private
|
26
|
+
def initialize(record, in_after_callback)
|
27
|
+
@record = record
|
28
|
+
@in_after_callback = in_after_callback
|
29
|
+
end
|
30
|
+
|
31
|
+
# Determines whether it is appropriate to generate a new version
|
32
|
+
# instance. A timestamp-only update (e.g. only `updated_at` changed) is
|
33
|
+
# considered notable unless an ignored attribute was also changed.
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def changed_notably?
|
37
|
+
if ignored_attr_has_changed?
|
38
|
+
timestamps = @record.send(:timestamp_attributes_for_update_in_model).map(&:to_s)
|
39
|
+
(notably_changed - timestamps).any?
|
40
|
+
else
|
41
|
+
notably_changed.any?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
48
|
+
# https://github.com/paper-trail-gem/paper_trail/pull/899
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
def attribute_changed_in_latest_version?(attr_name)
|
52
|
+
if @in_after_callback
|
53
|
+
@record.saved_change_to_attribute?(attr_name.to_s)
|
54
|
+
else
|
55
|
+
@record.attribute_changed?(attr_name.to_s)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @api private
|
60
|
+
def nonskipped_attributes_before_change(is_touch)
|
61
|
+
record_attributes = @record.attributes.except(*@record.paper_trail_options[:skip])
|
62
|
+
record_attributes.each_key do |k|
|
63
|
+
if @record.class.column_names.include?(k)
|
64
|
+
record_attributes[k] = attribute_in_previous_version(k, is_touch)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
70
|
+
# https://github.com/paper-trail-gem/paper_trail/pull/899
|
71
|
+
#
|
72
|
+
# Event can be any of the three (create, update, destroy).
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
def attribute_in_previous_version(attr_name, is_touch)
|
76
|
+
if @in_after_callback && !is_touch
|
77
|
+
# For most events, we want the original value of the attribute, before
|
78
|
+
# the last save.
|
79
|
+
@record.attribute_before_last_save(attr_name.to_s)
|
80
|
+
else
|
81
|
+
# We are either performing a `record_destroy` or a
|
82
|
+
# `record_update(is_touch: true)`.
|
83
|
+
@record.attribute_in_database(attr_name.to_s)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# @api private
|
88
|
+
def calculated_ignored_array
|
89
|
+
ignore = @record.paper_trail_options[:ignore].dup
|
90
|
+
# Remove Hash arguments and then evaluate whether the attributes (the
|
91
|
+
# keys of the hash) should also get pushed into the collection.
|
92
|
+
ignore.delete_if do |obj|
|
93
|
+
obj.is_a?(Hash) &&
|
94
|
+
obj.each { |attr, condition|
|
95
|
+
ignore << attr if condition.respond_to?(:call) && condition.call(@record)
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# @api private
|
101
|
+
def changed_and_not_ignored
|
102
|
+
skip = @record.paper_trail_options[:skip]
|
103
|
+
(changed_in_latest_version - calculated_ignored_array) - skip
|
104
|
+
end
|
105
|
+
|
106
|
+
# @api private
|
107
|
+
def changed_in_latest_version
|
108
|
+
# Memoized to reduce memory usage
|
109
|
+
@changed_in_latest_version ||= changes_in_latest_version.keys
|
110
|
+
end
|
111
|
+
|
112
|
+
# Memoized to reduce memory usage
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
def changes_in_latest_version
|
116
|
+
@changes_in_latest_version ||= load_changes_in_latest_version
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
def evaluate_only
|
121
|
+
only = @record.paper_trail_options[:only].dup
|
122
|
+
# Remove Hash arguments and then evaluate whether the attributes (the
|
123
|
+
# keys of the hash) should also get pushed into the collection.
|
124
|
+
only.delete_if do |obj|
|
125
|
+
obj.is_a?(Hash) &&
|
126
|
+
obj.each { |attr, condition|
|
127
|
+
only << attr if condition.respond_to?(:call) && condition.call(@record)
|
128
|
+
}
|
129
|
+
end
|
130
|
+
only
|
131
|
+
end
|
132
|
+
|
133
|
+
# An attributed is "ignored" if it is listed in the `:ignore` option
|
134
|
+
# and/or the `:skip` option. Returns true if an ignored attribute has
|
135
|
+
# changed.
|
136
|
+
#
|
137
|
+
# @api private
|
138
|
+
def ignored_attr_has_changed?
|
139
|
+
ignored = calculated_ignored_array + @record.paper_trail_options[:skip]
|
140
|
+
ignored.any? && (changed_in_latest_version & ignored).any?
|
141
|
+
end
|
142
|
+
|
143
|
+
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
144
|
+
# https://github.com/paper-trail-gem/paper_trail/pull/899
|
145
|
+
#
|
146
|
+
# @api private
|
147
|
+
def load_changes_in_latest_version
|
148
|
+
if @in_after_callback
|
149
|
+
@record.saved_changes
|
150
|
+
else
|
151
|
+
@record.changes
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# PT 10 has a new optional column, `item_subtype`
|
156
|
+
#
|
157
|
+
# @api private
|
158
|
+
def merge_item_subtype_into(data)
|
159
|
+
if @record.class.paper_trail.version_class.columns_hash.key?("item_subtype")
|
160
|
+
data.merge!(item_subtype: @record.class.name)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Updates `data` from the model's `meta` option and from `controller_info`.
|
165
|
+
# Metadata is always recorded; that means all three events (create, update,
|
166
|
+
# destroy) and `update_columns`.
|
167
|
+
#
|
168
|
+
# @api private
|
169
|
+
def merge_metadata_into(data)
|
170
|
+
merge_metadata_from_model_into(data)
|
171
|
+
merge_metadata_from_controller_into(data)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Updates `data` from `controller_info`.
|
175
|
+
#
|
176
|
+
# @api private
|
177
|
+
def merge_metadata_from_controller_into(data)
|
178
|
+
data.merge(PaperTrail.request.controller_info || {})
|
179
|
+
end
|
180
|
+
|
181
|
+
# Updates `data` from the model's `meta` option.
|
182
|
+
#
|
183
|
+
# @api private
|
184
|
+
def merge_metadata_from_model_into(data)
|
185
|
+
@record.paper_trail_options[:meta].each do |k, v|
|
186
|
+
data[k] = model_metadatum(v, data[:event])
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Given a `value` from the model's `meta` option, returns an object to be
|
191
|
+
# persisted. The `value` can be a simple scalar value, but it can also
|
192
|
+
# be a symbol that names a model method, or even a Proc.
|
193
|
+
#
|
194
|
+
# @api private
|
195
|
+
def model_metadatum(value, event)
|
196
|
+
if value.respond_to?(:call)
|
197
|
+
value.call(@record)
|
198
|
+
elsif value.is_a?(Symbol) && @record.respond_to?(value, true)
|
199
|
+
metadatum_from_model_method(event, value)
|
200
|
+
else
|
201
|
+
value
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# The model method can either be an attribute or a non-attribute method.
|
206
|
+
#
|
207
|
+
# If it is an attribute that is changing in an existing object,
|
208
|
+
# be sure to grab the correct version.
|
209
|
+
#
|
210
|
+
# @api private
|
211
|
+
def metadatum_from_model_method(event, method)
|
212
|
+
if event != "create" &&
|
213
|
+
@record.has_attribute?(method) &&
|
214
|
+
attribute_changed_in_latest_version?(method)
|
215
|
+
attribute_in_previous_version(method, false)
|
216
|
+
else
|
217
|
+
@record.send(method)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# @api private
|
222
|
+
def notable_changes
|
223
|
+
changes_in_latest_version.delete_if { |k, _v|
|
224
|
+
!notably_changed.include?(k)
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
# @api private
|
229
|
+
def notably_changed
|
230
|
+
# Memoized to reduce memory usage
|
231
|
+
@notably_changed ||= begin
|
232
|
+
only = evaluate_only
|
233
|
+
cani = changed_and_not_ignored
|
234
|
+
only.empty? ? cani : (cani & only)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns hash of attributes (with appropriate attributes serialized),
|
239
|
+
# omitting attributes to be skipped.
|
240
|
+
#
|
241
|
+
# @api private
|
242
|
+
def object_attrs_for_paper_trail(is_touch)
|
243
|
+
attrs = nonskipped_attributes_before_change(is_touch)
|
244
|
+
AttributeSerializers::ObjectAttribute.new(@record.class).serialize(attrs)
|
245
|
+
attrs
|
246
|
+
end
|
247
|
+
|
248
|
+
# @api private
|
249
|
+
def prepare_object_changes(changes)
|
250
|
+
changes = serialize_object_changes(changes)
|
251
|
+
recordable_object_changes(changes)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Returns an object which can be assigned to the `object_changes`
|
255
|
+
# attribute of a nascent version record. If the `object_changes` column is
|
256
|
+
# a postgres `json` column, then a hash can be used in the assignment,
|
257
|
+
# otherwise the column is a `text` column, and we must perform the
|
258
|
+
# serialization here, using `PaperTrail.serializer`.
|
259
|
+
#
|
260
|
+
# @api private
|
261
|
+
# @param changes HashWithIndifferentAccess
|
262
|
+
def recordable_object_changes(changes)
|
263
|
+
if PaperTrail.config.object_changes_adapter.respond_to?(:diff)
|
264
|
+
# We'd like to avoid the `to_hash` here, because it increases memory
|
265
|
+
# usage, but that would be a breaking change because
|
266
|
+
# `object_changes_adapter` expects a plain `Hash`, not a
|
267
|
+
# `HashWithIndifferentAccess`.
|
268
|
+
changes = PaperTrail.config.object_changes_adapter.diff(changes.to_hash)
|
269
|
+
end
|
270
|
+
|
271
|
+
if @record.class.paper_trail.version_class.object_changes_col_is_json?
|
272
|
+
changes
|
273
|
+
else
|
274
|
+
PaperTrail.serializer.dump(changes)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Returns a boolean indicating whether to store serialized version diffs
|
279
|
+
# in the `object_changes` column of the version record.
|
280
|
+
#
|
281
|
+
# @api private
|
282
|
+
def record_object_changes?
|
283
|
+
@record.class.paper_trail.version_class.column_names.include?("object_changes")
|
284
|
+
end
|
285
|
+
|
286
|
+
# Returns a boolean indicating whether to store the original object during save.
|
287
|
+
#
|
288
|
+
# @api private
|
289
|
+
def record_object?
|
290
|
+
@record.class.paper_trail.version_class.column_names.include?("object")
|
291
|
+
end
|
292
|
+
|
293
|
+
# Returns an object which can be assigned to the `object` attribute of a
|
294
|
+
# nascent version record. If the `object` column is a postgres `json`
|
295
|
+
# column, then a hash can be used in the assignment, otherwise the column
|
296
|
+
# is a `text` column, and we must perform the serialization here, using
|
297
|
+
# `PaperTrail.serializer`.
|
298
|
+
#
|
299
|
+
# @api private
|
300
|
+
def recordable_object(is_touch)
|
301
|
+
if @record.class.paper_trail.version_class.object_col_is_json?
|
302
|
+
object_attrs_for_paper_trail(is_touch)
|
303
|
+
else
|
304
|
+
PaperTrail.serializer.dump(object_attrs_for_paper_trail(is_touch))
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# @api private
|
309
|
+
def serialize_object_changes(changes)
|
310
|
+
AttributeSerializers::ObjectChangesAttribute.
|
311
|
+
new(@record.class).
|
312
|
+
serialize(changes)
|
313
|
+
|
314
|
+
# We'd like to convert this `HashWithIndifferentAccess` to a plain
|
315
|
+
# `Hash`, but we don't, to save memory.
|
316
|
+
changes
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paper_trail/events/base"
|
4
|
+
|
5
|
+
module PaperTrail
|
6
|
+
module Events
|
7
|
+
# See docs in `Base`.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Create < Base
|
11
|
+
# Return attributes of nascent `Version` record.
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def data
|
15
|
+
data = {
|
16
|
+
item: @record,
|
17
|
+
event: @record.paper_trail_event || "create",
|
18
|
+
whodunnit: PaperTrail.request.whodunnit
|
19
|
+
}
|
20
|
+
if @record.respond_to?(:updated_at)
|
21
|
+
data[:created_at] = @record.updated_at
|
22
|
+
end
|
23
|
+
if record_object_changes? && changed_notably?
|
24
|
+
changes = notable_changes
|
25
|
+
data[:object_changes] = prepare_object_changes(changes)
|
26
|
+
end
|
27
|
+
merge_item_subtype_into(data)
|
28
|
+
merge_metadata_into(data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paper_trail/events/base"
|
4
|
+
|
5
|
+
module PaperTrail
|
6
|
+
module Events
|
7
|
+
# See docs in `Base`.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Destroy < Base
|
11
|
+
# Return attributes of nascent `Version` record.
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def data
|
15
|
+
data = {
|
16
|
+
item_id: @record.id,
|
17
|
+
item_type: @record.class.base_class.name,
|
18
|
+
event: @record.paper_trail_event || "destroy",
|
19
|
+
whodunnit: PaperTrail.request.whodunnit
|
20
|
+
}
|
21
|
+
if record_object?
|
22
|
+
data[:object] = recordable_object(false)
|
23
|
+
end
|
24
|
+
if record_object_changes?
|
25
|
+
data[:object_changes] = prepare_object_changes(notable_changes)
|
26
|
+
end
|
27
|
+
merge_item_subtype_into(data)
|
28
|
+
merge_metadata_into(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Rails' implementation (eg. `@record.saved_changes`) returns nothing on
|
34
|
+
# destroy, so we have to build the hash we want.
|
35
|
+
#
|
36
|
+
# @override
|
37
|
+
def changes_in_latest_version
|
38
|
+
@record.attributes.transform_values { |value| [value, nil] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paper_trail/events/base"
|
4
|
+
|
5
|
+
module PaperTrail
|
6
|
+
module Events
|
7
|
+
# See docs in `Base`.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Update < Base
|
11
|
+
# - is_touch - [boolean] - Used in the two situations that are touch-like:
|
12
|
+
# - `after_touch` we call `RecordTrail#record_update`
|
13
|
+
# - force_changes - [Hash] - Only used by `RecordTrail#update_columns`,
|
14
|
+
# because there dirty-tracking is off, so it has to track its own changes.
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
def initialize(record, in_after_callback, is_touch, force_changes)
|
18
|
+
super(record, in_after_callback)
|
19
|
+
@is_touch = is_touch
|
20
|
+
@force_changes = force_changes
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return attributes of nascent `Version` record.
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def data
|
27
|
+
data = {
|
28
|
+
item: @record,
|
29
|
+
event: @record.paper_trail_event || "update",
|
30
|
+
whodunnit: PaperTrail.request.whodunnit
|
31
|
+
}
|
32
|
+
if @record.respond_to?(:updated_at)
|
33
|
+
data[:created_at] = @record.updated_at
|
34
|
+
end
|
35
|
+
if record_object?
|
36
|
+
data[:object] = recordable_object(@is_touch)
|
37
|
+
end
|
38
|
+
merge_object_changes_into(data)
|
39
|
+
merge_item_subtype_into(data)
|
40
|
+
merge_metadata_into(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
def merge_object_changes_into(data)
|
47
|
+
if record_object_changes?
|
48
|
+
changes = @force_changes.nil? ? notable_changes : @force_changes
|
49
|
+
data[:object_changes] = prepare_object_changes(changes)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# `touch` cannot record `object_changes` because rails' `touch` does not
|
54
|
+
# perform dirty-tracking. Specifically, methods from `Dirty`, like
|
55
|
+
# `saved_changes`, return the same values before and after `touch`.
|
56
|
+
#
|
57
|
+
# See https://github.com/rails/rails/issues/33429
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
def record_object_changes?
|
61
|
+
!@is_touch && super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,5 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Either ActiveRecord has already been loaded by the Lazy Load Hook in our
|
4
|
+
# Railtie, or else we load it now.
|
5
|
+
require "active_record"
|
6
|
+
::PaperTrail::Compatibility.check_activerecord(::ActiveRecord.gem_version)
|
7
|
+
|
8
|
+
# Now we can load the parts of PT that depend on AR.
|
9
|
+
require "paper_trail/has_paper_trail"
|
10
|
+
require "paper_trail/reifier"
|
5
11
|
require "paper_trail/frameworks/active_record/models/paper_trail/version"
|
12
|
+
ActiveRecord::Base.include PaperTrail::Model
|
@@ -25,9 +25,7 @@ module PaperTrail
|
|
25
25
|
# @api public
|
26
26
|
def user_for_paper_trail
|
27
27
|
return unless defined?(current_user)
|
28
|
-
|
29
|
-
rescue NoMethodError
|
30
|
-
current_user
|
28
|
+
current_user.try(:id) || current_user
|
31
29
|
end
|
32
30
|
|
33
31
|
# Returns any information about the controller or request that you
|
@@ -103,9 +101,3 @@ module PaperTrail
|
|
103
101
|
end
|
104
102
|
end
|
105
103
|
end
|
106
|
-
|
107
|
-
if defined?(::ActionController)
|
108
|
-
::ActiveSupport.on_load(:action_controller) do
|
109
|
-
include ::PaperTrail::Rails::Controller
|
110
|
-
end
|
111
|
-
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
4
|
+
# Represents code to load within Rails framework. See documentation in
|
5
|
+
# `railties/lib/rails/railtie.rb`.
|
6
|
+
# @api private
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
# PaperTrail only has one initializer.
|
9
|
+
#
|
10
|
+
# We specify `before: "load_config_initializers"` to ensure that the PT
|
11
|
+
# initializer happens before "app initializers" (those defined in
|
12
|
+
# the app's `config/initalizers`).
|
13
|
+
initializer "paper_trail", before: "load_config_initializers" do
|
14
|
+
# `on_load` is a "lazy load hook". It "declares a block that will be
|
15
|
+
# executed when a Rails component is fully loaded". (See
|
16
|
+
# `active_support/lazy_load_hooks.rb`)
|
17
|
+
ActiveSupport.on_load(:action_controller) do
|
18
|
+
require "paper_trail/frameworks/rails/controller"
|
19
|
+
|
20
|
+
# Mix our extensions into `ActionController::Base`, which is `self`
|
21
|
+
# because of the `class_eval` in `lazy_load_hooks.rb`.
|
22
|
+
include PaperTrail::Rails::Controller
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveSupport.on_load(:active_record) do
|
26
|
+
require "paper_trail/frameworks/active_record"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -12,7 +12,7 @@ module PaperTrail
|
|
12
12
|
# `.paper_trail` and `#paper_trail`.
|
13
13
|
module Model
|
14
14
|
def self.included(base)
|
15
|
-
base.
|
15
|
+
base.extend ClassMethods
|
16
16
|
end
|
17
17
|
|
18
18
|
# :nodoc:
|
@@ -23,18 +23,18 @@ module PaperTrail
|
|
23
23
|
# Options:
|
24
24
|
#
|
25
25
|
# - :on - The events to track (optional; defaults to all of them). Set
|
26
|
-
# to an array of `:create`, `:update`, `:destroy` as desired.
|
27
|
-
# - :class_name - The name of a custom Version class
|
28
|
-
#
|
26
|
+
# to an array of `:create`, `:update`, `:destroy` and `:touch` as desired.
|
27
|
+
# - :class_name (deprecated) - The name of a custom Version class that
|
28
|
+
# includes `PaperTrail::VersionConcern`.
|
29
29
|
# - :ignore - An array of attributes for which a new `Version` will not be
|
30
|
-
# created if only they change. It can also
|
30
|
+
# created if only they change. It can also accept a Hash as an
|
31
31
|
# argument where the key is the attribute to ignore (a `String` or
|
32
32
|
# `Symbol`), which will only be ignored if the value is a `Proc` which
|
33
33
|
# returns truthily.
|
34
34
|
# - :if, :unless - Procs that allow to specify conditions when to save
|
35
35
|
# versions for an object.
|
36
36
|
# - :only - Inverse of `ignore`. A new `Version` will be created only
|
37
|
-
# for these attributes if supplied it can also
|
37
|
+
# for these attributes if supplied it can also accept a Hash as an
|
38
38
|
# argument where the key is the attribute to track (a `String` or
|
39
39
|
# `Symbol`), which will only be counted if the value is a `Proc` which
|
40
40
|
# returns truthily.
|
@@ -47,22 +47,25 @@ module PaperTrail
|
|
47
47
|
# are called with `self`, i.e. the model with the paper trail). See
|
48
48
|
# `PaperTrail::Controller.info_for_paper_trail` for how to store data
|
49
49
|
# from the controller.
|
50
|
-
# - :versions -
|
51
|
-
#
|
50
|
+
# - :versions - Either,
|
51
|
+
# - A String (deprecated) - The name to use for the versions
|
52
|
+
# association. Default is `:versions`.
|
53
|
+
# - A Hash - options passed to `has_many`, plus `name:` and `scope:`.
|
52
54
|
# - :version - The name to use for the method which returns the version
|
53
55
|
# the instance was reified from. Default is `:version`.
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
56
|
+
#
|
57
|
+
# Plugins like the experimental `paper_trail-association_tracking` gem
|
58
|
+
# may accept additional options.
|
59
|
+
#
|
60
|
+
# You can define a default set of options via the configurable
|
61
|
+
# `PaperTrail.config.has_paper_trail_defaults` hash in your applications
|
62
|
+
# initializer. The hash can contain any of the following options and will
|
63
|
+
# provide an overridable default for all models.
|
62
64
|
#
|
63
65
|
# @api public
|
64
66
|
def has_paper_trail(options = {})
|
65
|
-
|
67
|
+
defaults = PaperTrail.config.has_paper_trail_defaults
|
68
|
+
paper_trail.setup(defaults.merge(options))
|
66
69
|
end
|
67
70
|
|
68
71
|
# @api public
|