logidze 0.12.0 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -5
- data/LICENSE.txt +1 -1
- data/README.md +263 -103
- data/lib/generators/logidze/fx_helper.rb +17 -0
- data/lib/generators/logidze/inject_sql.rb +18 -0
- data/lib/generators/logidze/install/USAGE +6 -1
- data/lib/generators/logidze/install/functions/logidze_compact_history.sql +38 -0
- data/lib/generators/logidze/install/functions/logidze_filter_keys.sql +27 -0
- data/lib/generators/logidze/install/functions/logidze_logger.sql +150 -0
- data/lib/generators/logidze/install/functions/logidze_snapshot.sql +24 -0
- data/lib/generators/logidze/install/functions/logidze_version.sql +20 -0
- data/lib/generators/logidze/install/install_generator.rb +58 -1
- data/lib/generators/logidze/install/templates/hstore.rb.erb +1 -1
- data/lib/generators/logidze/install/templates/migration.rb.erb +19 -232
- data/lib/generators/logidze/install/templates/migration_fx.rb.erb +41 -0
- data/lib/generators/logidze/model/model_generator.rb +49 -13
- data/lib/generators/logidze/model/templates/migration.rb.erb +57 -36
- data/lib/generators/logidze/model/triggers/logidze.sql +6 -0
- data/lib/logidze.rb +27 -14
- data/lib/logidze/history.rb +1 -10
- data/lib/logidze/ignore_log_data.rb +1 -4
- data/lib/logidze/model.rb +48 -35
- data/lib/logidze/version.rb +1 -1
- metadata +48 -73
- data/.gitattributes +0 -3
- data/.github/ISSUE_TEMPLATE.md +0 -20
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
- data/.gitignore +0 -40
- data/.rubocop.yml +0 -55
- data/.travis.yml +0 -46
- data/Gemfile +0 -15
- data/Rakefile +0 -28
- data/assets/pg_log_data_chart.png +0 -0
- data/bench/performance/README.md +0 -109
- data/bench/performance/diff_bench.rb +0 -38
- data/bench/performance/insert_bench.rb +0 -22
- data/bench/performance/memory_profile.rb +0 -56
- data/bench/performance/setup.rb +0 -315
- data/bench/performance/update_bench.rb +0 -38
- data/bench/triggers/Makefile +0 -56
- data/bench/triggers/Readme.md +0 -58
- data/bench/triggers/bench.sql +0 -6
- data/bench/triggers/hstore_trigger_setup.sql +0 -38
- data/bench/triggers/jsonb_minus_2_setup.sql +0 -47
- data/bench/triggers/jsonb_minus_setup.sql +0 -49
- data/bench/triggers/keys2_trigger_setup.sql +0 -44
- data/bench/triggers/keys_trigger_setup.sql +0 -50
- data/bin/console +0 -8
- data/bin/setup +0 -9
- data/gemfiles/rails42.gemfile +0 -6
- data/gemfiles/rails5.gemfile +0 -6
- data/gemfiles/rails52.gemfile +0 -6
- data/gemfiles/rails6.gemfile +0 -6
- data/gemfiles/railsmaster.gemfile +0 -7
- data/lib/logidze/ignore_log_data/ignored_columns.rb +0 -46
- data/lib/logidze/migration.rb +0 -20
- data/logidze.gemspec +0 -41
data/Gemfile
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
source 'https://rubygems.org'
|
3
|
-
|
4
|
-
# Specify your gem's dependencies in logidze.gemspec
|
5
|
-
gemspec
|
6
|
-
|
7
|
-
gem "pry-byebug", platform: :mri
|
8
|
-
|
9
|
-
local_gemfile = "#{File.dirname(__FILE__)}/Gemfile.local"
|
10
|
-
|
11
|
-
if File.exist?(local_gemfile)
|
12
|
-
eval(File.read(local_gemfile)) # rubocop:disable Lint/Eval
|
13
|
-
else
|
14
|
-
gem 'activerecord', '~> 6.0'
|
15
|
-
end
|
data/Rakefile
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rspec/core/rake_task"
|
4
|
-
require "rubocop/rake_task"
|
5
|
-
|
6
|
-
RuboCop::RakeTask.new
|
7
|
-
RSpec::Core::RakeTask.new(:spec)
|
8
|
-
|
9
|
-
namespace :dummy do
|
10
|
-
require_relative "spec/dummy/config/application"
|
11
|
-
Dummy::Application.load_tasks
|
12
|
-
end
|
13
|
-
|
14
|
-
task(:spec).clear
|
15
|
-
desc "Run specs other than spec/acceptance"
|
16
|
-
RSpec::Core::RakeTask.new("spec") do |task|
|
17
|
-
task.exclude_pattern = "spec/acceptance/**/*_spec.rb"
|
18
|
-
task.verbose = false
|
19
|
-
end
|
20
|
-
|
21
|
-
desc "Run acceptance specs in spec/acceptance"
|
22
|
-
RSpec::Core::RakeTask.new("spec:acceptance") do |task|
|
23
|
-
task.pattern = "spec/acceptance/**/*_spec.rb"
|
24
|
-
task.verbose = false
|
25
|
-
end
|
26
|
-
|
27
|
-
desc "Run the specs and acceptance tests"
|
28
|
-
task default: %w[rubocop spec spec:acceptance]
|
Binary file
|
data/bench/performance/README.md
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
# Performance benchmarks: PaperTail vs. Logidze
|
2
|
-
|
3
|
-
We want to compare Logidze with the most popular versioning library for Rails – PaperTrail.
|
4
|
-
|
5
|
-
|
6
|
-
## Insert ([source](insert_bench.rb))
|
7
|
-
|
8
|
-
```
|
9
|
-
PaperTrail INSERT 213.148 (± 8.9%) i/s - 1.060k in 5.018504s
|
10
|
-
Logidze INSERT 613.387 (±16.3%) i/s - 2.970k in 5.036127s
|
11
|
-
```
|
12
|
-
|
13
|
-
|
14
|
-
## Update ([source](update_bench.rb))
|
15
|
-
|
16
|
-
When changeset has 2 fields:
|
17
|
-
|
18
|
-
```
|
19
|
-
PaperTrail UPDATE #1 256.651 (±26.5%) i/s - 1.206k in 5.002300s
|
20
|
-
Logidze UPDATE #1 356.932 (±12.6%) i/s - 1.764k in 5.030560s
|
21
|
-
```
|
22
|
-
|
23
|
-
When changeset has 5 fields:
|
24
|
-
|
25
|
-
```
|
26
|
-
PaperTrail UPDATE #2 246.281 (±24.0%) i/s - 1.168k in 5.008234s
|
27
|
-
Logidze UPDATE #2 331.942 (±16.6%) i/s - 1.593k in 5.028135s
|
28
|
-
```
|
29
|
-
|
30
|
-
## Getting diff ([source](diff_bench.rb))
|
31
|
-
|
32
|
-
PaperTrail doesn't have built-in method to calculate diff between not adjacent versions.
|
33
|
-
We add `diff_from(ts)` and `diff_from_joined(ts)` (which uses SQL JOIN) methods to calculate diff from specified version using changesets.
|
34
|
-
|
35
|
-
When each record has 10 versions:
|
36
|
-
|
37
|
-
```
|
38
|
-
PT DIFF 20.874 (± 4.8%) i/s - 106.000 in 5.091402s
|
39
|
-
PT (join) DIFF 20.619 (± 4.8%) i/s - 104.000 in 5.070160s
|
40
|
-
Logidze DIFF 109.482 (±24.7%) i/s - 500.000 in 5.103534s
|
41
|
-
```
|
42
|
-
|
43
|
-
When each record has 100 versions:
|
44
|
-
|
45
|
-
```
|
46
|
-
PT DIFF 2.998 (± 0.0%) i/s - 15.000 in 5.019494s
|
47
|
-
PT (join) DIFF 3.193 (± 0.0%) i/s - 16.000 in 5.030155s
|
48
|
-
Logidze DIFF 19.627 (±25.5%) i/s - 88.000 in 5.035555s
|
49
|
-
```
|
50
|
-
|
51
|
-
And, finally, when each record has 1000 versions:
|
52
|
-
|
53
|
-
```
|
54
|
-
PT DIFF 0.270 (± 0.0%) i/s - 17.000 in 63.038374s
|
55
|
-
PT (join) DIFF 0.235 (± 0.0%) i/s - 14.000 in 60.350886s
|
56
|
-
Logidze DIFF 2.022 (± 0.0%) i/s - 120.000 in 60.142965s
|
57
|
-
```
|
58
|
-
|
59
|
-
## Select memory usage ([source](memory_profile.rb))
|
60
|
-
|
61
|
-
Logidze loads more data (because it stores log in-place). But how much more?
|
62
|
-
We consider two cases for PaperTrail: when we want to calculate diff (and thus loading versions) and when we don't need any history related data.
|
63
|
-
|
64
|
-
When each record has 10 versions:
|
65
|
-
|
66
|
-
```
|
67
|
-
PT records
|
68
|
-
Total Allocated: 27.8 KB
|
69
|
-
Total Retained: 16.59 KB
|
70
|
-
Retained memory (per record): 2.14 KB
|
71
|
-
|
72
|
-
PT with versions
|
73
|
-
Total Allocated: 228.01 KB
|
74
|
-
Total Retained: 170.78 KB
|
75
|
-
Retained memory (per record): 143.13 KB
|
76
|
-
|
77
|
-
Logidze records
|
78
|
-
Total Allocated: 46.45 KB
|
79
|
-
Total Retained: 34.73 KB
|
80
|
-
Retained memory (per record): 4.11 KB
|
81
|
-
```
|
82
|
-
|
83
|
-
When each record has 100 versions:
|
84
|
-
|
85
|
-
```
|
86
|
-
PT with versions
|
87
|
-
Total Allocated: 1.92 MB
|
88
|
-
Total Retained: 1.56 MB
|
89
|
-
Retained memory (per record): 1.53 MB
|
90
|
-
|
91
|
-
Logidze records
|
92
|
-
Total Allocated: 162.48 KB
|
93
|
-
Total Retained: 150.76 KB
|
94
|
-
Retained memory (per record): 15.4 KB
|
95
|
-
```
|
96
|
-
|
97
|
-
When each record has 1000 versions:
|
98
|
-
|
99
|
-
```
|
100
|
-
PT with versions
|
101
|
-
Total Allocated: 18.23 MB
|
102
|
-
Total Retained: 14.86 MB
|
103
|
-
Retained memory (per record): 14.83 MB
|
104
|
-
|
105
|
-
Logidze records
|
106
|
-
Total Allocated: 1.32 MB
|
107
|
-
Total Retained: 1.31 MB
|
108
|
-
Retained memory (per record): 131.59 KB
|
109
|
-
```
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "benchmark/ips"
|
4
|
-
require "./setup"
|
5
|
-
|
6
|
-
# How many records do you want?
|
7
|
-
N = (ENV["N"] || "100").to_i
|
8
|
-
|
9
|
-
# How many version each record has?
|
10
|
-
V = (ENV["V"] || "10").to_i
|
11
|
-
|
12
|
-
# Benchmark run time
|
13
|
-
BM_TIME = (ENV["BM_TIME"] || 5).to_i
|
14
|
-
|
15
|
-
BM_WARMUP = [(BM_TIME / 10), 2].max
|
16
|
-
|
17
|
-
LogidzeBench.cleanup
|
18
|
-
LogidzeBench.populate(N)
|
19
|
-
|
20
|
-
ts1 = LogidzeBench.generate_versions(V / 2)
|
21
|
-
|
22
|
-
LogidzeBench.generate_versions(V / 2)
|
23
|
-
|
24
|
-
Benchmark.ips do |x|
|
25
|
-
x.config(time: BM_TIME, warmup: BM_WARMUP)
|
26
|
-
|
27
|
-
x.report("PT DIFF") do
|
28
|
-
User.random(N / 2).diff_from(ts1)
|
29
|
-
end
|
30
|
-
|
31
|
-
x.report("PT (join) DIFF") do
|
32
|
-
User.random(N / 2).diff_from_joined(ts1)
|
33
|
-
end
|
34
|
-
|
35
|
-
x.report("Logidze DIFF") do
|
36
|
-
LogidzeUser.random(N / 2).diff_from(ts1)
|
37
|
-
end
|
38
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "benchmark/ips"
|
4
|
-
require "./setup"
|
5
|
-
|
6
|
-
params = {
|
7
|
-
email: Faker::Internet.email,
|
8
|
-
position: Faker::Number.number(3),
|
9
|
-
name: Faker::Name.name,
|
10
|
-
age: Faker::Number.number(2),
|
11
|
-
bio: Faker::Lorem.paragraph
|
12
|
-
}
|
13
|
-
|
14
|
-
Benchmark.ips do |x|
|
15
|
-
x.report("PaperTrail INSERT") do
|
16
|
-
User.create!(params)
|
17
|
-
end
|
18
|
-
|
19
|
-
x.report("Logidze INSERT") do
|
20
|
-
LogidzeUser.create!(params)
|
21
|
-
end
|
22
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "./setup"
|
4
|
-
require "active_support/core_ext"
|
5
|
-
require "memory_profiler"
|
6
|
-
|
7
|
-
# How many records do you want?
|
8
|
-
N = (ENV["N"] || "10").to_i
|
9
|
-
|
10
|
-
# How many version each record has?
|
11
|
-
V = (ENV["V"] || "10").to_i
|
12
|
-
|
13
|
-
LogidzeBench.cleanup
|
14
|
-
LogidzeBench.populate(N)
|
15
|
-
LogidzeBench.generate_versions(V)
|
16
|
-
|
17
|
-
module MemoryReport
|
18
|
-
KILO_BYTE = 1024
|
19
|
-
MEGA_BYTE = 1024 * 1024
|
20
|
-
|
21
|
-
module_function
|
22
|
-
|
23
|
-
def call(msg, relation)
|
24
|
-
buffer = nil
|
25
|
-
delta = N / 10
|
26
|
-
r0 = MemoryProfiler.report do
|
27
|
-
buffer = relation.random(N - delta).to_a
|
28
|
-
end
|
29
|
-
|
30
|
-
buffer = nil
|
31
|
-
r1 = MemoryProfiler.report do
|
32
|
-
buffer = relation.to_a
|
33
|
-
end
|
34
|
-
|
35
|
-
$stdout.puts msg
|
36
|
-
$stdout.puts "Total Allocated:\t\t\t\t#{to_human_size(r1.total_allocated_memsize)}"
|
37
|
-
$stdout.puts "Total Retained:\t\t\t\t\t#{to_human_size(r1.total_retained_memsize)}"
|
38
|
-
$stdout.puts "Retained_memsize memory (per record):\t\t#{to_human_size((r1.total_retained_memsize - r0.total_retained_memsize) / delta)}"
|
39
|
-
end
|
40
|
-
|
41
|
-
module_function
|
42
|
-
|
43
|
-
def to_human_size(size)
|
44
|
-
if size > MEGA_BYTE
|
45
|
-
"#{(size.to_f / MEGA_BYTE).round(2)} MB"
|
46
|
-
elsif size > KILO_BYTE
|
47
|
-
"#{(size.to_f / KILO_BYTE).round(2)} KB"
|
48
|
-
else
|
49
|
-
"#{size} B"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
MemoryReport.call("PT records", User.all)
|
55
|
-
MemoryReport.call("PT with versions", User.joins(:versions).all)
|
56
|
-
MemoryReport.call("Logidze records", LogidzeUser.all)
|
data/bench/performance/setup.rb
DELETED
@@ -1,315 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
begin
|
4
|
-
require "bundler/inline"
|
5
|
-
rescue LoadError => e
|
6
|
-
warn "Bundler version 1.10 or later is required. Please update your Bundler"
|
7
|
-
raise e
|
8
|
-
end
|
9
|
-
|
10
|
-
gemfile(true) do
|
11
|
-
source "https://rubygems.org"
|
12
|
-
gem "activerecord", "~>4.2"
|
13
|
-
gem "pg"
|
14
|
-
gem "paper_trail", "~>4.2", require: false
|
15
|
-
gem "pry-byebug"
|
16
|
-
gem "faker"
|
17
|
-
gem "benchmark-ips"
|
18
|
-
gem "memory_profiler"
|
19
|
-
end
|
20
|
-
|
21
|
-
DB_NAME = ENV["DB_NAME"] || "logidze_query_bench"
|
22
|
-
|
23
|
-
begin
|
24
|
-
system("createdb #{DB_NAME}")
|
25
|
-
rescue
|
26
|
-
$stdout.puts "DB already exists"
|
27
|
-
end
|
28
|
-
|
29
|
-
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
30
|
-
|
31
|
-
require "active_record"
|
32
|
-
require "logger"
|
33
|
-
require "logidze"
|
34
|
-
|
35
|
-
ActiveRecord::Base.send :include, Logidze::HasLogidze
|
36
|
-
|
37
|
-
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: DB_NAME)
|
38
|
-
|
39
|
-
at_exit do
|
40
|
-
ActiveRecord::Base.connection.disconnect!
|
41
|
-
end
|
42
|
-
|
43
|
-
require "paper_trail"
|
44
|
-
|
45
|
-
module LogidzeBench
|
46
|
-
module_function
|
47
|
-
|
48
|
-
def setup_db
|
49
|
-
ActiveRecord::Schema.define do
|
50
|
-
# PaperTrail setup
|
51
|
-
create_table :versions, force: true do |t|
|
52
|
-
t.string :item_type, null: false
|
53
|
-
t.integer :item_id, null: false
|
54
|
-
t.string :event, null: false
|
55
|
-
t.string :whodunnit
|
56
|
-
t.text :object
|
57
|
-
t.jsonb :object_changes
|
58
|
-
|
59
|
-
t.datetime :created_at
|
60
|
-
end
|
61
|
-
|
62
|
-
add_index :versions, [:item_type, :item_id]
|
63
|
-
|
64
|
-
# Logidze setup
|
65
|
-
enable_extension :hstore
|
66
|
-
|
67
|
-
execute <<~SQL
|
68
|
-
DO $$
|
69
|
-
BEGIN
|
70
|
-
EXECUTE 'ALTER DATABASE ' || current_database() || ' SET logidze.disabled TO off';
|
71
|
-
END;
|
72
|
-
$$
|
73
|
-
LANGUAGE plpgsql;
|
74
|
-
SQL
|
75
|
-
|
76
|
-
execute <<~SQL
|
77
|
-
CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
78
|
-
DECLARE
|
79
|
-
changes jsonb;
|
80
|
-
new_v integer;
|
81
|
-
ts bigint;
|
82
|
-
size integer;
|
83
|
-
history_limit integer;
|
84
|
-
current_version integer;
|
85
|
-
merged jsonb;
|
86
|
-
iterator integer;
|
87
|
-
item record;
|
88
|
-
BEGIN
|
89
|
-
ts := (extract(epoch from now()) * 1000)::bigint;
|
90
|
-
|
91
|
-
IF TG_OP = 'INSERT' THEN
|
92
|
-
changes := to_jsonb(NEW.*) - 'log_data';
|
93
|
-
new_v := 1;
|
94
|
-
|
95
|
-
NEW.log_data := json_build_object(
|
96
|
-
'v',
|
97
|
-
1,
|
98
|
-
'h',
|
99
|
-
jsonb_build_array(
|
100
|
-
jsonb_build_object(
|
101
|
-
'ts',
|
102
|
-
ts,
|
103
|
-
'v',
|
104
|
-
new_v,
|
105
|
-
'c',
|
106
|
-
changes
|
107
|
-
)
|
108
|
-
)
|
109
|
-
);
|
110
|
-
ELSIF TG_OP = 'UPDATE' THEN
|
111
|
-
history_limit := TG_ARGV[0];
|
112
|
-
current_version := (NEW.log_data->>'v')::int;
|
113
|
-
|
114
|
-
IF NEW = OLD THEN
|
115
|
-
RETURN NEW;
|
116
|
-
END IF;
|
117
|
-
|
118
|
-
IF current_version < (NEW.log_data#>>'{h,-1,v}')::int THEN
|
119
|
-
iterator := 0;
|
120
|
-
FOR item in SELECT * FROM jsonb_array_elements(NEW.log_data->'h')
|
121
|
-
LOOP
|
122
|
-
IF (item.value->>'v')::int > current_version THEN
|
123
|
-
NEW.log_data := jsonb_set(
|
124
|
-
NEW.log_data,
|
125
|
-
'{h}',
|
126
|
-
(NEW.log_data->'h') - iterator
|
127
|
-
);
|
128
|
-
END IF;
|
129
|
-
iterator := iterator + 1;
|
130
|
-
END LOOP;
|
131
|
-
END IF;
|
132
|
-
|
133
|
-
changes := hstore_to_jsonb_loose(
|
134
|
-
hstore(NEW.*) - hstore(OLD.*)
|
135
|
-
) - 'log_data';
|
136
|
-
|
137
|
-
new_v := (NEW.log_data#>>'{h,-1,v}')::int + 1;
|
138
|
-
|
139
|
-
size := jsonb_array_length(NEW.log_data->'h');
|
140
|
-
|
141
|
-
NEW.log_data := jsonb_set(
|
142
|
-
NEW.log_data,
|
143
|
-
ARRAY['h', size::text],
|
144
|
-
jsonb_build_object(
|
145
|
-
'ts',
|
146
|
-
ts,
|
147
|
-
'v',
|
148
|
-
new_v,
|
149
|
-
'c',
|
150
|
-
changes
|
151
|
-
),
|
152
|
-
true
|
153
|
-
);
|
154
|
-
|
155
|
-
NEW.log_data := jsonb_set(
|
156
|
-
NEW.log_data,
|
157
|
-
'{v}',
|
158
|
-
to_jsonb(new_v)
|
159
|
-
);
|
160
|
-
|
161
|
-
IF history_limit IS NOT NULL AND history_limit = size THEN
|
162
|
-
merged := jsonb_build_object(
|
163
|
-
'ts',
|
164
|
-
NEW.log_data#>'{h,1,ts}',
|
165
|
-
'v',
|
166
|
-
NEW.log_data#>'{h,1,v}',
|
167
|
-
'c',
|
168
|
-
(NEW.log_data#>'{h,0,c}') || (NEW.log_data#>'{h,1,c}')
|
169
|
-
);
|
170
|
-
|
171
|
-
NEW.log_data := jsonb_set(
|
172
|
-
NEW.log_data,
|
173
|
-
'{h}',
|
174
|
-
jsonb_set(
|
175
|
-
NEW.log_data->'h',
|
176
|
-
'{1}',
|
177
|
-
merged
|
178
|
-
) - 0
|
179
|
-
);
|
180
|
-
END IF;
|
181
|
-
END IF;
|
182
|
-
|
183
|
-
return NEW;
|
184
|
-
END;
|
185
|
-
$body$
|
186
|
-
LANGUAGE plpgsql;
|
187
|
-
SQL
|
188
|
-
|
189
|
-
create_table :users, force: true do |t|
|
190
|
-
t.string :email
|
191
|
-
t.integer :position
|
192
|
-
t.string :name
|
193
|
-
t.text :bio
|
194
|
-
t.integer :age
|
195
|
-
t.timestamps
|
196
|
-
end
|
197
|
-
|
198
|
-
create_table :logidze_users, force: true do |t|
|
199
|
-
t.string :email
|
200
|
-
t.integer :position
|
201
|
-
t.string :name
|
202
|
-
t.text :bio
|
203
|
-
t.integer :age
|
204
|
-
t.jsonb :log_data, default: "{}", null: false
|
205
|
-
t.timestamps
|
206
|
-
end
|
207
|
-
|
208
|
-
execute <<~SQL
|
209
|
-
CREATE TRIGGER logidze_on_logidze_users
|
210
|
-
BEFORE UPDATE OR INSERT ON logidze_users FOR EACH ROW
|
211
|
-
WHEN (current_setting('logidze.disabled') <> 'on')
|
212
|
-
EXECUTE PROCEDURE logidze_logger();
|
213
|
-
SQL
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
module_function
|
218
|
-
|
219
|
-
def populate(n = 1_000)
|
220
|
-
n.times do
|
221
|
-
params = fake_params
|
222
|
-
User.create!(params)
|
223
|
-
LogidzeUser.create!(params)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
module_function
|
228
|
-
|
229
|
-
def cleanup
|
230
|
-
LogidzeUser.delete_all
|
231
|
-
User.delete_all
|
232
|
-
PaperTrail::Version.delete_all
|
233
|
-
end
|
234
|
-
|
235
|
-
module_function
|
236
|
-
|
237
|
-
def generate_versions(num = 1)
|
238
|
-
num.times do
|
239
|
-
User.find_each do |u|
|
240
|
-
u.update!(fake_params(sample: true))
|
241
|
-
end
|
242
|
-
|
243
|
-
LogidzeUser.find_each do |u|
|
244
|
-
u.update!(fake_params(sample: true))
|
245
|
-
end
|
246
|
-
|
247
|
-
# make at least 1 second between versions
|
248
|
-
sleep 1
|
249
|
-
end
|
250
|
-
Time.now
|
251
|
-
end
|
252
|
-
|
253
|
-
module_function
|
254
|
-
|
255
|
-
def fake_params(sample: false)
|
256
|
-
params = {
|
257
|
-
email: Faker::Internet.email,
|
258
|
-
position: Faker::Number.number(3),
|
259
|
-
name: Faker::Name.name,
|
260
|
-
age: Faker::Number.number(2),
|
261
|
-
bio: Faker::Lorem.paragraph
|
262
|
-
}
|
263
|
-
|
264
|
-
return params.slice(%i[email position name age bio].sample) if sample
|
265
|
-
params
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
module ARandom
|
270
|
-
def random(num = 1)
|
271
|
-
rel = order("random()")
|
272
|
-
num == 1 ? rel.first : rel.limit(num)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
class User < ActiveRecord::Base
|
277
|
-
extend ARandom
|
278
|
-
has_paper_trail
|
279
|
-
|
280
|
-
def self.diff_from(ts)
|
281
|
-
includes(:versions).map { |u| {"id" => u.id, "changes" => u.diff_from(ts)} }
|
282
|
-
end
|
283
|
-
|
284
|
-
def self.diff_from_joined(ts)
|
285
|
-
eager_load(:versions).map { |u| {"id" => u.id, "changes" => u.diff_from(ts)} }
|
286
|
-
end
|
287
|
-
|
288
|
-
def diff_from(ts)
|
289
|
-
changes = {}
|
290
|
-
versions.each do |v|
|
291
|
-
next if v.created_at < ts
|
292
|
-
merge_changeset(changes, v.changeset)
|
293
|
-
end
|
294
|
-
changes
|
295
|
-
end
|
296
|
-
|
297
|
-
private
|
298
|
-
|
299
|
-
def merge_changeset(acc, data)
|
300
|
-
data.each do |k, v|
|
301
|
-
unless acc.key?(k)
|
302
|
-
acc[k] = {"old" => v[0]}
|
303
|
-
end
|
304
|
-
acc[k]["new"] = v[1]
|
305
|
-
end
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
class LogidzeUser < ActiveRecord::Base
|
310
|
-
extend ARandom
|
311
|
-
has_logidze
|
312
|
-
end
|
313
|
-
|
314
|
-
# Run migration only if neccessary
|
315
|
-
LogidzeBench.setup_db if ENV["FORCE"].present? || !ActiveRecord::Base.connection.tables.include?("logidze_users")
|