historiographer 1.1.0 → 1.2.0
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/VERSION +1 -1
- data/historiographer.gemspec +5 -3
- data/lib/historiographer/history_migration.rb +40 -29
- data/spec/db/database.yml +21 -22
- data/spec/db/migrate/20191024142304_create_thing_with_compound_index.rb +10 -0
- data/spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb +11 -0
- data/spec/db/schema.rb +31 -8
- data/spec/historiographer_spec.rb +53 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7cb91195d9d049faf9de52f631cd6c64c46c24e3e0d44fba5ea446197986d98
|
4
|
+
data.tar.gz: 0236e459a57ec0a4105f9dd9a8e933745f98e9942d2919298e97e6cf7a32c3d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ddac0ce23e3b5ded4dc95a6c8d2e5b0870bc95e40e89d5d3fbf2e867511cc6320d23f4ac1562191c025d011161afca0f1c107fcb0c9a64d59799183bb31ef5a
|
7
|
+
data.tar.gz: ac97602293b1488db300d33178bf1c697f69613e3929c62aa5199c2a11a3d6ae07e4d8a1afb1198a8a41761c6ff09726c4fee5570ef17f35721ff5e8ce8756f5
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.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.2.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.2.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-10-
|
14
|
+
s.date = "2019-10-24"
|
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 = [
|
@@ -47,6 +47,8 @@ Gem::Specification.new do |s|
|
|
47
47
|
"spec/db/migrate/20161121212232_create_users.rb",
|
48
48
|
"spec/db/migrate/20171011194624_create_safe_posts.rb",
|
49
49
|
"spec/db/migrate/20171011194715_create_safe_post_histories.rb",
|
50
|
+
"spec/db/migrate/20191024142304_create_thing_with_compound_index.rb",
|
51
|
+
"spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb",
|
50
52
|
"spec/db/schema.rb",
|
51
53
|
"spec/examples.txt",
|
52
54
|
"spec/historiographer_spec.rb",
|
@@ -16,7 +16,8 @@ module Historiographer
|
|
16
16
|
# Will automatically add user_id, history_started_at,
|
17
17
|
# and history_ended_at columns
|
18
18
|
#
|
19
|
-
def histories(except: [], only: [], no_business_columns: false)
|
19
|
+
def histories(except: [], only: [], no_business_columns: false, index_names: {})
|
20
|
+
index_names.symbolize_keys!
|
20
21
|
original_table_name = self.name.gsub(/_histories$/) {}.pluralize
|
21
22
|
foreign_key = original_table_name.singularize.foreign_key
|
22
23
|
|
@@ -32,7 +33,6 @@ module Historiographer
|
|
32
33
|
original_columns.each do |column|
|
33
34
|
opts = {}
|
34
35
|
opts.merge!(column.as_json.clone)
|
35
|
-
# opts.merge!(column.type.as_json.clone)
|
36
36
|
|
37
37
|
send(column.type, column.name, opts.symbolize_keys!)
|
38
38
|
end
|
@@ -41,38 +41,49 @@ module Historiographer
|
|
41
41
|
datetime :history_ended_at
|
42
42
|
integer :history_user_id
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
t.relname,
|
66
|
-
i.relname;
|
67
|
-
SQL
|
44
|
+
indices_sql = %q(
|
45
|
+
SELECT
|
46
|
+
DISTINCT(
|
47
|
+
ARRAY_TO_STRING(ARRAY(
|
48
|
+
SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
|
49
|
+
FROM generate_subscripts(idx.indkey, 1) as k
|
50
|
+
ORDER BY k
|
51
|
+
), ',')
|
52
|
+
) as indkey_names
|
53
|
+
FROM pg_class t,
|
54
|
+
pg_class i,
|
55
|
+
pg_index idx,
|
56
|
+
pg_attribute a,
|
57
|
+
pg_am am
|
58
|
+
WHERE t.oid = idx.indrelid
|
59
|
+
AND i.oid = idx.indexrelid
|
60
|
+
AND a.attrelid = t.oid
|
61
|
+
AND a.attnum = ANY(idx.indkey)
|
62
|
+
AND t.relkind = 'r'
|
63
|
+
AND t.relname = ?;
|
64
|
+
)
|
68
65
|
|
69
66
|
indices_query_array = [indices_sql, original_table_name]
|
70
67
|
indices_sanitized_query = klass.send(:sanitize_sql_array, indices_query_array)
|
71
68
|
|
72
|
-
klass.connection.execute(indices_sanitized_query).to_a.map(&:values).flatten.reject { |i| i == "id" }.
|
73
|
-
|
69
|
+
indexes = klass.connection.execute(indices_sanitized_query).to_a.map(&:values).flatten.reject { |i| i == "id" }.map { |i| i.split(",") }.concat([
|
70
|
+
foreign_key,
|
71
|
+
:history_started_at,
|
72
|
+
:history_ended_at,
|
73
|
+
:history_user_id
|
74
|
+
])
|
75
|
+
|
76
|
+
indexes.each do |index_definition|
|
77
|
+
index_definition = [index_definition].flatten.map(&:to_sym)
|
78
|
+
index_name = index_definition.count == 1 ? index_definition.first : index_definition
|
79
|
+
|
80
|
+
if index_names.key?(index_name)
|
81
|
+
index index_name, name: index_names[index_name]
|
82
|
+
else
|
83
|
+
index index_name
|
84
|
+
end
|
74
85
|
end
|
75
86
|
|
76
87
|
end
|
77
88
|
end
|
78
|
-
end
|
89
|
+
end
|
data/spec/db/database.yml
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# development:
|
7
|
-
# <<: *default
|
8
|
-
|
9
|
-
# test:
|
10
|
-
# adapter: postgresql
|
11
|
-
# database: historiographer_test
|
12
|
-
|
13
|
-
mysql_default: &mysql_default
|
14
|
-
adapter: mysql2
|
15
|
-
encoding: utf8
|
16
|
-
username: root
|
17
|
-
password:
|
18
|
-
host: 127.0.0.1
|
19
|
-
port: 3306
|
1
|
+
default: &default
|
2
|
+
adapter: postgresql
|
3
|
+
prepared_statements: false
|
4
|
+
url: "postgres://localhost/historiographer_development"
|
20
5
|
|
21
6
|
development:
|
22
|
-
<<: *
|
23
|
-
database: historiographer_development
|
7
|
+
<<: *default
|
24
8
|
|
25
9
|
test:
|
26
|
-
|
10
|
+
adapter: postgresql
|
27
11
|
database: historiographer_test
|
12
|
+
# mysql_default: &mysql_default
|
13
|
+
# adapter: mysql2
|
14
|
+
# encoding: utf8
|
15
|
+
# username: root
|
16
|
+
# password:
|
17
|
+
# host: 127.0.0.1
|
18
|
+
# port: 3306
|
19
|
+
|
20
|
+
# development:
|
21
|
+
# <<: *mysql_default
|
22
|
+
# database: historiographer_development
|
23
|
+
|
24
|
+
# test:
|
25
|
+
# <<: *mysql_default
|
26
|
+
# database: historiographer_test
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "historiographer/postgres_migration"
|
2
|
+
class CreateThingWithCompoundIndexHistory < ActiveRecord::Migration[5.2]
|
3
|
+
def change
|
4
|
+
create_table :thing_with_compound_index_histories do |t|
|
5
|
+
t.histories index_names: {
|
6
|
+
[:key, :value] => "idx_history_k_v",
|
7
|
+
:thing_with_compound_index_id => "idx_k_v_histories"
|
8
|
+
}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/spec/db/schema.rb
CHANGED
@@ -10,9 +10,12 @@
|
|
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_142352) do
|
14
14
|
|
15
|
-
|
15
|
+
# These are extensions that must be enabled in order to support this database
|
16
|
+
enable_extension "plpgsql"
|
17
|
+
|
18
|
+
create_table "author_histories", force: :cascade do |t|
|
16
19
|
t.integer "author_id", null: false
|
17
20
|
t.string "full_name", null: false
|
18
21
|
t.text "bio"
|
@@ -29,7 +32,7 @@ ActiveRecord::Schema.define(version: 2017_10_11_194715) do
|
|
29
32
|
t.index ["history_user_id"], name: "index_author_histories_on_history_user_id"
|
30
33
|
end
|
31
34
|
|
32
|
-
create_table "authors",
|
35
|
+
create_table "authors", force: :cascade do |t|
|
33
36
|
t.string "full_name", null: false
|
34
37
|
t.text "bio"
|
35
38
|
t.datetime "deleted_at"
|
@@ -38,7 +41,7 @@ ActiveRecord::Schema.define(version: 2017_10_11_194715) do
|
|
38
41
|
t.index ["deleted_at"], name: "index_authors_on_deleted_at"
|
39
42
|
end
|
40
43
|
|
41
|
-
create_table "post_histories",
|
44
|
+
create_table "post_histories", force: :cascade do |t|
|
42
45
|
t.integer "post_id", null: false
|
43
46
|
t.string "title", null: false
|
44
47
|
t.text "body", null: false
|
@@ -61,7 +64,7 @@ ActiveRecord::Schema.define(version: 2017_10_11_194715) do
|
|
61
64
|
t.index ["post_id"], name: "index_post_histories_on_post_id"
|
62
65
|
end
|
63
66
|
|
64
|
-
create_table "posts",
|
67
|
+
create_table "posts", force: :cascade do |t|
|
65
68
|
t.string "title", null: false
|
66
69
|
t.text "body", null: false
|
67
70
|
t.integer "author_id", null: false
|
@@ -76,7 +79,7 @@ ActiveRecord::Schema.define(version: 2017_10_11_194715) do
|
|
76
79
|
t.index ["live_at"], name: "index_posts_on_live_at"
|
77
80
|
end
|
78
81
|
|
79
|
-
create_table "safe_post_histories",
|
82
|
+
create_table "safe_post_histories", force: :cascade do |t|
|
80
83
|
t.integer "safe_post_id", null: false
|
81
84
|
t.string "title", null: false
|
82
85
|
t.text "body", null: false
|
@@ -99,7 +102,7 @@ ActiveRecord::Schema.define(version: 2017_10_11_194715) do
|
|
99
102
|
t.index ["safe_post_id"], name: "index_safe_post_histories_on_safe_post_id"
|
100
103
|
end
|
101
104
|
|
102
|
-
create_table "safe_posts",
|
105
|
+
create_table "safe_posts", force: :cascade do |t|
|
103
106
|
t.string "title", null: false
|
104
107
|
t.text "body", null: false
|
105
108
|
t.integer "author_id", null: false
|
@@ -114,7 +117,27 @@ ActiveRecord::Schema.define(version: 2017_10_11_194715) do
|
|
114
117
|
t.index ["live_at"], name: "index_safe_posts_on_live_at"
|
115
118
|
end
|
116
119
|
|
117
|
-
create_table "
|
120
|
+
create_table "thing_with_compound_index_histories", force: :cascade do |t|
|
121
|
+
t.integer "thing_with_compound_index_id", null: false
|
122
|
+
t.string "key"
|
123
|
+
t.string "value"
|
124
|
+
t.datetime "history_started_at", null: false
|
125
|
+
t.datetime "history_ended_at"
|
126
|
+
t.integer "history_user_id"
|
127
|
+
t.index ["history_ended_at"], name: "index_thing_with_compound_index_histories_on_history_ended_at"
|
128
|
+
t.index ["history_started_at"], name: "index_thing_with_compound_index_histories_on_history_started_at"
|
129
|
+
t.index ["history_user_id"], name: "index_thing_with_compound_index_histories_on_history_user_id"
|
130
|
+
t.index ["key", "value"], name: "idx_history_k_v"
|
131
|
+
t.index ["thing_with_compound_index_id"], name: "idx_k_v_histories"
|
132
|
+
end
|
133
|
+
|
134
|
+
create_table "thing_with_compound_indices", force: :cascade do |t|
|
135
|
+
t.string "key"
|
136
|
+
t.string "value"
|
137
|
+
t.index ["key", "value"], name: "idx_key_value"
|
138
|
+
end
|
139
|
+
|
140
|
+
create_table "users", force: :cascade do |t|
|
118
141
|
t.string "name"
|
119
142
|
end
|
120
143
|
|
@@ -24,6 +24,13 @@ end
|
|
24
24
|
class User < ActiveRecord::Base
|
25
25
|
end
|
26
26
|
|
27
|
+
class ThingWithCompoundIndex < ActiveRecord::Base
|
28
|
+
include Historiographer
|
29
|
+
end
|
30
|
+
|
31
|
+
class ThingWithCompoundIndexHistory < ActiveRecord::Base
|
32
|
+
end
|
33
|
+
|
27
34
|
describe Historiographer do
|
28
35
|
before(:all) do
|
29
36
|
@now = Timecop.freeze
|
@@ -317,6 +324,8 @@ describe Historiographer do
|
|
317
324
|
body,
|
318
325
|
post_id,
|
319
326
|
author_id,
|
327
|
+
created_at,
|
328
|
+
updated_at,
|
320
329
|
history_started_at,
|
321
330
|
history_ended_at
|
322
331
|
) VALUES (
|
@@ -324,6 +333,8 @@ describe Historiographer do
|
|
324
333
|
'Text',
|
325
334
|
1,
|
326
335
|
1,
|
336
|
+
now(),
|
337
|
+
now(),
|
327
338
|
now() - INTERVAL '1 day',
|
328
339
|
NULL
|
329
340
|
), (
|
@@ -331,6 +342,8 @@ describe Historiographer do
|
|
331
342
|
'Different text',
|
332
343
|
1,
|
333
344
|
1,
|
345
|
+
now(),
|
346
|
+
now(),
|
334
347
|
now() - INTERVAL '12 hours',
|
335
348
|
NULL
|
336
349
|
), (
|
@@ -338,6 +351,8 @@ describe Historiographer do
|
|
338
351
|
'Even more different text',
|
339
352
|
1,
|
340
353
|
1,
|
354
|
+
now(),
|
355
|
+
now(),
|
341
356
|
now() - INTERVAL '12 hours',
|
342
357
|
NULL
|
343
358
|
)
|
@@ -407,4 +422,42 @@ describe Historiographer do
|
|
407
422
|
expect(author.current_history.user.name).to eq username
|
408
423
|
end
|
409
424
|
end
|
425
|
+
|
426
|
+
describe "Migrations with compound indexes" do
|
427
|
+
it "supports renaming compound indexes and migrating them to history tables" do
|
428
|
+
indices_sql = %q(
|
429
|
+
SELECT
|
430
|
+
DISTINCT(
|
431
|
+
ARRAY_TO_STRING(ARRAY(
|
432
|
+
SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
|
433
|
+
FROM generate_subscripts(idx.indkey, 1) as k
|
434
|
+
ORDER BY k
|
435
|
+
), ',')
|
436
|
+
) as indkey_names
|
437
|
+
FROM pg_class t,
|
438
|
+
pg_class i,
|
439
|
+
pg_index idx,
|
440
|
+
pg_attribute a,
|
441
|
+
pg_am am
|
442
|
+
WHERE t.oid = idx.indrelid
|
443
|
+
AND i.oid = idx.indexrelid
|
444
|
+
AND a.attrelid = t.oid
|
445
|
+
AND a.attnum = ANY(idx.indkey)
|
446
|
+
AND t.relkind = 'r'
|
447
|
+
AND t.relname = ?;
|
448
|
+
)
|
449
|
+
|
450
|
+
indices_query_array = [indices_sql, :thing_with_compound_index_histories]
|
451
|
+
indices_sanitized_query = ThingWithCompoundIndexHistory.send(:sanitize_sql_array, indices_query_array)
|
452
|
+
|
453
|
+
indexes = ThingWithCompoundIndexHistory.connection.execute(indices_sanitized_query).to_a.map(&:values).flatten.map { |i| i.split(",") }
|
454
|
+
|
455
|
+
expect(indexes).to include(["history_started_at"])
|
456
|
+
expect(indexes).to include(["history_ended_at"])
|
457
|
+
expect(indexes).to include(["history_user_id"])
|
458
|
+
expect(indexes).to include(["id"])
|
459
|
+
expect(indexes).to include(["key", "value"])
|
460
|
+
expect(indexes).to include(["thing_with_compound_index_id"])
|
461
|
+
end
|
462
|
+
end
|
410
463
|
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.2.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-10-
|
11
|
+
date: 2019-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -228,6 +228,8 @@ files:
|
|
228
228
|
- spec/db/migrate/20161121212232_create_users.rb
|
229
229
|
- spec/db/migrate/20171011194624_create_safe_posts.rb
|
230
230
|
- spec/db/migrate/20171011194715_create_safe_post_histories.rb
|
231
|
+
- spec/db/migrate/20191024142304_create_thing_with_compound_index.rb
|
232
|
+
- spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb
|
231
233
|
- spec/db/schema.rb
|
232
234
|
- spec/examples.txt
|
233
235
|
- spec/historiographer_spec.rb
|