paper_trail 7.1.0 → 7.1.1

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 (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