logidze 1.2.3 → 1.3.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/CHANGELOG.md +19 -0
- data/README.md +40 -3
- data/lib/generators/logidze/fx_helper.rb +1 -1
- data/lib/generators/logidze/install/functions/logidze_logger.sql +47 -47
- data/lib/generators/logidze/install/functions/logidze_logger_after.sql +3 -0
- data/lib/generators/logidze/install/install_generator.rb +12 -3
- data/lib/generators/logidze/model/model_generator.rb +14 -8
- data/lib/generators/logidze/model/templates/migration.rb.erb +1 -1
- data/lib/generators/logidze/model/triggers/logidze_after.sql +6 -0
- data/lib/logidze/ignore_log_data.rb +0 -5
- data/lib/logidze/model.rb +16 -3
- data/lib/logidze/utils/check_pending.rb +6 -6
- data/lib/logidze/utils/pending_migration_error.rb +10 -12
- data/lib/logidze/version.rb +1 -1
- data/lib/logidze.rb +0 -1
- metadata +14 -27
- data/lib/logidze/ignore_log_data/cast_attribute_patch.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4ab83a61c2685b93d8fe58199eac1fb889c4459453db9315bd6a9880abb2bc9
|
4
|
+
data.tar.gz: f856bd257d81c6ad1fc3cbe6328bbd1adbf77670335a07a0f5234f572f8a7145
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d462f59ba20d402c5c75bb15ea40b709f278088dcd11be7e769640640450bd35a558794884fca89ccdaa403e09fb0d05c40d43bed8dd86698a41ecb870272a4b
|
7
|
+
data.tar.gz: f5bf80de51ffaff0f611e84bbf99f4291ebb82655ff23ce555d042c3ae29f3d79c172e1b7de7c1f19083892a847473ce6252364973e31eecb41d25fe8e1de65b
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
|
3
3
|
## master (unreleased)
|
4
4
|
|
5
|
+
## 1.3.0 (2024-01-09)
|
6
|
+
|
7
|
+
- Add retrieving list of versions support. ([@tagirahmad][])
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
post.versions # => Enumerator
|
11
|
+
post.versions.find do
|
12
|
+
_1.title == "old title"
|
13
|
+
end
|
14
|
+
```
|
15
|
+
|
16
|
+
- Add `--after-trigger` option to generate _after_ triggers for partitioned tables in older PostgreSQL versions. ([@SparLaimor][], [@prog-supdex][], [@palkan][])
|
17
|
+
|
18
|
+
- **Breaking**. Ruby 2.7, Rails 6.0, PostgreSQL 10.0+ are required.
|
19
|
+
|
5
20
|
## 1.2.3 (2023-01-03)
|
6
21
|
|
7
22
|
- [Fixes [#217](https://github.com/palkan/logidze/issues/217)] Fix switch_to with `append: true` when there are changes on JSONB columns. ([@miharekar][])
|
@@ -376,3 +391,7 @@ This is a quick fix for a more general problem (see [#59](https://github.com/pal
|
|
376
391
|
[@cavi21]: https://github.com/cavi21
|
377
392
|
[@danielmklein]: https://github.com/danielmklein
|
378
393
|
[@baygeldin]: https://github.com/baygeldin
|
394
|
+
[@miharekar]: https://github.com/miharekar
|
395
|
+
[@prog-supdex]: https://github.com/prog-supdex
|
396
|
+
[@SparLaimor]: https://github.com/SparLaimor
|
397
|
+
[@tagirahmad]: https://github.com/tagirahmad
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
# Logidze
|
7
7
|
|
8
|
-
Logidze provides tools for logging DB records changes when using PostgreSQL
|
8
|
+
Logidze provides tools for logging DB records changes when using PostgreSQL. Just like [audited](https://github.com/collectiveidea/audited) and [paper_trail](https://github.com/airblade/paper_trail) do (but [faster](bench/performance)).
|
9
9
|
|
10
10
|
Logidze allows you to create a DB-level log (using triggers) and gives you an API to browse this log.
|
11
11
|
The log is stored with the record itself in JSONB column. No additional tables required.
|
@@ -14,8 +14,9 @@ The log is stored with the record itself in JSONB column. No additional tables r
|
|
14
14
|
|
15
15
|
Other requirements:
|
16
16
|
|
17
|
-
- Ruby ~> 2.
|
18
|
-
- Rails >=
|
17
|
+
- Ruby ~> 2.7
|
18
|
+
- Rails >= 6.0 (for Rails 4.2 use version <=0.12.0, for Rails 5.x use version <= 1.2.3)
|
19
|
+
- PostgreSQL >= 10.0
|
19
20
|
|
20
21
|
<a href="https://evilmartians.com/">
|
21
22
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
@@ -35,6 +36,7 @@ Other requirements:
|
|
35
36
|
- [Tracking only selected columns](#tracking-only-selected-columns)
|
36
37
|
- [Logs timestamps](#logs-timestamps)
|
37
38
|
- [Undoing a Generated Invocation](#undoing-a-generated-invocation)
|
39
|
+
- [Using with partitioned tables](#using-with-partitioned-tables)
|
38
40
|
- [Usage](#usage)
|
39
41
|
- [Basic API](#basic-api)
|
40
42
|
- [Track meta information](#track-meta-information)
|
@@ -189,6 +191,18 @@ bundle exec rails destroy logidze:model Post
|
|
189
191
|
|
190
192
|
**IMPORTANT**: If you use non-UTC time zone for Active Record (`config.active_record.default_timezone`), you MUST always infer log timestamps from a timestamp column (e.g., when back-filling data); otherwise, you may end up with inconsistent logs ([#199](https://github.com/palkan/logidze/issues/199)). In general, we recommend using UTC as the database time unless there is a very strong reason not to.
|
191
193
|
|
194
|
+
### Using with partitioned tables
|
195
|
+
|
196
|
+
Logidze supports partitioned tables for PostgreSQL 13+ without any additional configuration. For PostgreSQL 11/12, you should use _after_ triggers. To do that, provide the `--after-trigger` option to the migration:
|
197
|
+
|
198
|
+
```sh
|
199
|
+
bundle exec rails generate logidze:model Post --after-trigger
|
200
|
+
```
|
201
|
+
|
202
|
+
**NOTE:** Record changes are written as a full snapshot if the partition has changed during the update.
|
203
|
+
|
204
|
+
**IMPORTANT:** Using Logidze for partitioned tables in PostgreSQL 10 is not supported.
|
205
|
+
|
192
206
|
## Usage
|
193
207
|
|
194
208
|
### Basic API
|
@@ -243,6 +257,25 @@ Post.where(created_at: Time.zone.today.all_day).diff_from(time: 1.hour.ago)
|
|
243
257
|
|
244
258
|
**NOTE:** If `log_data` is nil, `#diff_from` returns an empty Hash as `"changes"`.
|
245
259
|
|
260
|
+
Also, it is possible to retrieve list of model's `versions`:
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
post.versions # => Enumerator
|
264
|
+
|
265
|
+
# you can use Enumerator's #take to return all
|
266
|
+
post.versions.take
|
267
|
+
|
268
|
+
# or you take a few or call any Enumerable method
|
269
|
+
post.versions.take(2)
|
270
|
+
post.versions.find do
|
271
|
+
_1.title == "old title"
|
272
|
+
end
|
273
|
+
|
274
|
+
# we can also add options
|
275
|
+
post.versions(reverse: true) # from older to newer
|
276
|
+
post.versions(include_self: true) # returns self as the first one (default) or the last one record (if reverse: true)
|
277
|
+
```
|
278
|
+
|
246
279
|
There are also `#undo!` and `#redo!` options (and more general `#switch_to!`):
|
247
280
|
|
248
281
|
```ruby
|
@@ -302,6 +335,10 @@ Logidze.with_meta({ip: request.ip}, transactional: false) do
|
|
302
335
|
end
|
303
336
|
```
|
304
337
|
|
338
|
+
**Important:** If you use connection pooling (e.g., PgBouncer), using `.with_meta` without a transaction may lead to unexpected results (since meta is set for a connection). Without a transaction, we cannot guarantee that the same connection will be used for queries (including metadata cleanup).
|
339
|
+
|
340
|
+
**Important**: In Rails, `after_commit` callbacks are executed after transaction is committed, and, thus, after `with_meta` block is executed—the meta wouldn't be added to changes captured in the `after_commit` phase. One particular scenario is having associations with `touch: true` (_touch_ updates are executed after commit).
|
341
|
+
|
305
342
|
### Track responsibility
|
306
343
|
|
307
344
|
A special application of meta information is storing the author of the change, which is called _Responsible ID_. There is more likely that you would like to store the `current_user.id` that way.
|
@@ -1,9 +1,10 @@
|
|
1
1
|
CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
2
|
-
-- version:
|
2
|
+
-- version: 4
|
3
3
|
DECLARE
|
4
4
|
changes jsonb;
|
5
5
|
version jsonb;
|
6
|
-
|
6
|
+
full_snapshot boolean;
|
7
|
+
log_data jsonb;
|
7
8
|
new_v integer;
|
8
9
|
size integer;
|
9
10
|
history_limit integer;
|
@@ -30,59 +31,56 @@ CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
|
30
31
|
columns := NULLIF(TG_ARGV[2], 'null');
|
31
32
|
include_columns := NULLIF(TG_ARGV[3], 'null');
|
32
33
|
|
33
|
-
IF
|
34
|
+
IF NEW.log_data is NULL OR NEW.log_data = '{}'::jsonb
|
35
|
+
THEN
|
34
36
|
IF columns IS NOT NULL THEN
|
35
|
-
|
37
|
+
log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
|
36
38
|
ELSE
|
37
|
-
|
39
|
+
log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column);
|
38
40
|
END IF;
|
39
41
|
|
40
|
-
IF
|
41
|
-
NEW.log_data :=
|
42
|
+
IF log_data#>>'{h, -1, c}' != '{}' THEN
|
43
|
+
NEW.log_data := log_data;
|
42
44
|
END IF;
|
43
45
|
|
44
|
-
|
46
|
+
ELSE
|
45
47
|
|
46
|
-
IF
|
47
|
-
|
48
|
-
snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
|
49
|
-
ELSE
|
50
|
-
snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column);
|
51
|
-
END IF;
|
52
|
-
|
53
|
-
IF snapshot#>>'{h, -1, c}' != '{}' THEN
|
54
|
-
NEW.log_data := snapshot;
|
55
|
-
END IF;
|
56
|
-
RETURN NEW;
|
48
|
+
IF TG_OP = 'UPDATE' AND (to_jsonb(NEW.*) = to_jsonb(OLD.*)) THEN
|
49
|
+
RETURN NEW; -- pass
|
57
50
|
END IF;
|
58
51
|
|
59
52
|
history_limit := NULLIF(TG_ARGV[0], 'null');
|
60
53
|
debounce_time := NULLIF(TG_ARGV[4], 'null');
|
61
54
|
|
62
|
-
|
55
|
+
log_data := NEW.log_data;
|
56
|
+
|
57
|
+
current_version := (log_data->>'v')::int;
|
63
58
|
|
64
59
|
IF ts_column IS NULL THEN
|
65
60
|
ts := statement_timestamp();
|
66
|
-
|
67
|
-
ts := (to_jsonb(NEW.*)->>ts_column)::timestamp with time zone;
|
68
|
-
IF ts IS NULL OR ts = (to_jsonb(OLD.*)->>ts_column)::timestamp with time zone THEN
|
61
|
+
ELSEIF TG_OP = 'UPDATE' THEN
|
62
|
+
ts := (to_jsonb(NEW.*) ->> ts_column)::timestamp with time zone;
|
63
|
+
IF ts IS NULL OR ts = (to_jsonb(OLD.*) ->> ts_column)::timestamp with time zone THEN
|
64
|
+
ts := statement_timestamp();
|
65
|
+
END IF;
|
66
|
+
ELSEIF TG_OP = 'INSERT' THEN
|
67
|
+
ts := (to_jsonb(NEW.*) ->> ts_column)::timestamp with time zone;
|
68
|
+
IF ts IS NULL OR (extract(epoch from ts) * 1000)::bigint = (NEW.log_data #>> '{h,-1,ts}')::bigint THEN
|
69
69
|
ts := statement_timestamp();
|
70
70
|
END IF;
|
71
71
|
END IF;
|
72
72
|
|
73
|
-
|
74
|
-
RETURN NEW;
|
75
|
-
END IF;
|
73
|
+
full_snapshot := (coalesce(current_setting('logidze.full_snapshot', true), '') = 'on') OR (TG_OP = 'INSERT');
|
76
74
|
|
77
|
-
IF current_version < (
|
75
|
+
IF current_version < (log_data#>>'{h,-1,v}')::int THEN
|
78
76
|
iterator := 0;
|
79
|
-
FOR item in SELECT * FROM jsonb_array_elements(
|
77
|
+
FOR item in SELECT * FROM jsonb_array_elements(log_data->'h')
|
80
78
|
LOOP
|
81
79
|
IF (item.value->>'v')::int > current_version THEN
|
82
|
-
|
83
|
-
|
80
|
+
log_data := jsonb_set(
|
81
|
+
log_data,
|
84
82
|
'{h}',
|
85
|
-
(
|
83
|
+
(log_data->'h') - iterator
|
86
84
|
);
|
87
85
|
END IF;
|
88
86
|
iterator := iterator + 1;
|
@@ -91,7 +89,7 @@ CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
|
91
89
|
|
92
90
|
changes := '{}';
|
93
91
|
|
94
|
-
IF
|
92
|
+
IF full_snapshot THEN
|
95
93
|
BEGIN
|
96
94
|
changes = hstore_to_jsonb_loose(hstore(NEW.*));
|
97
95
|
EXCEPTION
|
@@ -132,48 +130,50 @@ CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
|
132
130
|
END IF;
|
133
131
|
|
134
132
|
IF changes = '{}' THEN
|
135
|
-
RETURN NEW;
|
133
|
+
RETURN NEW; -- pass
|
136
134
|
END IF;
|
137
135
|
|
138
|
-
new_v := (
|
136
|
+
new_v := (log_data#>>'{h,-1,v}')::int + 1;
|
139
137
|
|
140
|
-
size := jsonb_array_length(
|
138
|
+
size := jsonb_array_length(log_data->'h');
|
141
139
|
version := logidze_version(new_v, changes, ts);
|
142
140
|
|
143
141
|
IF (
|
144
142
|
debounce_time IS NOT NULL AND
|
145
|
-
(version->>'ts')::bigint - (
|
143
|
+
(version->>'ts')::bigint - (log_data#>'{h,-1,ts}')::text::bigint <= debounce_time
|
146
144
|
) THEN
|
147
145
|
-- merge new version with the previous one
|
148
|
-
new_v := (
|
149
|
-
version := logidze_version(new_v, (
|
146
|
+
new_v := (log_data#>>'{h,-1,v}')::int;
|
147
|
+
version := logidze_version(new_v, (log_data#>'{h,-1,c}')::jsonb || changes, ts);
|
150
148
|
-- remove the previous version from log
|
151
|
-
|
152
|
-
|
149
|
+
log_data := jsonb_set(
|
150
|
+
log_data,
|
153
151
|
'{h}',
|
154
|
-
(
|
152
|
+
(log_data->'h') - (size - 1)
|
155
153
|
);
|
156
154
|
END IF;
|
157
155
|
|
158
|
-
|
159
|
-
|
156
|
+
log_data := jsonb_set(
|
157
|
+
log_data,
|
160
158
|
ARRAY['h', size::text],
|
161
159
|
version,
|
162
160
|
true
|
163
161
|
);
|
164
162
|
|
165
|
-
|
166
|
-
|
163
|
+
log_data := jsonb_set(
|
164
|
+
log_data,
|
167
165
|
'{v}',
|
168
166
|
to_jsonb(new_v)
|
169
167
|
);
|
170
168
|
|
171
169
|
IF history_limit IS NOT NULL AND history_limit <= size THEN
|
172
|
-
|
170
|
+
log_data := logidze_compact_history(log_data, size - history_limit + 1);
|
173
171
|
END IF;
|
172
|
+
|
173
|
+
NEW.log_data := log_data;
|
174
174
|
END IF;
|
175
175
|
|
176
|
-
|
176
|
+
RETURN NEW; -- result
|
177
177
|
EXCEPTION
|
178
178
|
WHEN OTHERS THEN
|
179
179
|
GET STACKED DIAGNOSTICS err_sqlstate = RETURNED_SQLSTATE,
|
@@ -6,8 +6,6 @@ require "logidze/utils/function_definitions"
|
|
6
6
|
require_relative "../inject_sql"
|
7
7
|
require_relative "../fx_helper"
|
8
8
|
|
9
|
-
using RubyNext
|
10
|
-
|
11
9
|
module Logidze
|
12
10
|
module Generators
|
13
11
|
class InstallGenerator < ::Rails::Generators::Base # :nodoc:
|
@@ -19,7 +17,7 @@ module Logidze
|
|
19
17
|
source_paths << File.expand_path("functions", __dir__)
|
20
18
|
|
21
19
|
class_option :update, type: :boolean, optional: true,
|
22
|
-
|
20
|
+
desc: "Define whether this is an update migration"
|
23
21
|
|
24
22
|
def generate_migration
|
25
23
|
migration_template = fx? ? "migration_fx.rb.erb" : "migration.rb.erb"
|
@@ -81,6 +79,17 @@ module Logidze
|
|
81
79
|
def function_definitions
|
82
80
|
@function_definitions ||= Logidze::Utils::FunctionDefinitions.from_fs
|
83
81
|
end
|
82
|
+
|
83
|
+
# Generate `logidze_logger_after.sql` from the regular `logidze_logger.sql`
|
84
|
+
# by find-and-replacing a few lines
|
85
|
+
def generate_logidze_logger_after
|
86
|
+
source = File.read(File.join(__dir__, "functions", "logidze_logger.sql"))
|
87
|
+
source.sub!(/^CREATE OR REPLACE FUNCTION logidze_logger.*$/, "")
|
88
|
+
source.sub!(/^ -- version.*$/, "")
|
89
|
+
source.gsub!("RETURN NEW; -- pass", "RETURN NULL;")
|
90
|
+
source.gsub!("RETURN NEW; -- result", " EXECUTE format('UPDATE %I.%I SET \"log_data\" = $1 WHERE ctid = %L', TG_TABLE_SCHEMA, TG_TABLE_NAME, NEW.CTID) USING NEW.log_data;\n RETURN NULL;")
|
91
|
+
source
|
92
|
+
end
|
84
93
|
end
|
85
94
|
|
86
95
|
def self.next_migration_number(dir)
|
@@ -5,8 +5,6 @@ require "rails/generators/active_record/migration/migration_generator"
|
|
5
5
|
require_relative "../inject_sql"
|
6
6
|
require_relative "../fx_helper"
|
7
7
|
|
8
|
-
using RubyNext
|
9
|
-
|
10
8
|
module Logidze
|
11
9
|
module Generators
|
12
10
|
class ModelGenerator < ::ActiveRecord::Generators::Base # :nodoc:
|
@@ -19,13 +17,13 @@ module Logidze
|
|
19
17
|
class_option :limit, type: :numeric, optional: true, desc: "Specify history size limit"
|
20
18
|
|
21
19
|
class_option :debounce_time, type: :numeric, optional: true,
|
22
|
-
|
20
|
+
desc: "Specify debounce time in millisecond"
|
23
21
|
|
24
22
|
class_option :backfill, type: :boolean, optional: true,
|
25
|
-
|
23
|
+
desc: "Add query to backfill existing records history"
|
26
24
|
|
27
25
|
class_option :only_trigger, type: :boolean, optional: true,
|
28
|
-
|
26
|
+
desc: "Create trigger-only migration"
|
29
27
|
|
30
28
|
class_option :path, type: :string, optional: true, desc: "Specify path to the model file"
|
31
29
|
|
@@ -33,12 +31,14 @@ module Logidze
|
|
33
31
|
class_option :only, type: :array, optional: true
|
34
32
|
|
35
33
|
class_option :timestamp_column, type: :string, optional: true,
|
36
|
-
|
34
|
+
desc: "Specify timestamp column"
|
37
35
|
|
38
36
|
class_option :name, type: :string, optional: true, desc: "Migration name"
|
39
37
|
|
40
38
|
class_option :update, type: :boolean, optional: true,
|
41
|
-
|
39
|
+
desc: "Define whether this is an update migration"
|
40
|
+
|
41
|
+
class_option :after_trigger, type: :boolean, optional: true, desc: "Use after trigger"
|
42
42
|
|
43
43
|
def generate_migration
|
44
44
|
if options[:except] && options[:only]
|
@@ -51,7 +51,9 @@ module Logidze
|
|
51
51
|
def generate_fx_trigger
|
52
52
|
return unless fx?
|
53
53
|
|
54
|
-
|
54
|
+
template_name = after_trigger? ? "logidze_after.sql" : "logidze.sql"
|
55
|
+
|
56
|
+
template template_name, "db/triggers/logidze_on_#{table_name}_v#{next_version.to_s.rjust(2, "0")}.sql"
|
55
57
|
end
|
56
58
|
|
57
59
|
def inject_logidze_to_model
|
@@ -94,6 +96,10 @@ module Logidze
|
|
94
96
|
options[:update]
|
95
97
|
end
|
96
98
|
|
99
|
+
def after_trigger?
|
100
|
+
options[:after_trigger]
|
101
|
+
end
|
102
|
+
|
97
103
|
def filtered_columns
|
98
104
|
format_pgsql_array(options[:only] || options[:except])
|
99
105
|
end
|
@@ -37,7 +37,7 @@ class <%= @migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::M
|
|
37
37
|
|
38
38
|
<%- end -%>
|
39
39
|
execute <<~SQL
|
40
|
-
<%= inject_sql("logidze.sql", indent: 10) %>
|
40
|
+
<%= inject_sql(after_trigger? ? "logidze_after.sql" : "logidze.sql", indent: 10) %>
|
41
41
|
SQL
|
42
42
|
end
|
43
43
|
|
@@ -0,0 +1,6 @@
|
|
1
|
+
CREATE TRIGGER <%= %Q("logidze_on_#{full_table_name}") %>
|
2
|
+
AFTER UPDATE OR INSERT ON <%= %Q("#{full_table_name}") %> FOR EACH ROW
|
3
|
+
WHEN (coalesce(current_setting('logidze.disabled', true), '') <> 'on' AND pg_trigger_depth() < 1)
|
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_after(<%= logidze_logger_parameters %>);
|
@@ -5,11 +5,6 @@ module Logidze
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
if Rails::VERSION::MAJOR == 5
|
9
|
-
require "logidze/ignore_log_data/cast_attribute_patch"
|
10
|
-
include CastAttributePatch
|
11
|
-
end
|
12
|
-
|
13
8
|
scope :with_log_data, lambda {
|
14
9
|
if ignored_columns == ["log_data"]
|
15
10
|
select(arel_table[Arel.star])
|
data/lib/logidze/model.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
require "active_support"
|
4
4
|
|
5
5
|
module Logidze
|
6
|
-
using RubyNext
|
7
|
-
|
8
6
|
# Extends model with methods to browse history
|
9
7
|
module Model
|
10
8
|
require "logidze/history/type"
|
@@ -96,6 +94,20 @@ module Logidze
|
|
96
94
|
|
97
95
|
build_dup(log_entry, time)
|
98
96
|
end
|
97
|
+
|
98
|
+
def logidze_versions(reverse: false, include_self: false)
|
99
|
+
versions_meta = log_data.versions.dup
|
100
|
+
|
101
|
+
if reverse
|
102
|
+
versions_meta.reverse!
|
103
|
+
versions_meta.shift unless include_self
|
104
|
+
else
|
105
|
+
versions_meta.pop unless include_self
|
106
|
+
end
|
107
|
+
|
108
|
+
Enumerator.new { |yielder| versions_meta.each { yielder << at(version: _1.version) } }
|
109
|
+
end
|
110
|
+
|
99
111
|
# rubocop: enable Metrics/MethodLength
|
100
112
|
|
101
113
|
# Revert record to the version at specified time (without saving to DB)
|
@@ -116,6 +128,7 @@ module Logidze
|
|
116
128
|
|
117
129
|
# Return a dirty copy of specified version of record
|
118
130
|
def at_version(version)
|
131
|
+
return nil unless log_data
|
119
132
|
return self if log_data.version == version
|
120
133
|
|
121
134
|
log_entry = log_data.find_by_version(version)
|
@@ -217,7 +230,7 @@ module Logidze
|
|
217
230
|
|
218
231
|
# Loads log_data field from the database, stores to the attributes hash and returns it
|
219
232
|
def reload_log_data
|
220
|
-
self.log_data = self.class.where(self.class.primary_key => id).pluck("#{self.class.table_name}.log_data"
|
233
|
+
self.log_data = self.class.where(self.class.primary_key => id).pluck(:"#{self.class.table_name}.log_data").first
|
221
234
|
end
|
222
235
|
|
223
236
|
# Nullify log_data column for a single record
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
3
|
+
require_relative "function_definitions"
|
4
|
+
require_relative "pending_migration_error"
|
5
5
|
|
6
6
|
module Logidze
|
7
7
|
module Utils
|
@@ -31,10 +31,10 @@ module Logidze
|
|
31
31
|
def notify_or_raise!
|
32
32
|
case Logidze.on_pending_upgrade
|
33
33
|
when :warn
|
34
|
-
warn "\n**************************************************\n"\
|
35
|
-
"⛔️ WARNING: Logidze needs an upgrade and might not work correctly.\n"\
|
36
|
-
"Please, make sure to run `bundle exec rails generate logidze:install --update` "\
|
37
|
-
"and apply generated migration."\
|
34
|
+
warn "\n**************************************************\n" \
|
35
|
+
"⛔️ WARNING: Logidze needs an upgrade and might not work correctly.\n" \
|
36
|
+
"Please, make sure to run `bundle exec rails generate logidze:install --update` " \
|
37
|
+
"and apply generated migration." \
|
38
38
|
"\n**************************************************\n\n"
|
39
39
|
when :raise
|
40
40
|
raise Logidze::Utils::PendingMigrationError, "Logidze needs upgrade. Run `bundle exec rails generate logidze:install --update` and apply generated migration."
|
@@ -5,19 +5,17 @@ require "rails/generators"
|
|
5
5
|
module Logidze
|
6
6
|
module Utils
|
7
7
|
class PendingMigrationError < StandardError
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
include ActiveSupport::ActionableError
|
8
|
+
require "active_record"
|
9
|
+
require "active_support/actionable_error"
|
10
|
+
include ActiveSupport::ActionableError
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
12
|
+
action "Upgrade Logidze" do
|
13
|
+
Rails::Generators.invoke("logidze:install", ["--update"])
|
14
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
15
|
+
if ActiveRecord::Base.dump_schema_after_migration
|
16
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(
|
17
|
+
ActiveRecord::Base.connection_db_config
|
18
|
+
)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
end
|
data/lib/logidze/version.rb
CHANGED
data/lib/logidze.rb
CHANGED
@@ -5,7 +5,6 @@ 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"
|
9
8
|
require "logidze/history"
|
10
9
|
require "logidze/model"
|
11
10
|
require "logidze/versioned_association"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logidze
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -16,42 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '6.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: ruby-next-core
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.9'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.9'
|
40
|
+
version: '6.0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: ammeter
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +86,14 @@ dependencies:
|
|
100
86
|
requirements:
|
101
87
|
- - ">="
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0
|
89
|
+
version: '1.0'
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - ">="
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0
|
96
|
+
version: '1.0'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: rake
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +114,14 @@ dependencies:
|
|
128
114
|
requirements:
|
129
115
|
- - ">="
|
130
116
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
117
|
+
version: '4.0'
|
132
118
|
type: :development
|
133
119
|
prerelease: false
|
134
120
|
version_requirements: !ruby/object:Gem::Requirement
|
135
121
|
requirements:
|
136
122
|
- - ">="
|
137
123
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
124
|
+
version: '4.0'
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
126
|
name: timecop
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -167,6 +153,7 @@ files:
|
|
167
153
|
- lib/generators/logidze/install/functions/logidze_compact_history.sql
|
168
154
|
- lib/generators/logidze/install/functions/logidze_filter_keys.sql
|
169
155
|
- lib/generators/logidze/install/functions/logidze_logger.sql
|
156
|
+
- lib/generators/logidze/install/functions/logidze_logger_after.sql
|
170
157
|
- lib/generators/logidze/install/functions/logidze_snapshot.sql
|
171
158
|
- lib/generators/logidze/install/functions/logidze_version.sql
|
172
159
|
- lib/generators/logidze/install/install_generator.rb
|
@@ -177,6 +164,7 @@ files:
|
|
177
164
|
- lib/generators/logidze/model/model_generator.rb
|
178
165
|
- lib/generators/logidze/model/templates/migration.rb.erb
|
179
166
|
- lib/generators/logidze/model/triggers/logidze.sql
|
167
|
+
- lib/generators/logidze/model/triggers/logidze_after.sql
|
180
168
|
- lib/logidze.rb
|
181
169
|
- lib/logidze/engine.rb
|
182
170
|
- lib/logidze/has_logidze.rb
|
@@ -184,7 +172,6 @@ files:
|
|
184
172
|
- lib/logidze/history/type.rb
|
185
173
|
- lib/logidze/history/version.rb
|
186
174
|
- lib/logidze/ignore_log_data.rb
|
187
|
-
- lib/logidze/ignore_log_data/cast_attribute_patch.rb
|
188
175
|
- lib/logidze/meta.rb
|
189
176
|
- lib/logidze/model.rb
|
190
177
|
- lib/logidze/utils/check_pending.rb
|
@@ -209,14 +196,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
209
196
|
requirements:
|
210
197
|
- - ">="
|
211
198
|
- !ruby/object:Gem::Version
|
212
|
-
version: 2.
|
199
|
+
version: 2.7.0
|
213
200
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
214
201
|
requirements:
|
215
202
|
- - ">="
|
216
203
|
- !ruby/object:Gem::Version
|
217
204
|
version: '0'
|
218
205
|
requirements: []
|
219
|
-
rubygems_version: 3.
|
206
|
+
rubygems_version: 3.4.20
|
220
207
|
signing_key:
|
221
208
|
specification_version: 4
|
222
209
|
summary: PostgreSQL JSONB-based model changes tracking
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Logidze
|
4
|
-
module IgnoreLogData
|
5
|
-
# Fixes unexpected behavior (see more https://github.com/rails/rails/pull/34528):
|
6
|
-
# instead of using a type passed to `attribute` call, ignored column uses
|
7
|
-
# a type coming from the DB (in this case `.log_data` would return a plain hash
|
8
|
-
# instead of `Logidze::History`)
|
9
|
-
module CastAttributePatch
|
10
|
-
def log_data
|
11
|
-
return attributes["log_data"] if attributes["log_data"].is_a?(Logidze::History)
|
12
|
-
|
13
|
-
self.log_data = Logidze::History::Type.new.cast_value(super)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|