historiographer 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +9 -0
- data/README.md +18 -0
- data/VERSION +1 -1
- data/historiographer.gemspec +9 -3
- data/lib/historiographer.rb +4 -0
- data/lib/historiographer/history.rb +1 -5
- data/lib/historiographer/relation.rb +94 -0
- data/spec/db/migrate/20191024203106_create_thing_without_history.rb +7 -0
- data/spec/db/schema.rb +5 -1
- data/spec/factories/post.rb +7 -0
- data/spec/historiographer_spec.rb +148 -109
- data/spec/spec_helper.rb +12 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cceab0e39bc86b8e410a55d5c5b23556ad8ecf543fae2720d0761cbfc9070b1d
|
4
|
+
data.tar.gz: 70fd87e0ecf2fbf6829a50ac315746bced3e2533e0a3664ce005c237557265bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e77e1c8d9dc96c8d6db182b568529fc1d16e1947dc6e16a1234109a90ad3f02229ec9532ba560ea56246fa79d9ccc928801a885a9a6b5a14d2d132202d3cb220
|
7
|
+
data.tar.gz: 9e94ed498ca13062259f8031bfbd529ae516261e7a08e46a32e73bec86a066df4d9d83e1ea70754a7260c0f5fa38a933bcc4ce1431cffbd0a2944fbd3f21c686
|
data/Gemfile
CHANGED
@@ -4,6 +4,7 @@ ruby "2.6.3"
|
|
4
4
|
gem "activerecord", "~> 5.1"
|
5
5
|
gem "activesupport"
|
6
6
|
gem "rollbar"
|
7
|
+
gem "activerecord-import"
|
7
8
|
|
8
9
|
group :development, :test do
|
9
10
|
gem "pg"
|
@@ -26,4 +27,5 @@ group :test do
|
|
26
27
|
gem "guard"
|
27
28
|
gem "guard-rspec"
|
28
29
|
gem "database_cleaner"
|
30
|
+
gem "factory_bot_rails"
|
29
31
|
end
|
data/Gemfile.lock
CHANGED
@@ -37,6 +37,8 @@ GEM
|
|
37
37
|
activemodel (= 5.2.3)
|
38
38
|
activesupport (= 5.2.3)
|
39
39
|
arel (>= 9.0)
|
40
|
+
activerecord-import (1.0.2)
|
41
|
+
activerecord (>= 3.2)
|
40
42
|
activesupport (5.2.3)
|
41
43
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
42
44
|
i18n (>= 0.7, < 2)
|
@@ -54,6 +56,11 @@ GEM
|
|
54
56
|
diff-lcs (1.3)
|
55
57
|
docile (1.3.2)
|
56
58
|
erubi (1.8.0)
|
59
|
+
factory_bot (5.1.1)
|
60
|
+
activesupport (>= 4.2.0)
|
61
|
+
factory_bot_rails (5.1.1)
|
62
|
+
factory_bot (~> 5.1.0)
|
63
|
+
railties (>= 4.2.0)
|
57
64
|
faraday (0.9.2)
|
58
65
|
multipart-post (>= 1.2, < 3)
|
59
66
|
ffi (1.11.1)
|
@@ -178,9 +185,11 @@ PLATFORMS
|
|
178
185
|
|
179
186
|
DEPENDENCIES
|
180
187
|
activerecord (~> 5.1)
|
188
|
+
activerecord-import
|
181
189
|
activesupport
|
182
190
|
bundler (~> 1.0)
|
183
191
|
database_cleaner
|
192
|
+
factory_bot_rails
|
184
193
|
guard
|
185
194
|
guard-rspec
|
186
195
|
jeweler!
|
data/README.md
CHANGED
@@ -80,6 +80,24 @@ Additionally it will add indices on:
|
|
80
80
|
- The same columns that had indices on the original model (e.g. `enabled`)
|
81
81
|
- `history_started_at`, `history_ended_at`, and `history_user_id`
|
82
82
|
|
83
|
+
### what to do when generated index names are too long
|
84
|
+
|
85
|
+
Sometimes the generated index names are too long. Just like with standard Rails migrations, you can override the name of the index to fix this problem. To do so, use the `index_names` argument to override individual index names:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require "historiographer/postgres_migration"
|
89
|
+
class CreatePostHistories < ActiveRecord::Migration
|
90
|
+
def change
|
91
|
+
create_table :post_histories do |t|
|
92
|
+
t.histories, index_names: {
|
93
|
+
title: "my_index_name",
|
94
|
+
[:compound, :index] => "my_compound_index_name"
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
83
101
|
## models
|
84
102
|
|
85
103
|
The primary model should include `Historiographer`:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
data/historiographer.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: historiographer 1.
|
5
|
+
# stub: historiographer 1.3.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "historiographer".freeze
|
9
|
-
s.version = "1.
|
9
|
+
s.version = "1.3.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["brettshollenberger".freeze]
|
14
|
-
s.date = "2019-
|
14
|
+
s.date = "2019-11-08"
|
15
15
|
s.description = "Creates separate tables for each history table".freeze
|
16
16
|
s.email = "brett.shollenberger@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
"lib/historiographer/history_migration_mysql.rb",
|
39
39
|
"lib/historiographer/mysql_migration.rb",
|
40
40
|
"lib/historiographer/postgres_migration.rb",
|
41
|
+
"lib/historiographer/relation.rb",
|
41
42
|
"lib/historiographer/safe.rb",
|
42
43
|
"spec/db/database.yml",
|
43
44
|
"spec/db/migrate/20161121212228_create_posts.rb",
|
@@ -49,8 +50,10 @@ Gem::Specification.new do |s|
|
|
49
50
|
"spec/db/migrate/20171011194715_create_safe_post_histories.rb",
|
50
51
|
"spec/db/migrate/20191024142304_create_thing_with_compound_index.rb",
|
51
52
|
"spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb",
|
53
|
+
"spec/db/migrate/20191024203106_create_thing_without_history.rb",
|
52
54
|
"spec/db/schema.rb",
|
53
55
|
"spec/examples.txt",
|
56
|
+
"spec/factories/post.rb",
|
54
57
|
"spec/historiographer_spec.rb",
|
55
58
|
"spec/spec_helper.rb"
|
56
59
|
]
|
@@ -66,6 +69,7 @@ Gem::Specification.new do |s|
|
|
66
69
|
s.add_runtime_dependency(%q<activerecord>.freeze, ["~> 5.1"])
|
67
70
|
s.add_runtime_dependency(%q<activesupport>.freeze, [">= 0"])
|
68
71
|
s.add_runtime_dependency(%q<rollbar>.freeze, [">= 0"])
|
72
|
+
s.add_runtime_dependency(%q<activerecord-import>.freeze, [">= 0"])
|
69
73
|
s.add_development_dependency(%q<pg>.freeze, [">= 0"])
|
70
74
|
s.add_development_dependency(%q<pry>.freeze, [">= 0"])
|
71
75
|
s.add_development_dependency(%q<mysql2>.freeze, ["= 0.4.10"])
|
@@ -80,6 +84,7 @@ Gem::Specification.new do |s|
|
|
80
84
|
s.add_dependency(%q<activerecord>.freeze, ["~> 5.1"])
|
81
85
|
s.add_dependency(%q<activesupport>.freeze, [">= 0"])
|
82
86
|
s.add_dependency(%q<rollbar>.freeze, [">= 0"])
|
87
|
+
s.add_dependency(%q<activerecord-import>.freeze, [">= 0"])
|
83
88
|
s.add_dependency(%q<pg>.freeze, [">= 0"])
|
84
89
|
s.add_dependency(%q<pry>.freeze, [">= 0"])
|
85
90
|
s.add_dependency(%q<mysql2>.freeze, ["= 0.4.10"])
|
@@ -95,6 +100,7 @@ Gem::Specification.new do |s|
|
|
95
100
|
s.add_dependency(%q<activerecord>.freeze, ["~> 5.1"])
|
96
101
|
s.add_dependency(%q<activesupport>.freeze, [">= 0"])
|
97
102
|
s.add_dependency(%q<rollbar>.freeze, [">= 0"])
|
103
|
+
s.add_dependency(%q<activerecord-import>.freeze, [">= 0"])
|
98
104
|
s.add_dependency(%q<pg>.freeze, [">= 0"])
|
99
105
|
s.add_dependency(%q<pry>.freeze, [">= 0"])
|
100
106
|
s.add_dependency(%q<mysql2>.freeze, ["= 0.4.10"])
|
data/lib/historiographer.rb
CHANGED
@@ -66,11 +66,7 @@ module Historiographer
|
|
66
66
|
# access to a current scope, returning
|
67
67
|
# the most recent history.
|
68
68
|
#
|
69
|
-
|
70
|
-
# in the off chance this invariant is broken, this method is
|
71
|
-
# guaranteed to only return an array containing the most recent history.
|
72
|
-
#
|
73
|
-
scope :current, -> { where(history_ended_at: nil).order(id: :desc).limit(1) }
|
69
|
+
scope :current, -> { where(history_ended_at: nil).order(id: :desc) }
|
74
70
|
|
75
71
|
#
|
76
72
|
# A History class will be linked to the user
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Historiographer
|
2
|
+
module Relation
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def has_histories?
|
6
|
+
self.klass.respond_to?(:history_class)
|
7
|
+
end
|
8
|
+
|
9
|
+
def update_all_without_history(updates)
|
10
|
+
update_all(updates, false)
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_all(updates, histories=true)
|
14
|
+
unless histories
|
15
|
+
super(updates)
|
16
|
+
else
|
17
|
+
updates.symbolize_keys!
|
18
|
+
|
19
|
+
ActiveRecord::Base.transaction do
|
20
|
+
super(updates.except(:history_user_id))
|
21
|
+
now = UTC.now
|
22
|
+
records = self.reload
|
23
|
+
history_class = records.klass.history_class
|
24
|
+
|
25
|
+
records.new.send(:history_user_absent_action) if updates[:history_user_id].nil?
|
26
|
+
history_user_id = updates[:history_user_id]
|
27
|
+
|
28
|
+
new_histories = records.map do |record|
|
29
|
+
attrs = record.attributes.clone
|
30
|
+
foreign_key = history_class.history_foreign_key
|
31
|
+
|
32
|
+
now = UTC.now
|
33
|
+
attrs.merge!(foreign_key => attrs["id"], history_started_at: now, history_user_id: history_user_id)
|
34
|
+
|
35
|
+
attrs = attrs.except("id")
|
36
|
+
|
37
|
+
record.histories.build(attrs)
|
38
|
+
end
|
39
|
+
|
40
|
+
current_histories = history_class.current.where("#{history_class.history_foreign_key} IN (?)", records.map(&:id))
|
41
|
+
|
42
|
+
current_histories.update_all(history_ended_at: now)
|
43
|
+
|
44
|
+
history_class.import new_histories
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_all_without_history
|
50
|
+
delete_all(nil, false)
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete_all(options={}, histories=true)
|
54
|
+
unless histories
|
55
|
+
super()
|
56
|
+
else
|
57
|
+
ActiveRecord::Base.transaction do
|
58
|
+
records = self
|
59
|
+
history_class = records.first.class.history_class
|
60
|
+
history_user_id = options[:history_user_id]
|
61
|
+
records.first.send(:history_user_absent_action) if history_user_id.nil?
|
62
|
+
now = UTC.now
|
63
|
+
|
64
|
+
history_class.current.where("#{history_class.history_foreign_key} IN (?)", records.map(&:id)).update_all(history_ended_at: now)
|
65
|
+
|
66
|
+
if records.first.respond_to?(:paranoia_destroy)
|
67
|
+
new_histories = records.map do |record|
|
68
|
+
attrs = record.attributes.clone
|
69
|
+
foreign_key = history_class.history_foreign_key
|
70
|
+
|
71
|
+
now = UTC.now
|
72
|
+
attrs.merge!(foreign_key => attrs["id"], history_started_at: now, history_user_id: history_user_id, deleted_at: now)
|
73
|
+
|
74
|
+
attrs = attrs.except("id")
|
75
|
+
|
76
|
+
record.histories.build(attrs)
|
77
|
+
end
|
78
|
+
history_class.import new_histories
|
79
|
+
end
|
80
|
+
|
81
|
+
super()
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def destroy_all_without_history
|
87
|
+
records.each(&:destroy_without_history).tap { reset }
|
88
|
+
end
|
89
|
+
|
90
|
+
def destroy_all(history_user_id: nil)
|
91
|
+
records.each { |r| r.destroy(history_user_id: history_user_id) }.tap { reset }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/spec/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 2019_10_24_203106) do
|
14
14
|
|
15
15
|
# These are extensions that must be enabled in order to support this database
|
16
16
|
enable_extension "plpgsql"
|
@@ -137,6 +137,10 @@ ActiveRecord::Schema.define(version: 2019_10_24_142352) do
|
|
137
137
|
t.index ["key", "value"], name: "idx_key_value"
|
138
138
|
end
|
139
139
|
|
140
|
+
create_table "thing_without_histories", force: :cascade do |t|
|
141
|
+
t.string "name"
|
142
|
+
end
|
143
|
+
|
140
144
|
create_table "users", force: :cascade do |t|
|
141
145
|
t.string "name"
|
142
146
|
end
|
@@ -2,6 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
class Post < ActiveRecord::Base
|
4
4
|
include Historiographer
|
5
|
+
acts_as_paranoid
|
5
6
|
end
|
6
7
|
|
7
8
|
class PostHistory < ActiveRecord::Base
|
@@ -31,6 +32,9 @@ end
|
|
31
32
|
class ThingWithCompoundIndexHistory < ActiveRecord::Base
|
32
33
|
end
|
33
34
|
|
35
|
+
class ThingWithoutHistory < ActiveRecord::Base
|
36
|
+
end
|
37
|
+
|
34
38
|
describe Historiographer do
|
35
39
|
before(:all) do
|
36
40
|
@now = Timecop.freeze
|
@@ -129,6 +133,150 @@ describe Historiographer do
|
|
129
133
|
)
|
130
134
|
end
|
131
135
|
|
136
|
+
context "When directly hitting the database via SQL" do
|
137
|
+
context "#update_all" do
|
138
|
+
it "still updates histories" do
|
139
|
+
FactoryBot.create_list(:post, 3, history_user_id: 1)
|
140
|
+
|
141
|
+
posts = Post.all
|
142
|
+
expect(posts.count).to eq 3
|
143
|
+
expect(PostHistory.count).to eq 3
|
144
|
+
expect(posts.map(&:histories).map(&:count)).to all (eq 1)
|
145
|
+
|
146
|
+
posts.update_all(title: "My New Post Title", history_user_id: 1)
|
147
|
+
|
148
|
+
expect(PostHistory.count).to eq 6
|
149
|
+
expect(PostHistory.current.count).to eq 3
|
150
|
+
expect(posts.map(&:histories).map(&:count)).to all(eq 2)
|
151
|
+
expect(posts.map(&:current_history).map(&:title)).to all (eq "My New Post Title")
|
152
|
+
expect(Post.all).to respond_to :has_histories?
|
153
|
+
|
154
|
+
# It can update by sub-query
|
155
|
+
Post.where(id: [posts.first.id, posts.last.id]).update_all(title: "Brett's Post", history_user_id: 1)
|
156
|
+
posts = Post.all.reload.order(:id)
|
157
|
+
expect(posts.first.histories.count).to eq 3
|
158
|
+
expect(posts.second.histories.count).to eq 2
|
159
|
+
expect(posts.third.histories.count).to eq 3
|
160
|
+
expect(posts.first.title).to eq "Brett's Post"
|
161
|
+
expect(posts.second.title).to eq "My New Post Title"
|
162
|
+
expect(posts.third.title).to eq "Brett's Post"
|
163
|
+
expect(posts.first.current_history.title).to eq "Brett's Post"
|
164
|
+
expect(posts.second.current_history.title).to eq "My New Post Title"
|
165
|
+
expect(posts.third.current_history.title).to eq "Brett's Post"
|
166
|
+
|
167
|
+
posts.update_all_without_history(title: "Untracked")
|
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
|
+
|
172
|
+
thing1 = ThingWithoutHistory.create(name: "Thing 1")
|
173
|
+
thing2 = ThingWithoutHistory.create(name: "Thing 2")
|
174
|
+
|
175
|
+
ThingWithoutHistory.all.update_all(name: "Thing 3")
|
176
|
+
|
177
|
+
expect(ThingWithoutHistory.all.map(&:name)).to all(eq "Thing 3")
|
178
|
+
expect(ThingWithoutHistory.all).to_not respond_to :has_histories?
|
179
|
+
expect(ThingWithoutHistory.all).to_not respond_to :update_all_without_history
|
180
|
+
expect(ThingWithoutHistory.all).to_not respond_to :delete_all_without_history
|
181
|
+
end
|
182
|
+
|
183
|
+
it "respects safety" do
|
184
|
+
FactoryBot.create_list(:post, 3, history_user_id: 1)
|
185
|
+
|
186
|
+
posts = Post.all
|
187
|
+
expect(posts.count).to eq 3
|
188
|
+
expect(PostHistory.count).to eq 3
|
189
|
+
expect(posts.map(&:histories).map(&:count)).to all (eq 1)
|
190
|
+
|
191
|
+
expect {
|
192
|
+
posts.update_all(title: "My New Post Title")
|
193
|
+
}.to raise_error
|
194
|
+
|
195
|
+
posts.reload.map(&:title).each do |title|
|
196
|
+
expect(title).to_not eq "My New Post Title"
|
197
|
+
end
|
198
|
+
|
199
|
+
SafePost.create(
|
200
|
+
title: "Post 1",
|
201
|
+
body: "Great post",
|
202
|
+
author_id: 1,
|
203
|
+
)
|
204
|
+
|
205
|
+
safe_posts = SafePost.all
|
206
|
+
|
207
|
+
expect {
|
208
|
+
safe_posts.update_all(title: "New One")
|
209
|
+
}.to_not raise_error
|
210
|
+
|
211
|
+
expect(safe_posts.map(&:title)).to all(eq "New One")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context "#delete_all" do
|
216
|
+
it "includes histories when not paranoid" do
|
217
|
+
Timecop.freeze
|
218
|
+
authors = 3.times.map do
|
219
|
+
Author.create(full_name: "Brett", history_user_id: 1)
|
220
|
+
end
|
221
|
+
Author.delete_all(history_user_id: 1)
|
222
|
+
expect(AuthorHistory.count).to eq 3
|
223
|
+
expect(AuthorHistory.current.count).to eq 0
|
224
|
+
expect(AuthorHistory.where.not(history_ended_at: nil).count).to eq 3
|
225
|
+
expect(Author.count).to eq 0
|
226
|
+
Timecop.return
|
227
|
+
end
|
228
|
+
|
229
|
+
it "includes histories when paranoid" do
|
230
|
+
Timecop.freeze
|
231
|
+
posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
|
232
|
+
Post.delete_all(history_user_id: 1)
|
233
|
+
expect(PostHistory.count).to eq 6
|
234
|
+
expect(PostHistory.current.count).to eq 3
|
235
|
+
expect(PostHistory.current.map(&:deleted_at)).to all(eq Time.now)
|
236
|
+
expect(PostHistory.current.map(&:history_user_id)).to all(eq 1)
|
237
|
+
expect(PostHistory.where(deleted_at: nil).where.not(history_ended_at: nil).count).to eq 3
|
238
|
+
expect(PostHistory.where(history_ended_at: nil).count).to eq 3
|
239
|
+
expect(Post.count).to eq 0
|
240
|
+
Timecop.return
|
241
|
+
end
|
242
|
+
|
243
|
+
it "allows delete_all_without_history" do
|
244
|
+
authors = 3.times.map do
|
245
|
+
Author.create(full_name: "Brett", history_user_id: 1)
|
246
|
+
end
|
247
|
+
Author.all.delete_all_without_history
|
248
|
+
expect(AuthorHistory.current.count).to eq 3
|
249
|
+
expect(Author.count).to eq 0
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context "#destroy_all" do
|
254
|
+
it "includes histories" do
|
255
|
+
Timecop.freeze
|
256
|
+
posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
|
257
|
+
Post.destroy_all(history_user_id: 1)
|
258
|
+
expect(PostHistory.count).to eq 6
|
259
|
+
expect(PostHistory.current.count).to eq 3
|
260
|
+
expect(PostHistory.current.map(&:deleted_at)).to all(eq Time.now)
|
261
|
+
expect(PostHistory.current.map(&:history_user_id)).to all(eq 1)
|
262
|
+
expect(PostHistory.where(deleted_at: nil).where.not(history_ended_at: nil).count).to eq 3
|
263
|
+
expect(PostHistory.where(history_ended_at: nil).count).to eq 3
|
264
|
+
expect(Post.count).to eq 0
|
265
|
+
Timecop.return
|
266
|
+
end
|
267
|
+
|
268
|
+
it "destroys without histories" do
|
269
|
+
Timecop.freeze
|
270
|
+
posts = FactoryBot.create_list(:post, 3, history_user_id: 1)
|
271
|
+
Post.all.destroy_all_without_history
|
272
|
+
expect(PostHistory.count).to eq 3
|
273
|
+
expect(PostHistory.current.count).to eq 3
|
274
|
+
expect(Post.count).to eq 0
|
275
|
+
Timecop.return
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
132
280
|
context "When Safe mode" do
|
133
281
|
it "creates history without history_user_id" do
|
134
282
|
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")
|
@@ -274,20 +422,6 @@ describe Historiographer do
|
|
274
422
|
|
275
423
|
describe "Deletion" do
|
276
424
|
it "records deleted_at and history_user_id on primary and history if you use acts_as_paranoid" do
|
277
|
-
post = create_post
|
278
|
-
|
279
|
-
expect {
|
280
|
-
post.destroy(history_user_id: 2)
|
281
|
-
}.to_not change {
|
282
|
-
PostHistory.count
|
283
|
-
}
|
284
|
-
|
285
|
-
expect(PostHistory.last.history_ended_at).to_not be_nil
|
286
|
-
expect(PostHistory.last.deleted_at).to be_nil
|
287
|
-
class Post
|
288
|
-
acts_as_paranoid
|
289
|
-
end
|
290
|
-
|
291
425
|
post = Post.create(
|
292
426
|
title: "Post 1",
|
293
427
|
body: "Great post",
|
@@ -316,101 +450,6 @@ describe Historiographer do
|
|
316
450
|
|
317
451
|
expect(PostHistory.current.count).to be 1
|
318
452
|
end
|
319
|
-
|
320
|
-
it "finds current even when the db is updated in an invalid way" do
|
321
|
-
postgresql = <<-SQL
|
322
|
-
INSERT INTO post_histories (
|
323
|
-
title,
|
324
|
-
body,
|
325
|
-
post_id,
|
326
|
-
author_id,
|
327
|
-
created_at,
|
328
|
-
updated_at,
|
329
|
-
history_started_at,
|
330
|
-
history_ended_at
|
331
|
-
) VALUES (
|
332
|
-
'Post 1',
|
333
|
-
'Text',
|
334
|
-
1,
|
335
|
-
1,
|
336
|
-
now(),
|
337
|
-
now(),
|
338
|
-
now() - INTERVAL '1 day',
|
339
|
-
NULL
|
340
|
-
), (
|
341
|
-
'Post 1',
|
342
|
-
'Different text',
|
343
|
-
1,
|
344
|
-
1,
|
345
|
-
now(),
|
346
|
-
now(),
|
347
|
-
now() - INTERVAL '12 hours',
|
348
|
-
NULL
|
349
|
-
), (
|
350
|
-
'Post 1',
|
351
|
-
'Even more different text',
|
352
|
-
1,
|
353
|
-
1,
|
354
|
-
now(),
|
355
|
-
now(),
|
356
|
-
now() - INTERVAL '12 hours',
|
357
|
-
NULL
|
358
|
-
)
|
359
|
-
SQL
|
360
|
-
|
361
|
-
mysql = <<-SQL
|
362
|
-
INSERT INTO post_histories (
|
363
|
-
title,
|
364
|
-
body,
|
365
|
-
post_id,
|
366
|
-
author_id,
|
367
|
-
created_at,
|
368
|
-
updated_at,
|
369
|
-
history_started_at,
|
370
|
-
history_ended_at
|
371
|
-
) VALUES (
|
372
|
-
'Post 1',
|
373
|
-
'Text',
|
374
|
-
1,
|
375
|
-
1,
|
376
|
-
now(),
|
377
|
-
now(),
|
378
|
-
now() - INTERVAL 1 day,
|
379
|
-
NULL
|
380
|
-
), (
|
381
|
-
'Post 1',
|
382
|
-
'Different text',
|
383
|
-
1,
|
384
|
-
1,
|
385
|
-
now(),
|
386
|
-
now(),
|
387
|
-
now() - INTERVAL 12 hour,
|
388
|
-
NULL
|
389
|
-
), (
|
390
|
-
'Post 1',
|
391
|
-
'Even more different text',
|
392
|
-
1,
|
393
|
-
1,
|
394
|
-
now(),
|
395
|
-
now(),
|
396
|
-
now() - INTERVAL 12 hour,
|
397
|
-
NULL
|
398
|
-
)
|
399
|
-
SQL
|
400
|
-
|
401
|
-
sql = nil
|
402
|
-
case PostHistory.connection.instance_variable_get(:@config)[:adapter]
|
403
|
-
when "mysql2"
|
404
|
-
sql = mysql
|
405
|
-
when "postgresql"
|
406
|
-
sql = postgresql
|
407
|
-
end
|
408
|
-
|
409
|
-
PostHistory.connection.execute(sql)
|
410
|
-
|
411
|
-
expect(PostHistory.current.count).to be 1
|
412
|
-
expect(PostHistory.current.first.body).to eq "Even more different text"
|
413
|
-
end
|
414
453
|
end
|
415
454
|
|
416
455
|
describe "User associations" do
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,10 @@ ENV["HISTORIOGRAPHY_ENV"] = "test"
|
|
2
2
|
|
3
3
|
require_relative "../init.rb"
|
4
4
|
require "ostruct"
|
5
|
+
require "factory_bot"
|
6
|
+
|
7
|
+
FactoryBot.definition_file_paths = %w{./factories ./spec/factories}
|
8
|
+
FactoryBot.find_definitions
|
5
9
|
|
6
10
|
RSpec.configure do |config|
|
7
11
|
config.expect_with :rspec do |expectations|
|
@@ -37,4 +41,12 @@ RSpec.configure do |config|
|
|
37
41
|
example.run
|
38
42
|
end
|
39
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
|
40
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: historiographer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- brettshollenberger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activerecord-import
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: pg
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -219,6 +233,7 @@ files:
|
|
219
233
|
- lib/historiographer/history_migration_mysql.rb
|
220
234
|
- lib/historiographer/mysql_migration.rb
|
221
235
|
- lib/historiographer/postgres_migration.rb
|
236
|
+
- lib/historiographer/relation.rb
|
222
237
|
- lib/historiographer/safe.rb
|
223
238
|
- spec/db/database.yml
|
224
239
|
- spec/db/migrate/20161121212228_create_posts.rb
|
@@ -230,8 +245,10 @@ files:
|
|
230
245
|
- spec/db/migrate/20171011194715_create_safe_post_histories.rb
|
231
246
|
- spec/db/migrate/20191024142304_create_thing_with_compound_index.rb
|
232
247
|
- spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb
|
248
|
+
- spec/db/migrate/20191024203106_create_thing_without_history.rb
|
233
249
|
- spec/db/schema.rb
|
234
250
|
- spec/examples.txt
|
251
|
+
- spec/factories/post.rb
|
235
252
|
- spec/historiographer_spec.rb
|
236
253
|
- spec/spec_helper.rb
|
237
254
|
homepage: http://github.com/brettshollenberger/historiographer
|