paper_trail 5.2.3 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -74,8 +74,8 @@ module PaperTrail
74
74
  return where(arel_table[primary_key].gt(obj.id)).order(arel_table[primary_key].asc)
75
75
  end
76
76
 
77
- obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self)
78
- where(arel_table[PaperTrail.timestamp_field].gt(obj)).order(timestamp_sort_order)
77
+ obj = obj.send(:created_at) if obj.is_a?(self)
78
+ where(arel_table[:created_at].gt(obj)).order(timestamp_sort_order)
79
79
  end
80
80
 
81
81
  # Returns versions before `obj`.
@@ -90,22 +90,22 @@ module PaperTrail
90
90
  return where(arel_table[primary_key].lt(obj.id)).order(arel_table[primary_key].desc)
91
91
  end
92
92
 
93
- obj = obj.send(PaperTrail.timestamp_field) if obj.is_a?(self)
94
- where(arel_table[PaperTrail.timestamp_field].lt(obj)).
93
+ obj = obj.send(:created_at) if obj.is_a?(self)
94
+ where(arel_table[:created_at].lt(obj)).
95
95
  order(timestamp_sort_order("desc"))
96
96
  end
97
97
 
98
98
  def between(start_time, end_time)
99
99
  where(
100
- arel_table[PaperTrail.timestamp_field].gt(start_time).
101
- and(arel_table[PaperTrail.timestamp_field].lt(end_time))
100
+ arel_table[:created_at].gt(start_time).
101
+ and(arel_table[:created_at].lt(end_time))
102
102
  ).order(timestamp_sort_order)
103
103
  end
104
104
 
105
105
  # Defaults to using the primary key as the secondary sort order if
106
106
  # possible.
107
107
  def timestamp_sort_order(direction = "asc")
108
- [arel_table[PaperTrail.timestamp_field].send(direction.downcase)].tap do |array|
108
+ [arel_table[:created_at].send(direction.downcase)].tap do |array|
109
109
  array << arel_table[primary_key].send(direction.downcase) if primary_key_is_int?
110
110
  end
111
111
  end
@@ -1,9 +1,9 @@
1
1
  module PaperTrail
2
2
  # :nodoc:
3
3
  module VERSION
4
- MAJOR = 5
5
- MINOR = 2
6
- TINY = 3
4
+ MAJOR = 6
5
+ MINOR = 0
6
+ TINY = 0
7
7
  PRE = nil
8
8
 
9
9
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".").freeze
data/lib/paper_trail.rb CHANGED
@@ -72,21 +72,12 @@ module PaperTrail
72
72
 
73
73
  # Set the field which records when a version was created.
74
74
  # @api public
75
- # @deprecated
76
- def timestamp_field=(field_name)
77
- ::ActiveSupport::Deprecation.warn(
78
- "PaperTrail.timestamp_field= is deprecated without replacement." \
79
- "See https://github.com/airblade/paper_trail/pull/861 for discussion",
80
- caller(1)
75
+ def timestamp_field=(_field_name)
76
+ raise(
77
+ "PaperTrail.timestamp_field= has been removed, without replacement. " \
78
+ "It is no longer configurable. The timestamp field in the versions table " \
79
+ "must now be named created_at."
81
80
  )
82
- PaperTrail.config.timestamp_field = field_name
83
- end
84
-
85
- # Returns the field which records when a version was created.
86
- # @api public
87
- # @deprecated
88
- def timestamp_field
89
- PaperTrail.config.timestamp_field
90
81
  end
91
82
 
92
83
  # Sets who is responsible for any changes that occur. You would normally use
data/paper_trail.gemspec CHANGED
@@ -20,7 +20,8 @@ Gem::Specification.new do |s|
20
20
  s.required_rubygems_version = ">= 1.3.6"
21
21
  s.required_ruby_version = ">= 1.9.3"
22
22
 
23
- s.add_dependency "activerecord", [">= 3.0", "< 6.0"]
23
+ # Rails does not follow semver, makes breaking changes in minor versions.
24
+ s.add_dependency "activerecord", [">= 4.0", "< 5.2"]
24
25
  s.add_dependency "request_store", "~> 1.1"
25
26
 
26
27
  s.add_development_dependency "appraisal", "~> 2.1"
@@ -14,13 +14,28 @@ describe Widget, type: :model do
14
14
  widget.update_attributes!(name: "Bob")
15
15
  end
16
16
 
17
- it "is possible to do assertions on versions" do
17
+ it "is possible to do assertions on version attributes" do
18
18
  expect(widget).to have_a_version_with name: "Leonard", an_integer: 1
19
19
  expect(widget).to have_a_version_with an_integer: 1
20
20
  expect(widget).to have_a_version_with name: "Tom"
21
21
  end
22
22
  end
23
23
 
24
+ describe "`have_a_version_with_changes` matcher", versioning: true do
25
+ before do
26
+ widget.update_attributes!(name: "Leonard", an_integer: 2)
27
+ widget.update_attributes!(name: "Tom")
28
+ widget.update_attributes!(name: "Bob")
29
+ end
30
+
31
+ it "is possible to do assertions on version changes" do
32
+ expect(widget).to have_a_version_with_changes name: "Leonard", an_integer: 2
33
+ expect(widget).to have_a_version_with_changes an_integer: 2
34
+ expect(widget).to have_a_version_with_changes name: "Tom"
35
+ expect(widget).to have_a_version_with_changes name: "Bob"
36
+ end
37
+ end
38
+
24
39
  describe "versioning option" do
25
40
  context "enabled", versioning: true do
26
41
  it "should enable versioning" do
@@ -238,8 +253,6 @@ describe Widget, type: :model do
238
253
  end
239
254
 
240
255
  describe "#whodunnit" do
241
- it { is_expected.to respond_to(:whodunnit) }
242
-
243
256
  context "no block given" do
244
257
  it "should raise an error" do
245
258
  expect {
@@ -1,7 +1,6 @@
1
1
  # Example from 'Overwriting default accessors' in ActiveRecord::Base.
2
2
  class Song < ActiveRecord::Base
3
3
  has_paper_trail
4
- attr_accessor :name
5
4
 
6
5
  # Uses an integer of seconds to hold the length of the song
7
6
  def length=(minutes)
@@ -12,30 +11,36 @@ class Song < ActiveRecord::Base
12
11
  read_attribute(:length) / 60
13
12
  end
14
13
 
15
- # override attributes hashes like some libraries do
16
- def attributes_with_name
17
- if name
18
- attributes_without_name.merge(name: name)
19
- else
20
- attributes_without_name
14
+ if ActiveRecord::VERSION::MAJOR >= 5
15
+ attribute :name, :string
16
+ else
17
+ attr_accessor :name
18
+
19
+ # override attributes hashes like some libraries do
20
+ def attributes_with_name
21
+ if name
22
+ attributes_without_name.merge(name: name)
23
+ else
24
+ attributes_without_name
25
+ end
21
26
  end
22
- end
23
27
 
24
- # `alias_method_chain` is deprecated in rails 5, but we cannot use the
25
- # suggested replacement, `Module#prepend`, because we still support ruby 1.9.
26
- alias attributes_without_name attributes
27
- alias attributes attributes_with_name
28
+ # `alias_method_chain` is deprecated in rails 5, but we cannot use the
29
+ # suggested replacement, `Module#prepend`, because we still support ruby 1.9.
30
+ alias attributes_without_name attributes
31
+ alias attributes attributes_with_name
28
32
 
29
- def changed_attributes_with_name
30
- if name
31
- changed_attributes_without_name.merge(name: name)
32
- else
33
- changed_attributes_without_name
33
+ def changed_attributes_with_name
34
+ if name
35
+ changed_attributes_without_name.merge(name: name)
36
+ else
37
+ changed_attributes_without_name
38
+ end
34
39
  end
35
- end
36
40
 
37
- # `alias_method_chain` is deprecated in rails 5, but we cannot use the
38
- # suggested replacement, `Module#prepend`, because we still support ruby 1.9.
39
- alias changed_attributes_without_name changed_attributes
40
- alias changed_attributes changed_attributes_with_name
41
+ # `alias_method_chain` is deprecated in rails 5, but we cannot use the
42
+ # suggested replacement, `Module#prepend`, because we still support ruby 1.9.
43
+ alias changed_attributes_without_name changed_attributes
44
+ alias changed_attributes changed_attributes_with_name
45
+ end
41
46
  end
data/test/test_helper.rb CHANGED
@@ -88,19 +88,6 @@ module ActiveSupport
88
88
  end
89
89
  end
90
90
 
91
- #
92
- # Helpers
93
- #
94
-
95
- def change_schema
96
- ActiveRecord::Migration.verbose = false
97
- ActiveRecord::Schema.define do
98
- add_column :versions, :custom_created_at, :datetime
99
- end
100
- ActiveRecord::Migration.verbose = true
101
- reset_version_class_column_info!
102
- end
103
-
104
91
  # Wrap args in a hash to support the ActionController::TestCase and
105
92
  # ActionDispatch::Integration HTTP request method switch to keyword args
106
93
  # (see https://github.com/rails/rails/blob/master/actionpack/CHANGELOG.md)
@@ -112,16 +99,27 @@ def params_wrapper(args)
112
99
  end
113
100
  end
114
101
 
115
- def reset_version_class_column_info!
116
- PaperTrail::Version.connection.schema_cache.clear!
117
- PaperTrail::Version.reset_column_information
118
- end
102
+ module CleanupCallbacks
103
+ def cleanup_callbacks(target, type)
104
+ original_callbacks = nil
119
105
 
120
- def restore_schema
121
- ActiveRecord::Migration.verbose = false
122
- ActiveRecord::Schema.define do
123
- remove_column :versions, :custom_created_at
106
+ setup do
107
+ if ActiveRecord::VERSION::MAJOR > 3
108
+ original_callbacks = target.send(:get_callbacks, type).deep_dup
109
+ else
110
+ # While this defeats the purpose of targeted callback
111
+ # cleanup, callbacks were incredibly difficult to modify
112
+ # prior to Rails 4, and Rails internal callbacks were only
113
+ # used for autosaving associations in Rails 3. Our tests
114
+ # don't care whether a Fluxor's widget is autosaved.
115
+ target.reset_callbacks(type)
116
+ end
117
+ end
118
+
119
+ teardown do
120
+ if original_callbacks
121
+ target.send(:set_callbacks, type, original_callbacks)
122
+ end
123
+ end
124
124
  end
125
- ActiveRecord::Migration.verbose = true
126
- reset_version_class_column_info!
127
125
  end
@@ -785,7 +785,7 @@ class AssociationsTest < ActiveSupport::TestCase
785
785
  setup { @wotsit_0 = @wotsit.versions.last.reify(belongs_to: true) }
786
786
 
787
787
  should "see the associated as it was at the time" do
788
- assert_equal nil, @wotsit_0.widget
788
+ assert_nil @wotsit_0.widget
789
789
  end
790
790
 
791
791
  should "not persist changes to the live association" do
@@ -148,41 +148,4 @@ class PaperTrailCleanerTest < ActiveSupport::TestCase
148
148
  end
149
149
  end
150
150
  end # clean_versions! method
151
-
152
- context "Custom timestamp field" do
153
- setup do
154
- change_schema
155
- populate_db!
156
- # now mess with the timestamps
157
- @animals.each do |animal|
158
- animal.versions.reverse.each_with_index do |version, index|
159
- version.update_attribute(:custom_created_at, Time.now.utc + index.days)
160
- end
161
- end
162
- PaperTrail.timestamp_field = :custom_created_at
163
- @animals.map { |a| a.versions.reload } # reload the `versions` association for each animal
164
- end
165
-
166
- teardown do
167
- PaperTrail.timestamp_field = :created_at
168
- restore_schema
169
- end
170
-
171
- should "Baseline" do
172
- assert_equal 9, PaperTrail::Version.count
173
- @animals.each do |animal|
174
- assert_equal 3, animal.versions.size
175
- animal.versions.each_cons(2) do |a, b|
176
- assert_equal a.created_at.to_date, b.created_at.to_date
177
- assert_not_equal a.custom_created_at.to_date, b.custom_created_at.to_date
178
- end
179
- end
180
- end
181
-
182
- should "group by `PaperTrail.timestamp_field` when seperating the versions by date to clean" do
183
- assert_equal 9, PaperTrail::Version.count
184
- PaperTrail.clean_versions!
185
- assert_equal 9, PaperTrail::Version.count
186
- end
187
- end
188
151
  end
@@ -27,14 +27,12 @@ class InheritanceColumnTest < ActiveSupport::TestCase
27
27
 
28
28
  # For some reason `@dog.versions` doesn't include the final `destroy` version.
29
29
  # Neither do `@dog.versions.scoped` nor `@dog.versions(true)` nor `@dog.versions.reload`.
30
- dog_versions = PaperTrail::Version.where(item_id: @dog.id).
31
- order(PaperTrail.timestamp_field)
30
+ dog_versions = PaperTrail::Version.where(item_id: @dog.id).order(:created_at)
32
31
  assert_equal 4, dog_versions.count
33
32
  assert_nil dog_versions.first.reify
34
33
  assert_equal %w(NilClass Dog Dog Dog), dog_versions.map { |v| v.reify.class.name }
35
34
 
36
- cat_versions = PaperTrail::Version.where(item_id: @cat.id).
37
- order(PaperTrail.timestamp_field)
35
+ cat_versions = PaperTrail::Version.where(item_id: @cat.id).order(:created_at)
38
36
  assert_equal 4, cat_versions.count
39
37
  assert_nil cat_versions.first.reify
40
38
  assert_equal %w(NilClass Cat Cat Cat), cat_versions.map { |v| v.reify.class.name }
@@ -2,6 +2,8 @@ require "test_helper"
2
2
  require "time_travel_helper"
3
3
 
4
4
  class HasPaperTrailModelTest < ActiveSupport::TestCase
5
+ extend CleanupCallbacks
6
+
5
7
  context "A record with defined 'only' and 'ignore' attributes" do
6
8
  setup { @article = Article.create }
7
9
 
@@ -135,7 +137,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
135
137
  end
136
138
 
137
139
  should "have removed the skipped attributes when saving the previous version" do
138
- assert_equal nil, PaperTrail.serializer.load(@old_article.object)["file_upload"]
140
+ assert_nil PaperTrail.serializer.load(@old_article.object)["file_upload"]
139
141
  end
140
142
 
141
143
  should "have kept the non-skipped attributes in the previous version" do
@@ -517,16 +519,9 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
517
519
 
518
520
  context "after a column is removed from the record's schema" do
519
521
  setup do
520
- change_schema
521
- Widget.connection.schema_cache.clear!
522
- Widget.reset_column_information
523
522
  @last = @widget.versions.last
524
523
  end
525
524
 
526
- teardown do
527
- restore_schema
528
- end
529
-
530
525
  should "reify previous version" do
531
526
  assert_kind_of Widget, @last.reify
532
527
  end
@@ -1291,6 +1286,11 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1291
1286
 
1292
1287
  context "The `on` option" do
1293
1288
  context "on create" do
1289
+ cleanup_callbacks(Fluxor, :create)
1290
+ cleanup_callbacks(Fluxor, :update)
1291
+ cleanup_callbacks(Fluxor, :destroy)
1292
+ cleanup_callbacks(Fluxor, :save)
1293
+
1294
1294
  setup do
1295
1295
  Fluxor.instance_eval <<-END
1296
1296
  has_paper_trail :on => [:create]
@@ -1299,16 +1299,20 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1299
1299
  @fluxor.update_attributes name: "blah"
1300
1300
  @fluxor.destroy
1301
1301
  end
1302
+
1302
1303
  should "only have a version for the create event" do
1303
1304
  assert_equal 1, @fluxor.versions.length
1304
1305
  assert_equal "create", @fluxor.versions.last.event
1305
1306
  end
1306
1307
  end
1308
+
1307
1309
  context "on update" do
1310
+ cleanup_callbacks(Fluxor, :create)
1311
+ cleanup_callbacks(Fluxor, :update)
1312
+ cleanup_callbacks(Fluxor, :destroy)
1313
+ cleanup_callbacks(Fluxor, :save)
1314
+
1308
1315
  setup do
1309
- Fluxor.reset_callbacks :create
1310
- Fluxor.reset_callbacks :update
1311
- Fluxor.reset_callbacks :destroy
1312
1316
  Fluxor.instance_eval <<-END
1313
1317
  has_paper_trail :on => [:update]
1314
1318
  END
@@ -1316,16 +1320,20 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1316
1320
  @fluxor.update_attributes name: "blah"
1317
1321
  @fluxor.destroy
1318
1322
  end
1323
+
1319
1324
  should "only have a version for the update event" do
1320
1325
  assert_equal 1, @fluxor.versions.length
1321
1326
  assert_equal "update", @fluxor.versions.last.event
1322
1327
  end
1323
1328
  end
1329
+
1324
1330
  context "on destroy" do
1331
+ cleanup_callbacks(Fluxor, :create)
1332
+ cleanup_callbacks(Fluxor, :update)
1333
+ cleanup_callbacks(Fluxor, :destroy)
1334
+ cleanup_callbacks(Fluxor, :save)
1335
+
1325
1336
  setup do
1326
- Fluxor.reset_callbacks :create
1327
- Fluxor.reset_callbacks :update
1328
- Fluxor.reset_callbacks :destroy
1329
1337
  Fluxor.instance_eval <<-END
1330
1338
  has_paper_trail :on => [:destroy]
1331
1339
  END
@@ -1333,16 +1341,20 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1333
1341
  @fluxor.update_attributes name: "blah"
1334
1342
  @fluxor.destroy
1335
1343
  end
1344
+
1336
1345
  should "only have a version for the destroy event" do
1337
1346
  assert_equal 1, @fluxor.versions.length
1338
1347
  assert_equal "destroy", @fluxor.versions.last.event
1339
1348
  end
1340
1349
  end
1350
+
1341
1351
  context "on []" do
1352
+ cleanup_callbacks(Fluxor, :create)
1353
+ cleanup_callbacks(Fluxor, :update)
1354
+ cleanup_callbacks(Fluxor, :destroy)
1355
+ cleanup_callbacks(Fluxor, :save)
1356
+
1342
1357
  setup do
1343
- Fluxor.reset_callbacks :create
1344
- Fluxor.reset_callbacks :update
1345
- Fluxor.reset_callbacks :destroy
1346
1358
  Fluxor.instance_eval <<-END
1347
1359
  has_paper_trail :on => []
1348
1360
  END
@@ -1363,11 +1375,14 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1363
1375
  assert_equal 1, @fluxor.versions.length
1364
1376
  end
1365
1377
  end
1378
+
1366
1379
  context "allows a symbol to be passed" do
1380
+ cleanup_callbacks(Fluxor, :create)
1381
+ cleanup_callbacks(Fluxor, :update)
1382
+ cleanup_callbacks(Fluxor, :destroy)
1383
+ cleanup_callbacks(Fluxor, :save)
1384
+
1367
1385
  setup do
1368
- Fluxor.reset_callbacks :create
1369
- Fluxor.reset_callbacks :update
1370
- Fluxor.reset_callbacks :destroy
1371
1386
  Fluxor.instance_eval <<-END
1372
1387
  has_paper_trail :on => :create
1373
1388
  END
@@ -1375,6 +1390,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1375
1390
  @fluxor.update_attributes name: "blah"
1376
1391
  @fluxor.destroy
1377
1392
  end
1393
+
1378
1394
  should "only have a version for hte create event" do
1379
1395
  assert_equal 1, @fluxor.versions.length
1380
1396
  assert_equal "create", @fluxor.versions.last.event
@@ -1420,10 +1436,12 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1420
1436
 
1421
1437
  context "custom events" do
1422
1438
  context "on create" do
1439
+ cleanup_callbacks(Fluxor, :create)
1440
+ cleanup_callbacks(Fluxor, :update)
1441
+ cleanup_callbacks(Fluxor, :destroy)
1442
+ cleanup_callbacks(Fluxor, :save)
1443
+
1423
1444
  setup do
1424
- Fluxor.reset_callbacks :create
1425
- Fluxor.reset_callbacks :update
1426
- Fluxor.reset_callbacks :destroy
1427
1445
  Fluxor.instance_eval <<-END
1428
1446
  has_paper_trail :on => [:create]
1429
1447
  END
@@ -1431,16 +1449,20 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1431
1449
  @fluxor.update_attributes name: "blah"
1432
1450
  @fluxor.destroy
1433
1451
  end
1452
+
1434
1453
  should "only have a version for the created event" do
1435
1454
  assert_equal 1, @fluxor.versions.length
1436
1455
  assert_equal "created", @fluxor.versions.last.event
1437
1456
  end
1438
1457
  end
1458
+
1439
1459
  context "on update" do
1460
+ cleanup_callbacks(Fluxor, :create)
1461
+ cleanup_callbacks(Fluxor, :update)
1462
+ cleanup_callbacks(Fluxor, :destroy)
1463
+ cleanup_callbacks(Fluxor, :save)
1464
+
1440
1465
  setup do
1441
- Fluxor.reset_callbacks :create
1442
- Fluxor.reset_callbacks :update
1443
- Fluxor.reset_callbacks :destroy
1444
1466
  Fluxor.instance_eval <<-END
1445
1467
  has_paper_trail :on => [:update]
1446
1468
  END
@@ -1448,16 +1470,20 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1448
1470
  @fluxor.update_attributes name: "blah"
1449
1471
  @fluxor.destroy
1450
1472
  end
1473
+
1451
1474
  should "only have a version for the name_updated event" do
1452
1475
  assert_equal 1, @fluxor.versions.length
1453
1476
  assert_equal "name_updated", @fluxor.versions.last.event
1454
1477
  end
1455
1478
  end
1479
+
1456
1480
  context "on destroy" do
1481
+ cleanup_callbacks(Fluxor, :create)
1482
+ cleanup_callbacks(Fluxor, :update)
1483
+ cleanup_callbacks(Fluxor, :destroy)
1484
+ cleanup_callbacks(Fluxor, :save)
1485
+
1457
1486
  setup do
1458
- Fluxor.reset_callbacks :create
1459
- Fluxor.reset_callbacks :update
1460
- Fluxor.reset_callbacks :destroy
1461
1487
  Fluxor.instance_eval <<-END
1462
1488
  has_paper_trail :on => [:destroy]
1463
1489
  END
@@ -1465,6 +1491,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
1465
1491
  @fluxor.update_attributes name: "blah"
1466
1492
  @fluxor.destroy
1467
1493
  end
1494
+
1468
1495
  should "only have a version for the destroy event" do
1469
1496
  assert_equal 1, @fluxor.versions.length
1470
1497
  assert_equal "destroyed", @fluxor.versions.last.event