historiographer 4.1.0 → 4.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/history_migration.rb +1 -0
  3. data/lib/historiographer/version.rb +3 -0
  4. metadata +12 -41
  5. data/Users/brettshollenberger/programming/historiographer/Gemfile +0 -33
  6. data/Users/brettshollenberger/programming/historiographer/Gemfile.lock +0 -341
  7. data/Users/brettshollenberger/programming/historiographer/Guardfile +0 -4
  8. data/Users/brettshollenberger/programming/historiographer/LICENSE.txt +0 -20
  9. data/Users/brettshollenberger/programming/historiographer/README.md +0 -298
  10. data/Users/brettshollenberger/programming/historiographer/Rakefile +0 -54
  11. data/Users/brettshollenberger/programming/historiographer/historiographer-4.1.0.gem +0 -0
  12. data/Users/brettshollenberger/programming/historiographer/historiographer.gemspec +0 -65
  13. data/Users/brettshollenberger/programming/historiographer/init.rb +0 -18
  14. data/Users/brettshollenberger/programming/historiographer/lib/historiographer/version.rb +0 -3
  15. data/Users/brettshollenberger/programming/historiographer/spec/db/database.yml +0 -27
  16. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20161121212228_create_posts.rb +0 -19
  17. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20161121212229_create_post_histories.rb +0 -10
  18. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20161121212230_create_authors.rb +0 -13
  19. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20161121212231_create_author_histories.rb +0 -10
  20. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20161121212232_create_users.rb +0 -9
  21. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20171011194624_create_safe_posts.rb +0 -19
  22. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20171011194715_create_safe_post_histories.rb +0 -9
  23. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20191024142304_create_thing_with_compound_index.rb +0 -10
  24. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb +0 -11
  25. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20191024203106_create_thing_without_history.rb +0 -7
  26. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20221018204220_create_silent_posts.rb +0 -21
  27. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20221018204255_create_silent_post_histories.rb +0 -9
  28. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20241109182017_create_comments.rb +0 -13
  29. data/Users/brettshollenberger/programming/historiographer/spec/db/migrate/20241109182020_create_comment_histories.rb +0 -9
  30. data/Users/brettshollenberger/programming/historiographer/spec/db/schema.rb +0 -225
  31. data/Users/brettshollenberger/programming/historiographer/spec/examples.txt +0 -40
  32. data/Users/brettshollenberger/programming/historiographer/spec/factories/post.rb +0 -7
  33. data/Users/brettshollenberger/programming/historiographer/spec/historiographer_spec.rb +0 -813
  34. data/Users/brettshollenberger/programming/historiographer/spec/spec_helper.rb +0 -56
  35. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/configuration.rb +0 -0
  36. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/history.rb +0 -0
  37. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/history_migration_mysql.rb +0 -0
  38. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/mysql_migration.rb +0 -0
  39. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/postgres_migration.rb +0 -0
  40. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/relation.rb +0 -0
  41. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/safe.rb +0 -0
  42. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer/silent.rb +0 -0
  43. /data/{Users/brettshollenberger/programming/historiographer/lib → lib}/historiographer.rb +0 -0
@@ -1,813 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- # Helper method to handle Rails error expectations
6
- def expect_rails_errors(errors, expected_errors)
7
- actual_errors = errors.respond_to?(:to_hash) ? errors.to_hash : errors.to_h
8
- # Ensure all error messages are arrays for compatibility
9
- actual_errors.each { |key, value| actual_errors[key] = Array(value) }
10
- expected_errors.each { |key, value| expected_errors[key] = Array(value) }
11
- expect(actual_errors).to eq(expected_errors)
12
- end
13
-
14
- class Post < ActiveRecord::Base
15
- include Historiographer
16
- acts_as_paranoid
17
- has_many :comments
18
-
19
- def summary
20
- "This is a summary of the post."
21
- end
22
-
23
- def formatted_title
24
- "Title: #{title}"
25
- end
26
- end
27
-
28
- class PostHistory < Post
29
- self.table_name = "post_histories"
30
- end
31
-
32
- class Comment < ActiveRecord::Base
33
- include Historiographer
34
- belongs_to :post
35
- belongs_to :author
36
- end
37
-
38
- class CommentHistory < Comment
39
- self.table_name = "comment_histories"
40
- end
41
-
42
- class SafePost < ActiveRecord::Base
43
- include Historiographer::Safe
44
- acts_as_paranoid
45
- end
46
-
47
- class SafePostHistory < SafePost
48
- self.table_name = "safe_post_histories"
49
- end
50
-
51
- class SilentPost < ActiveRecord::Base
52
- include Historiographer::Silent
53
- acts_as_paranoid
54
- end
55
-
56
- class SilentPostHistory < SilentPost
57
- self.table_name = "silent_post_histories"
58
- end
59
-
60
- class Author < ActiveRecord::Base
61
- include Historiographer
62
- has_many :comments
63
- has_many :posts
64
- end
65
-
66
- class AuthorHistory < Author
67
- self.table_name = "author_histories"
68
- end
69
-
70
- class User < ActiveRecord::Base
71
- end
72
-
73
- class ThingWithCompoundIndex < ActiveRecord::Base
74
- include Historiographer
75
- end
76
-
77
- class ThingWithCompoundIndexHistory < ThingWithCompoundIndex
78
- self.table_name = "thing_with_compound_index_histories"
79
- end
80
-
81
- class ThingWithoutHistory < ActiveRecord::Base
82
- end
83
-
84
- class Comment < ActiveRecord::Base
85
- include Historiographer
86
- belongs_to :post
87
- belongs_to :author
88
- end
89
-
90
- class CommentHistory < Comment
91
- self.table_name = "comment_histories"
92
- end
93
-
94
- describe Historiographer do
95
- before(:each) do
96
- @now = Timecop.freeze
97
- end
98
- after(:each) do
99
- Timecop.return
100
- end
101
-
102
- before(:all) do
103
- Historiographer::Configuration.mode = :histories
104
- end
105
-
106
- after(:all) do
107
- Timecop.return
108
- end
109
-
110
- let(:username) { 'Test User' }
111
-
112
- let(:user) do
113
- User.create(name: username)
114
- end
115
-
116
- let(:create_post) do
117
- Post.create(
118
- title: 'Post 1',
119
- body: 'Great post',
120
- author_id: 1,
121
- history_user_id: user.id
122
- )
123
- end
124
-
125
- let(:create_author) do
126
- Author.create(
127
- full_name: 'Breezy',
128
- history_user_id: user.id
129
- )
130
- end
131
-
132
- before(:each) do
133
- Historiographer::Configuration.mode = :histories
134
- end
135
-
136
- describe 'History counting' do
137
- it 'creates history on creation of primary model record' do
138
- expect do
139
- create_post
140
- end.to change {
141
- PostHistory.count
142
- }.by 1
143
- end
144
-
145
- it 'appends new history on update' do
146
- post = create_post
147
- expect do
148
- post.update(title: 'Better Title')
149
- end.to change {
150
- PostHistory.count
151
- }.by 1
152
- end
153
-
154
- it 'does not append new history if nothing has changed' do
155
- post = create_post
156
-
157
- expect do
158
- post.update(title: post.title)
159
- end.to_not change {
160
- PostHistory.count
161
- }
162
- end
163
- end
164
-
165
- describe 'History recording' do
166
-
167
- it 'records all fields from the parent' do
168
- post = create_post
169
- post_history = post.histories.first
170
-
171
- expect(post_history.title).to eq post.title
172
- expect(post_history.body).to eq post.body
173
- expect(post_history.author_id).to eq post.author_id
174
- expect(post_history.post_id).to eq post.id
175
- expect(post_history.history_started_at).to be_within(1.second).of(@now.in_time_zone(Historiographer::UTC))
176
- expect(post_history.history_ended_at).to be_nil
177
- expect(post_history.history_user_id).to eq user.id
178
-
179
- post.update(title: 'Better title')
180
- post_histories = post.histories.reload.order('id asc')
181
- first_history = post_histories.first
182
- second_history = post_histories.second
183
-
184
- expect(first_history.history_ended_at).to be_within(1.second).of(@now.in_time_zone(Historiographer::UTC))
185
- expect(second_history.history_ended_at).to be_nil
186
- end
187
-
188
- it 'cannot create without history_user_id' do
189
- post = Post.create(
190
- title: 'Post 1',
191
- body: 'Great post',
192
- author_id: 1
193
- )
194
-
195
- # Use the helper method for error expectation
196
- expect_rails_errors(post.errors, history_user_id: ['must be an integer'])
197
-
198
- expect do
199
- post.send(:record_history)
200
- end.to raise_error(
201
- Historiographer::HistoryUserIdMissingError
202
- )
203
- end
204
-
205
- context 'When directly hitting the database via SQL' do
206
- context '#update_all' do
207
- it 'still updates histories' do
208
- FactoryBot.create_list(:post, 3, history_user_id: 1)
209
-
210
- posts = Post.all
211
- expect(posts.count).to eq 3
212
- expect(PostHistory.count).to eq 3
213
- expect(posts.map(&:histories).map(&:count)).to all (eq 1)
214
-
215
- posts.update_all(title: 'My New Post Title', history_user_id: 1)
216
-
217
- expect(PostHistory.count).to eq 6
218
- expect(PostHistory.current.count).to eq 3
219
- expect(posts.map(&:histories).map(&:count)).to all(eq 2)
220
- expect(posts.map(&:current_history).map(&:title)).to all (eq 'My New Post Title')
221
- expect(Post.all).to respond_to :has_histories?
222
-
223
- # It can update by sub-query
224
- Post.where(id: [posts.first.id, posts.last.id]).update_all(title: "Brett's Post", history_user_id: 1)
225
- posts = Post.all.reload.order(:id)
226
- expect(posts.first.histories.count).to eq 3
227
- expect(posts.second.histories.count).to eq 2
228
- expect(posts.third.histories.count).to eq 3
229
- expect(posts.first.title).to eq "Brett's Post"
230
- expect(posts.second.title).to eq 'My New Post Title'
231
- expect(posts.third.title).to eq "Brett's Post"
232
- expect(posts.first.current_history.title).to eq "Brett's Post"
233
- expect(posts.second.current_history.title).to eq 'My New Post Title'
234
- expect(posts.third.current_history.title).to eq "Brett's Post"
235
-
236
- # It does not update histories if nothing changed
237
- Post.all.update_all(title: "Brett's Post", history_user_id: 1)
238
- posts = Post.all.reload.order(:id)
239
- expect(posts.map(&:histories).map(&:count)).to all(eq 3)
240
-
241
- posts.update_all_without_history(title: 'Untracked')
242
- expect(posts.first.histories.count).to eq 3
243
- expect(posts.second.histories.count).to eq 3
244
- expect(posts.third.histories.count).to eq 3
245
-
246
- thing1 = ThingWithoutHistory.create(name: 'Thing 1')
247
- thing2 = ThingWithoutHistory.create(name: 'Thing 2')
248
-
249
- ThingWithoutHistory.all.update_all(name: 'Thing 3')
250
- end
251
-
252
- it 'respects safety' do
253
- FactoryBot.create_list(:post, 3, history_user_id: 1)
254
-
255
- posts = Post.all
256
- expect(posts.count).to eq 3
257
- expect(PostHistory.count).to eq 3
258
- expect(posts.map(&:histories).map(&:count)).to all (eq 1)
259
-
260
- expect do
261
- posts.update_all(title: 'My New Post Title')
262
- end.to raise_error
263
-
264
- posts.reload.map(&:title).each do |title|
265
- expect(title).to_not eq 'My New Post Title'
266
- end
267
-
268
- SafePost.create(
269
- title: 'Post 1',
270
- body: 'Great post',
271
- author_id: 1
272
- )
273
-
274
- safe_posts = SafePost.all
275
-
276
- expect do
277
- safe_posts.update_all(title: 'New One')
278
- end.to_not raise_error
279
-
280
- expect(safe_posts.map(&:title)).to all(eq 'New One')
281
- end
282
- end
283
-
284
- context '#delete_all' do
285
- it 'includes histories when not paranoid' do
286
- Timecop.freeze
287
- authors = 3.times.map do
288
- Author.create(full_name: 'Brett', history_user_id: 1)
289
- end
290
- Author.delete_all(history_user_id: 1)
291
- expect(AuthorHistory.count).to eq 3
292
- expect(AuthorHistory.current.count).to eq 0
293
- expect(AuthorHistory.where.not(history_ended_at: nil).count).to eq 3
294
- expect(Author.count).to eq 0
295
- Timecop.return
296
- end
297
-
298
- it 'includes histories when paranoid' do
299
- Timecop.freeze
300
- posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
301
- Post.delete_all(history_user_id: 1)
302
- expect(PostHistory.unscoped.count).to eq 6
303
- expect(PostHistory.unscoped.current.count).to eq 3
304
- expect(PostHistory.unscoped.current.map(&:deleted_at)).to all(eq Time.now)
305
- expect(PostHistory.unscoped.current.map(&:history_user_id)).to all(eq 1)
306
- expect(PostHistory.unscoped.where(deleted_at: nil).where.not(history_ended_at: nil).count).to eq 3
307
- expect(PostHistory.unscoped.where(history_ended_at: nil).count).to eq 3
308
- expect(Post.count).to eq 0
309
- Timecop.return
310
- end
311
-
312
- it 'allows delete_all_without_history' do
313
- authors = 3.times.map do
314
- Author.create(full_name: 'Brett', history_user_id: 1)
315
- end
316
- Author.all.delete_all_without_history
317
- expect(AuthorHistory.current.count).to eq 3
318
- expect(Author.count).to eq 0
319
- end
320
- end
321
-
322
- context '#destroy_all' do
323
- it 'includes histories' do
324
- Timecop.freeze
325
- posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
326
- Post.destroy_all(history_user_id: 1)
327
- expect(PostHistory.unscoped.count).to eq 6
328
- expect(PostHistory.unscoped.current.count).to eq 3
329
- expect(PostHistory.unscoped.current.map(&:deleted_at)).to all(eq Time.now)
330
- expect(PostHistory.unscoped.current.map(&:history_user_id)).to all(eq 1)
331
- expect(PostHistory.unscoped.where(deleted_at: nil).where.not(history_ended_at: nil).count).to eq 3
332
- expect(PostHistory.unscoped.where(history_ended_at: nil).count).to eq 3
333
- expect(Post.count).to eq 0
334
- Timecop.return
335
- end
336
-
337
- it 'destroys without histories' do
338
- Timecop.freeze
339
- posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
340
- Post.all.destroy_all_without_history
341
- expect(PostHistory.count).to eq 3
342
- expect(PostHistory.current.count).to eq 3
343
- expect(Post.count).to eq 0
344
- Timecop.return
345
- end
346
- end
347
- end
348
-
349
- context 'When Safe mode' do
350
- it 'creates history without history_user_id' do
351
- expect(Rollbar).to receive(:error).with('history_user_id must be passed in order to save record with histories! If you are in a context with no history_user_id, explicitly call #save_without_history')
352
-
353
- post = SafePost.create(
354
- title: 'Post 1',
355
- body: 'Great post',
356
- author_id: 1
357
- )
358
- expect_rails_errors(post.errors, {})
359
- expect(post).to be_persisted
360
- expect(post.histories.count).to eq 1
361
- expect(post.histories.first.history_user_id).to be_nil
362
- end
363
-
364
- it 'creates history with history_user_id' do
365
- expect(Rollbar).to_not receive(:error)
366
-
367
- post = SafePost.create(
368
- title: 'Post 1',
369
- body: 'Great post',
370
- author_id: 1,
371
- history_user_id: user.id
372
- )
373
- expect_rails_errors(post.errors, {})
374
- expect(post).to be_persisted
375
- expect(post.histories.count).to eq 1
376
- expect(post.histories.first.history_user_id).to eq user.id
377
- end
378
-
379
- it 'skips history creation if desired' do
380
- post = SafePost.new(
381
- title: 'Post 1',
382
- body: 'Great post',
383
- author_id: 1
384
- )
385
-
386
- post.save_without_history
387
- expect(post).to be_persisted
388
- expect(post.histories.count).to eq 0
389
- end
390
- end
391
-
392
- context 'When Silent mode' do
393
- it 'creates history without history_user_id' do
394
- expect(Rollbar).to_not receive(:error)
395
-
396
- post = SilentPost.create(
397
- title: 'Post 1',
398
- body: 'Great post',
399
- author_id: 1
400
- )
401
-
402
- expect_rails_errors(post.errors, {})
403
- expect(post).to be_persisted
404
- expect(post.histories.count).to eq 1
405
- expect(post.histories.first.history_user_id).to be_nil
406
-
407
- post.update(title: 'New Title')
408
- post.reload
409
- expect(post.title).to eq 'New Title' # No error was raised
410
- end
411
-
412
- it 'creates history with history_user_id' do
413
- expect(Rollbar).to_not receive(:error)
414
-
415
- post = SilentPost.create(
416
- title: 'Post 1',
417
- body: 'Great post',
418
- author_id: 1,
419
- history_user_id: user.id
420
- )
421
- expect_rails_errors(post.errors, {})
422
- expect(post).to be_persisted
423
- expect(post.histories.count).to eq 1
424
- expect(post.histories.first.history_user_id).to eq user.id
425
- end
426
-
427
- it 'skips history creation if desired' do
428
- post = SilentPost.new(
429
- title: 'Post 1',
430
- body: 'Great post',
431
- author_id: 1
432
- )
433
-
434
- post.save_without_history
435
- expect(post).to be_persisted
436
- expect(post.histories.count).to eq 0
437
- end
438
- end
439
- it 'can override without history_user_id' do
440
- expect do
441
- post = Post.new(
442
- title: 'Post 1',
443
- body: 'Great post',
444
- author_id: 1
445
- )
446
-
447
- post.save_without_history
448
- end.to_not raise_error
449
- end
450
-
451
- it 'can override without history_user_id' do
452
- expect do
453
- post = Post.new(
454
- title: 'Post 1',
455
- body: 'Great post',
456
- author_id: 1
457
- )
458
-
459
- post.save_without_history!
460
- end.to_not raise_error
461
- end
462
-
463
- it 'does not record histories when main model fails to save' do
464
- class Post
465
- after_save :raise_error, prepend: true
466
-
467
- def raise_error
468
- raise 'Oh no, db issue!'
469
- end
470
- end
471
-
472
- expect { create_post }.to raise_error
473
- expect(Post.count).to be 0
474
- expect(PostHistory.count).to be 0
475
-
476
- Post.skip_callback(:save, :after, :raise_error)
477
- end
478
- end
479
-
480
- describe 'Scopes' do
481
- it 'finds current histories' do
482
- post1 = create_post
483
- post1.update(title: 'Better title')
484
-
485
- post2 = create_post
486
- post2.update(title: 'Better title')
487
-
488
- expect(PostHistory.current.pluck(:title)).to all eq 'Better title'
489
- expect(post1.current_history.title).to eq 'Better title'
490
- end
491
- end
492
-
493
- describe 'Associations' do
494
- it 'names associated records' do
495
- post1 = create_post
496
- expect(post1.histories.first).to be_a(PostHistory)
497
-
498
- expect(post1.histories.first.post).to be(post1)
499
-
500
- author1 = create_author
501
- expect(author1.histories.first).to be_a(AuthorHistory)
502
-
503
- expect(author1.histories.first.author).to be(author1)
504
- end
505
- end
506
-
507
- describe 'Histories' do
508
- it 'does not allow direct updates of histories' do
509
- post1 = create_post
510
- hist1 = post1.histories.first
511
-
512
- expect(hist1.update(title: 'A different title')).to be false
513
- expect(hist1.reload.title).to eq post1.title
514
-
515
- expect(hist1.update!(title: 'A different title')).to be false
516
- expect(hist1.reload.title).to eq post1.title
517
-
518
- hist1.title = 'A different title'
519
- expect(hist1.save).to be false
520
- expect(hist1.reload.title).to eq post1.title
521
-
522
- hist1.title = 'A different title'
523
- expect(hist1.save!).to be false
524
- expect(hist1.reload.title).to eq post1.title
525
- end
526
-
527
- it 'does not allow destroys of histories' do
528
- post1 = create_post
529
- hist1 = post1.histories.first
530
- original_history_count = post1.histories.count
531
-
532
- expect(hist1.destroy).to be false
533
- expect(hist1.destroy!).to be false
534
-
535
- expect(post1.histories.count).to be original_history_count
536
- end
537
- end
538
-
539
- describe 'Deletion' do
540
- it 'records deleted_at and history_user_id on primary and history if you use acts_as_paranoid' do
541
- post = Post.create(
542
- title: 'Post 1',
543
- body: 'Great post',
544
- author_id: 1,
545
- history_user_id: user.id
546
- )
547
-
548
- expect do
549
- post.destroy(history_user_id: 2)
550
- end.to change {
551
- PostHistory.unscoped.count
552
- }.by 1
553
-
554
- expect(Post.unscoped.where.not(deleted_at: nil).count).to eq 1
555
- expect(Post.unscoped.where(deleted_at: nil).count).to eq 0
556
- expect(PostHistory.unscoped.where.not(deleted_at: nil).count).to eq 1
557
- expect(PostHistory.unscoped.last.history_user_id).to eq 2
558
- end
559
-
560
- it 'works with Historiographer::Safe' do
561
- post = SafePost.create(title: 'HELLO', body: 'YO', author_id: 1)
562
-
563
- expect do
564
- post.destroy
565
- end.to_not raise_error
566
-
567
- expect(SafePost.unscoped.count).to eq 1
568
- expect(post.deleted_at).to_not be_nil
569
- expect(SafePostHistory.unscoped.count).to eq 2
570
- expect(SafePostHistory.unscoped.current.last.deleted_at).to eq post.deleted_at
571
-
572
- post2 = SafePost.create(title: 'HELLO', body: 'YO', author_id: 1)
573
-
574
- expect do
575
- post2.destroy!
576
- end.to_not raise_error
577
-
578
- expect(SafePost.count).to eq 0
579
- expect(post2.deleted_at).to_not be_nil
580
- expect(SafePostHistory.unscoped.count).to eq 4
581
- expect(SafePostHistory.unscoped.current.where(safe_post_id: post2.id).last.deleted_at).to eq post2.deleted_at
582
- end
583
- end
584
-
585
- describe 'Scopes' do
586
- it 'finds current' do
587
- post = create_post
588
- post.update(title: 'New Title')
589
- post.update(title: 'New Title 2')
590
-
591
- expect(PostHistory.current.count).to be 1
592
- end
593
- end
594
-
595
- describe 'User associations' do
596
- it 'links to user' do
597
- post = create_post
598
- author = create_author
599
-
600
- expect(post.current_history.user.name).to eq username
601
- expect(author.current_history.user.name).to eq username
602
- end
603
- end
604
-
605
- describe 'Migrations with compound indexes' do
606
- it 'supports renaming compound indexes and migrating them to history tables' do
607
- indices_sql = "
608
- SELECT
609
- DISTINCT(
610
- ARRAY_TO_STRING(ARRAY(
611
- SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
612
- FROM generate_subscripts(idx.indkey, 1) as k
613
- ORDER BY k
614
- ), ',')
615
- ) as indkey_names
616
- FROM pg_class t,
617
- pg_class i,
618
- pg_index idx,
619
- pg_attribute a,
620
- pg_am am
621
- WHERE t.oid = idx.indrelid
622
- AND i.oid = idx.indexrelid
623
- AND a.attrelid = t.oid
624
- AND a.attnum = ANY(idx.indkey)
625
- AND t.relkind = 'r'
626
- AND t.relname = ?;
627
- "
628
-
629
- indices_query_array = [indices_sql, :thing_with_compound_index_histories]
630
- indices_sanitized_query = ThingWithCompoundIndexHistory.send(:sanitize_sql_array, indices_query_array)
631
-
632
- indexes = ThingWithCompoundIndexHistory.connection.execute(indices_sanitized_query).to_a.map(&:values).flatten.map { |i| i.split(',') }
633
-
634
- expect(indexes).to include(['history_started_at'])
635
- expect(indexes).to include(['history_ended_at'])
636
- expect(indexes).to include(['history_user_id'])
637
- expect(indexes).to include(['id'])
638
- expect(indexes).to include(%w[key value])
639
- expect(indexes).to include(['thing_with_compound_index_id'])
640
- end
641
- end
642
-
643
- describe 'Reified Histories' do
644
- let(:post) { create_post }
645
- let(:post_history) { post.histories.first }
646
- let(:author) { Author.create(full_name: 'Commenter Jones', history_user_id: user.id) }
647
- let(:comment) { Comment.create(post: post, author: author, history_user_id: user.id) }
648
-
649
- it 'responds to methods defined on the original class' do
650
- expect(post_history).to respond_to(:summary)
651
- expect(post_history.summary).to eq('This is a summary of the post.')
652
- end
653
-
654
- it 'behaves like the original class for attribute methods' do
655
- expect(post_history.title).to eq(post.title)
656
- expect(post_history.body).to eq(post.body)
657
- end
658
-
659
- it 'supports custom instance methods' do
660
- expect(post_history).to respond_to(:formatted_title)
661
- expect(post_history.formatted_title).to eq("Title: #{post.title}")
662
- end
663
-
664
- it "does not do things histories shouldn't do" do
665
- post_history.update(title: "new title")
666
- expect(post_history.reload.title).to eq "Post 1"
667
-
668
- post_history.destroy
669
- expect(post_history.reload.title).to eq "Post 1"
670
- end
671
-
672
- end
673
-
674
- describe 'Snapshots' do
675
- let(:post) { create_post }
676
- let(:author) { Author.create(full_name: 'Commenter Jones', history_user_id: user.id) }
677
- let(:comment) { Comment.create(body: "Mean comment! I hate you!", post: post, author: author, history_user_id: user.id) }
678
-
679
- it 'creates a snapshot of the post and its associations' do
680
- # Take a snapshot
681
- comment # Make sure all records are created
682
- post.snapshot
683
-
684
- # Verify snapshot
685
- snapshot_post = PostHistory.where.not(snapshot_id: nil).last
686
- expect(snapshot_post.title).to eq post.title
687
- expect(snapshot_post.formatted_title).to eq post.formatted_title
688
-
689
- snapshot_comment = snapshot_post.comments.first
690
- expect(snapshot_comment.body).to eq comment.body
691
- expect(snapshot_comment.post_id).to eq post.id
692
- expect(snapshot_comment.class.name).to eq "CommentHistory"
693
-
694
- snapshot_author = snapshot_comment.author
695
- expect(snapshot_author.full_name).to eq author.full_name
696
- expect(snapshot_author.class.name).to eq "AuthorHistory"
697
-
698
- # Snapshots do not allow change
699
- expect(snapshot_post.update(title: "My title")).to eq false
700
- expect(snapshot_post.reload.title).to eq post.title
701
- end
702
-
703
- it "returns the latest snapshot" do
704
- Timecop.freeze(Time.now)
705
- # Take a snapshot
706
- comment # Make sure all records are created
707
- post.snapshot(history_user_id: user.id)
708
- comment.destroy(history_user_id: user.id)
709
- post.comments.create!(post: post, author: author, history_user_id: user.id, body: "Sorry man, didn't mean to post that")
710
-
711
- expect(PostHistory.count).to eq 1
712
- expect(CommentHistory.count).to eq 2
713
- expect(AuthorHistory.count).to eq 1
714
-
715
- Timecop.freeze(Time.now + 5.minutes)
716
- post.snapshot(history_user_id: user.id)
717
-
718
- expect(PostHistory.count).to eq 2
719
- expect(CommentHistory.count).to eq 2
720
- expect(AuthorHistory.count).to eq 2
721
-
722
- # Verify snapshot
723
- snapshot_post = Post.latest_snapshot
724
- expect(snapshot_post.title).to eq post.title
725
- expect(snapshot_post.formatted_title).to eq post.formatted_title
726
-
727
- snapshot_comment = snapshot_post.comments.first
728
- expect(snapshot_post.comments.count).to eq 1
729
- expect(snapshot_comment.body).to eq "Sorry man, didn't mean to post that"
730
- expect(snapshot_comment.post_id).to eq post.id
731
- expect(snapshot_comment.class.name).to eq "CommentHistory"
732
-
733
- snapshot_author = snapshot_comment.author
734
- expect(snapshot_author.full_name).to eq author.full_name
735
- expect(snapshot_author.class.name).to eq "AuthorHistory"
736
-
737
- # Snapshots do not allow change
738
- expect(snapshot_post.update(title: "My title")).to eq false
739
- expect(snapshot_post.reload.title).to eq post.title
740
-
741
- Timecop.return
742
- end
743
-
744
- it "uses snapshot_only mode" do
745
- Historiographer::Configuration.mode = :snapshot_only
746
-
747
- comment # Make sure all records are created
748
- post
749
- expect(PostHistory.count).to eq 0
750
- expect(CommentHistory.count).to eq 0
751
- expect(AuthorHistory.count).to eq 0
752
-
753
- post.snapshot
754
- expect(PostHistory.count).to eq 1
755
- expect(CommentHistory.count).to eq 1
756
- expect(AuthorHistory.count).to eq 1
757
-
758
- comment.destroy(history_user_id: user.id)
759
- post.comments.create!(post: post, author: author, history_user_id: user.id, body: "Sorry man, didn't mean to post that")
760
-
761
- expect(PostHistory.count).to eq 1
762
- expect(CommentHistory.count).to eq 1
763
- expect(AuthorHistory.count).to eq 1
764
-
765
- Timecop.freeze(Time.now + 5.minutes)
766
- post.snapshot
767
-
768
- expect(PostHistory.count).to eq 2
769
- expect(CommentHistory.count).to eq 2
770
- expect(AuthorHistory.count).to eq 2
771
- end
772
-
773
- end
774
-
775
- describe 'Class-level mode setting' do
776
- before(:each) do
777
- Historiographer::Configuration.mode = :histories
778
- end
779
-
780
- it "uses class-level snapshot_only mode" do
781
- class Post < ActiveRecord::Base
782
- historiographer_mode :snapshot_only
783
- end
784
-
785
- author = Author.create(full_name: 'Commenter Jones', history_user_id: user.id)
786
- post = Post.create(title: 'Snapshot Only Post', body: 'Test', author_id: 1, history_user_id: user.id)
787
- comment = Comment.create(post: post, author: author, history_user_id: user.id, body: "Initial comment")
788
-
789
- expect(PostHistory.count).to eq 0
790
- expect(CommentHistory.count).to eq 1 # Comment still uses default :histories mode
791
-
792
- post.snapshot
793
- expect(PostHistory.count).to eq 1
794
- expect(CommentHistory.count).to eq 1
795
-
796
- post.update(title: 'Updated Snapshot Only Post', history_user_id: user.id)
797
- expect(PostHistory.count).to eq 1 # No new history created for update
798
- expect(CommentHistory.count).to eq 1
799
-
800
- Timecop.freeze(Time.now + 5.minutes)
801
- post.snapshot
802
-
803
- expect(PostHistory.count).to eq 2
804
- expect(CommentHistory.count).to eq 2 # Comment creates a new history
805
-
806
- Timecop.return
807
-
808
- class Post < ActiveRecord::Base
809
- historiographer_mode nil
810
- end
811
- end
812
- end
813
- end