paper_trail 4.0.0 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|