historiographer 4.0.0 → 4.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.
- checksums.yaml +4 -4
- data/README.md +115 -39
- data/lib/historiographer/configuration.rb +36 -0
- data/lib/historiographer/history.rb +9 -2
- data/lib/historiographer/history_migration.rb +9 -6
- data/lib/historiographer/relation.rb +1 -1
- data/lib/historiographer/version.rb +3 -0
- data/lib/historiographer.rb +176 -11
- metadata +3 -30
- data/.document +0 -5
- data/.rspec +0 -1
- data/.ruby-version +0 -1
- data/.standalone_migrations +0 -6
- data/Gemfile +0 -34
- data/Gemfile.lock +0 -289
- data/Guardfile +0 -70
- data/Rakefile +0 -54
- data/VERSION +0 -1
- data/historiographer.gemspec +0 -106
- data/init.rb +0 -18
- data/spec/db/database.yml +0 -25
- data/spec/db/migrate/20161121212228_create_posts.rb +0 -19
- data/spec/db/migrate/20161121212229_create_post_histories.rb +0 -10
- data/spec/db/migrate/20161121212230_create_authors.rb +0 -13
- data/spec/db/migrate/20161121212231_create_author_histories.rb +0 -10
- data/spec/db/migrate/20161121212232_create_users.rb +0 -9
- data/spec/db/migrate/20171011194624_create_safe_posts.rb +0 -19
- data/spec/db/migrate/20171011194715_create_safe_post_histories.rb +0 -9
- data/spec/db/migrate/20191024142304_create_thing_with_compound_index.rb +0 -10
- data/spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb +0 -11
- data/spec/db/migrate/20191024203106_create_thing_without_history.rb +0 -7
- data/spec/db/migrate/20221018204220_create_silent_posts.rb +0 -21
- data/spec/db/migrate/20221018204255_create_silent_post_histories.rb +0 -9
- data/spec/db/schema.rb +0 -186
- data/spec/examples.txt +0 -32
- data/spec/factories/post.rb +0 -7
- data/spec/historiographer_spec.rb +0 -588
- data/spec/spec_helper.rb +0 -52
@@ -1,588 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
class Post < ActiveRecord::Base
|
6
|
-
include Historiographer
|
7
|
-
acts_as_paranoid
|
8
|
-
end
|
9
|
-
|
10
|
-
class PostHistory < ActiveRecord::Base
|
11
|
-
end
|
12
|
-
|
13
|
-
class SafePost < ActiveRecord::Base
|
14
|
-
include Historiographer::Safe
|
15
|
-
acts_as_paranoid
|
16
|
-
end
|
17
|
-
|
18
|
-
class SafePostHistory < ActiveRecord::Base
|
19
|
-
end
|
20
|
-
|
21
|
-
class SilentPost < ActiveRecord::Base
|
22
|
-
include Historiographer::Silent
|
23
|
-
acts_as_paranoid
|
24
|
-
end
|
25
|
-
|
26
|
-
class SilentPostHistory < ActiveRecord::Base
|
27
|
-
end
|
28
|
-
|
29
|
-
class Author < ActiveRecord::Base
|
30
|
-
include Historiographer
|
31
|
-
end
|
32
|
-
|
33
|
-
class AuthorHistory < ActiveRecord::Base
|
34
|
-
end
|
35
|
-
|
36
|
-
class User < ActiveRecord::Base
|
37
|
-
end
|
38
|
-
|
39
|
-
class ThingWithCompoundIndex < ActiveRecord::Base
|
40
|
-
include Historiographer
|
41
|
-
end
|
42
|
-
|
43
|
-
class ThingWithCompoundIndexHistory < ActiveRecord::Base
|
44
|
-
end
|
45
|
-
|
46
|
-
class ThingWithoutHistory < ActiveRecord::Base
|
47
|
-
end
|
48
|
-
|
49
|
-
describe Historiographer do
|
50
|
-
before(:all) do
|
51
|
-
@now = Timecop.freeze
|
52
|
-
end
|
53
|
-
|
54
|
-
after(:all) do
|
55
|
-
Timecop.return
|
56
|
-
end
|
57
|
-
|
58
|
-
let(:username) { 'Test User' }
|
59
|
-
|
60
|
-
let(:user) do
|
61
|
-
User.create(name: username)
|
62
|
-
end
|
63
|
-
|
64
|
-
let(:create_post) do
|
65
|
-
Post.create(
|
66
|
-
title: 'Post 1',
|
67
|
-
body: 'Great post',
|
68
|
-
author_id: 1,
|
69
|
-
history_user_id: user.id
|
70
|
-
)
|
71
|
-
end
|
72
|
-
|
73
|
-
let(:create_author) do
|
74
|
-
Author.create(
|
75
|
-
full_name: 'Breezy',
|
76
|
-
history_user_id: user.id
|
77
|
-
)
|
78
|
-
end
|
79
|
-
|
80
|
-
describe 'History counting' do
|
81
|
-
it 'creates history on creation of primary model record' do
|
82
|
-
expect do
|
83
|
-
create_post
|
84
|
-
end.to change {
|
85
|
-
PostHistory.count
|
86
|
-
}.by 1
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'appends new history on update' do
|
90
|
-
post = create_post
|
91
|
-
|
92
|
-
expect do
|
93
|
-
post.update(title: 'Better Title')
|
94
|
-
end.to change {
|
95
|
-
PostHistory.count
|
96
|
-
}.by 1
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'does not append new history if nothing has changed' do
|
100
|
-
post = create_post
|
101
|
-
|
102
|
-
expect do
|
103
|
-
post.update(title: post.title)
|
104
|
-
end.to_not change {
|
105
|
-
PostHistory.count
|
106
|
-
}
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
describe 'History recording' do
|
111
|
-
it 'records all fields from the parent' do
|
112
|
-
post = create_post
|
113
|
-
post_history = post.histories.first
|
114
|
-
|
115
|
-
expect(post_history.title).to eq post.title
|
116
|
-
expect(post_history.body).to eq post.body
|
117
|
-
expect(post_history.author_id).to eq post.author_id
|
118
|
-
expect(post_history.post_id).to eq post.id
|
119
|
-
expect(post_history.history_started_at.to_s).to eq @now.in_time_zone(Historiographer::UTC).to_s
|
120
|
-
expect(post_history.history_ended_at).to be_nil
|
121
|
-
expect(post_history.history_user_id).to eq user.id
|
122
|
-
|
123
|
-
post.update(title: 'Better title')
|
124
|
-
post_histories = post.histories.reload.order('id asc')
|
125
|
-
first_history = post_histories.first
|
126
|
-
second_history = post_histories.second
|
127
|
-
|
128
|
-
expect(first_history.history_ended_at.to_s).to eq @now.in_time_zone(Historiographer::UTC).to_s
|
129
|
-
expect(second_history.history_ended_at).to be_nil
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'cannot create without history_user_id' do
|
133
|
-
post = Post.create(
|
134
|
-
title: 'Post 1',
|
135
|
-
body: 'Great post',
|
136
|
-
author_id: 1
|
137
|
-
)
|
138
|
-
expect(post.errors.to_h).to eq(history_user_id: 'must be an integer')
|
139
|
-
|
140
|
-
expect do
|
141
|
-
post.send(:record_history)
|
142
|
-
end.to raise_error(
|
143
|
-
Historiographer::HistoryUserIdMissingError
|
144
|
-
)
|
145
|
-
end
|
146
|
-
|
147
|
-
context 'When directly hitting the database via SQL' do
|
148
|
-
context '#update_all' do
|
149
|
-
it 'still updates histories' do
|
150
|
-
FactoryBot.create_list(:post, 3, history_user_id: 1)
|
151
|
-
|
152
|
-
posts = Post.all
|
153
|
-
expect(posts.count).to eq 3
|
154
|
-
expect(PostHistory.count).to eq 3
|
155
|
-
expect(posts.map(&:histories).map(&:count)).to all (eq 1)
|
156
|
-
|
157
|
-
posts.update_all(title: 'My New Post Title', history_user_id: 1)
|
158
|
-
|
159
|
-
expect(PostHistory.count).to eq 6
|
160
|
-
expect(PostHistory.current.count).to eq 3
|
161
|
-
expect(posts.map(&:histories).map(&:count)).to all(eq 2)
|
162
|
-
expect(posts.map(&:current_history).map(&:title)).to all (eq 'My New Post Title')
|
163
|
-
expect(Post.all).to respond_to :has_histories?
|
164
|
-
|
165
|
-
# It can update by sub-query
|
166
|
-
Post.where(id: [posts.first.id, posts.last.id]).update_all(title: "Brett's Post", history_user_id: 1)
|
167
|
-
posts = Post.all.reload.order(:id)
|
168
|
-
expect(posts.first.histories.count).to eq 3
|
169
|
-
expect(posts.second.histories.count).to eq 2
|
170
|
-
expect(posts.third.histories.count).to eq 3
|
171
|
-
expect(posts.first.title).to eq "Brett's Post"
|
172
|
-
expect(posts.second.title).to eq 'My New Post Title'
|
173
|
-
expect(posts.third.title).to eq "Brett's Post"
|
174
|
-
expect(posts.first.current_history.title).to eq "Brett's Post"
|
175
|
-
expect(posts.second.current_history.title).to eq 'My New Post Title'
|
176
|
-
expect(posts.third.current_history.title).to eq "Brett's Post"
|
177
|
-
|
178
|
-
# It does not update histories if nothing changed
|
179
|
-
Post.all.update_all(title: "Brett's Post", history_user_id: 1)
|
180
|
-
posts = Post.all.reload.order(:id)
|
181
|
-
expect(posts.map(&:histories).map(&:count)).to all(eq 3)
|
182
|
-
|
183
|
-
posts.update_all_without_history(title: 'Untracked')
|
184
|
-
expect(posts.first.histories.count).to eq 3
|
185
|
-
expect(posts.second.histories.count).to eq 3
|
186
|
-
expect(posts.third.histories.count).to eq 3
|
187
|
-
|
188
|
-
thing1 = ThingWithoutHistory.create(name: 'Thing 1')
|
189
|
-
thing2 = ThingWithoutHistory.create(name: 'Thing 2')
|
190
|
-
|
191
|
-
ThingWithoutHistory.all.update_all(name: 'Thing 3')
|
192
|
-
|
193
|
-
expect(ThingWithoutHistory.all.map(&:name)).to all(eq 'Thing 3')
|
194
|
-
expect(ThingWithoutHistory.all).to_not respond_to :has_histories?
|
195
|
-
expect(ThingWithoutHistory.all).to_not respond_to :update_all_without_history
|
196
|
-
expect(ThingWithoutHistory.all).to_not respond_to :delete_all_without_history
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'respects safety' do
|
200
|
-
FactoryBot.create_list(:post, 3, history_user_id: 1)
|
201
|
-
|
202
|
-
posts = Post.all
|
203
|
-
expect(posts.count).to eq 3
|
204
|
-
expect(PostHistory.count).to eq 3
|
205
|
-
expect(posts.map(&:histories).map(&:count)).to all (eq 1)
|
206
|
-
|
207
|
-
expect do
|
208
|
-
posts.update_all(title: 'My New Post Title')
|
209
|
-
end.to raise_error
|
210
|
-
|
211
|
-
posts.reload.map(&:title).each do |title|
|
212
|
-
expect(title).to_not eq 'My New Post Title'
|
213
|
-
end
|
214
|
-
|
215
|
-
SafePost.create(
|
216
|
-
title: 'Post 1',
|
217
|
-
body: 'Great post',
|
218
|
-
author_id: 1
|
219
|
-
)
|
220
|
-
|
221
|
-
safe_posts = SafePost.all
|
222
|
-
|
223
|
-
expect do
|
224
|
-
safe_posts.update_all(title: 'New One')
|
225
|
-
end.to_not raise_error
|
226
|
-
|
227
|
-
expect(safe_posts.map(&:title)).to all(eq 'New One')
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
context '#delete_all' do
|
232
|
-
it 'includes histories when not paranoid' do
|
233
|
-
Timecop.freeze
|
234
|
-
authors = 3.times.map do
|
235
|
-
Author.create(full_name: 'Brett', history_user_id: 1)
|
236
|
-
end
|
237
|
-
Author.delete_all(history_user_id: 1)
|
238
|
-
expect(AuthorHistory.count).to eq 3
|
239
|
-
expect(AuthorHistory.current.count).to eq 0
|
240
|
-
expect(AuthorHistory.where.not(history_ended_at: nil).count).to eq 3
|
241
|
-
expect(Author.count).to eq 0
|
242
|
-
Timecop.return
|
243
|
-
end
|
244
|
-
|
245
|
-
it 'includes histories when paranoid' do
|
246
|
-
Timecop.freeze
|
247
|
-
posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
|
248
|
-
Post.delete_all(history_user_id: 1)
|
249
|
-
expect(PostHistory.count).to eq 6
|
250
|
-
expect(PostHistory.current.count).to eq 3
|
251
|
-
expect(PostHistory.current.map(&:deleted_at)).to all(eq Time.now)
|
252
|
-
expect(PostHistory.current.map(&:history_user_id)).to all(eq 1)
|
253
|
-
expect(PostHistory.where(deleted_at: nil).where.not(history_ended_at: nil).count).to eq 3
|
254
|
-
expect(PostHistory.where(history_ended_at: nil).count).to eq 3
|
255
|
-
expect(Post.count).to eq 0
|
256
|
-
Timecop.return
|
257
|
-
end
|
258
|
-
|
259
|
-
it 'allows delete_all_without_history' do
|
260
|
-
authors = 3.times.map do
|
261
|
-
Author.create(full_name: 'Brett', history_user_id: 1)
|
262
|
-
end
|
263
|
-
Author.all.delete_all_without_history
|
264
|
-
expect(AuthorHistory.current.count).to eq 3
|
265
|
-
expect(Author.count).to eq 0
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
context '#destroy_all' do
|
270
|
-
it 'includes histories' do
|
271
|
-
Timecop.freeze
|
272
|
-
posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
|
273
|
-
Post.destroy_all(history_user_id: 1)
|
274
|
-
expect(PostHistory.count).to eq 6
|
275
|
-
expect(PostHistory.current.count).to eq 3
|
276
|
-
expect(PostHistory.current.map(&:deleted_at)).to all(eq Time.now)
|
277
|
-
expect(PostHistory.current.map(&:history_user_id)).to all(eq 1)
|
278
|
-
expect(PostHistory.where(deleted_at: nil).where.not(history_ended_at: nil).count).to eq 3
|
279
|
-
expect(PostHistory.where(history_ended_at: nil).count).to eq 3
|
280
|
-
expect(Post.count).to eq 0
|
281
|
-
Timecop.return
|
282
|
-
end
|
283
|
-
|
284
|
-
it 'destroys without histories' do
|
285
|
-
Timecop.freeze
|
286
|
-
posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
|
287
|
-
Post.all.destroy_all_without_history
|
288
|
-
expect(PostHistory.count).to eq 3
|
289
|
-
expect(PostHistory.current.count).to eq 3
|
290
|
-
expect(Post.count).to eq 0
|
291
|
-
Timecop.return
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
context 'When Safe mode' do
|
297
|
-
it 'creates history without history_user_id' do
|
298
|
-
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')
|
299
|
-
|
300
|
-
post = SafePost.create(
|
301
|
-
title: 'Post 1',
|
302
|
-
body: 'Great post',
|
303
|
-
author_id: 1
|
304
|
-
)
|
305
|
-
expect(post.errors.to_h.keys).to be_empty
|
306
|
-
expect(post).to be_persisted
|
307
|
-
expect(post.histories.count).to eq 1
|
308
|
-
expect(post.histories.first.history_user_id).to be_nil
|
309
|
-
end
|
310
|
-
|
311
|
-
it 'creates history with history_user_id' do
|
312
|
-
expect(Rollbar).to_not receive(:error)
|
313
|
-
|
314
|
-
post = SafePost.create(
|
315
|
-
title: 'Post 1',
|
316
|
-
body: 'Great post',
|
317
|
-
author_id: 1,
|
318
|
-
history_user_id: user.id
|
319
|
-
)
|
320
|
-
expect(post.errors.to_h.keys).to be_empty
|
321
|
-
expect(post).to be_persisted
|
322
|
-
expect(post.histories.count).to eq 1
|
323
|
-
expect(post.histories.first.history_user_id).to eq user.id
|
324
|
-
end
|
325
|
-
|
326
|
-
it 'skips history creation if desired' do
|
327
|
-
post = SafePost.new(
|
328
|
-
title: 'Post 1',
|
329
|
-
body: 'Great post',
|
330
|
-
author_id: 1
|
331
|
-
)
|
332
|
-
|
333
|
-
post.save_without_history
|
334
|
-
expect(post).to be_persisted
|
335
|
-
expect(post.histories.count).to eq 0
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
context 'When Silent mode' do
|
340
|
-
it 'creates history without history_user_id' do
|
341
|
-
expect(Rollbar).to_not receive(:error)
|
342
|
-
|
343
|
-
post = SilentPost.create(
|
344
|
-
title: 'Post 1',
|
345
|
-
body: 'Great post',
|
346
|
-
author_id: 1
|
347
|
-
)
|
348
|
-
expect(post.errors.to_h.keys).to be_empty
|
349
|
-
expect(post).to be_persisted
|
350
|
-
expect(post.histories.count).to eq 1
|
351
|
-
expect(post.histories.first.history_user_id).to be_nil
|
352
|
-
|
353
|
-
post.update(title: 'New Title')
|
354
|
-
post.reload
|
355
|
-
expect(post.title).to eq 'New Title' # No error was raised
|
356
|
-
end
|
357
|
-
|
358
|
-
it 'creates history with history_user_id' do
|
359
|
-
expect(Rollbar).to_not receive(:error)
|
360
|
-
|
361
|
-
post = SilentPost.create(
|
362
|
-
title: 'Post 1',
|
363
|
-
body: 'Great post',
|
364
|
-
author_id: 1,
|
365
|
-
history_user_id: user.id
|
366
|
-
)
|
367
|
-
expect(post.errors.to_h.keys).to be_empty
|
368
|
-
expect(post).to be_persisted
|
369
|
-
expect(post.histories.count).to eq 1
|
370
|
-
expect(post.histories.first.history_user_id).to eq user.id
|
371
|
-
end
|
372
|
-
|
373
|
-
it 'skips history creation if desired' do
|
374
|
-
post = SilentPost.new(
|
375
|
-
title: 'Post 1',
|
376
|
-
body: 'Great post',
|
377
|
-
author_id: 1
|
378
|
-
)
|
379
|
-
|
380
|
-
post.save_without_history
|
381
|
-
expect(post).to be_persisted
|
382
|
-
expect(post.histories.count).to eq 0
|
383
|
-
end
|
384
|
-
end
|
385
|
-
it 'can override without history_user_id' do
|
386
|
-
expect do
|
387
|
-
post = Post.new(
|
388
|
-
title: 'Post 1',
|
389
|
-
body: 'Great post',
|
390
|
-
author_id: 1
|
391
|
-
)
|
392
|
-
|
393
|
-
post.save_without_history
|
394
|
-
end.to_not raise_error
|
395
|
-
end
|
396
|
-
|
397
|
-
it 'can override without history_user_id' do
|
398
|
-
expect do
|
399
|
-
post = Post.new(
|
400
|
-
title: 'Post 1',
|
401
|
-
body: 'Great post',
|
402
|
-
author_id: 1
|
403
|
-
)
|
404
|
-
|
405
|
-
post.save_without_history!
|
406
|
-
end.to_not raise_error
|
407
|
-
end
|
408
|
-
|
409
|
-
it 'does not record histories when main model fails to save' do
|
410
|
-
class Post
|
411
|
-
after_save :raise_error, prepend: true
|
412
|
-
|
413
|
-
def raise_error
|
414
|
-
raise 'Oh no, db issue!'
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
expect { create_post }.to raise_error
|
419
|
-
expect(Post.count).to be 0
|
420
|
-
expect(PostHistory.count).to be 0
|
421
|
-
|
422
|
-
Post.skip_callback(:save, :after, :raise_error)
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
describe 'Scopes' do
|
427
|
-
it 'finds current histories' do
|
428
|
-
post1 = create_post
|
429
|
-
post1.update(title: 'Better title')
|
430
|
-
|
431
|
-
post2 = create_post
|
432
|
-
post2.update(title: 'Better title')
|
433
|
-
|
434
|
-
expect(PostHistory.current.pluck(:title)).to all eq 'Better title'
|
435
|
-
expect(post1.current_history.title).to eq 'Better title'
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
describe 'Associations' do
|
440
|
-
it 'names associated records' do
|
441
|
-
post1 = create_post
|
442
|
-
expect(post1.histories.first).to be_a(PostHistory)
|
443
|
-
|
444
|
-
expect(post1.histories.first.post).to be(post1)
|
445
|
-
|
446
|
-
author1 = create_author
|
447
|
-
expect(author1.histories.first).to be_a(AuthorHistory)
|
448
|
-
|
449
|
-
expect(author1.histories.first.author).to be(author1)
|
450
|
-
end
|
451
|
-
end
|
452
|
-
|
453
|
-
describe 'Histories' do
|
454
|
-
it 'does not allow direct updates of histories' do
|
455
|
-
post1 = create_post
|
456
|
-
hist1 = post1.histories.first
|
457
|
-
|
458
|
-
expect(hist1.update(title: 'A different title')).to be false
|
459
|
-
expect(hist1.reload.title).to eq post1.title
|
460
|
-
|
461
|
-
expect(hist1.update!(title: 'A different title')).to be false
|
462
|
-
expect(hist1.reload.title).to eq post1.title
|
463
|
-
|
464
|
-
hist1.title = 'A different title'
|
465
|
-
expect(hist1.save).to be false
|
466
|
-
expect(hist1.reload.title).to eq post1.title
|
467
|
-
|
468
|
-
hist1.title = 'A different title'
|
469
|
-
expect(hist1.save!).to be false
|
470
|
-
expect(hist1.reload.title).to eq post1.title
|
471
|
-
end
|
472
|
-
|
473
|
-
it 'does not allow destroys of histories' do
|
474
|
-
post1 = create_post
|
475
|
-
hist1 = post1.histories.first
|
476
|
-
original_history_count = post1.histories.count
|
477
|
-
|
478
|
-
expect(hist1.destroy).to be false
|
479
|
-
expect(hist1.destroy!).to be false
|
480
|
-
|
481
|
-
expect(post1.histories.count).to be original_history_count
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
describe 'Deletion' do
|
486
|
-
it 'records deleted_at and history_user_id on primary and history if you use acts_as_paranoid' do
|
487
|
-
post = Post.create(
|
488
|
-
title: 'Post 1',
|
489
|
-
body: 'Great post',
|
490
|
-
author_id: 1,
|
491
|
-
history_user_id: user.id
|
492
|
-
)
|
493
|
-
|
494
|
-
expect do
|
495
|
-
post.destroy(history_user_id: 2)
|
496
|
-
end.to change {
|
497
|
-
PostHistory.count
|
498
|
-
}.by 1
|
499
|
-
|
500
|
-
expect(Post.unscoped.where.not(deleted_at: nil).count).to eq 1
|
501
|
-
expect(Post.unscoped.where(deleted_at: nil).count).to eq 0
|
502
|
-
expect(PostHistory.where.not(deleted_at: nil).count).to eq 1
|
503
|
-
expect(PostHistory.last.history_user_id).to eq 2
|
504
|
-
end
|
505
|
-
|
506
|
-
it 'works with Historiographer::Safe' do
|
507
|
-
post = SafePost.create(title: 'HELLO', body: 'YO', author_id: 1)
|
508
|
-
|
509
|
-
expect do
|
510
|
-
post.destroy
|
511
|
-
end.to_not raise_error
|
512
|
-
|
513
|
-
expect(SafePost.count).to eq 0
|
514
|
-
expect(post.deleted_at).to_not be_nil
|
515
|
-
expect(SafePostHistory.count).to eq 2
|
516
|
-
expect(SafePostHistory.current.last.deleted_at).to eq post.deleted_at
|
517
|
-
|
518
|
-
post2 = SafePost.create(title: 'HELLO', body: 'YO', author_id: 1)
|
519
|
-
|
520
|
-
expect do
|
521
|
-
post2.destroy!
|
522
|
-
end.to_not raise_error
|
523
|
-
|
524
|
-
expect(SafePost.count).to eq 0
|
525
|
-
expect(post2.deleted_at).to_not be_nil
|
526
|
-
expect(SafePostHistory.count).to eq 4
|
527
|
-
expect(SafePostHistory.current.where(safe_post_id: post2.id).last.deleted_at).to eq post2.deleted_at
|
528
|
-
end
|
529
|
-
end
|
530
|
-
|
531
|
-
describe 'Scopes' do
|
532
|
-
it 'finds current' do
|
533
|
-
post = create_post
|
534
|
-
post.update(title: 'New Title')
|
535
|
-
post.update(title: 'New Title 2')
|
536
|
-
|
537
|
-
expect(PostHistory.current.count).to be 1
|
538
|
-
end
|
539
|
-
end
|
540
|
-
|
541
|
-
describe 'User associations' do
|
542
|
-
it 'links to user' do
|
543
|
-
post = create_post
|
544
|
-
author = create_author
|
545
|
-
|
546
|
-
expect(post.current_history.user.name).to eq username
|
547
|
-
expect(author.current_history.user.name).to eq username
|
548
|
-
end
|
549
|
-
end
|
550
|
-
|
551
|
-
describe 'Migrations with compound indexes' do
|
552
|
-
it 'supports renaming compound indexes and migrating them to history tables' do
|
553
|
-
indices_sql = "
|
554
|
-
SELECT
|
555
|
-
DISTINCT(
|
556
|
-
ARRAY_TO_STRING(ARRAY(
|
557
|
-
SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
|
558
|
-
FROM generate_subscripts(idx.indkey, 1) as k
|
559
|
-
ORDER BY k
|
560
|
-
), ',')
|
561
|
-
) as indkey_names
|
562
|
-
FROM pg_class t,
|
563
|
-
pg_class i,
|
564
|
-
pg_index idx,
|
565
|
-
pg_attribute a,
|
566
|
-
pg_am am
|
567
|
-
WHERE t.oid = idx.indrelid
|
568
|
-
AND i.oid = idx.indexrelid
|
569
|
-
AND a.attrelid = t.oid
|
570
|
-
AND a.attnum = ANY(idx.indkey)
|
571
|
-
AND t.relkind = 'r'
|
572
|
-
AND t.relname = ?;
|
573
|
-
"
|
574
|
-
|
575
|
-
indices_query_array = [indices_sql, :thing_with_compound_index_histories]
|
576
|
-
indices_sanitized_query = ThingWithCompoundIndexHistory.send(:sanitize_sql_array, indices_query_array)
|
577
|
-
|
578
|
-
indexes = ThingWithCompoundIndexHistory.connection.execute(indices_sanitized_query).to_a.map(&:values).flatten.map { |i| i.split(',') }
|
579
|
-
|
580
|
-
expect(indexes).to include(['history_started_at'])
|
581
|
-
expect(indexes).to include(['history_ended_at'])
|
582
|
-
expect(indexes).to include(['history_user_id'])
|
583
|
-
expect(indexes).to include(['id'])
|
584
|
-
expect(indexes).to include(%w[key value])
|
585
|
-
expect(indexes).to include(['thing_with_compound_index_id'])
|
586
|
-
end
|
587
|
-
end
|
588
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
ENV["HISTORIOGRAPHY_ENV"] = "test"
|
2
|
-
|
3
|
-
require_relative "../init.rb"
|
4
|
-
require "ostruct"
|
5
|
-
require "factory_bot"
|
6
|
-
|
7
|
-
FactoryBot.definition_file_paths = %w{./factories ./spec/factories}
|
8
|
-
FactoryBot.find_definitions
|
9
|
-
|
10
|
-
RSpec.configure do |config|
|
11
|
-
config.expect_with :rspec do |expectations|
|
12
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
13
|
-
end
|
14
|
-
|
15
|
-
config.mock_with :rspec do |mocks|
|
16
|
-
mocks.verify_partial_doubles = true
|
17
|
-
end
|
18
|
-
|
19
|
-
config.filter_run :focus
|
20
|
-
config.run_all_when_everything_filtered = true
|
21
|
-
|
22
|
-
config.example_status_persistence_file_path = "spec/examples.txt"
|
23
|
-
|
24
|
-
if config.files_to_run.one?
|
25
|
-
config.default_formatter = 'doc'
|
26
|
-
end
|
27
|
-
|
28
|
-
config.profile_examples = 10
|
29
|
-
|
30
|
-
config.order = :random
|
31
|
-
|
32
|
-
Kernel.srand config.seed
|
33
|
-
|
34
|
-
config.before(:suite) do
|
35
|
-
DatabaseCleaner.strategy = :transaction
|
36
|
-
DatabaseCleaner.clean_with(:truncation)
|
37
|
-
end
|
38
|
-
|
39
|
-
config.around(:each) do |example|
|
40
|
-
DatabaseCleaner.cleaning do
|
41
|
-
example.run
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
config.before(:each, :logsql) do
|
46
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
47
|
-
end
|
48
|
-
|
49
|
-
config.after(:each, :logsql) do
|
50
|
-
ActiveRecord::Base.logger = nil
|
51
|
-
end
|
52
|
-
end
|