logidze 1.0.0 → 1.1.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: c273494bd5a85f1f9d967a8fb474bc8ef4df75d42bf756a96f4a83e6cc9ed1a2
4
- data.tar.gz: da791cced5e41ff2025ba349d16b394f6398a06b22782a5e78b7be1f18edbb48
3
+ metadata.gz: e6c441d5e6e60ef695eb354e4bfb5be9fa182bd8b70755d829aee6eb2781e401
4
+ data.tar.gz: d64374a0d6327f2f713fd7341c31de93472f32d045cb5c825c35874c756307d4
5
5
  SHA512:
6
- metadata.gz: 30e03d1adf876881e88e630774963d3a347b3c3114a2e345177d9c6b6480bab9bfd76d572fe681dd5a9b006cd2c212ed07e1a340fbc0a9275df48c6da0d11553
7
- data.tar.gz: 52663d375ac840f39d783eb2441b104b9ab910d3ee93d8ca309be9d9be626396caa6d9daac72d348e0d225945f37948bb197f18928bc865a9d2114735520cecb
6
+ metadata.gz: f3f453e410de263ed8b0704aece35e6274dfc19c9074a8f75450e84033318b149ae81c9e1f48cc9d515e523b95c98c14cbd3453a7c41db1690febfe8060f640d
7
+ data.tar.gz: dbf4a22b357889bc0d2aaeda52bffc8bc245cdc12d2cb34d0f7c9ccec7c0f60807ecc3cbd5272fee567420024309f8be05f6e1e8c9f45334ab3d2ad155a97a12
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change log
2
2
 
3
+ ## master (unreleased)
4
+
5
+ ## 1.1.0 (2021-03-31)
6
+
7
+ - Add pending upgrade checks [Experimental]. ([@skryukov][])
8
+
9
+ Now Logidze can check for a pending upgrade. Use `Logidze.pending_upgrade = :warn` to be notified by warning, or `Logidze.pending_upgrade = :error` if you want Logidze to raise an error.
10
+
11
+ - [Fixes [#171](https://github.com/palkan/logidze/issues/171)] Stringify jsonb column values within snapshots. ([@skryukov][])
12
+
13
+ - [Fixes [#175](https://github.com/palkan/logidze/issues/175)] Set dynamic ActiveRecord version for migrations. ([@skryukov][])
14
+
15
+ - [Fixes [#184](https://github.com/palkan/logidze/issues/184)] Remove Rails meta-gem dependency ([@bf4][])
16
+
3
17
  ## 1.0.0 (2020-11-09)
4
18
 
5
19
  - Add `--name` option to model generator to specify the migration name. ([@palkan][])
@@ -332,3 +346,4 @@ This is a quick fix for a more general problem (see [#59](https://github.com/pal
332
346
  [@zocoi]: https://github.com/zocoi
333
347
  [@duderman]: https://github.com/duderman
334
348
  [@oleg-kiviljov]: https://github.com/oleg-kiviljov
349
+ [@skryukov]: https://github.com/skryukov
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](http://cultofmartians.com)
2
2
  [![Gem Version](https://badge.fury.io/rb/logidze.svg)](https://rubygems.org/gems/logidze)
3
- ![Build](https://github.com/palkan/logidze/workflows/Build/badge.svg)
3
+ [![Build](https://github.com/palkan/logidze/workflows/Build/badge.svg)](https://github.com/palkan/logidze/actions)
4
4
  [![Open Source Helpers](https://www.codetriage.com/palkan/logidze/badges/users.svg)](https://www.codetriage.com/palkan/logidze)
5
5
 
6
6
  # Logidze
@@ -22,16 +22,16 @@ Other requirements:
22
22
 
23
23
  ## Links
24
24
 
25
+ - [Logidze 1.0: Active Record, Postgres, Rails, and time travel](https://evilmartians.com/chronicles/logidze-1-0-active-record-postgresql-rails-and-time-travel?utm_source=logidze)
25
26
  - [Logidze: for all those tired of versioning data](https://evilmartians.com/chronicles/introducing-logidze?utm_source=logidze)
26
27
 
27
28
  ## Table of contents
28
29
 
29
- - [Main concepts](#main-concepts)
30
30
  - [Installation & Configuration](#installation)
31
31
  - [Using with schema.rb](#using-with-schemarb)
32
32
  - [Configuring models](#configuring-models)
33
33
  - [Backfill data](#backfill-data)
34
- - [Log size limit](#log-size-limit)
34
+ - [Log size limits](#log-size-limits)
35
35
  - [Tracking only selected columns](#tracking-only-selected-columns)
36
36
  - [Logs timestamps](#logs-timestamps)
37
37
  - [Usage](#usage)
@@ -439,7 +439,7 @@ See also the discussion: [#61](https://github.com/palkan/logidze/issues/61).
439
439
  We try to make an upgrade process as simple as possible. For now, the only required action is to create and run a migration:
440
440
 
441
441
  ```sh
442
- rails generate logidze:install --update
442
+ bundle exec rails generate logidze:install --update
443
443
  ```
444
444
 
445
445
  This updates core `logdize_logger` DB function. No need to update tables or triggers.
@@ -449,31 +449,37 @@ This updates core `logdize_logger` DB function. No need to update tables or trig
449
449
  If you want to update Logidze settings for the model, run migration with `--update` flag:
450
450
 
451
451
  ```sh
452
- rails generate logidze:model Post --update --only=title,body,rating
452
+ bundle exec rails generate logidze:model Post --update --only=title,body,rating
453
453
  ```
454
454
 
455
455
  You can also use the `--name` option to specify the migration name to avoid duplicate migration names:
456
456
 
457
457
  ```sh
458
- $ rails generate logidze:model Post --update --only=title,body,rating --name add_only_filter_to_posts_log_data
458
+ $ bundle exec rails generate logidze:model Post --update --only=title,body,rating --name add_only_filter_to_posts_log_data
459
459
 
460
460
  create db/migrate/20202309142344_add_only_filter_to_posts_log_data.rb
461
461
  ```
462
462
 
463
+ ### Pending upgrade check [Experimental]
464
+
465
+ Logidze can check for a pending upgrade. Use `Logidze.pending_upgrade = :warn` to be notified by warning, or `Logidze.pending_upgrade = :error` if you want Logidze to raise an error.
466
+
463
467
  ### Upgrading from 0.x to 1.0 (edge)
464
468
 
465
469
  #### Schema and migrations
466
470
 
467
- Most SQL functions definitions has changed without backward compatibility.
471
+ Most SQL function definitions have changed without backward compatibility.
468
472
  Perform the following steps to upgrade:
469
473
 
470
- 1. Re-install Logidze: `rails generate logidze:install --update`.
474
+ 1. Re-install Logidze: `bundle exec rails generate logidze:install --update`.
475
+
476
+ 1. Re-install Logidze triggers **for all models**: `bundle exec rails generate logidze:model <model> --update`.
471
477
 
472
- 1. Re-install Logidze triggers **for all models**: `rails generate logidze:model <model> --update`.
478
+ **NOTE:** If you had previously specified whitelist/blacklist attributes, you will need to include the `--only`/`--except` [option](#tracking-only-selected-columns) as appropriate. You can easily copy these column lists from the previous logidze migration for the model.
473
479
 
474
480
  1. Remove the `include Logidze::Migration` line from the old migration files (if any)—this module has been removed.
475
481
 
476
- Rewrite the migrations to not use the `#current_setting(name)` and `#current_setting_missing_supported?` methods or copy them from the latest [0.x release](https://github.com/palkan/logidze/blob/0-stable/lib/logidze/migration.rb).
482
+ Rewrite legacy logidze migrations to not use the `#current_setting(name)` and `#current_setting_missing_supported?` methods, or copy them from the latest [0.x release](https://github.com/palkan/logidze/blob/0-stable/lib/logidze/migration.rb).
477
483
 
478
484
  #### API changes
479
485
 
@@ -1,5 +1,5 @@
1
- -- version: 1
2
1
  CREATE OR REPLACE FUNCTION logidze_compact_history(log_data jsonb, cutoff integer DEFAULT 1) RETURNS jsonb AS $body$
2
+ -- version: 1
3
3
  DECLARE
4
4
  merged jsonb;
5
5
  BEGIN
@@ -1,5 +1,5 @@
1
- -- version: 1
2
1
  CREATE OR REPLACE FUNCTION logidze_filter_keys(obj jsonb, keys text[], include_columns boolean DEFAULT false) RETURNS jsonb AS $body$
2
+ -- version: 1
3
3
  DECLARE
4
4
  res jsonb;
5
5
  key text;
@@ -1,5 +1,5 @@
1
- -- version: 1
2
1
  CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
2
+ -- version: 1
3
3
  DECLARE
4
4
  changes jsonb;
5
5
  version jsonb;
@@ -1,7 +1,8 @@
1
- -- version: 1
2
1
  CREATE OR REPLACE FUNCTION logidze_snapshot(item jsonb, ts_column text DEFAULT NULL, columns text[] DEFAULT NULL, include_columns boolean DEFAULT false) RETURNS jsonb AS $body$
2
+ -- version: 2
3
3
  DECLARE
4
4
  ts timestamp with time zone;
5
+ k text;
5
6
  BEGIN
6
7
  IF ts_column IS NULL THEN
7
8
  ts := statement_timestamp();
@@ -13,6 +14,13 @@ CREATE OR REPLACE FUNCTION logidze_snapshot(item jsonb, ts_column text DEFAULT N
13
14
  item := logidze_filter_keys(item, columns, include_columns);
14
15
  END IF;
15
16
 
17
+ FOR k IN (SELECT key FROM jsonb_each(item))
18
+ LOOP
19
+ IF jsonb_typeof(item->k) = 'object' THEN
20
+ item := jsonb_set(item, ARRAY[k], to_jsonb(item->>k));
21
+ END IF;
22
+ END LOOP;
23
+
16
24
  return json_build_object(
17
25
  'v', 1,
18
26
  'h', jsonb_build_array(
@@ -1,5 +1,5 @@
1
- -- version: 1
2
1
  CREATE OR REPLACE FUNCTION logidze_version(v bigint, data jsonb, ts timestamp with time zone) RETURNS jsonb AS $body$
2
+ -- version: 1
3
3
  DECLARE
4
4
  buf jsonb;
5
5
  BEGIN
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "rails/generators"
4
4
  require "rails/generators/active_record"
5
+ require "logidze/utils/function_definitions"
5
6
  require_relative "../inject_sql"
6
7
  require_relative "../fx_helper"
7
8
 
@@ -14,8 +15,6 @@ module Logidze
14
15
  include InjectSql
15
16
  include FxHelper
16
17
 
17
- class FuncDef < Struct.new(:name, :version, :signature); end
18
-
19
18
  source_root File.expand_path("templates", __dir__)
20
19
  source_paths << File.expand_path("functions", __dir__)
21
20
 
@@ -80,21 +79,7 @@ module Logidze
80
79
  end
81
80
 
82
81
  def function_definitions
83
- @function_definitions ||=
84
- begin
85
- Dir.glob(File.join(__dir__, "functions", "*.sql")).map do |path|
86
- name = path.match(/([^\/]+)\.sql/)[1]
87
-
88
- file = File.open(path)
89
- header = file.readline
90
-
91
- version = header.match(/version:\s+(\d+)/)[1].to_i
92
- parameters = file.readline.match(/CREATE OR REPLACE FUNCTION\s+[\w_]+\((.*)\)/)[1]
93
- signature = parameters.split(/\s*,\s*/).map { |param| param.split(/\s+/, 2).last.sub(/\s+DEFAULT .*$/, "") }.join(", ")
94
-
95
- FuncDef.new(name, version, signature)
96
- end
97
- end
82
+ @function_definitions ||= Logidze::Utils::FunctionDefinitions.from_fs
98
83
  end
99
84
  end
100
85
 
@@ -1,4 +1,4 @@
1
- class <%= @migration_class_name %> < ActiveRecord::Migration[5.0]
1
+ class <%= @migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
3
  enable_extension :hstore
4
4
  end
@@ -1,4 +1,4 @@
1
- class <%= @migration_class_name %> < ActiveRecord::Migration[5.0]
1
+ class <%= @migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def up
3
3
  <%- if update? -%>
4
4
  # Drop legacy functions (<1.0)
@@ -1,4 +1,4 @@
1
- class <%= @migration_class_name %> < ActiveRecord::Migration[5.0]
1
+ class <%= @migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
3
  <%- if update? -%>
4
4
  reversible do |dir|
@@ -1,4 +1,4 @@
1
- class <%= @migration_class_name %> < ActiveRecord::Migration[5.0]
1
+ class <%= @migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
3
  <%- unless update? || only_trigger? -%>
4
4
  add_column :<%= table_name %>, :log_data, :jsonb
data/lib/logidze.rb CHANGED
@@ -26,6 +26,8 @@ module Logidze
26
26
  attr_accessor :ignore_log_data_by_default
27
27
  # Whether #at should return self or nil when log_data is nil
28
28
  attr_accessor :return_self_if_log_data_is_empty
29
+ # Determines what Logidze should do when upgrade is needed (:raise | :warn | :ignore)
30
+ attr_reader :on_pending_upgrade
29
31
 
30
32
  # Temporary disable DB triggers.
31
33
  #
@@ -35,7 +37,7 @@ module Logidze
35
37
  with_logidze_setting("logidze.disabled", "on") { yield }
36
38
  end
37
39
 
38
- # Instructure Logidze to create a full snapshot for the new versions, not a diff
40
+ # Instruct Logidze to create a full snapshot for the new versions, not a diff
39
41
  #
40
42
  # @example
41
43
  # Logidze.with_full_snapshot { post.touch }
@@ -43,6 +45,13 @@ module Logidze
43
45
  with_logidze_setting("logidze.full_snapshot", "on") { yield }
44
46
  end
45
47
 
48
+ def on_pending_upgrade=(mode)
49
+ if %i[raise warn ignore].exclude? mode
50
+ raise ArgumentError, "Unknown on_pending_upgrade option `#{mode.inspect}`. Expecting :raise, :warn or :ignore"
51
+ end
52
+ @on_pending_upgrade = mode
53
+ end
54
+
46
55
  private
47
56
 
48
57
  def with_logidze_setting(name, value)
@@ -59,4 +68,5 @@ module Logidze
59
68
  self.associations_versioning = false
60
69
  self.ignore_log_data_by_default = false
61
70
  self.return_self_if_log_data_is_empty = true
71
+ self.on_pending_upgrade = :ignore
62
72
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "logidze"
4
+ require "logidze/utils/check_pending"
4
5
 
5
6
  module Logidze
6
7
  class Engine < Rails::Engine # :nodoc:
@@ -11,5 +12,13 @@ module Logidze
11
12
  ActiveRecord::Base.send :include, Logidze::HasLogidze
12
13
  end
13
14
  end
15
+
16
+ initializer "check Logidze function versions" do |app|
17
+ if config.logidze.on_pending_upgrade != :ignore
18
+ ActiveSupport.on_load(:active_record) do
19
+ app.config.app_middleware.use Logidze::Utils::CheckPending
20
+ end
21
+ end
22
+ end
14
23
  end
15
24
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./function_definitions"
4
+ require_relative "./pending_migration_error"
5
+
6
+ module Logidze
7
+ module Utils
8
+ # This Rack middleware is used to verify that all functions are up to date
9
+ class CheckPending
10
+ def initialize(app)
11
+ @app = app
12
+ @needs_check = true
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ delegate :connection, to: ActiveRecord::Base
17
+
18
+ def call(env)
19
+ @mutex.synchronize do
20
+ if @needs_check
21
+ notify_or_raise! if needs_migration?
22
+ end
23
+ @needs_check = false
24
+ end
25
+
26
+ @app.call(env)
27
+ end
28
+
29
+ private
30
+
31
+ def notify_or_raise!
32
+ case Logidze.on_pending_upgrade
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."\
38
+ "\n**************************************************\n\n"
39
+ when :raise
40
+ raise Logidze::Utils::PendingMigrationError, "Logidze needs upgrade. Run `bundle exec rails generate logidze:install --update` and apply generated migration."
41
+ end
42
+ end
43
+
44
+ def needs_migration?
45
+ (library_function_versions - pg_function_versions).any?
46
+ end
47
+
48
+ def pg_function_versions
49
+ Logidze::Utils::FunctionDefinitions.from_db.map { |func| [func.name, func.version] }
50
+ end
51
+
52
+ def library_function_versions
53
+ @library_function_versions ||= Logidze::Utils::FunctionDefinitions.from_fs.map { |func| [func.name, func.version] }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logidze
4
+ module Utils
5
+ class FuncDef < Struct.new(:name, :version, :signature); end
6
+
7
+ module FunctionDefinitions
8
+ class << self
9
+ def from_fs
10
+ function_paths = Dir.glob(File.join(__dir__, "..", "..", "generators", "logidze", "install", "functions", "*.sql"))
11
+ function_paths.map do |path|
12
+ name = path.match(/([^\/]+)\.sql/)[1]
13
+
14
+ file = File.open(path)
15
+ header, version_comment = file.readline, file.readline
16
+
17
+ signature = parse_signature(header)
18
+ version = parse_version(version_comment)
19
+ FuncDef.new(name, version, signature)
20
+ end
21
+ end
22
+
23
+ def from_db
24
+ query = <<~SQL
25
+ SELECT pp.proname, pg_get_functiondef(pp.oid) AS definition
26
+ FROM pg_proc pp
27
+ WHERE pp.proname like 'logidze_%'
28
+ ORDER BY pp.oid;
29
+ SQL
30
+ ActiveRecord::Base.connection.execute(query).map do |row|
31
+ version = parse_version(row["definition"])
32
+ FuncDef.new(row["proname"], version, nil)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def parse_version(line)
39
+ line.match(/version:\s+(\d+)/)&.[](1).to_i
40
+ end
41
+
42
+ def parse_signature(line)
43
+ parameters = line.match(/CREATE OR REPLACE FUNCTION\s+[\w_]+\((.*)\)/)[1]
44
+ parameters.split(/\s*,\s*/).map { |param| param.split(/\s+/, 2).last.sub(/\s+DEFAULT .*$/, "") }.join(", ")
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Logidze
6
+ module Utils
7
+ class PendingMigrationError < StandardError
8
+ if Rails::VERSION::MAJOR >= 6
9
+ require "active_record"
10
+ require "active_support/actionable_error"
11
+ include ActiveSupport::ActionableError
12
+
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
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Logidze
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
metadata CHANGED
@@ -1,17 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logidze
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-09 00:00:00.000000000 Z
11
+ date: 2021-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - ">="
@@ -172,6 +186,9 @@ files:
172
186
  - lib/logidze/ignore_log_data/cast_attribute_patch.rb
173
187
  - lib/logidze/meta.rb
174
188
  - lib/logidze/model.rb
189
+ - lib/logidze/utils/check_pending.rb
190
+ - lib/logidze/utils/function_definitions.rb
191
+ - lib/logidze/utils/pending_migration_error.rb
175
192
  - lib/logidze/version.rb
176
193
  - lib/logidze/versioned_association.rb
177
194
  homepage: http://github.com/palkan/logidze