logidze 0.12.0 → 1.0.0.rc1
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/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
@@ -0,0 +1,41 @@
|
|
1
|
+
class <%= @migration_class_name %> < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
<%- if update? -%>
|
4
|
+
reversible do |dir|
|
5
|
+
dir.up do
|
6
|
+
# Drop legacy functions (<1.0)
|
7
|
+
execute <<~SQL
|
8
|
+
DROP FUNCTION IF EXISTS logidze_version(bigint, jsonb);
|
9
|
+
DROP FUNCTION IF EXISTS logidze_snapshot(jsonb);
|
10
|
+
DROP FUNCTION IF EXISTS logidze_version(bigint, jsonb, text[]);
|
11
|
+
DROP FUNCTION IF EXISTS logidze_snapshot(jsonb, text[]);
|
12
|
+
DROP FUNCTION IF EXISTS logidze_version(bigint, jsonb, timestamp with time zone, text[]);
|
13
|
+
DROP FUNCTION IF EXISTS logidze_snapshot(jsonb, text, text[]);
|
14
|
+
DROP FUNCTION IF EXISTS logidze_exclude_keys(jsonb, VARIADIC text[]);
|
15
|
+
DROP FUNCTION IF EXISTS logidze_compact_history(jsonb);
|
16
|
+
SQL
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
<%- end -%>
|
21
|
+
<%- function_definitions.each do |f| -%>
|
22
|
+
<%- previous_version = previous_version_for(f.name) -%>
|
23
|
+
<%- if previous_version -%>
|
24
|
+
<%- if previous_version != f.version -%>
|
25
|
+
update_function :<%= f.name %>, version: <%= f.version %>, revert_to_version: <%= previous_version %>
|
26
|
+
<%- end -%>
|
27
|
+
<%- else -%>
|
28
|
+
reversible do |dir|
|
29
|
+
dir.up do
|
30
|
+
create_function :<%= f.name %>, version: <%= f.version %>
|
31
|
+
end
|
32
|
+
|
33
|
+
dir.down do
|
34
|
+
execute "DROP FUNCTION IF EXISTS <%= f.name %>(<%= f.signature %>) CASCADE"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
<%- end -%>
|
39
|
+
<%- end -%>
|
40
|
+
end
|
41
|
+
end
|
@@ -2,11 +2,19 @@
|
|
2
2
|
|
3
3
|
require "rails/generators"
|
4
4
|
require "rails/generators/active_record/migration/migration_generator"
|
5
|
+
require_relative "../inject_sql"
|
6
|
+
require_relative "../fx_helper"
|
7
|
+
|
8
|
+
using RubyNext
|
5
9
|
|
6
10
|
module Logidze
|
7
11
|
module Generators
|
8
12
|
class ModelGenerator < ::ActiveRecord::Generators::Base # :nodoc:
|
13
|
+
include InjectSql
|
14
|
+
include FxHelper
|
15
|
+
|
9
16
|
source_root File.expand_path("templates", __dir__)
|
17
|
+
source_paths << File.expand_path("triggers", __dir__)
|
10
18
|
|
11
19
|
class_option :limit, type: :numeric, optional: true, desc: "Specify history size limit"
|
12
20
|
|
@@ -21,8 +29,8 @@ module Logidze
|
|
21
29
|
|
22
30
|
class_option :path, type: :string, optional: true, desc: "Specify path to the model file"
|
23
31
|
|
24
|
-
class_option :
|
25
|
-
class_option :
|
32
|
+
class_option :except, type: :array, optional: true
|
33
|
+
class_option :only, type: :array, optional: true
|
26
34
|
|
27
35
|
class_option :timestamp_column, type: :string, optional: true,
|
28
36
|
desc: "Specify timestamp column"
|
@@ -31,13 +39,19 @@ module Logidze
|
|
31
39
|
desc: "Define whether this is an update migration"
|
32
40
|
|
33
41
|
def generate_migration
|
34
|
-
if options[:
|
35
|
-
warn "Use only one: --
|
42
|
+
if options[:except] && options[:only]
|
43
|
+
warn "Use only one: --only or --except"
|
36
44
|
exit(1)
|
37
45
|
end
|
38
46
|
migration_template "migration.rb.erb", "db/migrate/#{migration_file_name}"
|
39
47
|
end
|
40
48
|
|
49
|
+
def generate_fx_trigger
|
50
|
+
return unless fx?
|
51
|
+
|
52
|
+
template "logidze.sql", "db/triggers/logidze_on_#{table_name}_v#{next_version.to_s.rjust(2, "0")}.sql"
|
53
|
+
end
|
54
|
+
|
41
55
|
def inject_logidze_to_model
|
42
56
|
return if update?
|
43
57
|
|
@@ -75,14 +89,13 @@ module Logidze
|
|
75
89
|
options[:update]
|
76
90
|
end
|
77
91
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
else
|
82
|
-
class_name.constantize.column_names - options[:whitelist]
|
83
|
-
end
|
92
|
+
def filtered_columns
|
93
|
+
format_pgsql_array(options[:only] || options[:except])
|
94
|
+
end
|
84
95
|
|
85
|
-
|
96
|
+
def include_columns
|
97
|
+
return unless options[:only] || options[:except]
|
98
|
+
options[:only].present?
|
86
99
|
end
|
87
100
|
|
88
101
|
def timestamp_column
|
@@ -96,12 +109,35 @@ module Logidze
|
|
96
109
|
options[:debounce_time]
|
97
110
|
end
|
98
111
|
|
112
|
+
def previous_version
|
113
|
+
@previous_version ||= all_triggers.filter_map { |path| Regexp.last_match[1].to_i if path =~ %r{logidze_on_#{table_name}_v(\d+).sql} }.max
|
114
|
+
end
|
115
|
+
|
116
|
+
def next_version
|
117
|
+
previous_version&.next || 1
|
118
|
+
end
|
119
|
+
|
120
|
+
def all_triggers
|
121
|
+
@all_triggers ||=
|
122
|
+
begin
|
123
|
+
res = nil
|
124
|
+
in_root do
|
125
|
+
res = if File.directory?("db/triggers")
|
126
|
+
Dir.entries("db/triggers")
|
127
|
+
else
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
res
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
99
135
|
def logidze_logger_parameters
|
100
|
-
format_pgsql_args(limit, timestamp_column,
|
136
|
+
format_pgsql_args(limit, timestamp_column, filtered_columns, include_columns, debounce_time)
|
101
137
|
end
|
102
138
|
|
103
139
|
def logidze_snapshot_parameters
|
104
|
-
format_pgsql_args("to_jsonb(t)", timestamp_column,
|
140
|
+
format_pgsql_args("to_jsonb(t)", timestamp_column, filtered_columns, include_columns)
|
105
141
|
end
|
106
142
|
|
107
143
|
def format_pgsql_array(ruby_array)
|
@@ -1,43 +1,64 @@
|
|
1
|
-
class <%= @migration_class_name %> < ActiveRecord::Migration
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def up
|
6
|
-
<% if update? %>
|
7
|
-
execute "DROP TRIGGER logidze_on_<%= table_name %> on <%= table_name %>;"
|
8
|
-
<% elsif !only_trigger? %>
|
1
|
+
class <%= @migration_class_name %> < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
<%- unless update? || only_trigger? -%>
|
9
4
|
add_column :<%= table_name %>, :log_data, :jsonb
|
10
|
-
|
5
|
+
<%- end -%>
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
<%- if fx? -%>
|
8
|
+
<%- if previous_version -%>
|
9
|
+
update_trigger :logidze_on_<%= table_name %>, on: :<%= table_name %>, version: <%= next_version %>, revert_to_version: <%= previous_version %>
|
10
|
+
<%- else -%>
|
11
|
+
reversible do |dir|
|
12
|
+
dir.up do
|
13
|
+
<%- if update? -%>
|
14
|
+
# Drop legacy trigger if any (<1.0)
|
15
|
+
execute "DROP TRIGGER IF EXISTS logidze_on_<%= table_name %> on <%= table_name %>;"
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
<%- end -%>
|
18
|
+
create_trigger :logidze_on_<%= table_name %>, on: :<%= table_name %>
|
19
|
+
end
|
20
|
+
|
21
|
+
dir.down do
|
22
|
+
execute "DROP TRIGGER IF EXISTS logidze_on_<%= table_name %> on <%= table_name %>;"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
<%- end -%>
|
26
|
+
<%- else -%>
|
27
|
+
reversible do |dir|
|
28
|
+
dir.up do
|
29
|
+
<%- if update? -%>
|
30
|
+
execute "DROP TRIGGER IF EXISTS logidze_on_<%= table_name %> on <%= table_name %>;"
|
31
|
+
|
32
|
+
<%- end -%>
|
33
|
+
execute <<~SQL
|
34
|
+
<%= inject_sql("logidze.sql", indent: 10) %>
|
35
|
+
SQL
|
36
|
+
end
|
26
37
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
dir.down do
|
39
|
+
<%- if update? -%>
|
40
|
+
# NOTE: We have no idea on how to revert the migration
|
41
|
+
# ('cause we don't know the previous trigger params),
|
42
|
+
# but you can do that on your own.
|
43
|
+
#
|
44
|
+
# Uncomment this line if you want to raise an error.
|
45
|
+
# raise ActiveRecord::IrreversibleMigration
|
46
|
+
<%- else -%>
|
47
|
+
execute "DROP TRIGGER IF EXISTS logidze_on_<%= table_name %> on <%= table_name %>;"
|
48
|
+
<%- end -%>
|
49
|
+
end
|
50
|
+
end
|
51
|
+
<%- end -%>
|
52
|
+
<%- if backfill? -%>
|
37
53
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
54
|
+
reversible do |dir|
|
55
|
+
dir.up do
|
56
|
+
execute <<~SQL
|
57
|
+
UPDATE <%= table_name %> as t
|
58
|
+
SET log_data = logidze_snapshot(<%= logidze_snapshot_parameters %>);
|
59
|
+
SQL
|
60
|
+
end
|
61
|
+
end
|
62
|
+
<%- end -%>
|
42
63
|
end
|
43
64
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
CREATE TRIGGER logidze_on_<%= table_name %>
|
2
|
+
BEFORE UPDATE OR INSERT ON <%= table_name %> FOR EACH ROW
|
3
|
+
WHEN (coalesce(current_setting('logidze.disabled', true), '') <> 'on')
|
4
|
+
-- Parameters: history_size_limit (integer), timestamp_column (text), filtered_columns (text[]),
|
5
|
+
-- include_columns (boolean), debounce_time_ms (integer)
|
6
|
+
EXECUTE PROCEDURE logidze_logger(<%= logidze_logger_parameters %>);
|
data/lib/logidze.rb
CHANGED
@@ -5,6 +5,7 @@ require "logidze/version"
|
|
5
5
|
# Logidze provides tools for adding in-table JSON-based audit to DB tables
|
6
6
|
# and ActiveRecord extensions to work with changes history.
|
7
7
|
module Logidze
|
8
|
+
require "ruby-next"
|
8
9
|
require "logidze/history"
|
9
10
|
require "logidze/model"
|
10
11
|
require "logidze/versioned_association"
|
@@ -19,31 +20,43 @@ module Logidze
|
|
19
20
|
class << self
|
20
21
|
# Determines if Logidze should append a version to the log after updating an old version.
|
21
22
|
attr_accessor :append_on_undo
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def associations_versioning
|
26
|
-
@associations_versioning || false
|
27
|
-
end
|
28
|
-
|
23
|
+
# Determines whether associations versioning is enabled or not
|
24
|
+
attr_accessor :associations_versioning
|
29
25
|
# Determines if Logidze should exclude log data from SELECT statements
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@ignore_log_data_by_default || false
|
34
|
-
end
|
26
|
+
attr_accessor :ignore_log_data_by_default
|
27
|
+
# Whether #at should return self or nil when log_data is nil
|
28
|
+
attr_accessor :return_self_if_log_data_is_empty
|
35
29
|
|
36
30
|
# Temporary disable DB triggers.
|
37
31
|
#
|
38
32
|
# @example
|
39
33
|
# Logidze.without_logging { Post.update_all(active: true) }
|
40
34
|
def without_logging
|
35
|
+
with_logidze_setting("logidze.disabled", "on") { yield }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Instructure Logidze to create a full snapshot for the new versions, not a diff
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# Logidze.with_full_snapshot { post.touch }
|
42
|
+
def with_full_snapshot
|
43
|
+
with_logidze_setting("logidze.full_snapshot", "on") { yield }
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def with_logidze_setting(name, value)
|
41
49
|
ActiveRecord::Base.transaction do
|
42
|
-
ActiveRecord::Base.connection.execute "SET LOCAL
|
50
|
+
ActiveRecord::Base.connection.execute "SET LOCAL #{name} TO #{value};"
|
43
51
|
res = yield
|
44
|
-
ActiveRecord::Base.connection.execute "SET LOCAL
|
52
|
+
ActiveRecord::Base.connection.execute "SET LOCAL #{name} TO DEFAULT;"
|
45
53
|
res
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
57
|
+
|
58
|
+
self.append_on_undo = false
|
59
|
+
self.associations_versioning = false
|
60
|
+
self.ignore_log_data_by_default = false
|
61
|
+
self.return_self_if_log_data_is_empty = true
|
49
62
|
end
|
data/lib/logidze/history.rb
CHANGED
@@ -17,15 +17,6 @@ module Logidze
|
|
17
17
|
delegate :size, to: :versions
|
18
18
|
delegate :responsible_id, :meta, to: :current_version
|
19
19
|
|
20
|
-
### Rails 4 ###
|
21
|
-
def self.dump(object)
|
22
|
-
ActiveSupport::JSON.encode(object)
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.load(json)
|
26
|
-
new(json) if json.present?
|
27
|
-
end
|
28
|
-
|
29
20
|
def initialize(data)
|
30
21
|
@data = data
|
31
22
|
end
|
@@ -108,7 +99,7 @@ module Logidze
|
|
108
99
|
|
109
100
|
# Return nearest (from the bottom) version to the specified time
|
110
101
|
def find_by_time(time)
|
111
|
-
versions.
|
102
|
+
versions.reverse_each.find { |v| v.time <= time }
|
112
103
|
end
|
113
104
|
|
114
105
|
def dup
|
@@ -5,10 +5,7 @@ module Logidze
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
if Rails::VERSION::MAJOR ==
|
9
|
-
require "logidze/ignore_log_data/ignored_columns"
|
10
|
-
attribute :log_data, ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb.new
|
11
|
-
elsif Rails::VERSION::MAJOR == 5
|
8
|
+
if Rails::VERSION::MAJOR == 5
|
12
9
|
require "logidze/ignore_log_data/cast_attribute_patch"
|
13
10
|
include CastAttributePatch
|
14
11
|
end
|
data/lib/logidze/model.rb
CHANGED
@@ -3,43 +3,28 @@
|
|
3
3
|
require "active_support"
|
4
4
|
|
5
5
|
module Logidze
|
6
|
-
|
7
|
-
def self.show_ts_deprecation_for(meth)
|
8
|
-
warn(
|
9
|
-
"[Deprecation] Usage of #{meth}(time) will be removed in the future releases, "\
|
10
|
-
"use #{meth}(time: ts) instead"
|
11
|
-
)
|
12
|
-
end
|
13
|
-
end
|
6
|
+
using RubyNext
|
14
7
|
|
15
8
|
# Extends model with methods to browse history
|
16
9
|
module Model
|
17
|
-
require "logidze/history/type"
|
10
|
+
require "logidze/history/type"
|
18
11
|
|
19
12
|
extend ActiveSupport::Concern
|
20
13
|
|
21
14
|
included do
|
22
|
-
|
23
|
-
serialize :log_data, Logidze::History
|
24
|
-
else
|
25
|
-
attribute :log_data, Logidze::History::Type.new
|
26
|
-
end
|
15
|
+
attribute :log_data, Logidze::History::Type.new
|
27
16
|
|
28
17
|
delegate :version, to: :log_data, prefix: "log"
|
29
18
|
end
|
30
19
|
|
31
20
|
module ClassMethods # :nodoc:
|
32
21
|
# Return records reverted to specified time
|
33
|
-
def at(
|
34
|
-
|
35
|
-
time ||= ts
|
36
|
-
all.map { |record| record.at(time: time, version: version) }.compact
|
22
|
+
def at(time: nil, version: nil)
|
23
|
+
all.to_a.filter_map { |record| record.at(time: time, version: version) }
|
37
24
|
end
|
38
25
|
|
39
26
|
# Return changes made to records since specified time
|
40
|
-
def diff_from(
|
41
|
-
Deprecations.show_ts_deprecation_for(".diff_from") if ts
|
42
|
-
time ||= ts
|
27
|
+
def diff_from(time: nil, version: nil)
|
43
28
|
all.map { |record| record.diff_from(time: time, version: version) }
|
44
29
|
end
|
45
30
|
|
@@ -58,6 +43,28 @@ module Logidze
|
|
58
43
|
def reset_log_data
|
59
44
|
without_logging { update_all(log_data: nil) }
|
60
45
|
end
|
46
|
+
|
47
|
+
# Initialize log_data with the current state if it's null
|
48
|
+
def create_logidze_snapshot(timestamp: nil, only: nil, except: nil)
|
49
|
+
args = ["'null'"]
|
50
|
+
|
51
|
+
args[0] = "'#{timestamp}'" if timestamp
|
52
|
+
|
53
|
+
columns = only || except
|
54
|
+
|
55
|
+
if columns
|
56
|
+
args[1] = "'{#{columns.join(",")}}'"
|
57
|
+
args[2] = only ? "true" : "false"
|
58
|
+
end
|
59
|
+
|
60
|
+
without_logging do
|
61
|
+
where(log_data: nil).update_all(
|
62
|
+
<<~SQL
|
63
|
+
log_data = logidze_snapshot(to_jsonb(#{quoted_table_name}), #{args.join(", ")})
|
64
|
+
SQL
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
61
68
|
end
|
62
69
|
|
63
70
|
# Use this to convert Ruby time to milliseconds
|
@@ -69,14 +76,15 @@ module Logidze
|
|
69
76
|
# If time/version is less then the first version, then return nil.
|
70
77
|
# If time/version is greater then the last version, then return self.
|
71
78
|
# rubocop: disable Metrics/MethodLength
|
72
|
-
def at(
|
73
|
-
Deprecations.show_ts_deprecation_for("#at") if ts
|
74
|
-
|
79
|
+
def at(time: nil, version: nil)
|
75
80
|
return at_version(version) if version
|
76
81
|
|
77
|
-
time ||= ts
|
78
82
|
time = parse_time(time)
|
79
83
|
|
84
|
+
unless log_data
|
85
|
+
return Logidze.return_self_if_log_data_is_empty ? self : nil
|
86
|
+
end
|
87
|
+
|
80
88
|
return nil unless log_data.exists_ts?(time)
|
81
89
|
|
82
90
|
if log_data.current_ts?(time)
|
@@ -91,12 +99,11 @@ module Logidze
|
|
91
99
|
# rubocop: enable Metrics/MethodLength
|
92
100
|
|
93
101
|
# Revert record to the version at specified time (without saving to DB)
|
94
|
-
def at!(
|
95
|
-
Deprecations.show_ts_deprecation_for("#at!") if ts
|
96
|
-
|
102
|
+
def at!(time: nil, version: nil)
|
97
103
|
return at_version!(version) if version
|
98
104
|
|
99
|
-
|
105
|
+
raise ArgumentError, "#log_data is empty" unless log_data
|
106
|
+
|
100
107
|
time = parse_time(time)
|
101
108
|
|
102
109
|
return self if log_data.current_ts?(time)
|
@@ -119,6 +126,8 @@ module Logidze
|
|
119
126
|
|
120
127
|
# Revert record to the specified version (without saving to DB)
|
121
128
|
def at_version!(version)
|
129
|
+
raise ArgumentError, "#log_data is empty" unless log_data
|
130
|
+
|
122
131
|
return self if log_data.version == version
|
123
132
|
return false unless log_data.find_by_version(version)
|
124
133
|
|
@@ -131,13 +140,11 @@ module Logidze
|
|
131
140
|
#
|
132
141
|
# post.diff_from(time: 2.days.ago) # or post.diff_from(version: 2)
|
133
142
|
# #=> { "id" => 1, "changes" => { "title" => { "old" => "Hello!", "new" => "World" } } }
|
134
|
-
def diff_from(
|
135
|
-
Deprecations.show_ts_deprecation_for("#diff_from") if ts
|
136
|
-
time ||= ts
|
143
|
+
def diff_from(version: nil, time: nil)
|
137
144
|
time = parse_time(time) if time
|
138
|
-
changes = log_data
|
145
|
+
changes = log_data&.diff_from(time: time, version: version)&.tap do |v|
|
139
146
|
deserialize_changes!(v)
|
140
|
-
end
|
147
|
+
end || {}
|
141
148
|
|
142
149
|
changes.delete_if { |k, _v| deleted_column?(k) }
|
143
150
|
|
@@ -206,7 +213,7 @@ module Logidze
|
|
206
213
|
|
207
214
|
# Loads log_data field from the database, stores to the attributes hash and returns it
|
208
215
|
def reload_log_data
|
209
|
-
self.log_data = self.class.where(self.class.primary_key => id).pluck(
|
216
|
+
self.log_data = self.class.where(self.class.primary_key => id).pluck("#{self.class.table_name}.log_data").first
|
210
217
|
end
|
211
218
|
|
212
219
|
# Nullify log_data column for a single record
|
@@ -214,6 +221,12 @@ module Logidze
|
|
214
221
|
self.class.without_logging { update_column(:log_data, nil) }
|
215
222
|
end
|
216
223
|
|
224
|
+
def create_logidze_snapshot!(**opts)
|
225
|
+
self.class.where(self.class.primary_key => id).create_logidze_snapshot(**opts)
|
226
|
+
|
227
|
+
reload_log_data
|
228
|
+
end
|
229
|
+
|
217
230
|
protected
|
218
231
|
|
219
232
|
def apply_diff(version, diff)
|