paper_trail 7.0.2 → 7.0.3

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +1 -1
  3. data/.rubocop_todo.yml +10 -0
  4. data/.travis.yml +4 -4
  5. data/Appraisals +3 -5
  6. data/CHANGELOG.md +16 -1
  7. data/README.md +70 -119
  8. data/Rakefile +6 -1
  9. data/doc/bug_report_template.rb +4 -2
  10. data/doc/warning_about_not_setting_whodunnit.md +6 -5
  11. data/gemfiles/ar_4.0.gemfile +1 -1
  12. data/gemfiles/ar_4.2.gemfile +1 -1
  13. data/gemfiles/ar_5.0.gemfile +2 -3
  14. data/gemfiles/ar_5.1.gemfile +8 -0
  15. data/gemfiles/ar_master.gemfile +2 -2
  16. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb.erb +1 -1
  17. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb.erb +1 -1
  18. data/lib/generators/paper_trail/templates/create_version_associations.rb.erb +1 -1
  19. data/lib/paper_trail/model_config.rb +4 -4
  20. data/lib/paper_trail/version_concern.rb +4 -4
  21. data/lib/paper_trail/version_number.rb +1 -1
  22. data/paper_trail.gemspec +5 -5
  23. data/spec/controllers/articles_controller_spec.rb +1 -1
  24. data/spec/generators/install_generator_spec.rb +2 -2
  25. data/spec/models/animal_spec.rb +5 -5
  26. data/spec/models/boolit_spec.rb +2 -2
  27. data/spec/models/callback_modifier_spec.rb +2 -2
  28. data/spec/models/car_spec.rb +2 -2
  29. data/spec/models/custom_primary_key_record_spec.rb +2 -2
  30. data/spec/models/document_spec.rb +2 -2
  31. data/spec/models/gadget_spec.rb +2 -2
  32. data/spec/models/joined_version_spec.rb +1 -1
  33. data/spec/models/json_version_spec.rb +4 -4
  34. data/spec/models/kitchen/banana_spec.rb +2 -2
  35. data/spec/models/not_on_update_spec.rb +2 -2
  36. data/spec/models/post_with_status_spec.rb +4 -4
  37. data/spec/models/skipper_spec.rb +1 -1
  38. data/spec/models/thing_spec.rb +2 -2
  39. data/spec/models/vehicle_spec.rb +2 -2
  40. data/spec/models/version_spec.rb +26 -5
  41. data/spec/models/widget_spec.rb +10 -2
  42. data/spec/modules/paper_trail_spec.rb +2 -2
  43. data/spec/modules/version_concern_spec.rb +2 -2
  44. data/spec/modules/version_number_spec.rb +1 -1
  45. data/spec/paper_trail/associations_spec.rb +965 -0
  46. data/spec/paper_trail/cleaner_spec.rb +2 -2
  47. data/spec/paper_trail/config_spec.rb +2 -2
  48. data/spec/paper_trail/model_spec.rb +1421 -0
  49. data/spec/paper_trail/serializer_spec.rb +85 -0
  50. data/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb +1 -1
  51. data/spec/paper_trail/serializers/json_spec.rb +2 -2
  52. data/spec/paper_trail/serializers/yaml_spec.rb +42 -0
  53. data/spec/paper_trail/version_limit_spec.rb +2 -2
  54. data/spec/paper_trail/version_spec.rb +96 -0
  55. data/spec/paper_trail_spec.rb +1 -1
  56. data/spec/requests/articles_spec.rb +2 -2
  57. data/spec/spec_helper.rb +47 -79
  58. data/{test → spec/support}/custom_json_serializer.rb +0 -0
  59. data/test/dummy/app/models/document.rb +1 -1
  60. data/test/dummy/app/models/not_on_update.rb +1 -1
  61. data/test/dummy/app/models/widget.rb +1 -1
  62. data/test/dummy/config/routes.rb +1 -1
  63. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +18 -9
  64. data/test/dummy/db/schema.rb +64 -64
  65. data/test/test_helper.rb +1 -33
  66. data/test/unit/serializers/mixin_json_test.rb +1 -1
  67. metadata +27 -32
  68. data/spec/models/truck_spec.rb +0 -5
  69. data/spec/rails_helper.rb +0 -34
  70. data/test/time_travel_helper.rb +0 -1
  71. data/test/unit/associations_test.rb +0 -1032
  72. data/test/unit/model_test.rb +0 -1416
  73. data/test/unit/serializer_test.rb +0 -107
  74. data/test/unit/serializers/yaml_test.rb +0 -50
  75. data/test/unit/version_test.rb +0 -112
@@ -1,5 +0,0 @@
1
- require "rails_helper"
2
-
3
- describe Truck, type: :model do
4
- it { is_expected.not_to be_versioned }
5
- end
@@ -1,34 +0,0 @@
1
- # This file is copied to spec/ when you run 'rails generate rspec:install'
2
- ENV["RAILS_ENV"] ||= "test"
3
- ENV["DB"] ||= "sqlite"
4
-
5
- unless File.exist?(File.expand_path("../../test/dummy/config/database.yml", __FILE__))
6
- warn "WARNING: No database.yml detected for the dummy app, please run `rake prepare` first"
7
- end
8
-
9
- require "spec_helper"
10
- require File.expand_path("../../test/dummy/config/environment", __FILE__)
11
- require "rspec/rails"
12
- require "paper_trail/frameworks/rspec"
13
- require "ffaker"
14
- require "timecop"
15
-
16
- # prevent Test::Unit's AutoRunner from executing during RSpec's rake task
17
- Test::Unit.run = true if defined?(Test::Unit) && Test::Unit.respond_to?(:run=)
18
-
19
- # Checks for pending migrations before tests are run.
20
- # If you are not using ActiveRecord, you can remove this line.
21
- ActiveRecord::Migration.check_pending! if ActiveRecord::Migration.respond_to?(:check_pending!)
22
-
23
- RSpec.configure do |config|
24
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
25
-
26
- # If you're not using ActiveRecord, or you'd prefer not to run each of your
27
- # examples within a transaction, remove the following line or assign false
28
- # instead of true.
29
- config.use_transactional_fixtures = true
30
-
31
- # The different available types are documented in the features, such as in
32
- # https://relishapp.com/rspec/rspec-rails/docs
33
- # config.infer_spec_type_from_file_location!
34
- end
@@ -1 +0,0 @@
1
- require "timecop"
@@ -1,1032 +0,0 @@
1
- require "test_helper"
2
- require "time_travel_helper"
3
-
4
- class AssociationsTest < ActiveSupport::TestCase
5
- CHAPTER_NAMES = [
6
- "Down the Rabbit-Hole",
7
- "The Pool of Tears",
8
- "A Caucus-Race and a Long Tale",
9
- "The Rabbit Sends in a Little Bill",
10
- "Advice from a Caterpillar",
11
- "Pig and Pepper",
12
- "A Mad Tea-Party",
13
- "The Queen's Croquet-Ground",
14
- "The Mock Turtle's Story",
15
- "The Lobster Quadrille",
16
- "Who Stole the Tarts?",
17
- "Alice's Evidence"
18
- ].freeze
19
-
20
- # These would have been done in test_helper.rb if using_mysql? is true
21
- unless using_mysql?
22
- if respond_to? :use_transactional_tests=
23
- self.use_transactional_tests = false
24
- else
25
- self.use_transactional_fixtures = false
26
- end
27
- setup { DatabaseCleaner.start }
28
- end
29
-
30
- teardown do
31
- Timecop.return
32
- # This would have been done in test_helper.rb if using_mysql? is true
33
- DatabaseCleaner.clean unless using_mysql?
34
- end
35
-
36
- context "a has_one association" do
37
- setup { @widget = Widget.create name: "widget_0" }
38
-
39
- context "before the associated was created" do
40
- setup do
41
- @widget.update_attributes name: "widget_1"
42
- @wotsit = @widget.create_wotsit name: "wotsit_0"
43
- end
44
-
45
- context "when reified" do
46
- setup { @widget0 = @widget.versions.last.reify(has_one: true) }
47
-
48
- should "see the associated as it was at the time" do
49
- assert_nil @widget0.wotsit
50
- end
51
-
52
- should "not persist changes to the live association" do
53
- assert_equal @wotsit, @widget.reload.wotsit
54
- end
55
- end
56
- end
57
-
58
- context "where the association is created between model versions" do
59
- setup do
60
- @wotsit = @widget.create_wotsit name: "wotsit_0"
61
- Timecop.travel 1.second.since
62
- @widget.update_attributes name: "widget_1"
63
- end
64
-
65
- context "when reified" do
66
- setup { @widget0 = @widget.versions.last.reify(has_one: true) }
67
-
68
- should "see the associated as it was at the time" do
69
- assert_equal "wotsit_0", @widget0.wotsit.name
70
- end
71
-
72
- should "not persist changes to the live association" do
73
- assert_equal @wotsit, @widget.reload.wotsit
74
- end
75
- end
76
-
77
- context "and then the associated is updated between model versions" do
78
- setup do
79
- @wotsit.update_attributes name: "wotsit_1"
80
- @wotsit.update_attributes name: "wotsit_2"
81
- Timecop.travel 1.second.since
82
- @widget.update_attributes name: "widget_2"
83
- @wotsit.update_attributes name: "wotsit_3"
84
- end
85
-
86
- context "when reified" do
87
- setup { @widget1 = @widget.versions.last.reify(has_one: true) }
88
-
89
- should "see the associated as it was at the time" do
90
- assert_equal "wotsit_2", @widget1.wotsit.name
91
- end
92
-
93
- should "not persist changes to the live association" do
94
- assert_equal "wotsit_3", @widget.reload.wotsit.name
95
- end
96
- end
97
-
98
- context "when reified opting out of has_one reification" do
99
- setup { @widget1 = @widget.versions.last.reify(has_one: false) }
100
-
101
- should "see the associated as it is live" do
102
- assert_equal "wotsit_3", @widget1.wotsit.name
103
- end
104
- end
105
- end
106
-
107
- context "and then the associated is destroyed" do
108
- setup do
109
- @wotsit.destroy
110
- end
111
-
112
- context "when reify" do
113
- setup { @widget1 = @widget.versions.last.reify(has_one: true) }
114
-
115
- should "see the associated as it was at the time" do
116
- assert_equal @wotsit, @widget1.wotsit
117
- end
118
-
119
- should "not persist changes to the live association" do
120
- assert_nil @widget.reload.wotsit
121
- end
122
- end
123
-
124
- context "and then the model is updated" do
125
- setup do
126
- Timecop.travel 1.second.since
127
- @widget.update_attributes name: "widget_3"
128
- end
129
-
130
- context "when reified" do
131
- setup { @widget2 = @widget.versions.last.reify(has_one: true) }
132
-
133
- should "see the associated as it was at the time" do
134
- assert_nil @widget2.wotsit
135
- end
136
- end
137
- end
138
- end
139
- end
140
- end
141
-
142
- context "a has_many association" do
143
- setup { @customer = Customer.create name: "customer_0" }
144
-
145
- context "updated before the associated was created" do
146
- setup do
147
- @customer.update_attributes! name: "customer_1"
148
- @customer.orders.create! order_date: Date.today
149
- end
150
-
151
- context "when reified" do
152
- setup { @customer0 = @customer.versions.last.reify(has_many: true) }
153
-
154
- should "see the associated as it was at the time" do
155
- assert_equal [], @customer0.orders
156
- end
157
-
158
- should "not persist changes to the live association" do
159
- assert_not_equal [], @customer.orders.reload
160
- end
161
- end
162
-
163
- context "when reified with option mark_for_destruction" do
164
- should "mark the associated for destruction" do
165
- @customer0 = @customer.versions.last.reify(
166
- has_many: true,
167
- mark_for_destruction: true
168
- )
169
- assert_equal [true], @customer0.orders.map(&:marked_for_destruction?)
170
- end
171
- end
172
- end
173
-
174
- context "where the association is created between model versions" do
175
- setup do
176
- @order = @customer.orders.create! order_date: "order_date_0"
177
- Timecop.travel 1.second.since
178
- @customer.update_attributes name: "customer_1"
179
- end
180
-
181
- context "when reified" do
182
- setup { @customer0 = @customer.versions.last.reify(has_many: true) }
183
-
184
- should "see the associated as it was at the time" do
185
- assert_equal ["order_date_0"], @customer0.orders.map(&:order_date)
186
- end
187
- end
188
-
189
- context "and then a nested has_many association is created" do
190
- setup do
191
- @order.line_items.create! product: "product_0"
192
- end
193
-
194
- context "when reified" do
195
- setup { @customer0 = @customer.versions.last.reify(has_many: true) }
196
-
197
- should "see the live version of the nested association" do
198
- assert_equal ["product_0"], @customer0.orders.first.line_items.map(&:product)
199
- end
200
- end
201
- end
202
-
203
- context "and then the associated is updated between model versions" do
204
- setup do
205
- @order.update_attributes order_date: "order_date_1"
206
- @order.update_attributes order_date: "order_date_2"
207
- Timecop.travel 1.second.since
208
- @customer.update_attributes name: "customer_2"
209
- @order.update_attributes order_date: "order_date_3"
210
- end
211
-
212
- context "when reified" do
213
- setup { @customer1 = @customer.versions.last.reify(has_many: true) }
214
-
215
- should "see the associated as it was at the time" do
216
- assert_equal ["order_date_2"], @customer1.orders.map(&:order_date)
217
- end
218
-
219
- should "not persist changes to the live association" do
220
- assert_equal ["order_date_3"], @customer.orders.reload.map(&:order_date)
221
- end
222
- end
223
-
224
- context "when reified opting out of has_many reification" do
225
- setup { @customer1 = @customer.versions.last.reify(has_many: false) }
226
-
227
- should "see the associated as it is live" do
228
- assert_equal ["order_date_3"], @customer1.orders.map(&:order_date)
229
- end
230
- end
231
-
232
- context "and then the associated is destroyed" do
233
- setup do
234
- @order.destroy
235
- end
236
-
237
- context "when reified" do
238
- setup { @customer1 = @customer.versions.last.reify(has_many: true) }
239
-
240
- should "see the associated as it was at the time" do
241
- assert_equal ["order_date_2"], @customer1.orders.map(&:order_date)
242
- end
243
-
244
- should "not persist changes to the live association" do
245
- assert_equal [], @customer.orders.reload
246
- end
247
- end
248
- end
249
- end
250
-
251
- context "and then the associated is destroyed" do
252
- setup do
253
- @order.destroy
254
- end
255
-
256
- context "when reified" do
257
- setup { @customer1 = @customer.versions.last.reify(has_many: true) }
258
-
259
- should "see the associated as it was at the time" do
260
- assert_equal [@order.order_date], @customer1.orders.map(&:order_date)
261
- end
262
-
263
- should "not persist changes to the live association" do
264
- assert_equal [], @customer.orders.reload
265
- end
266
- end
267
- end
268
-
269
- context "and then the associated is destroyed between model versions" do
270
- setup do
271
- @order.destroy
272
- Timecop.travel 1.second.since
273
- @customer.update_attributes name: "customer_2"
274
- end
275
-
276
- context "when reified" do
277
- setup { @customer1 = @customer.versions.last.reify(has_many: true) }
278
-
279
- should "see the associated as it was at the time" do
280
- assert_equal [], @customer1.orders
281
- end
282
- end
283
- end
284
-
285
- context "and then another association is added" do
286
- setup do
287
- @customer.orders.create! order_date: "order_date_1"
288
- end
289
-
290
- context "when reified" do
291
- setup { @customer0 = @customer.versions.last.reify(has_many: true) }
292
-
293
- should "see the associated as it was at the time" do
294
- assert_equal ["order_date_0"], @customer0.orders.map(&:order_date)
295
- end
296
-
297
- should "not persist changes to the live association" do
298
- assert_equal %w(order_date_0 order_date_1),
299
- @customer.orders.reload.map(&:order_date).sort
300
- end
301
- end
302
-
303
- context "when reified with option mark_for_destruction" do
304
- should "mark the newly associated for destruction" do
305
- @customer0 = @customer.versions.last.reify(
306
- has_many: true,
307
- mark_for_destruction: true
308
- )
309
- assert @customer0.
310
- orders.
311
- detect { |o| o.order_date == "order_date_1" }.
312
- marked_for_destruction?
313
- end
314
- end
315
- end
316
- end
317
- end
318
-
319
- context "has_many through associations" do
320
- context "Books, Authors, and Authorships" do
321
- setup { @book = Book.create title: "book_0" }
322
-
323
- context "updated before the associated was created" do
324
- setup do
325
- @book.update_attributes! title: "book_1"
326
- @book.authors.create! name: "author_0"
327
- end
328
-
329
- context "when reified" do
330
- setup { @book0 = @book.versions.last.reify(has_many: true) }
331
-
332
- should "see the associated as it was at the time" do
333
- assert_equal [], @book0.authors
334
- end
335
-
336
- should "not persist changes to the live association" do
337
- assert_equal ["author_0"], @book.authors.reload.map(&:name)
338
- end
339
- end
340
-
341
- context "when reified with option mark_for_destruction" do
342
- setup do
343
- @book0 = @book.versions.last.reify(
344
- has_many: true,
345
- mark_for_destruction: true
346
- )
347
- end
348
-
349
- should "mark the associated for destruction" do
350
- assert_equal [true], @book0.authors.map(&:marked_for_destruction?)
351
- end
352
-
353
- should "mark the associated-through for destruction" do
354
- assert_equal [true], @book0.authorships.map(&:marked_for_destruction?)
355
- end
356
- end
357
- end
358
-
359
- context "updated before it is associated with an existing one" do
360
- setup do
361
- person_existing = Person.create(name: "person_existing")
362
- Timecop.travel 1.second.since
363
- @book.update_attributes! title: "book_1"
364
- @book.authors << person_existing
365
- end
366
-
367
- context "when reified" do
368
- setup do
369
- @book0 = @book.versions.last.reify(has_many: true)
370
- end
371
-
372
- should "see the associated as it was at the time" do
373
- assert_equal [], @book0.authors
374
- end
375
- end
376
-
377
- context "when reified with option mark_for_destruction" do
378
- setup do
379
- @book0 = @book.versions.last.reify(
380
- has_many: true,
381
- mark_for_destruction: true
382
- )
383
- end
384
-
385
- should "not mark the associated for destruction" do
386
- assert_equal [false], @book0.authors.map(&:marked_for_destruction?)
387
- end
388
-
389
- should "mark the associated-through for destruction" do
390
- assert_equal [true], @book0.authorships.map(&:marked_for_destruction?)
391
- end
392
- end
393
- end
394
-
395
- context "where the association is created between model versions" do
396
- setup do
397
- @author = @book.authors.create! name: "author_0"
398
- @person_existing = Person.create(name: "person_existing")
399
- Timecop.travel 1.second.since
400
- @book.update_attributes! title: "book_1"
401
- end
402
-
403
- context "when reified" do
404
- setup { @book0 = @book.versions.last.reify(has_many: true) }
405
-
406
- should "see the associated as it was at the time" do
407
- assert_equal ["author_0"], @book0.authors.map(&:name)
408
- end
409
- end
410
-
411
- context "and then the associated is updated between model versions" do
412
- setup do
413
- @author.update_attributes name: "author_1"
414
- @author.update_attributes name: "author_2"
415
- Timecop.travel 1.second.since
416
- @book.update_attributes title: "book_2"
417
- @author.update_attributes name: "author_3"
418
- end
419
-
420
- context "when reified" do
421
- setup { @book1 = @book.versions.last.reify(has_many: true) }
422
-
423
- should "see the associated as it was at the time" do
424
- assert_equal ["author_2"], @book1.authors.map(&:name)
425
- end
426
-
427
- should "not persist changes to the live association" do
428
- assert_equal ["author_3"], @book.authors.reload.map(&:name)
429
- end
430
- end
431
-
432
- context "when reified opting out of has_many reification" do
433
- setup { @book1 = @book.versions.last.reify(has_many: false) }
434
-
435
- should "see the associated as it is live" do
436
- assert_equal ["author_3"], @book1.authors.map(&:name)
437
- end
438
- end
439
- end
440
-
441
- context "and then the associated is destroyed" do
442
- setup do
443
- @author.destroy
444
- end
445
-
446
- context "when reified" do
447
- setup { @book1 = @book.versions.last.reify(has_many: true) }
448
-
449
- should "see the associated as it was at the time" do
450
- assert_equal [@author.name], @book1.authors.map(&:name)
451
- end
452
-
453
- should "not persist changes to the live association" do
454
- assert_equal [], @book.authors.reload
455
- end
456
- end
457
- end
458
-
459
- context "and then the associated is destroyed between model versions" do
460
- setup do
461
- @author.destroy
462
- Timecop.travel 1.second.since
463
- @book.update_attributes title: "book_2"
464
- end
465
-
466
- context "when reified" do
467
- setup { @book1 = @book.versions.last.reify(has_many: true) }
468
-
469
- should "see the associated as it was at the time" do
470
- assert_equal [], @book1.authors
471
- end
472
- end
473
- end
474
-
475
- context "and then the associated is dissociated between model versions" do
476
- setup do
477
- @book.authors = []
478
- Timecop.travel 1.second.since
479
- @book.update_attributes title: "book_2"
480
- end
481
-
482
- context "when reified" do
483
- setup { @book1 = @book.versions.last.reify(has_many: true) }
484
-
485
- should "see the associated as it was at the time" do
486
- assert_equal [], @book1.authors
487
- end
488
- end
489
- end
490
-
491
- context "and then another associated is created" do
492
- setup do
493
- @book.authors.create! name: "author_1"
494
- end
495
-
496
- context "when reified" do
497
- setup { @book0 = @book.versions.last.reify(has_many: true) }
498
-
499
- should "only see the first associated" do
500
- assert_equal ["author_0"], @book0.authors.map(&:name)
501
- end
502
-
503
- should "not persist changes to the live association" do
504
- assert_equal %w(author_0 author_1), @book.authors.reload.map(&:name)
505
- end
506
- end
507
-
508
- context "when reified with option mark_for_destruction" do
509
- setup do
510
- @book0 = @book.versions.last.reify(
511
- has_many: true,
512
- mark_for_destruction: true
513
- )
514
- end
515
-
516
- should "mark the newly associated for destruction" do
517
- assert @book0.
518
- authors.
519
- detect { |a| a.name == "author_1" }.
520
- marked_for_destruction?
521
- end
522
-
523
- should "mark the newly associated-through for destruction" do
524
- assert @book0.
525
- authorships.
526
- detect { |as| as.author.name == "author_1" }.
527
- marked_for_destruction?
528
- end
529
- end
530
- end
531
-
532
- context "and then an existing one is associated" do
533
- setup do
534
- @book.authors << @person_existing
535
- end
536
-
537
- context "when reified" do
538
- setup { @book0 = @book.versions.last.reify(has_many: true) }
539
-
540
- should "only see the first associated" do
541
- assert_equal ["author_0"], @book0.authors.map(&:name)
542
- end
543
-
544
- should "not persist changes to the live association" do
545
- assert_equal %w(author_0 person_existing), @book.authors.reload.map(&:name).sort
546
- end
547
- end
548
-
549
- context "when reified with option mark_for_destruction" do
550
- setup do
551
- @book0 = @book.versions.last.reify(
552
- has_many: true,
553
- mark_for_destruction: true
554
- )
555
- end
556
-
557
- should "not mark the newly associated for destruction" do
558
- assert !@book0.
559
- authors.
560
- detect { |a| a.name == "person_existing" }.
561
- marked_for_destruction?
562
- end
563
-
564
- should "mark the newly associated-through for destruction" do
565
- assert @book0.
566
- authorships.
567
- detect { |as| as.author.name == "person_existing" }.
568
- marked_for_destruction?
569
- end
570
- end
571
- end
572
- end
573
-
574
- context "updated before the associated without paper_trail was created" do
575
- setup do
576
- @book.update_attributes! title: "book_1"
577
- @book.editors.create! name: "editor_0"
578
- end
579
-
580
- context "when reified" do
581
- setup { @book0 = @book.versions.last.reify(has_many: true) }
582
-
583
- should "see the live association" do
584
- assert_equal ["editor_0"], @book0.editors.map(&:name)
585
- end
586
- end
587
- end
588
- end
589
-
590
- context "Chapters, Sections, Paragraphs, Quotations, and Citations" do
591
- setup { @chapter = Chapter.create(name: CHAPTER_NAMES[0]) }
592
-
593
- context "before any associations are created" do
594
- setup do
595
- @chapter.update_attributes(name: CHAPTER_NAMES[1])
596
- end
597
-
598
- should "not reify any associations" do
599
- chapter_v1 = @chapter.versions[1].reify(has_many: true)
600
- assert_equal CHAPTER_NAMES[0], chapter_v1.name
601
- assert_equal [], chapter_v1.sections
602
- assert_equal [], chapter_v1.paragraphs
603
- end
604
- end
605
-
606
- context "after the first has_many through relationship is created" do
607
- setup do
608
- assert_equal 1, @chapter.versions.size
609
- @chapter.update_attributes name: CHAPTER_NAMES[1]
610
- assert_equal 2, @chapter.versions.size
611
-
612
- Timecop.travel 1.second.since
613
- @chapter.sections.create name: "section 1"
614
- Timecop.travel 1.second.since
615
- @chapter.sections.first.update_attributes name: "section 2"
616
- Timecop.travel 1.second.since
617
- @chapter.update_attributes name: CHAPTER_NAMES[2]
618
- assert_equal 3, @chapter.versions.size
619
-
620
- Timecop.travel 1.second.since
621
- @chapter.sections.first.update_attributes name: "section 3"
622
- end
623
-
624
- context "version 1" do
625
- should "have no sections" do
626
- chapter_v1 = @chapter.versions[1].reify(has_many: true)
627
- assert_equal [], chapter_v1.sections
628
- end
629
- end
630
-
631
- context "version 2" do
632
- should "have one section" do
633
- chapter_v2 = @chapter.versions[2].reify(has_many: true)
634
- assert_equal 1, chapter_v2.sections.size
635
-
636
- # Shows the value of the section as it was before
637
- # the chapter was updated.
638
- assert_equal ["section 2"], chapter_v2.sections.map(&:name)
639
-
640
- # Shows the value of the chapter as it was before
641
- assert_equal CHAPTER_NAMES[1], chapter_v2.name
642
- end
643
- end
644
-
645
- context "version 2, before the section was destroyed" do
646
- setup do
647
- @chapter.update_attributes name: CHAPTER_NAMES[2]
648
- Timecop.travel 1.second.since
649
- @chapter.sections.destroy_all
650
- Timecop.travel 1.second.since
651
- end
652
-
653
- should "have the one section" do
654
- chapter_v2 = @chapter.versions[2].reify(has_many: true)
655
- assert_equal ["section 2"], chapter_v2.sections.map(&:name)
656
- end
657
- end
658
-
659
- context "version 3, after the section was destroyed" do
660
- setup do
661
- @chapter.sections.destroy_all
662
- Timecop.travel 1.second.since
663
- @chapter.update_attributes name: CHAPTER_NAMES[3]
664
- Timecop.travel 1.second.since
665
- end
666
-
667
- should "have no sections" do
668
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
669
- assert_equal 0, chapter_v3.sections.size
670
- end
671
- end
672
-
673
- context "after creating a paragraph" do
674
- setup do
675
- assert_equal 3, @chapter.versions.size
676
- @section = @chapter.sections.first
677
- Timecop.travel 1.second.since
678
- @paragraph = @section.paragraphs.create name: "para1"
679
- end
680
-
681
- context "new chapter version" do
682
- should "have one paragraph" do
683
- initial_section_name = @section.name
684
- initial_paragraph_name = @paragraph.name
685
- Timecop.travel 1.second.since
686
- @chapter.update_attributes name: CHAPTER_NAMES[4]
687
- assert_equal 4, @chapter.versions.size
688
- Timecop.travel 1.second.since
689
- @paragraph.update_attributes name: "para3"
690
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
691
- assert_equal [initial_section_name], chapter_v3.sections.map(&:name)
692
- paragraphs = chapter_v3.sections.first.paragraphs
693
- assert_equal 1, paragraphs.size
694
- assert_equal [initial_paragraph_name], paragraphs.map(&:name)
695
- end
696
- end
697
-
698
- context "the version before a section is destroyed" do
699
- should "have the section and paragraph" do
700
- Timecop.travel 1.second.since
701
- @chapter.update_attributes(name: CHAPTER_NAMES[3])
702
- assert_equal 4, @chapter.versions.size
703
- Timecop.travel 1.second.since
704
- @section.destroy
705
- assert_equal 4, @chapter.versions.size
706
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
707
- assert_equal CHAPTER_NAMES[2], chapter_v3.name
708
- assert_equal [@section], chapter_v3.sections
709
- assert_equal [@paragraph], chapter_v3.sections[0].paragraphs
710
- assert_equal [@paragraph], chapter_v3.paragraphs
711
- end
712
- end
713
-
714
- context "the version after a section is destroyed" do
715
- should "not have any sections or paragraphs" do
716
- @section.destroy
717
- Timecop.travel 1.second.since
718
- @chapter.update_attributes(name: CHAPTER_NAMES[5])
719
- assert_equal 4, @chapter.versions.size
720
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
721
- assert_equal 0, chapter_v3.sections.size
722
- assert_equal 0, chapter_v3.paragraphs.size
723
- end
724
- end
725
-
726
- context "the version before a paragraph is destroyed" do
727
- should "have the one paragraph" do
728
- initial_paragraph_name = @section.paragraphs.first.name
729
- Timecop.travel 1.second.since
730
- @chapter.update_attributes(name: CHAPTER_NAMES[5])
731
- Timecop.travel 1.second.since
732
- @paragraph.destroy
733
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
734
- paragraphs = chapter_v3.sections.first.paragraphs
735
- assert_equal 1, paragraphs.size
736
- assert_equal initial_paragraph_name, paragraphs.first.name
737
- end
738
- end
739
-
740
- context "the version after a paragraph is destroyed" do
741
- should "have no paragraphs" do
742
- @paragraph.destroy
743
- Timecop.travel 1.second.since
744
- @chapter.update_attributes(name: CHAPTER_NAMES[5])
745
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
746
- assert_equal 0, chapter_v3.paragraphs.size
747
- assert_equal [], chapter_v3.sections.first.paragraphs
748
- end
749
- end
750
- end
751
- end
752
-
753
- context "a chapter with one paragraph and one citation" do
754
- should "reify paragraphs and citations" do
755
- chapter = Chapter.create(name: CHAPTER_NAMES[0])
756
- section = Section.create(name: "Section One", chapter: chapter)
757
- paragraph = Paragraph.create(name: "Paragraph One", section: section)
758
- quotation = Quotation.create(chapter: chapter)
759
- citation = Citation.create(quotation: quotation)
760
- Timecop.travel 1.second.since
761
- chapter.update_attributes(name: CHAPTER_NAMES[1])
762
- assert_equal 2, chapter.versions.count
763
- paragraph.destroy
764
- citation.destroy
765
- reified = chapter.versions[1].reify(has_many: true)
766
- assert_equal [paragraph], reified.sections.first.paragraphs
767
- assert_equal [citation], reified.quotations.first.citations
768
- end
769
- end
770
- end
771
- end
772
-
773
- context "belongs_to associations" do
774
- context "Wotsit and Widget" do
775
- setup { @widget = Widget.create(name: "widget_0") }
776
-
777
- context "where the association is created between model versions" do
778
- setup do
779
- @wotsit = Wotsit.create(name: "wotsit_0")
780
- Timecop.travel 1.second.since
781
- @wotsit.update_attributes widget_id: @widget.id, name: "wotsit_1"
782
- end
783
-
784
- context "when reified" do
785
- setup { @wotsit0 = @wotsit.versions.last.reify(belongs_to: true) }
786
-
787
- should "see the associated as it was at the time" do
788
- assert_nil @wotsit0.widget
789
- end
790
-
791
- should "not persist changes to the live association" do
792
- assert_equal @widget, @wotsit.reload.widget
793
- end
794
- end
795
-
796
- context "and then the associated is updated between model versions" do
797
- setup do
798
- @widget.update_attributes name: "widget_1"
799
- @widget.update_attributes name: "widget_2"
800
- Timecop.travel 1.second.since
801
- @wotsit.update_attributes name: "wotsit_2"
802
- @widget.update_attributes name: "widget_3"
803
- end
804
-
805
- context "when reified" do
806
- setup { @wotsit1 = @wotsit.versions.last.reify(belongs_to: true) }
807
-
808
- should "see the associated as it was at the time" do
809
- assert_equal "widget_2", @wotsit1.widget.name
810
- end
811
-
812
- should "not persist changes to the live association" do
813
- assert_equal "widget_3", @wotsit.reload.widget.name
814
- end
815
- end
816
-
817
- context "when reified opting out of belongs_to reification" do
818
- setup { @wotsit1 = @wotsit.versions.last.reify(belongs_to: false) }
819
-
820
- should "see the associated as it is live" do
821
- assert_equal "widget_3", @wotsit1.widget.name
822
- end
823
- end
824
- end
825
-
826
- context "and then the associated is destroyed" do
827
- setup do
828
- @wotsit.update_attributes name: "wotsit_2"
829
- @widget.destroy
830
- end
831
-
832
- context "when reified with belongs_to: true" do
833
- setup { @wotsit2 = @wotsit.versions.last.reify(belongs_to: true) }
834
-
835
- should "see the associated as it was at the time" do
836
- assert_equal @widget, @wotsit2.widget
837
- end
838
-
839
- should "not persist changes to the live association" do
840
- assert_nil @wotsit.reload.widget
841
- end
842
-
843
- should "be able to persist the reified record" do
844
- assert_nothing_raised { @wotsit2.save! }
845
- end
846
- end
847
-
848
- context "when reified with belongs_to: false" do
849
- setup do
850
- @wotsit2 = @wotsit.versions.last.reify(belongs_to: false)
851
- end
852
-
853
- should "save should not re-create the widget record" do
854
- # Save succeeds because Wotsit does not validate presence of widget
855
- @wotsit2.save!
856
- assert_nil ::Widget.find_by(id: @widget.id)
857
- end
858
- end
859
-
860
- context "and then the model is updated" do
861
- setup do
862
- Timecop.travel 1.second.since
863
- @wotsit.update_attributes name: "wotsit_3"
864
- end
865
-
866
- context "when reified" do
867
- setup { @wotsit2 = @wotsit.versions.last.reify(belongs_to: true) }
868
-
869
- should "see the associated as it was the time" do
870
- assert_nil @wotsit2.widget
871
- end
872
- end
873
- end
874
- end
875
- end
876
-
877
- context "where the association is changed between model versions" do
878
- setup do
879
- @wotsit = @widget.create_wotsit(name: "wotsit_0")
880
- Timecop.travel 1.second.since
881
- @new_widget = Widget.create(name: "new_widget")
882
- @wotsit.update_attributes(widget_id: @new_widget.id, name: "wotsit_1")
883
- end
884
-
885
- context "when reified" do
886
- setup { @wotsit0 = @wotsit.versions.last.reify(belongs_to: true) }
887
-
888
- should "see the association as it was at the time" do
889
- assert_equal "widget_0", @wotsit0.widget.name
890
- end
891
-
892
- should "not persist changes to the live association" do
893
- assert_equal @new_widget, @wotsit.reload.widget
894
- end
895
- end
896
-
897
- context "when reified with option mark_for_destruction" do
898
- setup do
899
- @wotsit0 = @wotsit.versions.last.
900
- reify(belongs_to: true, mark_for_destruction: true)
901
- end
902
-
903
- should "should not mark the new associated for destruction" do
904
- assert_equal false, @new_widget.marked_for_destruction?
905
- end
906
- end
907
- end
908
- end
909
- end
910
-
911
- context "has_and_belongs_to_many associations" do
912
- context "foo and bar" do
913
- setup do
914
- @foo = FooHabtm.create(name: "foo")
915
- Timecop.travel 1.second.since
916
- end
917
-
918
- context "where the association is created between model versions" do
919
- setup do
920
- @foo.update_attributes(name: "foo1", bar_habtms: [BarHabtm.create(name: "bar")])
921
- end
922
-
923
- context "when reified" do
924
- setup { @reified = @foo.versions.last.reify(has_and_belongs_to_many: true) }
925
-
926
- should "see the associated as it was at the time" do
927
- assert_equal 0, @reified.bar_habtms.length
928
- end
929
-
930
- should "not persist changes to the live association" do
931
- assert_not_equal @reified.bar_habtms, @foo.reload.bar_habtms
932
- end
933
- end
934
- end
935
-
936
- context "where the association is changed between model versions" do
937
- setup do
938
- @foo.update_attributes(name: "foo2", bar_habtms: [BarHabtm.create(name: "bar2")])
939
- Timecop.travel 1.second.since
940
- @foo.update_attributes(name: "foo3", bar_habtms: [BarHabtm.create(name: "bar3")])
941
- end
942
-
943
- context "when reified" do
944
- setup { @reified = @foo.versions.last.reify(has_and_belongs_to_many: true) }
945
-
946
- should "see the association as it was at the time" do
947
- assert_equal "bar2", @reified.bar_habtms.first.name
948
- end
949
-
950
- should "not persist changes to the live association" do
951
- assert_not_equal @reified.bar_habtms.first, @foo.reload.bar_habtms.first
952
- end
953
- end
954
-
955
- context "when reified with has_and_belongs_to_many: false" do
956
- setup { @reified = @foo.versions.last.reify }
957
-
958
- should "see the association as it is now" do
959
- assert_equal "bar3", @reified.bar_habtms.first.name
960
- end
961
- end
962
- end
963
-
964
- context "where the association is destroyed between model versions" do
965
- setup do
966
- @foo.update_attributes(name: "foo2", bar_habtms: [BarHabtm.create(name: "bar2")])
967
- Timecop.travel 1.second.since
968
- @foo.update_attributes(name: "foo3", bar_habtms: [])
969
- end
970
-
971
- context "when reified" do
972
- setup { @reified = @foo.versions.last.reify(has_and_belongs_to_many: true) }
973
-
974
- should "see the association as it was at the time" do
975
- assert_equal "bar2", @reified.bar_habtms.first.name
976
- end
977
-
978
- should "not persist changes to the live association" do
979
- assert_not_equal @reified.bar_habtms.first, @foo.reload.bar_habtms.first
980
- end
981
- end
982
- end
983
-
984
- context "where the unassociated model changes" do
985
- setup do
986
- @bar = BarHabtm.create(name: "bar2")
987
- @foo.update_attributes(name: "foo2", bar_habtms: [@bar])
988
- Timecop.travel 1.second.since
989
- @foo.update_attributes(name: "foo3", bar_habtms: [BarHabtm.create(name: "bar4")])
990
- Timecop.travel 1.second.since
991
- @bar.update_attributes(name: "bar3")
992
- end
993
-
994
- context "when reified" do
995
- setup { @reified = @foo.versions.last.reify(has_and_belongs_to_many: true) }
996
-
997
- should "see the association as it was at the time" do
998
- assert_equal "bar2", @reified.bar_habtms.first.name
999
- end
1000
-
1001
- should "not persist changes to the live association" do
1002
- assert_not_equal @reified.bar_habtms.first, @foo.reload.bar_habtms.first
1003
- end
1004
- end
1005
- end
1006
- end
1007
-
1008
- context "updated via nested attributes" do
1009
- setup do
1010
- @foo = FooHabtm.create(
1011
- name: "foo",
1012
- bar_habtms_attributes: [{ name: "bar" }]
1013
- )
1014
- Timecop.travel 1.second.since
1015
- @foo.update_attributes(
1016
- name: "foo2",
1017
- bar_habtms_attributes: [{ id: @foo.bar_habtms.first.id, name: "bar2" }]
1018
- )
1019
-
1020
- @reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
1021
- end
1022
-
1023
- should "see the associated object as it was at the time" do
1024
- assert_equal "bar", @reified.bar_habtms.first.name
1025
- end
1026
-
1027
- should "not persist changes to the live object" do
1028
- assert_not_equal @reified.bar_habtms.first.name, @foo.reload.bar_habtms.first.name
1029
- end
1030
- end
1031
- end
1032
- end