waldit 0.0.1
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 +7 -0
- data/.rspec +1 -0
- data/LICENSE.txt +21 -0
- data/README.md +1 -0
- data/Rakefile +12 -0
- data/lib/waldit/context.rb +44 -0
- data/lib/waldit/postgresql_adapter.rb +55 -0
- data/lib/waldit/railtie.rb +16 -0
- data/lib/waldit/record.rb +23 -0
- data/lib/waldit/version.rb +6 -0
- data/lib/waldit/watcher.rb +124 -0
- data/lib/waldit.rb +62 -0
- data/rbi/waldit.rbi +90 -0
- data/sig/waldit.rbs +56 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: afad3b16943e3984c6584b02ef8edb2fb8abb8413687726a657110018e9fcbd4
|
4
|
+
data.tar.gz: 934aacd6442c00b7fd40287360e2d8a1b52e0f37328711e029d36812d9cf94d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ff61b8c0d473390f5197ae4544ad932014483ba68cdb8972e79eee35d4747814c5e64ae60cc53a078653de82f483ae94e98db69b40796a8be0d38b1a9db694d1
|
7
|
+
data.tar.gz: a9e6c961b67f1be3335d773af2c42d76b7f74fd5d0eb48bd3cb5dce4cf355ce88144caa626ede05de357af47ce03986893bd0b9accab687bf4490c077fc8c787
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Rodrigo Navarro
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Waldit
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
task(:test) { sh "bundle exec rspec" }
|
6
|
+
|
7
|
+
task default: %i[build]
|
8
|
+
|
9
|
+
task("sig/waldit.rbi") { sh "bundle exec parlour" }
|
10
|
+
task("rbi/waldit.rbs" => "sig/waldit.rbi") { sh "rbs prototype rbi rbi/waldit.rbi > sig/waldit.rbs" }
|
11
|
+
|
12
|
+
Rake::Task["build"].enhance(["sig/waldit.rbi", "rbi/waldit.rbs"])
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module Waldit
|
5
|
+
module Context
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
Context = T.type_alias { T::Hash[T.any(String, Symbol), T.untyped] }
|
9
|
+
|
10
|
+
sig do
|
11
|
+
type_parameters(:U)
|
12
|
+
.params(context: Context, block: T.proc.returns(T.type_parameter(:U)))
|
13
|
+
.returns(T.type_parameter(:U))
|
14
|
+
end
|
15
|
+
def with_context(context, &block)
|
16
|
+
current_context = self.context || {}
|
17
|
+
Thread.current[:waldit_context] ||= []
|
18
|
+
Thread.current[:waldit_context].push(current_context.merge(context.as_json))
|
19
|
+
block.call
|
20
|
+
ensure
|
21
|
+
Thread.current[:waldit_context].pop
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(T.nilable(Context)) }
|
25
|
+
def context
|
26
|
+
Thread.current[:waldit_context]&.last
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(added_context: Context).void }
|
30
|
+
def add_context(added_context)
|
31
|
+
if (context = self.context)
|
32
|
+
context.merge!(added_context.as_json)
|
33
|
+
else
|
34
|
+
new_context(added_context)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { params(context: Context).void }
|
39
|
+
def new_context(context = {})
|
40
|
+
Thread.current[:waldit_context] ||= []
|
41
|
+
Thread.current[:waldit_context].push(context.as_json)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: ignore
|
3
|
+
|
4
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
5
|
+
|
6
|
+
module Waldit
|
7
|
+
class PostgreSQLAdapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
8
|
+
READ_QUERY_REGEXP = build_read_query_regexp(%i[close declare fetch move set show])
|
9
|
+
|
10
|
+
def raw_execute(sql, ...)
|
11
|
+
return super if READ_QUERY_REGEXP.match? sql
|
12
|
+
return super if @current_waldit_context == Waldit.context.hash
|
13
|
+
|
14
|
+
if transaction_open?
|
15
|
+
set_waldit_context!
|
16
|
+
super
|
17
|
+
|
18
|
+
elsif Waldit.context
|
19
|
+
# We are trying to execute a query with waldit context while not in a transaction, so we start one
|
20
|
+
transaction do
|
21
|
+
set_waldit_context!
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def begin_db_transaction(...)
|
31
|
+
@current_waldit_context = nil.hash
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def begin_isolated_db_transaction(...)
|
36
|
+
@current_waldit_context = nil.hash
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def commit_db_transaction
|
41
|
+
@current_waldit_context = nil.hash
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def set_waldit_context!
|
48
|
+
context = Waldit.context
|
49
|
+
prefix = Waldit.context_prefix
|
50
|
+
context_hash = context.hash
|
51
|
+
set_wal_watcher_context(context, prefix:) if context_hash != @current_waldit_context
|
52
|
+
@current_waldit_context = context_hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
require "rails/railtie"
|
5
|
+
|
6
|
+
module Waldit
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
config.before_configuration do
|
9
|
+
ActiveRecord::ConnectionAdapters.register(
|
10
|
+
"postgresqlwaldit",
|
11
|
+
"Waldit::PostgreSQLAdapter",
|
12
|
+
"waldit/postgresql_adapter",
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module Waldit
|
5
|
+
module Record
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
abstract!
|
9
|
+
|
10
|
+
sig { abstract.returns(T::Hash[T.any(String, Symbol), T.untyped]) }
|
11
|
+
def new; end
|
12
|
+
|
13
|
+
sig { abstract.returns(T::Hash[T.any(String, Symbol), T.untyped]) }
|
14
|
+
def old; end
|
15
|
+
|
16
|
+
sig { returns(T::Hash[T.any(String, Symbol), [T.untyped, T.untyped]]) }
|
17
|
+
def diff
|
18
|
+
(old.keys | new.keys).reduce({}.with_indifferent_access) do |diff, key|
|
19
|
+
old[key] != new[key] ? diff.merge(key => [old[key], new[key]]) : diff
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require "wal"
|
5
|
+
|
6
|
+
module Waldit
|
7
|
+
class Watcher < Wal::StreamingWatcher
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(event: T.any(InsertEvent, UpdateEvent, DeleteEvent)).void }
|
11
|
+
def audit_event(event)
|
12
|
+
return unless event.primary_key
|
13
|
+
|
14
|
+
audit = {
|
15
|
+
transaction_id: event.transaction_id,
|
16
|
+
lsn: event.lsn,
|
17
|
+
context: event.context,
|
18
|
+
table_name: event.table,
|
19
|
+
primary_key: event.primary_key,
|
20
|
+
}
|
21
|
+
|
22
|
+
unique_by = %i[table_name primary_key transaction_id]
|
23
|
+
|
24
|
+
case event
|
25
|
+
when InsertEvent
|
26
|
+
record.upsert(
|
27
|
+
audit.merge(action: "insert", new: event.new),
|
28
|
+
unique_by:,
|
29
|
+
on_duplicate: :update,
|
30
|
+
)
|
31
|
+
|
32
|
+
when UpdateEvent
|
33
|
+
return if event.diff.without(ignored_columns(event.table)).empty?
|
34
|
+
record.upsert(
|
35
|
+
audit.merge(action: "update", old: event.old, new: event.new),
|
36
|
+
unique_by:,
|
37
|
+
on_duplicate: :update,
|
38
|
+
update_only: %w[new],
|
39
|
+
)
|
40
|
+
|
41
|
+
when DeleteEvent
|
42
|
+
case record.where(audit.slice(*unique_by)).pluck(:action, :old).first
|
43
|
+
in ["insert", _]
|
44
|
+
# We are deleting a record that was inserted on this transaction, which means we don't need to audit anything,
|
45
|
+
# as the record was never commited
|
46
|
+
record.where(audit.slice(*unique_by)).delete_all
|
47
|
+
|
48
|
+
in ["update", old]
|
49
|
+
# We are deleting a record we updated on this transaction. Here we are making sure we keep the correct previous
|
50
|
+
# state, and not the state at the moment of the deletion
|
51
|
+
record.upsert(
|
52
|
+
audit.merge(action: "delete", old:, new: {}),
|
53
|
+
unique_by:,
|
54
|
+
on_duplicate: :update,
|
55
|
+
)
|
56
|
+
|
57
|
+
in ["delete", _]
|
58
|
+
# This should never happend, we wouldn't be able to delete a record that was already deleted on this transaction
|
59
|
+
|
60
|
+
else
|
61
|
+
# Finally the most common case: just deleting a record not created or updated on this transaction
|
62
|
+
record.upsert(
|
63
|
+
audit.merge(action: "delete", old:),
|
64
|
+
unique_by:,
|
65
|
+
on_duplicate: :update,
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
sig { override.params(events: T::Enumerator[Event]).void }
|
72
|
+
def on_transaction_events(events)
|
73
|
+
counter = 0
|
74
|
+
catch :finish do
|
75
|
+
loop do
|
76
|
+
record.transaction do
|
77
|
+
events.each do |event|
|
78
|
+
case event
|
79
|
+
when CommitTransactionEvent
|
80
|
+
record
|
81
|
+
.where(transaction_id: event.transaction_id)
|
82
|
+
.update_all(commited_at: event.timestamp) if counter > 0
|
83
|
+
# Using throw to break the outside loop and finish the thread gracefully
|
84
|
+
throw :finish
|
85
|
+
|
86
|
+
when InsertEvent, UpdateEvent, DeleteEvent
|
87
|
+
audit_event(event)
|
88
|
+
|
89
|
+
counter += 1
|
90
|
+
# We break here to force a commit, so we don't keep a single big transaction pending
|
91
|
+
break if counter % max_transaction_size == 0
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
sig { params(table: String).returns(T::Boolean) }
|
100
|
+
def should_watch_table?(table)
|
101
|
+
Waldit.watched_tables.call(table)
|
102
|
+
end
|
103
|
+
|
104
|
+
sig { params(prefix: String).returns(T::Boolean) }
|
105
|
+
def valid_context_prefix?(prefix)
|
106
|
+
prefix == Waldit.context_prefix
|
107
|
+
end
|
108
|
+
|
109
|
+
sig { params(table: String).returns(T::Array[String]) }
|
110
|
+
def ignored_columns(table)
|
111
|
+
Waldit.ignored_columns.call(table)
|
112
|
+
end
|
113
|
+
|
114
|
+
sig { returns(Integer) }
|
115
|
+
def max_transaction_size
|
116
|
+
Waldit.max_transaction_size
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { returns(T.class_of(ActiveRecord::Base)) }
|
120
|
+
def record
|
121
|
+
Waldit.model
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/waldit.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require_relative "waldit/version"
|
5
|
+
require_relative "waldit/context"
|
6
|
+
require_relative "waldit/railtie"
|
7
|
+
require_relative "waldit/record"
|
8
|
+
require_relative "waldit/watcher"
|
9
|
+
|
10
|
+
module Waldit
|
11
|
+
extend T::Sig
|
12
|
+
extend Waldit::Context
|
13
|
+
|
14
|
+
class << self
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { returns(String) }
|
18
|
+
attr_accessor :context_prefix
|
19
|
+
|
20
|
+
sig { returns(T.proc.params(table: String).returns(T::Boolean)) }
|
21
|
+
attr_reader :watched_tables
|
22
|
+
|
23
|
+
sig { params(tables: T.any(T::Array[String], T.proc.params(table: String).returns(T::Boolean))).void }
|
24
|
+
def watched_tables=(tables)
|
25
|
+
case tables
|
26
|
+
when Array
|
27
|
+
@watched_tables = -> table { tables.include? table }
|
28
|
+
else
|
29
|
+
@watched_tables = tables
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { returns(T.proc.params(table: String).returns(T::Array[String])) }
|
34
|
+
attr_accessor :ignored_columns
|
35
|
+
|
36
|
+
sig { returns(Integer) }
|
37
|
+
attr_accessor :max_transaction_size
|
38
|
+
|
39
|
+
sig { returns(T.class_of(ActiveRecord::Base)) }
|
40
|
+
attr_accessor :model
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(block: T.proc.params(config: T.class_of(Waldit)).void).void }
|
44
|
+
def self.configure(&block)
|
45
|
+
yield self
|
46
|
+
end
|
47
|
+
|
48
|
+
configure do |config|
|
49
|
+
config.context_prefix = "waldit_context"
|
50
|
+
|
51
|
+
config.watched_tables = -> table { table != "waldit" }
|
52
|
+
|
53
|
+
config.ignored_columns = -> table { %w[created_at updated_at] }
|
54
|
+
|
55
|
+
config.max_transaction_size = 10_000
|
56
|
+
|
57
|
+
config.model = Class.new(ActiveRecord::Base) do
|
58
|
+
include Waldit::Record
|
59
|
+
self.table_name = "waldit"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/rbi/waldit.rbi
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# typed: strong
|
2
|
+
module Waldit
|
3
|
+
extend T::Sig
|
4
|
+
extend Waldit::Context
|
5
|
+
VERSION = "0.0.1"
|
6
|
+
|
7
|
+
class << self
|
8
|
+
sig { returns(String) }
|
9
|
+
attr_accessor :context_prefix
|
10
|
+
|
11
|
+
sig { returns(T.proc.params(table: String).returns(T::Boolean)) }
|
12
|
+
attr_reader :watched_tables
|
13
|
+
|
14
|
+
sig { returns(T.proc.params(table: String).returns(T::Array[String])) }
|
15
|
+
attr_accessor :ignored_columns
|
16
|
+
|
17
|
+
sig { returns(Integer) }
|
18
|
+
attr_accessor :max_transaction_size
|
19
|
+
|
20
|
+
sig { returns(T.class_of(ActiveRecord::Base)) }
|
21
|
+
attr_accessor :model
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { params(tables: T.any(T::Array[String], T.proc.params(table: String).returns(T::Boolean))).void }
|
25
|
+
def self.watched_tables=(tables); end
|
26
|
+
|
27
|
+
sig { params(block: T.proc.params(config: T.class_of(Waldit)).void).void }
|
28
|
+
def self.configure(&block); end
|
29
|
+
|
30
|
+
module Context
|
31
|
+
extend T::Sig
|
32
|
+
Context = T.type_alias { T::Hash[T.any(String, Symbol), T.untyped] }
|
33
|
+
|
34
|
+
sig { type_parameters(:U).params(context: Context, block: T.proc.returns(T.type_parameter(:U))).returns(T.type_parameter(:U)) }
|
35
|
+
def with_context(context, &block); end
|
36
|
+
|
37
|
+
sig { returns(T.nilable(Context)) }
|
38
|
+
def context; end
|
39
|
+
|
40
|
+
sig { params(added_context: Context).void }
|
41
|
+
def add_context(added_context); end
|
42
|
+
|
43
|
+
sig { params(context: Context).void }
|
44
|
+
def new_context(context = {}); end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Railtie < Rails::Railtie
|
48
|
+
end
|
49
|
+
|
50
|
+
module Record
|
51
|
+
abstract!
|
52
|
+
|
53
|
+
extend T::Sig
|
54
|
+
extend T::Helpers
|
55
|
+
|
56
|
+
sig { abstract.returns(T::Hash[T.any(String, Symbol), T.untyped]) }
|
57
|
+
def new; end
|
58
|
+
|
59
|
+
sig { abstract.returns(T::Hash[T.any(String, Symbol), T.untyped]) }
|
60
|
+
def old; end
|
61
|
+
|
62
|
+
sig { returns(T::Hash[T.any(String, Symbol), [T.untyped, T.untyped]]) }
|
63
|
+
def diff; end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Watcher < Wal::StreamingWatcher
|
67
|
+
extend T::Sig
|
68
|
+
|
69
|
+
sig { params(event: T.any(InsertEvent, UpdateEvent, DeleteEvent)).void }
|
70
|
+
def audit_event(event); end
|
71
|
+
|
72
|
+
sig { override.params(events: T::Enumerator[Event]).void }
|
73
|
+
def on_transaction_events(events); end
|
74
|
+
|
75
|
+
sig { params(table: String).returns(T::Boolean) }
|
76
|
+
def should_watch_table?(table); end
|
77
|
+
|
78
|
+
sig { params(prefix: String).returns(T::Boolean) }
|
79
|
+
def valid_context_prefix?(prefix); end
|
80
|
+
|
81
|
+
sig { params(table: String).returns(T::Array[String]) }
|
82
|
+
def ignored_columns(table); end
|
83
|
+
|
84
|
+
sig { returns(Integer) }
|
85
|
+
def max_transaction_size; end
|
86
|
+
|
87
|
+
sig { returns(T.class_of(ActiveRecord::Base)) }
|
88
|
+
def record; end
|
89
|
+
end
|
90
|
+
end
|
data/sig/waldit.rbs
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# typed: strong
|
2
|
+
module Waldit
|
3
|
+
extend Waldit::Context
|
4
|
+
|
5
|
+
def self.watched_tables=: () -> String
|
6
|
+
| () -> ^(String table) -> bool
|
7
|
+
| () -> ^(String table) -> ::Array[String]
|
8
|
+
| () -> Integer
|
9
|
+
| () -> singleton(ActiveRecord::Base)
|
10
|
+
| (::Array[String] | ^(String table) -> bool tables) -> void
|
11
|
+
|
12
|
+
def self.configure: () { (singleton(Waldit) config) -> void } -> void
|
13
|
+
end
|
14
|
+
|
15
|
+
Waldit::VERSION: untyped
|
16
|
+
|
17
|
+
module Waldit::Context
|
18
|
+
def with_context: [U] (Context context) { () -> U } -> U
|
19
|
+
|
20
|
+
def context: () -> Context?
|
21
|
+
|
22
|
+
def add_context: (Context added_context) -> void
|
23
|
+
|
24
|
+
def new_context: (?Context context) -> void
|
25
|
+
end
|
26
|
+
|
27
|
+
Waldit::Waldit::Context::Context: untyped
|
28
|
+
|
29
|
+
class Waldit::Railtie < Rails::Railtie
|
30
|
+
end
|
31
|
+
|
32
|
+
module Waldit::Record
|
33
|
+
extend T::Helpers
|
34
|
+
|
35
|
+
def new: () -> ::Hash[String | Symbol, untyped]
|
36
|
+
|
37
|
+
def old: () -> ::Hash[String | Symbol, untyped]
|
38
|
+
|
39
|
+
def diff: () -> ::Hash[String | Symbol, [ untyped, untyped ]]
|
40
|
+
end
|
41
|
+
|
42
|
+
class Waldit::Watcher < Wal::StreamingWatcher
|
43
|
+
def audit_event: (InsertEvent | UpdateEvent | DeleteEvent event) -> void
|
44
|
+
|
45
|
+
def on_transaction_events: (::Enumerator[Event] events) -> void
|
46
|
+
|
47
|
+
def should_watch_table?: (String table) -> bool
|
48
|
+
|
49
|
+
def valid_context_prefix?: (String prefix) -> bool
|
50
|
+
|
51
|
+
def ignored_columns: (String table) -> ::Array[String]
|
52
|
+
|
53
|
+
def max_transaction_size: () -> Integer
|
54
|
+
|
55
|
+
def record: () -> singleton(ActiveRecord::Base)
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: waldit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rodrigo Navarro
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-06-29 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: wal
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.0.2
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.0.2
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activerecord
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '7'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rbs
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: sorbet
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: tapioca
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: parlour
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
description: Postgres based audit trail for your Active Records, with 100% consistency.
|
97
|
+
email:
|
98
|
+
- rnavarro@rnavarro.com.br
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- ".rspec"
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- lib/waldit.rb
|
108
|
+
- lib/waldit/context.rb
|
109
|
+
- lib/waldit/postgresql_adapter.rb
|
110
|
+
- lib/waldit/railtie.rb
|
111
|
+
- lib/waldit/record.rb
|
112
|
+
- lib/waldit/version.rb
|
113
|
+
- lib/waldit/watcher.rb
|
114
|
+
- rbi/waldit.rbi
|
115
|
+
- sig/waldit.rbs
|
116
|
+
homepage: https://github.com/reu/waldit
|
117
|
+
licenses:
|
118
|
+
- MIT
|
119
|
+
metadata:
|
120
|
+
homepage_uri: https://github.com/reu/waldit
|
121
|
+
source_code_uri: https://github.com/reu/waldit
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: 3.2.0
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubygems_version: 3.6.2
|
137
|
+
specification_version: 4
|
138
|
+
summary: Postgres based audit trail for Rails.
|
139
|
+
test_files: []
|