paper_trail 3.0.9 → 4.0.0.beta1
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/.gitignore +2 -0
- data/.rspec +1 -2
- data/.travis.yml +5 -0
- data/CHANGELOG.md +37 -23
- data/README.md +170 -63
- data/gemfiles/3.0.gemfile +10 -4
- data/lib/generators/paper_trail/install_generator.rb +19 -3
- data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +11 -0
- data/lib/generators/paper_trail/templates/create_version_associations.rb +17 -0
- data/lib/paper_trail.rb +24 -4
- data/lib/paper_trail/cleaner.rb +3 -3
- data/lib/paper_trail/config.rb +17 -0
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +7 -0
- data/lib/paper_trail/frameworks/rails.rb +1 -0
- data/lib/paper_trail/frameworks/rspec.rb +5 -0
- data/lib/paper_trail/has_paper_trail.rb +112 -38
- data/lib/paper_trail/version_association_concern.rb +13 -0
- data/lib/paper_trail/version_concern.rb +145 -38
- data/lib/paper_trail/version_number.rb +3 -3
- data/paper_trail.gemspec +11 -4
- data/spec/generators/install_generator_spec.rb +4 -4
- data/spec/models/fluxor_spec.rb +19 -0
- data/spec/models/gadget_spec.rb +10 -10
- data/spec/models/joined_version_spec.rb +9 -9
- data/spec/models/post_with_status_spec.rb +3 -3
- data/spec/models/version_spec.rb +49 -71
- data/spec/models/widget_spec.rb +124 -71
- data/spec/modules/version_concern_spec.rb +8 -8
- data/spec/modules/version_number_spec.rb +16 -16
- data/spec/paper_trail_spec.rb +17 -17
- data/spec/rails_helper.rb +34 -0
- data/spec/requests/articles_spec.rb +11 -11
- data/spec/spec_helper.rb +77 -36
- data/test/dummy/app/models/animal.rb +0 -2
- data/test/dummy/app/models/book.rb +4 -0
- data/test/dummy/app/models/customer.rb +4 -0
- data/test/dummy/app/models/editor.rb +4 -0
- data/test/dummy/app/models/editorship.rb +5 -0
- data/test/dummy/app/models/line_item.rb +4 -0
- data/test/dummy/app/models/order.rb +5 -0
- data/test/dummy/app/models/person.rb +1 -1
- data/test/dummy/app/models/post.rb +0 -1
- data/test/dummy/app/models/song.rb +0 -20
- data/test/dummy/app/models/widget.rb +4 -0
- data/test/dummy/config/application.rb +3 -0
- data/test/dummy/config/initializers/paper_trail.rb +1 -1
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +41 -0
- data/test/dummy/db/schema.rb +95 -25
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/rails.js +175 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/test_helper.rb +2 -2
- data/test/time_travel_helper.rb +15 -0
- data/test/unit/model_test.rb +613 -185
- data/test/unit/serializer_test.rb +3 -3
- metadata +104 -54
- data/spec/models/animal_spec.rb +0 -19
- data/test/dummy/public/javascripts/prototype.js +0 -6001
data/gemfiles/3.0.gemfile
CHANGED
@@ -6,7 +6,7 @@ gem 'i18n', '~> 0.6.11'
|
|
6
6
|
group :development, :test do
|
7
7
|
gem 'rake', '~> 10.1.1'
|
8
8
|
gem 'shoulda', '~> 3.5'
|
9
|
-
gem 'ffaker', '
|
9
|
+
gem 'ffaker', '>= 1.15'
|
10
10
|
|
11
11
|
# Testing of Rails
|
12
12
|
gem 'railties', '~> 3.0'
|
@@ -16,16 +16,23 @@ group :development, :test do
|
|
16
16
|
gem 'rack-test', '>= 0.6'
|
17
17
|
|
18
18
|
# RSpec testing
|
19
|
-
gem 'rspec-rails', '~>
|
19
|
+
gem 'rspec-rails', '~> 3.1.0'
|
20
20
|
gem 'generator_spec'
|
21
21
|
|
22
22
|
# To do proper transactional testing with ActiveSupport::TestCase on MySQL
|
23
23
|
gem 'database_cleaner', '~> 1.2.0'
|
24
24
|
|
25
|
+
# Allow time travel in testing. timecop is only supported after 1.9.2 but does a better cleanup at 'return'
|
26
|
+
if RUBY_VERSION < "1.9.2"
|
27
|
+
gem 'delorean'
|
28
|
+
else
|
29
|
+
gem 'timecop'
|
30
|
+
end
|
31
|
+
|
25
32
|
platforms :ruby do
|
26
33
|
gem 'sqlite3', '~> 1.2'
|
27
34
|
gem 'mysql2', '~> 0.3'
|
28
|
-
gem 'pg', '~> 0.17
|
35
|
+
gem 'pg', '~> 0.17'
|
29
36
|
end
|
30
37
|
|
31
38
|
platforms :jruby, :ruby_18 do
|
@@ -39,6 +46,5 @@ group :development, :test do
|
|
39
46
|
gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
|
40
47
|
gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3'
|
41
48
|
gem 'activerecord-jdbcmysql-adapter', '~> 1.3'
|
42
|
-
gem 'activerecord-jdbc-adapter', '1.3.15'
|
43
49
|
end
|
44
50
|
end
|
@@ -6,17 +6,33 @@ module PaperTrail
|
|
6
6
|
include ::Rails::Generators::Migration
|
7
7
|
|
8
8
|
source_root File.expand_path('../templates', __FILE__)
|
9
|
-
class_option :with_changes, :type => :boolean, :default => false,
|
9
|
+
class_option :with_changes, :type => :boolean, :default => false,
|
10
|
+
:desc => "Store changeset (diff) with each version"
|
11
|
+
class_option :with_associations, :type => :boolean, :default => false,
|
12
|
+
:desc => "Store transactional IDs to support association restoration"
|
10
13
|
|
11
14
|
desc 'Generates (but does not run) a migration to add a versions table.'
|
12
15
|
|
13
16
|
def create_migration_file
|
14
|
-
|
15
|
-
|
17
|
+
add_paper_trail_migration('create_versions')
|
18
|
+
add_paper_trail_migration('add_object_changes_to_versions') if options.with_changes?
|
19
|
+
if options.with_associations?
|
20
|
+
add_paper_trail_migration('create_version_associations')
|
21
|
+
add_paper_trail_migration('add_transaction_id_column_to_versions')
|
22
|
+
end
|
16
23
|
end
|
17
24
|
|
18
25
|
def self.next_migration_number(dirname)
|
19
26
|
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
20
27
|
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def add_paper_trail_migration(template)
|
31
|
+
migration_dir = File.expand_path('db/migrate')
|
32
|
+
|
33
|
+
if !self.class.migration_exists?(migration_dir, template)
|
34
|
+
migration_template "#{template}.rb", "db/migrate/#{template}.rb"
|
35
|
+
end
|
36
|
+
end
|
21
37
|
end
|
22
38
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AddTransactionIdColumnToVersions < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
add_column :versions, :transaction_id, :integer
|
4
|
+
add_index :versions, [:transaction_id]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
remove_index :versions, [:transaction_id]
|
9
|
+
remove_column :versions, :transaction_id
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateVersionAssociations < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :version_associations do |t|
|
4
|
+
t.integer :version_id
|
5
|
+
t.string :foreign_key_name, :null => false
|
6
|
+
t.integer :foreign_key_id
|
7
|
+
end
|
8
|
+
add_index :version_associations, [:version_id]
|
9
|
+
add_index :version_associations, [:foreign_key_name, :foreign_key_id], :name => 'index_version_associations_on_foreign_key'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.down
|
13
|
+
remove_index :version_associations, [:version_id]
|
14
|
+
remove_index :version_associations, :name => 'index_version_associations_on_foreign_key'
|
15
|
+
drop_table :version_associations
|
16
|
+
end
|
17
|
+
end
|
data/lib/paper_trail.rb
CHANGED
@@ -22,6 +22,13 @@ module PaperTrail
|
|
22
22
|
!!PaperTrail.config.enabled
|
23
23
|
end
|
24
24
|
|
25
|
+
# ActiveRecord 5 drops support for serialized attributes; for previous
|
26
|
+
# versions of ActiveRecord it is supported, we have a config option
|
27
|
+
# to enable it within PaperTrail.
|
28
|
+
def self.serialized_attributes?
|
29
|
+
!!PaperTrail.config.serialized_attributes && ::ActiveRecord::VERSION::MAJOR < 5
|
30
|
+
end
|
31
|
+
|
25
32
|
# Sets whether PaperTrail is enabled or disabled for the current request.
|
26
33
|
def self.enabled_for_controller=(value)
|
27
34
|
paper_trail_store[:request_enabled_for_controller] = value
|
@@ -94,6 +101,18 @@ module PaperTrail
|
|
94
101
|
@active_record_protected_attributes ||= ::ActiveRecord::VERSION::MAJOR < 4 || !!defined?(ProtectedAttributes)
|
95
102
|
end
|
96
103
|
|
104
|
+
def self.transaction?
|
105
|
+
ActiveRecord::Base.connection.open_transactions > 0
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.transaction_id
|
109
|
+
paper_trail_store[:transaction_id]
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.transaction_id=(id)
|
113
|
+
paper_trail_store[:transaction_id] = id
|
114
|
+
end
|
115
|
+
|
97
116
|
private
|
98
117
|
|
99
118
|
# Thread-safe hash to hold PaperTrail's data.
|
@@ -125,8 +144,9 @@ ActiveSupport.on_load(:active_record) do
|
|
125
144
|
end
|
126
145
|
|
127
146
|
# Require frameworks
|
128
|
-
require 'paper_trail/frameworks/active_record'
|
129
147
|
require 'paper_trail/frameworks/sinatra'
|
130
|
-
|
131
|
-
require 'paper_trail/frameworks/
|
132
|
-
|
148
|
+
if defined? Rails
|
149
|
+
require 'paper_trail/frameworks/rails'
|
150
|
+
else
|
151
|
+
require 'paper_trail/frameworks/active_record'
|
152
|
+
end
|
data/lib/paper_trail/cleaner.rb
CHANGED
@@ -12,10 +12,10 @@ module PaperTrail
|
|
12
12
|
def clean_versions!(options = {})
|
13
13
|
options = {:keeping => 1, :date => :all}.merge(options)
|
14
14
|
gather_versions(options[:item_id], options[:date]).each do |item_id, versions|
|
15
|
-
versions.group_by { |v| v.send(PaperTrail.timestamp_field).to_date }.each do |date,
|
15
|
+
versions.group_by { |v| v.send(PaperTrail.timestamp_field).to_date }.each do |date, _versions|
|
16
16
|
# remove the number of versions we wish to keep from the collection of versions prior to destruction
|
17
|
-
|
18
|
-
|
17
|
+
_versions.pop(options[:keeping])
|
18
|
+
_versions.map(&:destroy)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
data/lib/paper_trail/config.rb
CHANGED
@@ -4,11 +4,28 @@ module PaperTrail
|
|
4
4
|
class Config
|
5
5
|
include Singleton
|
6
6
|
attr_accessor :enabled, :timestamp_field, :serializer, :version_limit
|
7
|
+
attr_reader :serialized_attributes
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
@enabled = true # Indicates whether PaperTrail is on or off.
|
10
11
|
@timestamp_field = :created_at
|
11
12
|
@serializer = PaperTrail::Serializers::YAML
|
13
|
+
|
14
|
+
# This setting only defaults to false on AR 4.2+, because that's when
|
15
|
+
# it was deprecated. We want it to function with older versions of
|
16
|
+
# ActiveRecord by default.
|
17
|
+
if ::ActiveRecord::VERSION::STRING < '4.2'
|
18
|
+
@serialized_attributes = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def serialized_attributes=(value)
|
23
|
+
if ::ActiveRecord::VERSION::MAJOR >= 5
|
24
|
+
warn("DEPRECATED: ActiveRecord 5.0 deprecated `serialized_attributes` " +
|
25
|
+
"without replacement, so this PaperTrail config setting does " +
|
26
|
+
"nothing with this version, and is always turned off")
|
27
|
+
end
|
28
|
+
@serialized_attributes = value
|
12
29
|
end
|
13
30
|
end
|
14
31
|
end
|
@@ -22,3 +22,8 @@ RSpec::Matchers.define :be_versioned do
|
|
22
22
|
# check to see if the model has `has_paper_trail` declared on it
|
23
23
|
match { |actual| actual.kind_of?(::PaperTrail::Model::InstanceMethods) }
|
24
24
|
end
|
25
|
+
|
26
|
+
RSpec::Matchers.define :have_a_version_with do |attributes|
|
27
|
+
# check if the model has a version with the specified attributes
|
28
|
+
match { |actual| actual.versions.where_object(attributes).any? }
|
29
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/object' # provides the `try` method
|
2
|
-
|
3
1
|
module PaperTrail
|
4
2
|
module Model
|
5
3
|
|
@@ -76,10 +74,15 @@ module PaperTrail
|
|
76
74
|
after_create :record_create, :if => :save_version? if options_on.empty? || options_on.include?(:create)
|
77
75
|
if options_on.empty? || options_on.include?(:update)
|
78
76
|
before_save :reset_timestamp_attrs_for_update_if_needed!, :on => :update
|
79
|
-
|
77
|
+
after_update :record_update, :if => :save_version?
|
80
78
|
after_update :clear_version_instance!
|
81
79
|
end
|
82
80
|
after_destroy :record_destroy, :if => :save_version? if options_on.empty? || options_on.include?(:destroy)
|
81
|
+
|
82
|
+
# Reset the transaction id when the transaction is closed
|
83
|
+
after_commit :reset_transaction_id
|
84
|
+
after_rollback :reset_transaction_id
|
85
|
+
after_rollback :clear_rolled_back_versions
|
83
86
|
end
|
84
87
|
|
85
88
|
# Switches PaperTrail off for this class.
|
@@ -103,6 +106,7 @@ module PaperTrail
|
|
103
106
|
end
|
104
107
|
|
105
108
|
def paper_trail_enabled_for_model?
|
109
|
+
return false unless self.include?(PaperTrail::Model::InstanceMethods)
|
106
110
|
PaperTrail.enabled_for_model?(self)
|
107
111
|
end
|
108
112
|
|
@@ -117,7 +121,8 @@ module PaperTrail
|
|
117
121
|
|
118
122
|
serialized_attributes.each do |key, coder|
|
119
123
|
if attributes.key?(key)
|
120
|
-
|
124
|
+
# Fall back to current serializer if `coder` has no `dump` method
|
125
|
+
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
121
126
|
attributes[key] = coder.dump(attributes[key])
|
122
127
|
end
|
123
128
|
end
|
@@ -129,7 +134,7 @@ module PaperTrail
|
|
129
134
|
|
130
135
|
serialized_attributes.each do |key, coder|
|
131
136
|
if attributes.key?(key)
|
132
|
-
coder = PaperTrail
|
137
|
+
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
133
138
|
attributes[key] = coder.load(attributes[key])
|
134
139
|
end
|
135
140
|
end
|
@@ -142,7 +147,8 @@ module PaperTrail
|
|
142
147
|
|
143
148
|
serialized_attributes.each do |key, coder|
|
144
149
|
if changes.key?(key)
|
145
|
-
|
150
|
+
# Fall back to current serializer if `coder` has no `dump` method
|
151
|
+
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
146
152
|
old_value, new_value = changes[key]
|
147
153
|
changes[key] = [coder.dump(old_value),
|
148
154
|
coder.dump(new_value)]
|
@@ -156,7 +162,7 @@ module PaperTrail
|
|
156
162
|
|
157
163
|
serialized_attributes.each do |key, coder|
|
158
164
|
if changes.key?(key)
|
159
|
-
coder = PaperTrail
|
165
|
+
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
160
166
|
old_value, new_value = changes[key]
|
161
167
|
changes[key] = [coder.load(old_value),
|
162
168
|
coder.load(new_value)]
|
@@ -175,13 +181,14 @@ module PaperTrail
|
|
175
181
|
end
|
176
182
|
|
177
183
|
# Returns who put the object into its current state.
|
178
|
-
def
|
184
|
+
def originator
|
179
185
|
(source_version || send(self.class.versions_association_name).last).try(:whodunnit)
|
180
186
|
end
|
181
187
|
|
182
|
-
|
183
|
-
|
184
|
-
|
188
|
+
# Invoked after rollbacks to ensure versions records are not created
|
189
|
+
# for changes that never actually took place
|
190
|
+
def clear_rolled_back_versions
|
191
|
+
send(self.class.versions_association_name).reload
|
185
192
|
end
|
186
193
|
|
187
194
|
# Returns the object (not a Version) as it was at the given timestamp.
|
@@ -228,6 +235,16 @@ module PaperTrail
|
|
228
235
|
self.class.paper_trail_on! if paper_trail_was_enabled
|
229
236
|
end
|
230
237
|
|
238
|
+
# Utility method for reifying. Anything executed inside the block will appear like a new record
|
239
|
+
def appear_as_new_record
|
240
|
+
instance_eval {
|
241
|
+
alias :old_new_record? :new_record?
|
242
|
+
alias :new_record? :present?
|
243
|
+
}
|
244
|
+
yield
|
245
|
+
instance_eval { alias :new_record? :old_new_record? }
|
246
|
+
end
|
247
|
+
|
231
248
|
# Temporarily overwrites the value of whodunnit and then executes the provided block.
|
232
249
|
def whodunnit(value)
|
233
250
|
raise ArgumentError, 'expected to receive a block' unless block_given?
|
@@ -263,15 +280,20 @@ module PaperTrail
|
|
263
280
|
def record_create
|
264
281
|
if paper_trail_switched_on?
|
265
282
|
data = {
|
266
|
-
:event
|
267
|
-
:whodunnit
|
283
|
+
:event => paper_trail_event || 'create',
|
284
|
+
:whodunnit => PaperTrail.whodunnit,
|
285
|
+
:transaction_id => PaperTrail.transaction_id
|
268
286
|
}
|
269
|
-
|
287
|
+
if respond_to?(:created_at)
|
288
|
+
data[PaperTrail.timestamp_field] = created_at
|
289
|
+
end
|
270
290
|
if changed_notably? and self.class.paper_trail_version_class.column_names.include?('object_changes')
|
271
291
|
data[:object_changes] = self.class.paper_trail_version_class.object_changes_col_is_json? ? changes_for_paper_trail :
|
272
292
|
PaperTrail.serializer.dump(changes_for_paper_trail)
|
273
293
|
end
|
274
|
-
send(self.class.versions_association_name).create! merge_metadata(data)
|
294
|
+
version = send(self.class.versions_association_name).create! merge_metadata(data)
|
295
|
+
set_transaction_id(version)
|
296
|
+
save_associations(version)
|
275
297
|
end
|
276
298
|
end
|
277
299
|
|
@@ -279,23 +301,30 @@ module PaperTrail
|
|
279
301
|
if paper_trail_switched_on? && changed_notably?
|
280
302
|
object_attrs = object_attrs_for_paper_trail(item_before_change)
|
281
303
|
data = {
|
282
|
-
:event
|
283
|
-
:object
|
284
|
-
:whodunnit
|
304
|
+
:event => paper_trail_event || 'update',
|
305
|
+
:object => self.class.paper_trail_version_class.object_col_is_json? ? object_attrs : PaperTrail.serializer.dump(object_attrs),
|
306
|
+
:whodunnit => PaperTrail.whodunnit,
|
307
|
+
:transaction_id => PaperTrail.transaction_id
|
285
308
|
}
|
286
|
-
|
309
|
+
if respond_to?(:updated_at)
|
310
|
+
data[PaperTrail.timestamp_field] = updated_at
|
311
|
+
end
|
287
312
|
if self.class.paper_trail_version_class.column_names.include?('object_changes')
|
288
313
|
data[:object_changes] = self.class.paper_trail_version_class.object_changes_col_is_json? ? changes_for_paper_trail :
|
289
314
|
PaperTrail.serializer.dump(changes_for_paper_trail)
|
290
315
|
end
|
291
|
-
send(self.class.versions_association_name).
|
316
|
+
version = send(self.class.versions_association_name).create merge_metadata(data)
|
317
|
+
set_transaction_id(version)
|
318
|
+
save_associations(version)
|
292
319
|
end
|
293
320
|
end
|
294
321
|
|
295
322
|
def changes_for_paper_trail
|
296
|
-
|
297
|
-
|
298
|
-
|
323
|
+
_changes = changes.delete_if { |k,v| !notably_changed.include?(k) }
|
324
|
+
if PaperTrail.serialized_attributes?
|
325
|
+
self.class.serialize_attribute_changes(_changes)
|
326
|
+
end
|
327
|
+
_changes.to_hash
|
299
328
|
end
|
300
329
|
|
301
330
|
# Invoked via`after_update` callback for when a previous version is reified and then saved
|
@@ -305,24 +334,59 @@ module PaperTrail
|
|
305
334
|
|
306
335
|
def reset_timestamp_attrs_for_update_if_needed!
|
307
336
|
return if self.live? # invoked via callback when a user attempts to persist a reified `Version`
|
308
|
-
timestamp_attributes_for_update_in_model.each
|
337
|
+
timestamp_attributes_for_update_in_model.each do |column|
|
338
|
+
# ActiveRecord 4.2 deprecated `reset_column!` in favor of `restore_column!`
|
339
|
+
if respond_to?("restore_#{column}!")
|
340
|
+
send("restore_#{column}!")
|
341
|
+
else
|
342
|
+
send("reset_#{column}!")
|
343
|
+
end
|
344
|
+
end
|
309
345
|
end
|
310
346
|
|
311
347
|
def record_destroy
|
312
348
|
if paper_trail_switched_on? and not new_record?
|
313
349
|
object_attrs = object_attrs_for_paper_trail(item_before_change)
|
314
350
|
data = {
|
315
|
-
:item_id
|
316
|
-
:item_type
|
317
|
-
:event
|
318
|
-
:object
|
319
|
-
:whodunnit
|
351
|
+
:item_id => self.id,
|
352
|
+
:item_type => self.class.base_class.name,
|
353
|
+
:event => paper_trail_event || 'destroy',
|
354
|
+
:object => self.class.paper_trail_version_class.object_col_is_json? ? object_attrs : PaperTrail.serializer.dump(object_attrs),
|
355
|
+
:whodunnit => PaperTrail.whodunnit,
|
356
|
+
:transaction_id => PaperTrail.transaction_id
|
320
357
|
}
|
321
|
-
|
358
|
+
version = self.class.paper_trail_version_class.create(merge_metadata(data))
|
359
|
+
send("#{self.class.version_association_name}=", version)
|
322
360
|
send(self.class.versions_association_name).send :load_target
|
361
|
+
set_transaction_id(version)
|
362
|
+
save_associations(version)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def save_associations(version)
|
367
|
+
self.class.reflect_on_all_associations(:belongs_to).each do |assoc|
|
368
|
+
if assoc.klass.paper_trail_enabled_for_model?
|
369
|
+
PaperTrail::VersionAssociation.create(
|
370
|
+
:version_id => version.id,
|
371
|
+
:foreign_key_name => assoc.foreign_key,
|
372
|
+
:foreign_key_id => self.send(assoc.foreign_key)
|
373
|
+
)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def set_transaction_id(version)
|
379
|
+
if PaperTrail.transaction? && PaperTrail.transaction_id.nil?
|
380
|
+
PaperTrail.transaction_id = version.id
|
381
|
+
version.transaction_id = version.id
|
382
|
+
version.save
|
323
383
|
end
|
324
384
|
end
|
325
385
|
|
386
|
+
def reset_transaction_id
|
387
|
+
PaperTrail.transaction_id = nil
|
388
|
+
end
|
389
|
+
|
326
390
|
def merge_metadata(data)
|
327
391
|
# First we merge the model-level metadata in `meta`.
|
328
392
|
paper_trail_options[:meta].each do |k,v|
|
@@ -345,8 +409,16 @@ module PaperTrail
|
|
345
409
|
end
|
346
410
|
|
347
411
|
def item_before_change
|
348
|
-
|
349
|
-
|
412
|
+
previous = self.dup
|
413
|
+
# `dup` clears timestamps so we add them back.
|
414
|
+
all_timestamp_attributes.each do |column|
|
415
|
+
if self.class.column_names.include?(column.to_s) and not send("#{column}_was").nil?
|
416
|
+
previous[column] = send("#{column}_was")
|
417
|
+
end
|
418
|
+
end
|
419
|
+
enums = previous.respond_to?(:defined_enums) ? previous.defined_enums : {}
|
420
|
+
previous.tap do |prev|
|
421
|
+
prev.id = id # `dup` clears the `id` so we add that back
|
350
422
|
changed_attributes.select { |k,v| self.class.column_names.include?(k) }.each do |attr, before|
|
351
423
|
before = enums[attr][before] if enums[attr]
|
352
424
|
prev[attr] = before
|
@@ -356,16 +428,18 @@ module PaperTrail
|
|
356
428
|
|
357
429
|
# returns hash of attributes (with appropriate attributes serialized),
|
358
430
|
# ommitting attributes to be skipped
|
359
|
-
def object_attrs_for_paper_trail(
|
360
|
-
|
361
|
-
|
431
|
+
def object_attrs_for_paper_trail(object)
|
432
|
+
attrs = object.attributes.except(*self.paper_trail_options[:skip])
|
433
|
+
if PaperTrail.serialized_attributes?
|
434
|
+
self.class.serialize_attributes_for_paper_trail(attrs)
|
362
435
|
end
|
436
|
+
attrs
|
363
437
|
end
|
364
438
|
|
365
439
|
# This method is invoked in order to determine whether it is appropriate to generate a new version instance.
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
440
|
+
# Because we are now using `after_(create/update/etc)` callbacks, we need to go out of our way to
|
441
|
+
# ensure that during updates timestamp attributes are not acknowledged as a notable changes
|
442
|
+
# to raise false positives when attributes are ignored.
|
369
443
|
def changed_notably?
|
370
444
|
if self.paper_trail_options[:ignore].any? && (changed & self.paper_trail_options[:ignore]).any?
|
371
445
|
(notably_changed - timestamp_attributes_for_update_in_model.map(&:to_s)).any?
|