paper_trail 7.1.0 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/lib/paper_trail/record_trail.rb +1 -1
  3. data/lib/paper_trail/version_number.rb +1 -1
  4. metadata +3 -274
  5. data/.github/CONTRIBUTING.md +0 -151
  6. data/.github/ISSUE_TEMPLATE.md +0 -13
  7. data/.gitignore +0 -23
  8. data/.rspec +0 -2
  9. data/.rubocop.yml +0 -135
  10. data/.rubocop_todo.yml +0 -41
  11. data/.travis.yml +0 -41
  12. data/Appraisals +0 -26
  13. data/CHANGELOG.md +0 -739
  14. data/Gemfile +0 -2
  15. data/MIT-LICENSE +0 -20
  16. data/README.md +0 -1613
  17. data/Rakefile +0 -38
  18. data/doc/bug_report_template.rb +0 -73
  19. data/doc/triage.md +0 -27
  20. data/doc/warning_about_not_setting_whodunnit.md +0 -33
  21. data/gemfiles/ar_4.0.gemfile +0 -7
  22. data/gemfiles/ar_4.2.gemfile +0 -7
  23. data/gemfiles/ar_5.0.gemfile +0 -8
  24. data/gemfiles/ar_5.1.gemfile +0 -8
  25. data/gemfiles/ar_master.gemfile +0 -9
  26. data/lib/generators/paper_trail/default_initializer.rb +0 -0
  27. data/paper_trail.gemspec +0 -49
  28. data/spec/controllers/articles_controller_spec.rb +0 -28
  29. data/spec/controllers/widgets_controller_spec.rb +0 -85
  30. data/spec/dummy_app/Rakefile +0 -7
  31. data/spec/dummy_app/app/controllers/application_controller.rb +0 -30
  32. data/spec/dummy_app/app/controllers/articles_controller.rb +0 -16
  33. data/spec/dummy_app/app/controllers/test_controller.rb +0 -5
  34. data/spec/dummy_app/app/controllers/widgets_controller.rb +0 -28
  35. data/spec/dummy_app/app/models/animal.rb +0 -4
  36. data/spec/dummy_app/app/models/article.rb +0 -25
  37. data/spec/dummy_app/app/models/authorship.rb +0 -5
  38. data/spec/dummy_app/app/models/bar_habtm.rb +0 -4
  39. data/spec/dummy_app/app/models/book.rb +0 -9
  40. data/spec/dummy_app/app/models/boolit.rb +0 -4
  41. data/spec/dummy_app/app/models/callback_modifier.rb +0 -45
  42. data/spec/dummy_app/app/models/car.rb +0 -3
  43. data/spec/dummy_app/app/models/cat.rb +0 -2
  44. data/spec/dummy_app/app/models/chapter.rb +0 -9
  45. data/spec/dummy_app/app/models/citation.rb +0 -5
  46. data/spec/dummy_app/app/models/custom_primary_key_record.rb +0 -15
  47. data/spec/dummy_app/app/models/customer.rb +0 -4
  48. data/spec/dummy_app/app/models/document.rb +0 -8
  49. data/spec/dummy_app/app/models/dog.rb +0 -2
  50. data/spec/dummy_app/app/models/editor.rb +0 -4
  51. data/spec/dummy_app/app/models/editorship.rb +0 -5
  52. data/spec/dummy_app/app/models/elephant.rb +0 -3
  53. data/spec/dummy_app/app/models/fluxor.rb +0 -3
  54. data/spec/dummy_app/app/models/foo_habtm.rb +0 -5
  55. data/spec/dummy_app/app/models/foo_widget.rb +0 -2
  56. data/spec/dummy_app/app/models/fruit.rb +0 -5
  57. data/spec/dummy_app/app/models/gadget.rb +0 -3
  58. data/spec/dummy_app/app/models/kitchen/banana.rb +0 -5
  59. data/spec/dummy_app/app/models/legacy_widget.rb +0 -6
  60. data/spec/dummy_app/app/models/line_item.rb +0 -4
  61. data/spec/dummy_app/app/models/not_on_update.rb +0 -4
  62. data/spec/dummy_app/app/models/on/create.rb +0 -6
  63. data/spec/dummy_app/app/models/on/destroy.rb +0 -6
  64. data/spec/dummy_app/app/models/on/empty_array.rb +0 -6
  65. data/spec/dummy_app/app/models/on/update.rb +0 -6
  66. data/spec/dummy_app/app/models/order.rb +0 -5
  67. data/spec/dummy_app/app/models/paragraph.rb +0 -5
  68. data/spec/dummy_app/app/models/person.rb +0 -39
  69. data/spec/dummy_app/app/models/post.rb +0 -3
  70. data/spec/dummy_app/app/models/post_with_status.rb +0 -7
  71. data/spec/dummy_app/app/models/quotation.rb +0 -5
  72. data/spec/dummy_app/app/models/section.rb +0 -6
  73. data/spec/dummy_app/app/models/skipper.rb +0 -3
  74. data/spec/dummy_app/app/models/song.rb +0 -37
  75. data/spec/dummy_app/app/models/thing.rb +0 -3
  76. data/spec/dummy_app/app/models/translation.rb +0 -11
  77. data/spec/dummy_app/app/models/truck.rb +0 -4
  78. data/spec/dummy_app/app/models/vehicle.rb +0 -4
  79. data/spec/dummy_app/app/models/whatchamajigger.rb +0 -4
  80. data/spec/dummy_app/app/models/widget.rb +0 -8
  81. data/spec/dummy_app/app/models/wotsit.rb +0 -8
  82. data/spec/dummy_app/app/versions/custom_primary_key_record_version.rb +0 -3
  83. data/spec/dummy_app/app/versions/joined_version.rb +0 -6
  84. data/spec/dummy_app/app/versions/json_version.rb +0 -3
  85. data/spec/dummy_app/app/versions/kitchen/banana_version.rb +0 -5
  86. data/spec/dummy_app/app/versions/post_version.rb +0 -3
  87. data/spec/dummy_app/config.ru +0 -4
  88. data/spec/dummy_app/config/application.rb +0 -39
  89. data/spec/dummy_app/config/boot.rb +0 -24
  90. data/spec/dummy_app/config/database.mysql.yml +0 -19
  91. data/spec/dummy_app/config/database.postgres.yml +0 -15
  92. data/spec/dummy_app/config/database.sqlite.yml +0 -15
  93. data/spec/dummy_app/config/environment.rb +0 -5
  94. data/spec/dummy_app/config/environments/development.rb +0 -36
  95. data/spec/dummy_app/config/environments/production.rb +0 -74
  96. data/spec/dummy_app/config/environments/test.rb +0 -46
  97. data/spec/dummy_app/config/initializers/backtrace_silencers.rb +0 -9
  98. data/spec/dummy_app/config/initializers/inflections.rb +0 -10
  99. data/spec/dummy_app/config/initializers/mime_types.rb +0 -5
  100. data/spec/dummy_app/config/initializers/paper_trail.rb +0 -1
  101. data/spec/dummy_app/config/initializers/secret_token.rb +0 -9
  102. data/spec/dummy_app/config/initializers/session_store.rb +0 -8
  103. data/spec/dummy_app/config/locales/en.yml +0 -5
  104. data/spec/dummy_app/config/routes.rb +0 -4
  105. data/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb +0 -344
  106. data/spec/dummy_app/db/schema.rb +0 -298
  107. data/spec/generators/install_generator_spec.rb +0 -88
  108. data/spec/models/animal_spec.rb +0 -61
  109. data/spec/models/article_spec.rb +0 -186
  110. data/spec/models/boolit_spec.rb +0 -41
  111. data/spec/models/callback_modifier_spec.rb +0 -92
  112. data/spec/models/car_spec.rb +0 -13
  113. data/spec/models/custom_primary_key_record_spec.rb +0 -18
  114. data/spec/models/document_spec.rb +0 -57
  115. data/spec/models/gadget_spec.rb +0 -63
  116. data/spec/models/joined_version_spec.rb +0 -41
  117. data/spec/models/json_version_spec.rb +0 -101
  118. data/spec/models/kitchen/banana_spec.rb +0 -14
  119. data/spec/models/legacy_widget_spec.rb +0 -40
  120. data/spec/models/not_on_update_spec.rb +0 -22
  121. data/spec/models/on/create_spec.rb +0 -27
  122. data/spec/models/on/destroy_spec.rb +0 -27
  123. data/spec/models/on/empty_array_spec.rb +0 -30
  124. data/spec/models/on/update_spec.rb +0 -27
  125. data/spec/models/post_with_status_spec.rb +0 -46
  126. data/spec/models/skipper_spec.rb +0 -42
  127. data/spec/models/thing_spec.rb +0 -11
  128. data/spec/models/translation_spec.rb +0 -70
  129. data/spec/models/vehicle_spec.rb +0 -5
  130. data/spec/models/version_spec.rb +0 -282
  131. data/spec/models/widget_spec.rb +0 -338
  132. data/spec/modules/paper_trail_spec.rb +0 -27
  133. data/spec/modules/version_concern_spec.rb +0 -28
  134. data/spec/modules/version_number_spec.rb +0 -18
  135. data/spec/paper_trail/associations_spec.rb +0 -965
  136. data/spec/paper_trail/cleaner_spec.rb +0 -152
  137. data/spec/paper_trail/config_spec.rb +0 -45
  138. data/spec/paper_trail/model_spec.rb +0 -992
  139. data/spec/paper_trail/serializer_spec.rb +0 -85
  140. data/spec/paper_trail/serializers/custom_json_serializer_spec.rb +0 -18
  141. data/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb +0 -45
  142. data/spec/paper_trail/serializers/json_spec.rb +0 -57
  143. data/spec/paper_trail/serializers/yaml_spec.rb +0 -42
  144. data/spec/paper_trail/thread_safety_spec.rb +0 -44
  145. data/spec/paper_trail/version_limit_spec.rb +0 -55
  146. data/spec/paper_trail/version_spec.rb +0 -96
  147. data/spec/paper_trail_spec.rb +0 -122
  148. data/spec/requests/articles_spec.rb +0 -34
  149. data/spec/spec_helper.rb +0 -78
  150. data/spec/support/alt_db_init.rb +0 -54
  151. data/spec/support/custom_json_serializer.rb +0 -13
@@ -1,338 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe Widget, type: :model do
4
- describe "`be_versioned` matcher" do
5
- it { is_expected.to be_versioned }
6
- end
7
-
8
- let(:widget) { Widget.create! name: "Bob", an_integer: 1 }
9
-
10
- describe "`have_a_version_with` matcher", versioning: true do
11
- before do
12
- widget.update_attributes!(name: "Leonard", an_integer: 1)
13
- widget.update_attributes!(name: "Tom")
14
- widget.update_attributes!(name: "Bob")
15
- end
16
-
17
- it "is possible to do assertions on version attributes" do
18
- expect(widget).to have_a_version_with name: "Leonard", an_integer: 1
19
- expect(widget).to have_a_version_with an_integer: 1
20
- expect(widget).to have_a_version_with name: "Tom"
21
- end
22
- end
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
-
39
- describe "versioning option" do
40
- context "enabled", versioning: true do
41
- it "enables versioning" do
42
- expect(widget.versions.size).to eq(1)
43
- end
44
- end
45
-
46
- context "disabled (default)" do
47
- it "does not enable versioning" do
48
- expect(widget.versions.size).to eq(0)
49
- end
50
- end
51
- end
52
-
53
- describe "Callbacks", versioning: true do
54
- describe "before_save" do
55
- before { widget.update_attributes!(name: "Foobar") }
56
-
57
- subject { widget.versions.last.reify }
58
-
59
- it "resets value for timestamp attrs for update so that value gets updated properly" do
60
- # Travel 1 second because MySQL lacks sub-second resolution
61
- Timecop.travel(1) do
62
- expect { subject.save! }.to change(subject, :updated_at)
63
- end
64
- end
65
- end
66
-
67
- describe "after_create" do
68
- let(:widget) { Widget.create!(name: "Foobar", created_at: Time.now - 1.week) }
69
-
70
- it "corresponding version uses the widget's `updated_at`" do
71
- expect(widget.versions.last.created_at.to_i).to eq(widget.updated_at.to_i)
72
- end
73
- end
74
-
75
- describe "after_update" do
76
- before { widget.update_attributes!(name: "Foobar", updated_at: Time.now + 1.week) }
77
-
78
- subject { widget.versions.last.reify }
79
-
80
- it { expect(subject.paper_trail).not_to be_live }
81
-
82
- it "clears the `versions_association_name` virtual attribute" do
83
- subject.save!
84
- expect(subject.paper_trail).to be_live
85
- end
86
-
87
- it "corresponding version uses the widget updated_at" do
88
- expect(widget.versions.last.created_at.to_i).to eq(widget.updated_at.to_i)
89
- end
90
- end
91
-
92
- describe "after_destroy" do
93
- it "creates a version for that event" do
94
- expect { widget.destroy }.to change(widget.versions, :count).by(1)
95
- end
96
-
97
- it "assigns the version into the `versions_association_name`" do
98
- expect(widget.version).to be_nil
99
- widget.destroy
100
- expect(widget.version).not_to be_nil
101
- expect(widget.version).to eq(widget.versions.last)
102
- end
103
- end
104
-
105
- describe "after_rollback" do
106
- let(:rolled_back_name) { "Big Moo" }
107
-
108
- before do
109
- begin
110
- widget.transaction do
111
- widget.update_attributes!(name: rolled_back_name)
112
- widget.update_attributes!(name: Widget::EXCLUDED_NAME)
113
- end
114
- rescue ActiveRecord::RecordInvalid
115
- widget.reload
116
- widget.name = nil
117
- widget.save
118
- end
119
- end
120
-
121
- it "does not create an event for changes that did not happen" do
122
- widget.versions.map(&:changeset).each do |changeset|
123
- expect(changeset.fetch("name", [])).not_to include(rolled_back_name)
124
- end
125
- end
126
-
127
- it "has not yet loaded the assocation" do
128
- expect(widget.versions).not_to be_loaded
129
- end
130
- end
131
- end
132
-
133
- describe "Association", versioning: true do
134
- describe "sort order" do
135
- it "sorts by the timestamp order from the `VersionConcern`" do
136
- expect(widget.versions.to_sql).to eq(
137
- widget.versions.reorder(PaperTrail::Version.timestamp_sort_order).to_sql
138
- )
139
- end
140
- end
141
- end
142
-
143
- if defined?(ActiveRecord::IdentityMap) && ActiveRecord::IdentityMap.respond_to?(:without)
144
- describe "IdentityMap", versioning: true do
145
- it "does not clobber the IdentityMap when reifying" do
146
- widget.update_attributes name: "Henry", created_at: Time.now - 1.day
147
- widget.update_attributes name: "Harry"
148
- expect(ActiveRecord::IdentityMap).to receive(:without).once
149
- widget.versions.last.reify
150
- end
151
- end
152
- end
153
-
154
- describe "#create", versioning: true do
155
- it "creates a version record" do
156
- wordget = Widget.create
157
- assert_equal 1, wordget.versions.length
158
- end
159
- end
160
-
161
- describe "#destroy", versioning: true do
162
- it "creates a version record" do
163
- widget = Widget.create
164
- assert_equal 1, widget.versions.length
165
- widget.destroy
166
- versions_for_widget = PaperTrail::Version.with_item_keys("Widget", widget.id)
167
- assert_equal 2, versions_for_widget.length
168
- end
169
-
170
- it "can have multiple destruction records" do
171
- versions = lambda { |widget|
172
- # Workaround for AR 3. When we drop AR 3 support, we can simply use
173
- # the `widget.versions` association, instead of `with_item_keys`.
174
- PaperTrail::Version.with_item_keys("Widget", widget.id)
175
- }
176
- widget = Widget.create
177
- assert_equal 1, widget.versions.length
178
- widget.destroy
179
- assert_equal 2, versions.call(widget).length
180
- widget = widget.version.reify
181
- widget.save
182
- assert_equal 3, versions.call(widget).length
183
- widget.destroy
184
- assert_equal 4, versions.call(widget).length
185
- assert_equal 2, versions.call(widget).where(event: "destroy").length
186
- end
187
- end
188
-
189
- describe "#paper_trail.originator", versioning: true do
190
- describe "return value" do
191
- let(:orig_name) { FFaker::Name.name }
192
- let(:new_name) { FFaker::Name.name }
193
-
194
- before do
195
- PaperTrail.whodunnit = orig_name
196
- end
197
-
198
- it "returns the originator for the model at a given state" do
199
- expect(widget.paper_trail).to be_live
200
- expect(widget.paper_trail.originator).to eq(orig_name)
201
- widget.paper_trail.whodunnit(new_name) { |w|
202
- w.update_attributes(name: "Elizabeth")
203
- }
204
- expect(widget.paper_trail.originator).to eq(new_name)
205
- end
206
-
207
- it "returns the appropriate originator" do
208
- widget.update_attributes(name: "Andy")
209
- PaperTrail.whodunnit = new_name
210
- widget.update_attributes(name: "Elizabeth")
211
- reified_widget = widget.versions[1].reify
212
- expect(reified_widget.paper_trail.originator).to eq(orig_name)
213
- expect(reified_widget).not_to be_new_record
214
- end
215
-
216
- it "can create a new instance with options[:dup]" do
217
- widget.update_attributes(name: "Andy")
218
- PaperTrail.whodunnit = new_name
219
- widget.update_attributes(name: "Elizabeth")
220
- reified_widget = widget.versions[1].reify(dup: true)
221
- expect(reified_widget.paper_trail.originator).to eq(orig_name)
222
- expect(reified_widget).to be_new_record
223
- end
224
- end
225
- end
226
-
227
- describe "#version_at", versioning: true do
228
- context "Timestamp argument is AFTER object has been destroyed" do
229
- it "returns nil" do
230
- widget.update_attribute(:name, "foobar")
231
- widget.destroy
232
- expect(widget.paper_trail.version_at(Time.now)).to be_nil
233
- end
234
- end
235
- end
236
-
237
- describe "#whodunnit", versioning: true do
238
- context "no block given" do
239
- it "raises an error" do
240
- expect {
241
- widget.paper_trail.whodunnit("Ben")
242
- }.to raise_error(ArgumentError, "expected to receive a block")
243
- end
244
- end
245
-
246
- context "block given" do
247
- let(:orig_name) { FFaker::Name.name }
248
- let(:new_name) { FFaker::Name.name }
249
-
250
- before do
251
- PaperTrail.whodunnit = orig_name
252
- expect(widget.versions.last.whodunnit).to eq(orig_name) # persist `widget`
253
- end
254
-
255
- it "modifies value of `PaperTrail.whodunnit` while executing the block" do
256
- widget.paper_trail.whodunnit(new_name) do
257
- expect(PaperTrail.whodunnit).to eq(new_name)
258
- widget.update_attributes(name: "Elizabeth")
259
- end
260
- expect(widget.versions.last.whodunnit).to eq(new_name)
261
- end
262
-
263
- it "reverts value of whodunnit to previous value after executing the block" do
264
- widget.paper_trail.whodunnit(new_name) { |w|
265
- w.update_attributes(name: "Elizabeth")
266
- }
267
- expect(PaperTrail.whodunnit).to eq(orig_name)
268
- end
269
-
270
- it "reverts to previous value, even if error within block" do
271
- expect {
272
- widget.paper_trail.whodunnit(new_name) { raise }
273
- }.to raise_error(RuntimeError)
274
- expect(PaperTrail.whodunnit).to eq(orig_name)
275
- end
276
- end
277
- end
278
-
279
- describe "#touch_with_version", versioning: true do
280
- it "creates a version" do
281
- count = widget.versions.size
282
- # Travel 1 second because MySQL lacks sub-second resolution
283
- Timecop.travel(1) do
284
- widget.paper_trail.touch_with_version
285
- end
286
- expect(widget.versions.size).to eq(count + 1)
287
- end
288
-
289
- it "increments the `:updated_at` timestamp" do
290
- time_was = widget.updated_at
291
- # Travel 1 second because MySQL lacks sub-second resolution
292
- Timecop.travel(1) do
293
- widget.paper_trail.touch_with_version
294
- end
295
- expect(widget.updated_at).to be > time_was
296
- end
297
- end
298
-
299
- describe "#update", versioning: true do
300
- it "creates a version record" do
301
- widget = Widget.create
302
- assert_equal 1, widget.versions.length
303
- widget.update_attributes(name: "Bugle")
304
- assert_equal 2, widget.versions.length
305
- end
306
- end
307
-
308
- describe ".paper_trail.enabled?" do
309
- it "returns true" do
310
- expect(Widget.paper_trail.enabled?).to eq(true)
311
- end
312
- end
313
-
314
- describe ".disable" do
315
- it "sets the `paper_trail.enabled?` to `false`" do
316
- expect(Widget.paper_trail.enabled?).to eq(true)
317
- Widget.paper_trail.disable
318
- expect(Widget.paper_trail.enabled?).to eq(false)
319
- end
320
-
321
- after do
322
- Widget.paper_trail.enable
323
- end
324
- end
325
-
326
- describe ".enable" do
327
- it "sets the `paper_trail.enabled?` to `true`" do
328
- Widget.paper_trail.disable
329
- expect(Widget.paper_trail.enabled?).to eq(false)
330
- Widget.paper_trail.enable
331
- expect(Widget.paper_trail.enabled?).to eq(true)
332
- end
333
-
334
- after do
335
- Widget.paper_trail.enable
336
- end
337
- end
338
- end
@@ -1,27 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe PaperTrail, type: :module, versioning: true do
4
- describe "#config" do
5
- it { is_expected.to respond_to(:config) }
6
-
7
- it "allows for config values to be set" do
8
- expect(subject.config.enabled).to eq(true)
9
- subject.config.enabled = false
10
- expect(subject.config.enabled).to eq(false)
11
- end
12
-
13
- it "accepts blocks and yield the config instance" do
14
- expect(subject.config.enabled).to eq(true)
15
- subject.config { |c| c.enabled = false }
16
- expect(subject.config.enabled).to eq(false)
17
- end
18
- end
19
-
20
- describe "#configure" do
21
- it { is_expected.to respond_to(:configure) }
22
-
23
- it "is an alias for the `config` method" do
24
- expect(subject.method(:configure)).to eq(subject.method(:config))
25
- end
26
- end
27
- end
@@ -1,28 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe PaperTrail::VersionConcern do
4
- before(:all) { require "support/alt_db_init" }
5
-
6
- it "allows included class to have different connections" do
7
- expect(Foo::Version.connection).not_to eq(Bar::Version.connection)
8
- end
9
-
10
- it "allows custom version class to share connection with superclass" do
11
- expect(Foo::Version.connection).to eq(Foo::Document.connection)
12
- expect(Bar::Version.connection).to eq(Bar::Document.connection)
13
- end
14
-
15
- it "can be used with class_name option" do
16
- expect(Foo::Document.version_class_name).to eq("Foo::Version")
17
- expect(Bar::Document.version_class_name).to eq("Bar::Version")
18
- end
19
-
20
- describe "persistence", versioning: true do
21
- it "stores versions in the correct corresponding db location" do
22
- foo_doc = Foo::Document.create!(name: "foobar")
23
- bar_doc = Bar::Document.create!(name: "raboof")
24
- expect(foo_doc.versions.first).to be_instance_of(Foo::Version)
25
- expect(bar_doc.versions.first).to be_instance_of(Bar::Version)
26
- end
27
- end
28
- end
@@ -1,18 +0,0 @@
1
- require "spec_helper"
2
-
3
- module PaperTrail
4
- ::RSpec.describe VERSION do
5
- describe "STRING" do
6
- it "joins the numbers into a period separated string" do
7
- expect(described_class::STRING).to eq(
8
- [
9
- described_class::MAJOR,
10
- described_class::MINOR,
11
- described_class::TINY,
12
- described_class::PRE
13
- ].compact.join(".")
14
- )
15
- end
16
- end
17
- end
18
- end
@@ -1,965 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe(::PaperTrail, versioning: true) do
4
- CHAPTER_NAMES = [
5
- "Down the Rabbit-Hole",
6
- "The Pool of Tears",
7
- "A Caucus-Race and a Long Tale",
8
- "The Rabbit Sends in a Little Bill",
9
- "Advice from a Caterpillar",
10
- "Pig and Pepper",
11
- "A Mad Tea-Party",
12
- "The Queen's Croquet-Ground",
13
- "The Mock Turtle's Story",
14
- "The Lobster Quadrille",
15
- "Who Stole the Tarts?",
16
- "Alice's Evidence"
17
- ].freeze
18
-
19
- after do
20
- Timecop.return
21
- end
22
-
23
- context "a has_one association" do
24
- before { @widget = Widget.create(name: "widget_0") }
25
-
26
- context "before the associated was created" do
27
- before do
28
- @widget.update_attributes(name: "widget_1")
29
- @wotsit = @widget.create_wotsit(name: "wotsit_0")
30
- end
31
-
32
- context "when reified" do
33
- before { @widget0 = @widget.versions.last.reify(has_one: true) }
34
-
35
- it "see the associated as it was at the time" do
36
- expect(@widget0.wotsit).to be_nil
37
- end
38
-
39
- it "not persist changes to the live association" do
40
- expect(@widget.reload.wotsit).to(eq(@wotsit))
41
- end
42
- end
43
- end
44
-
45
- context "where the association is created between model versions" do
46
- before do
47
- @wotsit = @widget.create_wotsit(name: "wotsit_0")
48
- Timecop.travel(1.second.since)
49
- @widget.update_attributes(name: "widget_1")
50
- end
51
-
52
- context "when reified" do
53
- before { @widget0 = @widget.versions.last.reify(has_one: true) }
54
-
55
- it "see the associated as it was at the time" do
56
- expect(@widget0.wotsit.name).to(eq("wotsit_0"))
57
- end
58
-
59
- it "not persist changes to the live association" do
60
- expect(@widget.reload.wotsit).to(eq(@wotsit))
61
- end
62
- end
63
-
64
- context "and then the associated is updated between model versions" do
65
- before do
66
- @wotsit.update_attributes(name: "wotsit_1")
67
- @wotsit.update_attributes(name: "wotsit_2")
68
- Timecop.travel(1.second.since)
69
- @widget.update_attributes(name: "widget_2")
70
- @wotsit.update_attributes(name: "wotsit_3")
71
- end
72
-
73
- context "when reified" do
74
- before { @widget1 = @widget.versions.last.reify(has_one: true) }
75
-
76
- it "see the associated as it was at the time" do
77
- expect(@widget1.wotsit.name).to(eq("wotsit_2"))
78
- end
79
-
80
- it "not persist changes to the live association" do
81
- expect(@widget.reload.wotsit.name).to(eq("wotsit_3"))
82
- end
83
- end
84
-
85
- context "when reified opting out of has_one reification" do
86
- before { @widget1 = @widget.versions.last.reify(has_one: false) }
87
-
88
- it "see the associated as it is live" do
89
- expect(@widget1.wotsit.name).to(eq("wotsit_3"))
90
- end
91
- end
92
- end
93
-
94
- context "and then the associated is destroyed" do
95
- before { @wotsit.destroy }
96
-
97
- context "when reify" do
98
- before { @widget1 = @widget.versions.last.reify(has_one: true) }
99
-
100
- it "see the associated as it was at the time" do
101
- expect(@widget1.wotsit).to(eq(@wotsit))
102
- end
103
-
104
- it "not persist changes to the live association" do
105
- expect(@widget.reload.wotsit).to be_nil
106
- end
107
- end
108
-
109
- context "and then the model is updated" do
110
- before do
111
- Timecop.travel(1.second.since)
112
- @widget.update_attributes(name: "widget_3")
113
- end
114
-
115
- context "when reified" do
116
- before { @widget2 = @widget.versions.last.reify(has_one: true) }
117
-
118
- it "see the associated as it was at the time" do
119
- expect(@widget2.wotsit).to be_nil
120
- end
121
- end
122
- end
123
- end
124
- end
125
- end
126
-
127
- context "a has_many association" do
128
- before { @customer = Customer.create(name: "customer_0") }
129
-
130
- context "updated before the associated was created" do
131
- before do
132
- @customer.update_attributes!(name: "customer_1")
133
- @customer.orders.create!(order_date: Date.today)
134
- end
135
-
136
- context "when reified" do
137
- before { @customer0 = @customer.versions.last.reify(has_many: true) }
138
-
139
- it "see the associated as it was at the time" do
140
- expect(@customer0.orders).to(eq([]))
141
- end
142
-
143
- it "not persist changes to the live association" do
144
- expect(@customer.orders.reload).not_to(eq([]))
145
- end
146
- end
147
-
148
- context "when reified with option mark_for_destruction" do
149
- it "mark the associated for destruction" do
150
- @customer0 = @customer.versions.last.reify(has_many: true, mark_for_destruction: true)
151
- expect(@customer0.orders.map(&:marked_for_destruction?)).to(eq([true]))
152
- end
153
- end
154
- end
155
-
156
- context "where the association is created between model versions" do
157
- before do
158
- @order = @customer.orders.create!(order_date: "order_date_0")
159
- Timecop.travel(1.second.since)
160
- @customer.update_attributes(name: "customer_1")
161
- end
162
-
163
- context "when reified" do
164
- before { @customer0 = @customer.versions.last.reify(has_many: true) }
165
-
166
- it "see the associated as it was at the time" do
167
- expect(@customer0.orders.map(&:order_date)).to(eq(["order_date_0"]))
168
- end
169
- end
170
-
171
- context "and then a nested has_many association is created" do
172
- before { @order.line_items.create!(product: "product_0") }
173
-
174
- context "when reified" do
175
- before { @customer0 = @customer.versions.last.reify(has_many: true) }
176
-
177
- it "see the live version of the nested association" do
178
- expect(@customer0.orders.first.line_items.map(&:product)).to(eq(["product_0"]))
179
- end
180
- end
181
- end
182
-
183
- context "and then the associated is updated between model versions" do
184
- before do
185
- @order.update_attributes(order_date: "order_date_1")
186
- @order.update_attributes(order_date: "order_date_2")
187
- Timecop.travel(1.second.since)
188
- @customer.update_attributes(name: "customer_2")
189
- @order.update_attributes(order_date: "order_date_3")
190
- end
191
-
192
- context "when reified" do
193
- before { @customer1 = @customer.versions.last.reify(has_many: true) }
194
-
195
- it "see the associated as it was at the time" do
196
- expect(@customer1.orders.map(&:order_date)).to(eq(["order_date_2"]))
197
- end
198
-
199
- it "not persist changes to the live association" do
200
- expect(@customer.orders.reload.map(&:order_date)).to(eq(["order_date_3"]))
201
- end
202
- end
203
-
204
- context "when reified opting out of has_many reification" do
205
- before { @customer1 = @customer.versions.last.reify(has_many: false) }
206
-
207
- it "see the associated as it is live" do
208
- expect(@customer1.orders.map(&:order_date)).to(eq(["order_date_3"]))
209
- end
210
- end
211
-
212
- context "and then the associated is destroyed" do
213
- before { @order.destroy }
214
-
215
- context "when reified" do
216
- before { @customer1 = @customer.versions.last.reify(has_many: true) }
217
-
218
- it "see the associated as it was at the time" do
219
- expect(@customer1.orders.map(&:order_date)).to(eq(["order_date_2"]))
220
- end
221
-
222
- it "not persist changes to the live association" do
223
- expect(@customer.orders.reload).to(eq([]))
224
- end
225
- end
226
- end
227
- end
228
-
229
- context "and then the associated is destroyed" do
230
- before { @order.destroy }
231
-
232
- context "when reified" do
233
- before { @customer1 = @customer.versions.last.reify(has_many: true) }
234
-
235
- it "see the associated as it was at the time" do
236
- expect(@customer1.orders.map(&:order_date)).to(eq([@order.order_date]))
237
- end
238
-
239
- it "not persist changes to the live association" do
240
- expect(@customer.orders.reload).to(eq([]))
241
- end
242
- end
243
- end
244
-
245
- context "and then the associated is destroyed between model versions" do
246
- before do
247
- @order.destroy
248
- Timecop.travel(1.second.since)
249
- @customer.update_attributes(name: "customer_2")
250
- end
251
-
252
- context "when reified" do
253
- before { @customer1 = @customer.versions.last.reify(has_many: true) }
254
-
255
- it "see the associated as it was at the time" do
256
- expect(@customer1.orders).to(eq([]))
257
- end
258
- end
259
- end
260
-
261
- context "and then another association is added" do
262
- before { @customer.orders.create!(order_date: "order_date_1") }
263
-
264
- context "when reified" do
265
- before { @customer0 = @customer.versions.last.reify(has_many: true) }
266
-
267
- it "see the associated as it was at the time" do
268
- expect(@customer0.orders.map(&:order_date)).to(eq(["order_date_0"]))
269
- end
270
-
271
- it "not persist changes to the live association" do
272
- expect(
273
- @customer.orders.reload.map(&:order_date)
274
- ).to match_array(%w[order_date_0 order_date_1])
275
- end
276
- end
277
-
278
- context "when reified with option mark_for_destruction" do
279
- it "mark the newly associated for destruction" do
280
- @customer0 = @customer.versions.last.reify(has_many: true, mark_for_destruction: true)
281
- order = @customer0.orders.detect { |o| o.order_date == "order_date_1" }
282
- expect(order).to be_marked_for_destruction
283
- end
284
- end
285
- end
286
- end
287
- end
288
-
289
- context "has_many through associations" do
290
- context "Books, Authors, and Authorships" do
291
- before { @book = Book.create(title: "book_0") }
292
-
293
- context "updated before the associated was created" do
294
- before do
295
- @book.update_attributes!(title: "book_1")
296
- @book.authors.create!(name: "author_0")
297
- end
298
-
299
- context "when reified" do
300
- before { @book0 = @book.versions.last.reify(has_many: true) }
301
-
302
- it "see the associated as it was at the time" do
303
- expect(@book0.authors).to(eq([]))
304
- end
305
-
306
- it "not persist changes to the live association" do
307
- expect(@book.authors.reload.map(&:name)).to(eq(["author_0"]))
308
- end
309
- end
310
-
311
- context "when reified with option mark_for_destruction" do
312
- before do
313
- @book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
314
- end
315
-
316
- it "mark the associated for destruction" do
317
- expect(@book0.authors.map(&:marked_for_destruction?)).to(eq([true]))
318
- end
319
-
320
- it "mark the associated-through for destruction" do
321
- expect(@book0.authorships.map(&:marked_for_destruction?)).to(eq([true]))
322
- end
323
- end
324
- end
325
-
326
- context "updated before it is associated with an existing one" do
327
- before do
328
- person_existing = Person.create(name: "person_existing")
329
- Timecop.travel(1.second.since)
330
- @book.update_attributes!(title: "book_1")
331
- (@book.authors << person_existing)
332
- end
333
-
334
- context "when reified" do
335
- before { @book0 = @book.versions.last.reify(has_many: true) }
336
-
337
- it "see the associated as it was at the time" do
338
- expect(@book0.authors).to(eq([]))
339
- end
340
- end
341
-
342
- context "when reified with option mark_for_destruction" do
343
- before do
344
- @book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
345
- end
346
-
347
- it "not mark the associated for destruction" do
348
- expect(@book0.authors.map(&:marked_for_destruction?)).to(eq([false]))
349
- end
350
-
351
- it "mark the associated-through for destruction" do
352
- expect(@book0.authorships.map(&:marked_for_destruction?)).to(eq([true]))
353
- end
354
- end
355
- end
356
-
357
- context "where the association is created between model versions" do
358
- before do
359
- @author = @book.authors.create!(name: "author_0")
360
- @person_existing = Person.create(name: "person_existing")
361
- Timecop.travel(1.second.since)
362
- @book.update_attributes!(title: "book_1")
363
- end
364
-
365
- context "when reified" do
366
- before { @book0 = @book.versions.last.reify(has_many: true) }
367
-
368
- it "see the associated as it was at the time" do
369
- expect(@book0.authors.map(&:name)).to(eq(["author_0"]))
370
- end
371
- end
372
-
373
- context "and then the associated is updated between model versions" do
374
- before do
375
- @author.update_attributes(name: "author_1")
376
- @author.update_attributes(name: "author_2")
377
- Timecop.travel(1.second.since)
378
- @book.update_attributes(title: "book_2")
379
- @author.update_attributes(name: "author_3")
380
- end
381
-
382
- context "when reified" do
383
- before { @book1 = @book.versions.last.reify(has_many: true) }
384
-
385
- it "see the associated as it was at the time" do
386
- expect(@book1.authors.map(&:name)).to(eq(["author_2"]))
387
- end
388
-
389
- it "not persist changes to the live association" do
390
- expect(@book.authors.reload.map(&:name)).to(eq(["author_3"]))
391
- end
392
- end
393
-
394
- context "when reified opting out of has_many reification" do
395
- before { @book1 = @book.versions.last.reify(has_many: false) }
396
-
397
- it "see the associated as it is live" do
398
- expect(@book1.authors.map(&:name)).to(eq(["author_3"]))
399
- end
400
- end
401
- end
402
-
403
- context "and then the associated is destroyed" do
404
- before { @author.destroy }
405
-
406
- context "when reified" do
407
- before { @book1 = @book.versions.last.reify(has_many: true) }
408
-
409
- it "see the associated as it was at the time" do
410
- expect(@book1.authors.map(&:name)).to(eq([@author.name]))
411
- end
412
-
413
- it "not persist changes to the live association" do
414
- expect(@book.authors.reload).to(eq([]))
415
- end
416
- end
417
- end
418
-
419
- context "and then the associated is destroyed between model versions" do
420
- before do
421
- @author.destroy
422
- Timecop.travel(1.second.since)
423
- @book.update_attributes(title: "book_2")
424
- end
425
-
426
- context "when reified" do
427
- before { @book1 = @book.versions.last.reify(has_many: true) }
428
-
429
- it "see the associated as it was at the time" do
430
- expect(@book1.authors).to(eq([]))
431
- end
432
- end
433
- end
434
-
435
- context "and then the associated is dissociated between model versions" do
436
- before do
437
- @book.authors = []
438
- Timecop.travel(1.second.since)
439
- @book.update_attributes(title: "book_2")
440
- end
441
-
442
- context "when reified" do
443
- before { @book1 = @book.versions.last.reify(has_many: true) }
444
-
445
- it "see the associated as it was at the time" do
446
- expect(@book1.authors).to(eq([]))
447
- end
448
- end
449
- end
450
-
451
- context "and then another associated is created" do
452
- before { @book.authors.create!(name: "author_1") }
453
-
454
- context "when reified" do
455
- before { @book0 = @book.versions.last.reify(has_many: true) }
456
-
457
- it "only see the first associated" do
458
- expect(@book0.authors.map(&:name)).to(eq(["author_0"]))
459
- end
460
-
461
- it "not persist changes to the live association" do
462
- expect(@book.authors.reload.map(&:name)).to(eq(%w[author_0 author_1]))
463
- end
464
- end
465
-
466
- context "when reified with option mark_for_destruction" do
467
- before do
468
- @book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
469
- end
470
-
471
- it "mark the newly associated for destruction" do
472
- author = @book0.authors.detect { |a| a.name == "author_1" }
473
- expect(author).to be_marked_for_destruction
474
- end
475
-
476
- it "mark the newly associated-through for destruction" do
477
- authorship = @book0.authorships.detect { |as| as.author.name == "author_1" }
478
- expect(authorship).to be_marked_for_destruction
479
- end
480
- end
481
- end
482
-
483
- context "and then an existing one is associated" do
484
- before { (@book.authors << @person_existing) }
485
-
486
- context "when reified" do
487
- before { @book0 = @book.versions.last.reify(has_many: true) }
488
-
489
- it "only see the first associated" do
490
- expect(@book0.authors.map(&:name)).to(eq(["author_0"]))
491
- end
492
-
493
- it "not persist changes to the live association" do
494
- expect(@book.authors.reload.map(&:name).sort).to(eq(%w[author_0 person_existing]))
495
- end
496
- end
497
-
498
- context "when reified with option mark_for_destruction" do
499
- before do
500
- @book0 = @book.versions.last.reify(has_many: true, mark_for_destruction: true)
501
- end
502
-
503
- it "not mark the newly associated for destruction" do
504
- author = @book0.authors.detect { |a| a.name == "person_existing" }
505
- expect(author).not_to be_marked_for_destruction
506
- end
507
-
508
- it "mark the newly associated-through for destruction" do
509
- authorship = @book0.authorships.detect { |as| as.author.name == "person_existing" }
510
- expect(authorship).to be_marked_for_destruction
511
- end
512
- end
513
- end
514
- end
515
-
516
- context "updated before the associated without paper_trail was created" do
517
- before do
518
- @book.update_attributes!(title: "book_1")
519
- @book.editors.create!(name: "editor_0")
520
- end
521
-
522
- context "when reified" do
523
- before { @book0 = @book.versions.last.reify(has_many: true) }
524
-
525
- it "see the live association" do
526
- expect(@book0.editors.map(&:name)).to(eq(["editor_0"]))
527
- end
528
- end
529
- end
530
- end
531
-
532
- context "Chapters, Sections, Paragraphs, Quotations, and Citations" do
533
- before { @chapter = Chapter.create(name: CHAPTER_NAMES[0]) }
534
-
535
- context "before any associations are created" do
536
- before { @chapter.update_attributes(name: CHAPTER_NAMES[1]) }
537
-
538
- it "not reify any associations" do
539
- chapter_v1 = @chapter.versions[1].reify(has_many: true)
540
- expect(chapter_v1.name).to(eq(CHAPTER_NAMES[0]))
541
- expect(chapter_v1.sections).to(eq([]))
542
- expect(chapter_v1.paragraphs).to(eq([]))
543
- end
544
- end
545
-
546
- context "after the first has_many through relationship is created" do
547
- before do
548
- expect(@chapter.versions.size).to(eq(1))
549
- @chapter.update_attributes(name: CHAPTER_NAMES[1])
550
- expect(@chapter.versions.size).to(eq(2))
551
- Timecop.travel(1.second.since)
552
- @chapter.sections.create(name: "section 1")
553
- Timecop.travel(1.second.since)
554
- @chapter.sections.first.update_attributes(name: "section 2")
555
- Timecop.travel(1.second.since)
556
- @chapter.update_attributes(name: CHAPTER_NAMES[2])
557
- expect(@chapter.versions.size).to(eq(3))
558
- Timecop.travel(1.second.since)
559
- @chapter.sections.first.update_attributes(name: "section 3")
560
- end
561
-
562
- context "version 1" do
563
- it "have no sections" do
564
- chapter_v1 = @chapter.versions[1].reify(has_many: true)
565
- expect(chapter_v1.sections).to(eq([]))
566
- end
567
- end
568
-
569
- context "version 2" do
570
- it "have one section" do
571
- chapter_v2 = @chapter.versions[2].reify(has_many: true)
572
- expect(chapter_v2.sections.size).to(eq(1))
573
- expect(chapter_v2.sections.map(&:name)).to(eq(["section 2"]))
574
- expect(chapter_v2.name).to(eq(CHAPTER_NAMES[1]))
575
- end
576
- end
577
-
578
- context "version 2, before the section was destroyed" do
579
- before do
580
- @chapter.update_attributes(name: CHAPTER_NAMES[2])
581
- Timecop.travel(1.second.since)
582
- @chapter.sections.destroy_all
583
- Timecop.travel(1.second.since)
584
- end
585
-
586
- it "have the one section" do
587
- chapter_v2 = @chapter.versions[2].reify(has_many: true)
588
- expect(chapter_v2.sections.map(&:name)).to(eq(["section 2"]))
589
- end
590
- end
591
-
592
- context "version 3, after the section was destroyed" do
593
- before do
594
- @chapter.sections.destroy_all
595
- Timecop.travel(1.second.since)
596
- @chapter.update_attributes(name: CHAPTER_NAMES[3])
597
- Timecop.travel(1.second.since)
598
- end
599
-
600
- it "have no sections" do
601
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
602
- expect(chapter_v3.sections.size).to(eq(0))
603
- end
604
- end
605
-
606
- context "after creating a paragraph" do
607
- before do
608
- expect(@chapter.versions.size).to(eq(3))
609
- @section = @chapter.sections.first
610
- Timecop.travel(1.second.since)
611
- @paragraph = @section.paragraphs.create(name: "para1")
612
- end
613
-
614
- context "new chapter version" do
615
- it "have one paragraph" do
616
- initial_section_name = @section.name
617
- initial_paragraph_name = @paragraph.name
618
- Timecop.travel(1.second.since)
619
- @chapter.update_attributes(name: CHAPTER_NAMES[4])
620
- expect(@chapter.versions.size).to(eq(4))
621
- Timecop.travel(1.second.since)
622
- @paragraph.update_attributes(name: "para3")
623
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
624
- expect(chapter_v3.sections.map(&:name)).to(eq([initial_section_name]))
625
- paragraphs = chapter_v3.sections.first.paragraphs
626
- expect(paragraphs.size).to(eq(1))
627
- expect(paragraphs.map(&:name)).to(eq([initial_paragraph_name]))
628
- end
629
- end
630
-
631
- context "the version before a section is destroyed" do
632
- it "have the section and paragraph" do
633
- Timecop.travel(1.second.since)
634
- @chapter.update_attributes(name: CHAPTER_NAMES[3])
635
- expect(@chapter.versions.size).to(eq(4))
636
- Timecop.travel(1.second.since)
637
- @section.destroy
638
- expect(@chapter.versions.size).to(eq(4))
639
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
640
- expect(chapter_v3.name).to(eq(CHAPTER_NAMES[2]))
641
- expect(chapter_v3.sections).to(eq([@section]))
642
- expect(chapter_v3.sections[0].paragraphs).to(eq([@paragraph]))
643
- expect(chapter_v3.paragraphs).to(eq([@paragraph]))
644
- end
645
- end
646
-
647
- context "the version after a section is destroyed" do
648
- it "not have any sections or paragraphs" do
649
- @section.destroy
650
- Timecop.travel(1.second.since)
651
- @chapter.update_attributes(name: CHAPTER_NAMES[5])
652
- expect(@chapter.versions.size).to(eq(4))
653
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
654
- expect(chapter_v3.sections.size).to(eq(0))
655
- expect(chapter_v3.paragraphs.size).to(eq(0))
656
- end
657
- end
658
-
659
- context "the version before a paragraph is destroyed" do
660
- it "have the one paragraph" do
661
- initial_paragraph_name = @section.paragraphs.first.name
662
- Timecop.travel(1.second.since)
663
- @chapter.update_attributes(name: CHAPTER_NAMES[5])
664
- Timecop.travel(1.second.since)
665
- @paragraph.destroy
666
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
667
- paragraphs = chapter_v3.sections.first.paragraphs
668
- expect(paragraphs.size).to(eq(1))
669
- expect(paragraphs.first.name).to(eq(initial_paragraph_name))
670
- end
671
- end
672
-
673
- context "the version after a paragraph is destroyed" do
674
- it "have no paragraphs" do
675
- @paragraph.destroy
676
- Timecop.travel(1.second.since)
677
- @chapter.update_attributes(name: CHAPTER_NAMES[5])
678
- chapter_v3 = @chapter.versions[3].reify(has_many: true)
679
- expect(chapter_v3.paragraphs.size).to(eq(0))
680
- expect(chapter_v3.sections.first.paragraphs).to(eq([]))
681
- end
682
- end
683
- end
684
- end
685
-
686
- context "a chapter with one paragraph and one citation" do
687
- it "reify paragraphs and citations" do
688
- chapter = Chapter.create(name: CHAPTER_NAMES[0])
689
- section = Section.create(name: "Section One", chapter: chapter)
690
- paragraph = Paragraph.create(name: "Paragraph One", section: section)
691
- quotation = Quotation.create(chapter: chapter)
692
- citation = Citation.create(quotation: quotation)
693
- Timecop.travel(1.second.since)
694
- chapter.update_attributes(name: CHAPTER_NAMES[1])
695
- expect(chapter.versions.count).to(eq(2))
696
- paragraph.destroy
697
- citation.destroy
698
- reified = chapter.versions[1].reify(has_many: true)
699
- expect(reified.sections.first.paragraphs).to(eq([paragraph]))
700
- expect(reified.quotations.first.citations).to(eq([citation]))
701
- end
702
- end
703
- end
704
- end
705
-
706
- context "belongs_to associations" do
707
- context "Wotsit and Widget" do
708
- before { @widget = Widget.create(name: "widget_0") }
709
-
710
- context "where the association is created between model versions" do
711
- before do
712
- @wotsit = Wotsit.create(name: "wotsit_0")
713
- Timecop.travel(1.second.since)
714
- @wotsit.update_attributes(widget_id: @widget.id, name: "wotsit_1")
715
- end
716
-
717
- context "when reified" do
718
- before { @wotsit0 = @wotsit.versions.last.reify(belongs_to: true) }
719
-
720
- it "see the associated as it was at the time" do
721
- expect(@wotsit0.widget).to be_nil
722
- end
723
-
724
- it "not persist changes to the live association" do
725
- expect(@wotsit.reload.widget).to(eq(@widget))
726
- end
727
- end
728
-
729
- context "and then the associated is updated between model versions" do
730
- before do
731
- @widget.update_attributes(name: "widget_1")
732
- @widget.update_attributes(name: "widget_2")
733
- Timecop.travel(1.second.since)
734
- @wotsit.update_attributes(name: "wotsit_2")
735
- @widget.update_attributes(name: "widget_3")
736
- end
737
-
738
- context "when reified" do
739
- before { @wotsit1 = @wotsit.versions.last.reify(belongs_to: true) }
740
-
741
- it "see the associated as it was at the time" do
742
- expect(@wotsit1.widget.name).to(eq("widget_2"))
743
- end
744
-
745
- it "not persist changes to the live association" do
746
- expect(@wotsit.reload.widget.name).to(eq("widget_3"))
747
- end
748
- end
749
-
750
- context "when reified opting out of belongs_to reification" do
751
- before { @wotsit1 = @wotsit.versions.last.reify(belongs_to: false) }
752
-
753
- it "see the associated as it is live" do
754
- expect(@wotsit1.widget.name).to(eq("widget_3"))
755
- end
756
- end
757
- end
758
-
759
- context "and then the associated is destroyed" do
760
- before do
761
- @wotsit.update_attributes(name: "wotsit_2")
762
- @widget.destroy
763
- end
764
-
765
- context "when reified with belongs_to: true" do
766
- before { @wotsit2 = @wotsit.versions.last.reify(belongs_to: true) }
767
-
768
- it "see the associated as it was at the time" do
769
- expect(@wotsit2.widget).to(eq(@widget))
770
- end
771
-
772
- it "not persist changes to the live association" do
773
- expect(@wotsit.reload.widget).to be_nil
774
- end
775
-
776
- it "be able to persist the reified record" do
777
- expect { @wotsit2.save! }.not_to(raise_error)
778
- end
779
- end
780
-
781
- context "when reified with belongs_to: false" do
782
- before { @wotsit2 = @wotsit.versions.last.reify(belongs_to: false) }
783
-
784
- it "save should not re-create the widget record" do
785
- @wotsit2.save!
786
- expect(::Widget.find_by(id: @widget.id)).to be_nil
787
- end
788
- end
789
-
790
- context "and then the model is updated" do
791
- before do
792
- Timecop.travel(1.second.since)
793
- @wotsit.update_attributes(name: "wotsit_3")
794
- end
795
-
796
- context "when reified" do
797
- before { @wotsit2 = @wotsit.versions.last.reify(belongs_to: true) }
798
-
799
- it "see the associated as it was the time" do
800
- expect(@wotsit2.widget).to be_nil
801
- end
802
- end
803
- end
804
- end
805
- end
806
-
807
- context "where the association is changed between model versions" do
808
- before do
809
- @wotsit = @widget.create_wotsit(name: "wotsit_0")
810
- Timecop.travel(1.second.since)
811
- @new_widget = Widget.create(name: "new_widget")
812
- @wotsit.update_attributes(widget_id: @new_widget.id, name: "wotsit_1")
813
- end
814
-
815
- context "when reified" do
816
- before { @wotsit0 = @wotsit.versions.last.reify(belongs_to: true) }
817
-
818
- it "see the association as it was at the time" do
819
- expect(@wotsit0.widget.name).to(eq("widget_0"))
820
- end
821
-
822
- it "not persist changes to the live association" do
823
- expect(@wotsit.reload.widget).to(eq(@new_widget))
824
- end
825
- end
826
-
827
- context "when reified with option mark_for_destruction" do
828
- before do
829
- @wotsit0 = @wotsit.versions.last.reify(belongs_to: true, mark_for_destruction: true)
830
- end
831
-
832
- it "does not mark the new associated for destruction" do
833
- expect(@new_widget.marked_for_destruction?).to(eq(false))
834
- end
835
- end
836
- end
837
- end
838
- end
839
-
840
- context "has_and_belongs_to_many associations" do
841
- context "foo and bar" do
842
- before do
843
- @foo = FooHabtm.create(name: "foo")
844
- Timecop.travel(1.second.since)
845
- end
846
-
847
- context "where the association is created between model versions" do
848
- before do
849
- @foo.update_attributes(name: "foo1", bar_habtms: [BarHabtm.create(name: "bar")])
850
- end
851
-
852
- context "when reified" do
853
- before do
854
- @reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
855
- end
856
-
857
- it "see the associated as it was at the time" do
858
- expect(@reified.bar_habtms.length).to(eq(0))
859
- end
860
-
861
- it "not persist changes to the live association" do
862
- expect(@foo.reload.bar_habtms).not_to(eq(@reified.bar_habtms))
863
- end
864
- end
865
- end
866
-
867
- context "where the association is changed between model versions" do
868
- before do
869
- @foo.update_attributes(name: "foo2", bar_habtms: [BarHabtm.create(name: "bar2")])
870
- Timecop.travel(1.second.since)
871
- @foo.update_attributes(name: "foo3", bar_habtms: [BarHabtm.create(name: "bar3")])
872
- end
873
-
874
- context "when reified" do
875
- before do
876
- @reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
877
- end
878
-
879
- it "see the association as it was at the time" do
880
- expect(@reified.bar_habtms.first.name).to(eq("bar2"))
881
- end
882
-
883
- it "not persist changes to the live association" do
884
- expect(@foo.reload.bar_habtms.first).not_to(eq(@reified.bar_habtms.first))
885
- end
886
- end
887
-
888
- context "when reified with has_and_belongs_to_many: false" do
889
- before { @reified = @foo.versions.last.reify }
890
-
891
- it "see the association as it is now" do
892
- expect(@reified.bar_habtms.first.name).to(eq("bar3"))
893
- end
894
- end
895
- end
896
-
897
- context "where the association is destroyed between model versions" do
898
- before do
899
- @foo.update_attributes(name: "foo2", bar_habtms: [BarHabtm.create(name: "bar2")])
900
- Timecop.travel(1.second.since)
901
- @foo.update_attributes(name: "foo3", bar_habtms: [])
902
- end
903
-
904
- context "when reified" do
905
- before do
906
- @reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
907
- end
908
-
909
- it "see the association as it was at the time" do
910
- expect(@reified.bar_habtms.first.name).to(eq("bar2"))
911
- end
912
-
913
- it "not persist changes to the live association" do
914
- expect(@foo.reload.bar_habtms.first).not_to(eq(@reified.bar_habtms.first))
915
- end
916
- end
917
- end
918
-
919
- context "where the unassociated model changes" do
920
- before do
921
- @bar = BarHabtm.create(name: "bar2")
922
- @foo.update_attributes(name: "foo2", bar_habtms: [@bar])
923
- Timecop.travel(1.second.since)
924
- @foo.update_attributes(name: "foo3", bar_habtms: [BarHabtm.create(name: "bar4")])
925
- Timecop.travel(1.second.since)
926
- @bar.update_attributes(name: "bar3")
927
- end
928
-
929
- context "when reified" do
930
- before do
931
- @reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
932
- end
933
-
934
- it "see the association as it was at the time" do
935
- expect(@reified.bar_habtms.first.name).to(eq("bar2"))
936
- end
937
-
938
- it "not persist changes to the live association" do
939
- expect(@foo.reload.bar_habtms.first).not_to(eq(@reified.bar_habtms.first))
940
- end
941
- end
942
- end
943
- end
944
-
945
- context "updated via nested attributes" do
946
- before do
947
- @foo = FooHabtm.create(name: "foo", bar_habtms_attributes: [{ name: "bar" }])
948
- Timecop.travel(1.second.since)
949
- @foo.update_attributes(
950
- name: "foo2",
951
- bar_habtms_attributes: [{ id: @foo.bar_habtms.first.id, name: "bar2" }]
952
- )
953
- @reified = @foo.versions.last.reify(has_and_belongs_to_many: true)
954
- end
955
-
956
- it "see the associated object as it was at the time" do
957
- expect(@reified.bar_habtms.first.name).to(eq("bar"))
958
- end
959
-
960
- it "not persist changes to the live object" do
961
- expect(@foo.reload.bar_habtms.first.name).not_to(eq(@reified.bar_habtms.first.name))
962
- end
963
- end
964
- end
965
- end