logidze 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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