paper_trail 4.0.0 → 4.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/CHANGELOG.md +22 -7
- data/CONTRIBUTING.md +11 -0
- data/README.md +260 -229
- data/lib/paper_trail.rb +17 -13
- data/lib/paper_trail/cleaner.rb +21 -11
- data/lib/paper_trail/config.rb +3 -1
- data/lib/paper_trail/frameworks/active_record.rb +2 -2
- data/lib/paper_trail/frameworks/rails/controller.rb +11 -9
- data/lib/paper_trail/frameworks/sinatra.rb +2 -1
- data/lib/paper_trail/has_paper_trail.rb +90 -54
- data/lib/paper_trail/version_concern.rb +87 -58
- data/lib/paper_trail/version_number.rb +1 -1
- data/spec/models/skipper_spec.rb +38 -9
- data/spec/models/widget_spec.rb +2 -2
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +2 -1
- data/test/dummy/db/schema.rb +63 -25
- data/test/unit/model_test.rb +2 -2
- metadata +47 -46
data/lib/paper_trail.rb
CHANGED
@@ -25,7 +25,7 @@ module PaperTrail
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# ActiveRecord 5 drops support for serialized attributes; for previous
|
28
|
-
# versions of ActiveRecord it is supported, we have a config option
|
28
|
+
# versions of ActiveRecord it is supported, we have a config option
|
29
29
|
# to enable it within PaperTrail.
|
30
30
|
def self.serialized_attributes?
|
31
31
|
!!PaperTrail.config.serialized_attributes && ::ActiveRecord::VERSION::MAJOR < 5
|
@@ -43,12 +43,14 @@ module PaperTrail
|
|
43
43
|
!!paper_trail_store[:request_enabled_for_controller]
|
44
44
|
end
|
45
45
|
|
46
|
-
# Sets whether PaperTrail is enabled or disabled for this model in the
|
46
|
+
# Sets whether PaperTrail is enabled or disabled for this model in the
|
47
|
+
# current request.
|
47
48
|
def self.enabled_for_model(model, value)
|
48
49
|
paper_trail_store[:"enabled_for_#{model}"] = value
|
49
50
|
end
|
50
51
|
|
51
|
-
# Returns `true` if PaperTrail is enabled for this model in the current
|
52
|
+
# Returns `true` if PaperTrail is enabled for this model in the current
|
53
|
+
# request, `false` otherwise.
|
52
54
|
def self.enabled_for_model?(model)
|
53
55
|
!!paper_trail_store.fetch(:"enabled_for_#{model}", true)
|
54
56
|
end
|
@@ -63,10 +65,9 @@ module PaperTrail
|
|
63
65
|
PaperTrail.config.timestamp_field
|
64
66
|
end
|
65
67
|
|
66
|
-
# Sets who is responsible for any changes that occur.
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# automatically to the `current_user`.
|
68
|
+
# Sets who is responsible for any changes that occur. You would normally use
|
69
|
+
# this in a migration or on the console, when working with models directly.
|
70
|
+
# In a controller it is set automatically to the `current_user`.
|
70
71
|
def self.whodunnit=(value)
|
71
72
|
paper_trail_store[:whodunnit] = value
|
72
73
|
end
|
@@ -76,8 +77,8 @@ module PaperTrail
|
|
76
77
|
paper_trail_store[:whodunnit]
|
77
78
|
end
|
78
79
|
|
79
|
-
# Sets any information from the controller that you want PaperTrail
|
80
|
-
#
|
80
|
+
# Sets any information from the controller that you want PaperTrail to
|
81
|
+
# store. By default this is set automatically by a before filter.
|
81
82
|
def self.controller_info=(value)
|
82
83
|
paper_trail_store[:controller_info] = value
|
83
84
|
end
|
@@ -117,8 +118,8 @@ module PaperTrail
|
|
117
118
|
|
118
119
|
private
|
119
120
|
|
120
|
-
# Thread-safe hash to hold PaperTrail's data.
|
121
|
-
#
|
121
|
+
# Thread-safe hash to hold PaperTrail's data. Initializing with needed
|
122
|
+
# default values.
|
122
123
|
def self.paper_trail_store
|
123
124
|
RequestStore.store[:paper_trail] ||= { :request_enabled_for_controller => true }
|
124
125
|
end
|
@@ -135,12 +136,15 @@ module PaperTrail
|
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
138
|
-
# Ensure `ProtectedAttributes` gem gets required if it is available before the
|
139
|
+
# Ensure `ProtectedAttributes` gem gets required if it is available before the
|
140
|
+
# `Version` class gets loaded in.
|
139
141
|
unless PaperTrail.active_record_protected_attributes?
|
140
142
|
PaperTrail.send(:remove_instance_variable, :@active_record_protected_attributes)
|
141
143
|
begin
|
142
144
|
require 'protected_attributes'
|
143
|
-
rescue LoadError
|
145
|
+
rescue LoadError
|
146
|
+
# In case `ProtectedAttributes` gem is not available.
|
147
|
+
end
|
144
148
|
end
|
145
149
|
|
146
150
|
ActiveSupport.on_load(:active_record) do
|
data/lib/paper_trail/cleaner.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
1
|
module PaperTrail
|
2
2
|
module Cleaner
|
3
|
-
# Destroys all but the most recent version(s) for items on a given date
|
3
|
+
# Destroys all but the most recent version(s) for items on a given date
|
4
|
+
# (or on all dates). Useful for deleting drafts.
|
4
5
|
#
|
5
6
|
# Options:
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
7
|
+
#
|
8
|
+
# - :keeping - An `integer` indicating the number of versions to be kept for
|
9
|
+
# each item per date. Defaults to `1`.
|
10
|
+
# - :date - Should either be a `Date` object specifying which date to
|
11
|
+
# destroy versions for or `:all`, which will specify that all dates
|
12
|
+
# should be cleaned. Defaults to `:all`.
|
13
|
+
# - :item_id - The `id` for the item to be cleaned on, or `nil`, which
|
14
|
+
# causes all items to be cleaned. Defaults to `nil`.
|
15
|
+
#
|
12
16
|
def clean_versions!(options = {})
|
13
17
|
options = {:keeping => 1, :date => :all}.merge(options)
|
14
18
|
gather_versions(options[:item_id], options[:date]).each do |item_id, versions|
|
15
19
|
versions.group_by { |v| v.send(PaperTrail.timestamp_field).to_date }.each do |date, _versions|
|
16
|
-
#
|
20
|
+
# Remove the number of versions we wish to keep from the collection
|
21
|
+
# of versions prior to destruction.
|
17
22
|
_versions.pop(options[:keeping])
|
18
23
|
_versions.map(&:destroy)
|
19
24
|
end
|
@@ -22,13 +27,18 @@ module PaperTrail
|
|
22
27
|
|
23
28
|
private
|
24
29
|
|
25
|
-
# Returns a hash of versions grouped by the `item_id` attribute formatted
|
26
|
-
# If `item_id` or `date` is
|
30
|
+
# Returns a hash of versions grouped by the `item_id` attribute formatted
|
31
|
+
# like this: {:item_id => PaperTrail::Version}. If `item_id` or `date` is
|
32
|
+
# set, versions will be narrowed to those pointing at items with those ids
|
33
|
+
# that were created on specified date.
|
27
34
|
def gather_versions(item_id = nil, date = :all)
|
28
35
|
raise ArgumentError.new("`date` argument must receive a Timestamp or `:all`") unless date == :all || date.respond_to?(:to_date)
|
29
36
|
versions = item_id ? PaperTrail::Version.where(:item_id => item_id) : PaperTrail::Version
|
30
37
|
versions = versions.between(date.to_date, date.to_date + 1.day) unless date == :all
|
31
|
-
|
38
|
+
|
39
|
+
# If `versions` has not been converted to an ActiveRecord::Relation yet,
|
40
|
+
# do so now.
|
41
|
+
versions = PaperTrail::Version.all if versions == PaperTrail::Version
|
32
42
|
versions.group_by(&:item_id)
|
33
43
|
end
|
34
44
|
end
|
data/lib/paper_trail/config.rb
CHANGED
@@ -32,7 +32,9 @@ module PaperTrail
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def track_associations
|
35
|
-
@track_associations
|
35
|
+
@track_associations.nil? ?
|
36
|
+
PaperTrail::VersionAssociation.table_exists? :
|
37
|
+
@track_associations
|
36
38
|
end
|
37
39
|
alias_method :track_associations?, :track_associations
|
38
40
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# This file only needs to be loaded if the gem is being used outside of Rails,
|
2
|
-
# the model(s) will get loaded in via the `Rails::Engine
|
1
|
+
# This file only needs to be loaded if the gem is being used outside of Rails,
|
2
|
+
# since otherwise the model(s) will get loaded in via the `Rails::Engine`.
|
3
3
|
require "paper_trail/frameworks/active_record/models/paper_trail/version_association"
|
4
4
|
require "paper_trail/frameworks/active_record/models/paper_trail/version"
|
@@ -36,24 +36,26 @@ module PaperTrail
|
|
36
36
|
#
|
37
37
|
# The columns `ip` and `user_agent` must exist in your `versions` # table.
|
38
38
|
#
|
39
|
-
# Use the `:meta` option to
|
40
|
-
# to store any extra
|
39
|
+
# Use the `:meta` option to
|
40
|
+
# `PaperTrail::Model::ClassMethods.has_paper_trail` to store any extra
|
41
|
+
# model-level data you need.
|
41
42
|
def info_for_paper_trail
|
42
43
|
{}
|
43
44
|
end
|
44
45
|
|
45
|
-
# Returns `true` (default) or `false` depending on whether PaperTrail
|
46
|
-
# be active for the current request.
|
46
|
+
# Returns `true` (default) or `false` depending on whether PaperTrail
|
47
|
+
# should be active for the current request.
|
47
48
|
#
|
48
|
-
# Override this method in your controller to specify when PaperTrail
|
49
|
-
# be off.
|
49
|
+
# Override this method in your controller to specify when PaperTrail
|
50
|
+
# should be off.
|
50
51
|
def paper_trail_enabled_for_controller
|
51
52
|
::PaperTrail.enabled?
|
52
53
|
end
|
53
54
|
|
54
55
|
private
|
55
56
|
|
56
|
-
# Tells PaperTrail whether versions should be saved in the current
|
57
|
+
# Tells PaperTrail whether versions should be saved in the current
|
58
|
+
# request.
|
57
59
|
def set_paper_trail_enabled_for_controller
|
58
60
|
::PaperTrail.enabled_for_controller = paper_trail_enabled_for_controller
|
59
61
|
end
|
@@ -63,8 +65,8 @@ module PaperTrail
|
|
63
65
|
::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled_for_controller?
|
64
66
|
end
|
65
67
|
|
66
|
-
# Tells PaperTrail any information from the controller you want
|
67
|
-
#
|
68
|
+
# Tells PaperTrail any information from the controller you want to store
|
69
|
+
# alongside any changes that occur.
|
68
70
|
def set_paper_trail_controller_info
|
69
71
|
::PaperTrail.controller_info = info_for_paper_trail if ::PaperTrail.enabled_for_controller?
|
70
72
|
end
|
@@ -3,7 +3,8 @@ require 'active_support/core_ext/object' # provides the `try` method
|
|
3
3
|
module PaperTrail
|
4
4
|
module Sinatra
|
5
5
|
|
6
|
-
# Register this module inside your Sinatra application to gain access to
|
6
|
+
# Register this module inside your Sinatra application to gain access to
|
7
|
+
# controller-level methods used by PaperTrail.
|
7
8
|
def self.registered(app)
|
8
9
|
app.use RequestStore::Middleware
|
9
10
|
app.helpers self
|
@@ -8,31 +8,42 @@ module PaperTrail
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
-
# Declare this in your model to track every create, update, and destroy.
|
12
|
-
# the model is available in the `versions` association.
|
11
|
+
# Declare this in your model to track every create, update, and destroy.
|
12
|
+
# Each version of the model is available in the `versions` association.
|
13
13
|
#
|
14
14
|
# Options:
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# :
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# :
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# :
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
15
|
+
#
|
16
|
+
# - :on - The events to track (optional; defaults to all of them). Set
|
17
|
+
# to an array of `:create`, `:update`, `:destroy` as desired.
|
18
|
+
# - :class_name - The name of a custom Version class. This class should
|
19
|
+
# inherit from `PaperTrail::Version`.
|
20
|
+
# - :ignore - An array of attributes for which a new `Version` will not be
|
21
|
+
# created if only they change. It can also aceept a Hash as an
|
22
|
+
# argument where the key is the attribute to ignore (a `String` or
|
23
|
+
# `Symbol`), which will only be ignored if the value is a `Proc` which
|
24
|
+
# returns truthily.
|
25
|
+
# - :if, :unless - Procs that allow to specify conditions when to save
|
26
|
+
# versions for an object.
|
27
|
+
# - :only - Inverse of `ignore`. A new `Version` will be created only
|
28
|
+
# for these attributes if supplied it can also aceept a Hash as an
|
29
|
+
# argument where the key is the attribute to track (a `String` or
|
30
|
+
# `Symbol`), which will only be counted if the value is a `Proc` which
|
31
|
+
# returns truthily.
|
32
|
+
# - :skip - Fields to ignore completely. As with `ignore`, updates to
|
33
|
+
# these fields will not create a new `Version`. In addition, these
|
34
|
+
# fields will not be included in the serialized versions of the object
|
35
|
+
# whenever a new `Version` is created.
|
36
|
+
# - :meta - A hash of extra data to store. You must add a column to the
|
37
|
+
# `versions` table for each key. Values are objects or procs (which
|
38
|
+
# are called with `self`, i.e. the model with the paper trail). See
|
39
|
+
# `PaperTrail::Controller.info_for_paper_trail` for how to store data
|
40
|
+
# from the controller.
|
41
|
+
# - :versions - The name to use for the versions association. Default
|
42
|
+
# is `:versions`.
|
43
|
+
# - :version - The name to use for the method which returns the version
|
44
|
+
# the instance was reified from. Default is `:version`.
|
45
|
+
# - :save_changes - Whether or not to save changes to the object_changes
|
46
|
+
# column if it exists. Default is true
|
36
47
|
#
|
37
48
|
def has_paper_trail(options = {})
|
38
49
|
# Lazily include the instance methods so we don't clutter up
|
@@ -64,7 +75,8 @@ module PaperTrail
|
|
64
75
|
|
65
76
|
attr_accessor :paper_trail_event
|
66
77
|
|
67
|
-
|
78
|
+
# `has_many` syntax for specifying order uses a lambda in Rails 4
|
79
|
+
if ::ActiveRecord::VERSION::MAJOR >= 4
|
68
80
|
has_many self.versions_association_name,
|
69
81
|
lambda { order(model.timestamp_sort_order) },
|
70
82
|
:class_name => self.version_class_name, :as => :item
|
@@ -76,7 +88,11 @@ module PaperTrail
|
|
76
88
|
end
|
77
89
|
|
78
90
|
options[:on] ||= [:create, :update, :destroy]
|
79
|
-
|
91
|
+
|
92
|
+
# Wrap the :on option in an array if necessary. This allows a single
|
93
|
+
# symbol to be passed in.
|
94
|
+
options_on = Array(options[:on])
|
95
|
+
|
80
96
|
after_create :record_create, :if => :save_version? if options_on.include?(:create)
|
81
97
|
if options_on.include?(:update)
|
82
98
|
before_save :reset_timestamp_attrs_for_update_if_needed!, :on => :update
|
@@ -85,7 +101,7 @@ module PaperTrail
|
|
85
101
|
end
|
86
102
|
after_destroy :record_destroy, :if => :save_version? if options_on.include?(:destroy)
|
87
103
|
|
88
|
-
# Reset the transaction id when the transaction is closed
|
104
|
+
# Reset the transaction id when the transaction is closed.
|
89
105
|
after_commit :reset_transaction_id
|
90
106
|
after_rollback :reset_transaction_id
|
91
107
|
after_rollback :clear_rolled_back_versions
|
@@ -110,40 +126,47 @@ module PaperTrail
|
|
110
126
|
@paper_trail_version_class ||= version_class_name.constantize
|
111
127
|
end
|
112
128
|
|
113
|
-
# Used for Version#object attribute
|
129
|
+
# Used for `Version#object` attribute.
|
114
130
|
def serialize_attributes_for_paper_trail!(attributes)
|
115
|
-
#
|
131
|
+
# Don't serialize before values before inserting into columns of type
|
132
|
+
# `JSON` on `PostgreSQL` databases.
|
116
133
|
return attributes if self.paper_trail_version_class.object_col_is_json?
|
117
134
|
|
118
135
|
serialized_attributes.each do |key, coder|
|
119
136
|
if attributes.key?(key)
|
120
|
-
# Fall back to current serializer if `coder` has no `dump` method
|
137
|
+
# Fall back to current serializer if `coder` has no `dump` method.
|
121
138
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
122
139
|
attributes[key] = coder.dump(attributes[key])
|
123
140
|
end
|
124
141
|
end
|
125
142
|
end
|
126
143
|
|
144
|
+
# TODO: There is a lot of duplication between this and
|
145
|
+
# `serialize_attributes_for_paper_trail!`.
|
127
146
|
def unserialize_attributes_for_paper_trail!(attributes)
|
128
|
-
#
|
147
|
+
# Don't serialize before values before inserting into columns of type
|
148
|
+
# `JSON` on `PostgreSQL` databases.
|
129
149
|
return attributes if self.paper_trail_version_class.object_col_is_json?
|
130
150
|
|
131
151
|
serialized_attributes.each do |key, coder|
|
132
152
|
if attributes.key?(key)
|
153
|
+
# Fall back to current serializer if `coder` has no `dump` method.
|
154
|
+
# TODO: Shouldn't this be `:load`?
|
133
155
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
134
156
|
attributes[key] = coder.load(attributes[key])
|
135
157
|
end
|
136
158
|
end
|
137
159
|
end
|
138
160
|
|
139
|
-
# Used for Version#object_changes attribute
|
161
|
+
# Used for Version#object_changes attribute.
|
140
162
|
def serialize_attribute_changes_for_paper_trail!(changes)
|
141
|
-
#
|
163
|
+
# Don't serialize before values before inserting into columns of type `JSON`
|
164
|
+
# on `PostgreSQL` databases.
|
142
165
|
return changes if self.paper_trail_version_class.object_changes_col_is_json?
|
143
166
|
|
144
167
|
serialized_attributes.each do |key, coder|
|
145
168
|
if changes.key?(key)
|
146
|
-
# Fall back to current serializer if `coder` has no `dump` method
|
169
|
+
# Fall back to current serializer if `coder` has no `dump` method.
|
147
170
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
148
171
|
old_value, new_value = changes[key]
|
149
172
|
changes[key] = [coder.dump(old_value),
|
@@ -152,12 +175,17 @@ module PaperTrail
|
|
152
175
|
end
|
153
176
|
end
|
154
177
|
|
178
|
+
# TODO: There is a lot of duplication between this and
|
179
|
+
# `serialize_attribute_changes_for_paper_trail!`.
|
155
180
|
def unserialize_attribute_changes_for_paper_trail!(changes)
|
156
|
-
#
|
181
|
+
# Don't serialize before values before inserting into columns of type
|
182
|
+
# `JSON` on `PostgreSQL` databases.
|
157
183
|
return changes if self.paper_trail_version_class.object_changes_col_is_json?
|
158
184
|
|
159
185
|
serialized_attributes.each do |key, coder|
|
160
186
|
if changes.key?(key)
|
187
|
+
# Fall back to current serializer if `coder` has no `dump` method.
|
188
|
+
# TODO: Shouldn't this be `:load`?
|
161
189
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
162
190
|
old_value, new_value = changes[key]
|
163
191
|
changes[key] = [coder.load(old_value),
|
@@ -236,7 +264,8 @@ module PaperTrail
|
|
236
264
|
self.class.paper_trail_on! if paper_trail_was_enabled
|
237
265
|
end
|
238
266
|
|
239
|
-
# Utility method for reifying. Anything executed inside the block will
|
267
|
+
# Utility method for reifying. Anything executed inside the block will
|
268
|
+
# appear like a new record.
|
240
269
|
def appear_as_new_record
|
241
270
|
instance_eval {
|
242
271
|
alias :old_new_record? :new_record?
|
@@ -246,7 +275,8 @@ module PaperTrail
|
|
246
275
|
instance_eval { alias :new_record? :old_new_record? }
|
247
276
|
end
|
248
277
|
|
249
|
-
# Temporarily overwrites the value of whodunnit and then executes the
|
278
|
+
# Temporarily overwrites the value of whodunnit and then executes the
|
279
|
+
# provided block.
|
250
280
|
def whodunnit(value)
|
251
281
|
raise ArgumentError, 'expected to receive a block' unless block_given?
|
252
282
|
current_whodunnit = PaperTrail.whodunnit
|
@@ -296,8 +326,8 @@ module PaperTrail
|
|
296
326
|
:event => paper_trail_event || 'create',
|
297
327
|
:whodunnit => PaperTrail.whodunnit
|
298
328
|
}
|
299
|
-
if respond_to?(:
|
300
|
-
data[PaperTrail.timestamp_field] =
|
329
|
+
if respond_to?(:updated_at)
|
330
|
+
data[PaperTrail.timestamp_field] = updated_at
|
301
331
|
end
|
302
332
|
if paper_trail_options[:save_changes] && changed_notably? && self.class.paper_trail_version_class.column_names.include?('object_changes')
|
303
333
|
data[:object_changes] = self.class.paper_trail_version_class.object_changes_col_is_json? ? changes_for_paper_trail :
|
@@ -344,15 +374,19 @@ module PaperTrail
|
|
344
374
|
_changes.to_hash
|
345
375
|
end
|
346
376
|
|
347
|
-
# Invoked via`after_update` callback for when a previous version is
|
377
|
+
# Invoked via`after_update` callback for when a previous version is
|
378
|
+
# reified and then saved.
|
348
379
|
def clear_version_instance!
|
349
380
|
send("#{self.class.version_association_name}=", nil)
|
350
381
|
end
|
351
382
|
|
383
|
+
# Invoked via callback when a user attempts to persist a reified
|
384
|
+
# `Version`.
|
352
385
|
def reset_timestamp_attrs_for_update_if_needed!
|
353
|
-
return if self.live?
|
386
|
+
return if self.live?
|
354
387
|
timestamp_attributes_for_update_in_model.each do |column|
|
355
|
-
# ActiveRecord 4.2 deprecated `reset_column!` in favor of
|
388
|
+
# ActiveRecord 4.2 deprecated `reset_column!` in favor of
|
389
|
+
# `restore_column!`.
|
356
390
|
if respond_to?("restore_#{column}!")
|
357
391
|
send("restore_#{column}!")
|
358
392
|
else
|
@@ -382,7 +416,7 @@ module PaperTrail
|
|
382
416
|
end
|
383
417
|
end
|
384
418
|
|
385
|
-
#
|
419
|
+
# Saves associations if the join table for `VersionAssociation` exists.
|
386
420
|
def save_associations(version)
|
387
421
|
return unless PaperTrail.config.track_associations?
|
388
422
|
self.class.reflect_on_all_associations(:belongs_to).each do |assoc|
|
@@ -424,8 +458,8 @@ module PaperTrail
|
|
424
458
|
if v.respond_to?(:call)
|
425
459
|
v.call(self)
|
426
460
|
elsif v.is_a?(Symbol) && respond_to?(v)
|
427
|
-
#
|
428
|
-
# be sure to grab the current version
|
461
|
+
# If it is an attribute that is changing in an existing object,
|
462
|
+
# be sure to grab the current version.
|
429
463
|
if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] != 'create'
|
430
464
|
send("#{v}_was".to_sym)
|
431
465
|
else
|
@@ -435,6 +469,7 @@ module PaperTrail
|
|
435
469
|
v
|
436
470
|
end
|
437
471
|
end
|
472
|
+
|
438
473
|
# Second we merge any extra data from the controller (if available).
|
439
474
|
data.merge(PaperTrail.controller_info || {})
|
440
475
|
end
|
@@ -449,8 +484,8 @@ module PaperTrail
|
|
449
484
|
end
|
450
485
|
end
|
451
486
|
|
452
|
-
#
|
453
|
-
# ommitting attributes to be skipped
|
487
|
+
# Returns hash of attributes (with appropriate attributes serialized),
|
488
|
+
# ommitting attributes to be skipped.
|
454
489
|
def object_attrs_for_paper_trail(attributes_hash)
|
455
490
|
attrs = attributes_hash.except(*self.paper_trail_options[:skip])
|
456
491
|
if PaperTrail.serialized_attributes?
|
@@ -459,10 +494,9 @@ module PaperTrail
|
|
459
494
|
attrs
|
460
495
|
end
|
461
496
|
|
462
|
-
#
|
463
|
-
#
|
464
|
-
#
|
465
|
-
# changed.
|
497
|
+
# Determines whether it is appropriate to generate a new version
|
498
|
+
# instance. A timestamp-only update (e.g. only `updated_at` changed) is
|
499
|
+
# considered notable unless an ignored attribute was also changed.
|
466
500
|
def changed_notably?
|
467
501
|
if ignored_attr_has_changed?
|
468
502
|
timestamps = timestamp_attributes_for_update_in_model.map(&:to_s)
|
@@ -473,8 +507,8 @@ module PaperTrail
|
|
473
507
|
end
|
474
508
|
|
475
509
|
# An attributed is "ignored" if it is listed in the `:ignore` option
|
476
|
-
# and/or the `:skip` option. Returns true if an ignored attribute
|
477
|
-
#
|
510
|
+
# and/or the `:skip` option. Returns true if an ignored attribute has
|
511
|
+
# changed.
|
478
512
|
def ignored_attr_has_changed?
|
479
513
|
ignored = self.paper_trail_options[:ignore] + self.paper_trail_options[:skip]
|
480
514
|
ignored.any? && (changed & ignored).any?
|
@@ -482,7 +516,8 @@ module PaperTrail
|
|
482
516
|
|
483
517
|
def notably_changed
|
484
518
|
only = self.paper_trail_options[:only].dup
|
485
|
-
#
|
519
|
+
# Remove Hash arguments and then evaluate whether the attributes (the
|
520
|
+
# keys of the hash) should also get pushed into the collection.
|
486
521
|
only.delete_if do |obj|
|
487
522
|
obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) }
|
488
523
|
end
|
@@ -491,7 +526,8 @@ module PaperTrail
|
|
491
526
|
|
492
527
|
def changed_and_not_ignored
|
493
528
|
ignore = self.paper_trail_options[:ignore].dup
|
494
|
-
|
529
|
+
# Remove Hash arguments and then evaluate whether the attributes (the
|
530
|
+
# keys of the hash) should also get pushed into the collection.
|
495
531
|
ignore.delete_if do |obj|
|
496
532
|
obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) }
|
497
533
|
end
|