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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac315542fe9ec1866a4395ed04f1e431d65fe6c9dfc72874a423224c0b52b3cb
4
- data.tar.gz: deb4efcba8179273185738fbe395458f6bd4165bf8ed1263b0348bf9bc4b45ab
3
+ metadata.gz: c4ab83a61c2685b93d8fe58199eac1fb889c4459453db9315bd6a9880abb2bc9
4
+ data.tar.gz: f856bd257d81c6ad1fc3cbe6328bbd1adbf77670335a07a0f5234f572f8a7145
5
5
  SHA512:
6
- metadata.gz: bffd04a3005c169a56e3280eaceb7c22623b7d7cfdf82719d65f16804fd86adee32f9afdccd6e767923dd5f6b0b63c0354455ad5daba7a608a420190cae4c2fd
7
- data.tar.gz: c7296b56fed241dc7f55a16a5307b596f1ec016f334cb743c47272295db3ee4d1236bbda0f97c78a9b4a8e50309e07757322611c41bb5386bdbee3500fa57a1b
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 (>=9.6). Just like [audited](https://github.com/collectiveidea/audited) and [paper_trail](https://github.com/airblade/paper_trail) do (but [faster](bench/performance)).
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.5
18
- - Rails >= 5.0 (for Rails 4.2 use version <=0.12.0)
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.
@@ -6,7 +6,7 @@ module Logidze
6
6
  module FxHelper
7
7
  def self.included(base)
8
8
  base.class_option :fx, type: :boolean, optional: true,
9
- desc: "Define whether to use fx gem functionality"
9
+ desc: "Define whether to use fx gem functionality"
10
10
  end
11
11
 
12
12
  def fx?
@@ -1,9 +1,10 @@
1
1
  CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
2
- -- version: 3
2
+ -- version: 4
3
3
  DECLARE
4
4
  changes jsonb;
5
5
  version jsonb;
6
- snapshot jsonb;
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 TG_OP = 'INSERT' THEN
34
+ IF NEW.log_data is NULL OR NEW.log_data = '{}'::jsonb
35
+ THEN
34
36
  IF columns IS NOT NULL THEN
35
- snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
37
+ log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
36
38
  ELSE
37
- snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column);
39
+ log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column);
38
40
  END IF;
39
41
 
40
- IF snapshot#>>'{h, -1, c}' != '{}' THEN
41
- NEW.log_data := snapshot;
42
+ IF log_data#>>'{h, -1, c}' != '{}' THEN
43
+ NEW.log_data := log_data;
42
44
  END IF;
43
45
 
44
- ELSIF TG_OP = 'UPDATE' THEN
46
+ ELSE
45
47
 
46
- IF OLD.log_data is NULL OR OLD.log_data = '{}'::jsonb THEN
47
- IF columns IS NOT NULL THEN
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
- current_version := (NEW.log_data->>'v')::int;
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
- ELSE
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
- IF to_jsonb(NEW.*) = to_jsonb(OLD.*) THEN
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 < (NEW.log_data#>>'{h,-1,v}')::int THEN
75
+ IF current_version < (log_data#>>'{h,-1,v}')::int THEN
78
76
  iterator := 0;
79
- FOR item in SELECT * FROM jsonb_array_elements(NEW.log_data->'h')
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
- NEW.log_data := jsonb_set(
83
- NEW.log_data,
80
+ log_data := jsonb_set(
81
+ log_data,
84
82
  '{h}',
85
- (NEW.log_data->'h') - iterator
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 (coalesce(current_setting('logidze.full_snapshot', true), '') = 'on') THEN
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 := (NEW.log_data#>>'{h,-1,v}')::int + 1;
136
+ new_v := (log_data#>>'{h,-1,v}')::int + 1;
139
137
 
140
- size := jsonb_array_length(NEW.log_data->'h');
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 - (NEW.log_data#>'{h,-1,ts}')::text::bigint <= debounce_time
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 := (NEW.log_data#>>'{h,-1,v}')::int;
149
- version := logidze_version(new_v, (NEW.log_data#>'{h,-1,c}')::jsonb || changes, ts);
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
- NEW.log_data := jsonb_set(
152
- NEW.log_data,
149
+ log_data := jsonb_set(
150
+ log_data,
153
151
  '{h}',
154
- (NEW.log_data->'h') - (size - 1)
152
+ (log_data->'h') - (size - 1)
155
153
  );
156
154
  END IF;
157
155
 
158
- NEW.log_data := jsonb_set(
159
- NEW.log_data,
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
- NEW.log_data := jsonb_set(
166
- NEW.log_data,
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
- NEW.log_data := logidze_compact_history(NEW.log_data, size - history_limit + 1);
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
- return NEW;
176
+ RETURN NEW; -- result
177
177
  EXCEPTION
178
178
  WHEN OTHERS THEN
179
179
  GET STACKED DIAGNOSTICS err_sqlstate = RETURNED_SQLSTATE,
@@ -0,0 +1,3 @@
1
+ CREATE OR REPLACE FUNCTION logidze_logger_after() RETURNS TRIGGER AS $body$
2
+ -- version: 4
3
+ <%= generate_logidze_logger_after %>
@@ -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
- desc: "Define whether this is an update migration"
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
- desc: "Specify debounce time in millisecond"
20
+ desc: "Specify debounce time in millisecond"
23
21
 
24
22
  class_option :backfill, type: :boolean, optional: true,
25
- desc: "Add query to backfill existing records history"
23
+ desc: "Add query to backfill existing records history"
26
24
 
27
25
  class_option :only_trigger, type: :boolean, optional: true,
28
- desc: "Create trigger-only migration"
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
- desc: "Specify timestamp column"
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
- desc: "Define whether this is an update migration"
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
- template "logidze.sql", "db/triggers/logidze_on_#{table_name}_v#{next_version.to_s.rjust(2, "0")}.sql"
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".to_sym).first
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 "./function_definitions"
4
- require_relative "./pending_migration_error"
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
- if Rails::VERSION::MAJOR >= 6
9
- require "active_record"
10
- require "active_support/actionable_error"
11
- include ActiveSupport::ActionableError
8
+ require "active_record"
9
+ require "active_support/actionable_error"
10
+ include ActiveSupport::ActionableError
12
11
 
13
- action "Upgrade Logidze" do
14
- Rails::Generators.invoke("logidze:install", ["--update"])
15
- ActiveRecord::Tasks::DatabaseTasks.migrate
16
- if ActiveRecord::Base.dump_schema_after_migration
17
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(
18
- ActiveRecord::Base.connection_db_config
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Logidze
4
- VERSION = "1.2.3"
4
+ VERSION = "1.3.0"
5
5
  end
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.2.3
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: 2023-01-04 00:00:00.000000000 Z
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: '5.0'
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: '5.0'
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: '5.0'
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: '5.0'
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.18'
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.18'
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: '3.4'
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: '3.4'
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.5.0
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.3.11
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