waldit 0.0.5 → 0.0.7

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: 8b76edf8a202620aff6e09391009e6c00ae0f26a404d1b81741c5cd3e339db66
4
- data.tar.gz: f0c7582d6af476ecf536aa4e96846eea620b8c3bd5c215c240d9e68056ade643
3
+ metadata.gz: f5c8f24220664dba4cbdda331668fff84cfdf94fa0bc12eb0632613cc83e65c0
4
+ data.tar.gz: ad904cc5fa30b2a7de5a5b9844242be0a7fb568695106035956996f84e889731
5
5
  SHA512:
6
- metadata.gz: 5aee8ef8ff8c9f0b6fc636b866a9fe72a08c1ba98e507df1b7d3771339130808c04c9fb32345a07a12416967d386be92bdfe3bfd1f3215dc723f337baf06278b
7
- data.tar.gz: e5f139662394131f2bfbe1af5ac3df85fa7b6202756ec67c02f0e679d11c78e2a62ecdb3a5bb188fa20a25ddee23699824266831f86b09e3284c0ad2584e9d6b
6
+ metadata.gz: 9fe4ef8b61035be5b3be5a531e3db9f4bf2b12bf419e00655694a378f2020a627a2f98d497ab9c4db79ca48129f23209a6685f0ae2a513c8c97110b81142cb6b
7
+ data.tar.gz: 495601d9aed573a7e2bde43f8a1a85d33209aca29485868c540a9b1fe729092fcb2a7a491c7553ef6544a89909fe278ae6e275d48a705a73ed2ed35fc0109389
data/lib/waldit/record.rb CHANGED
@@ -2,8 +2,22 @@
2
2
 
3
3
  module Waldit
4
4
  module Record
5
+ def new
6
+ return self[:new] if self[:new]
7
+
8
+ (self[:diff] || {}).transform_values { |_old, new| new }
9
+ end
10
+
11
+ def old
12
+ return self[:old] if self[:old]
13
+
14
+ (self[:diff] || {}).transform_values { |old, _new| old }
15
+ end
16
+
5
17
  def diff
6
- (old.keys | new.keys).reduce({}.with_indifferent_access) do |diff, key|
18
+ return self[:diff] if self[:diff]
19
+
20
+ (old.keys | new.keys).reduce({}) do |diff, key|
7
21
  old[key] != new[key] ? diff.merge(key => [old[key], new[key]]) : diff
8
22
  end
9
23
  end
@@ -17,8 +17,13 @@ module Waldit
17
17
  include ::Sidekiq::ServerMiddleware
18
18
 
19
19
  def call(job_instance, job, queue, &block)
20
- context = deserialize_context(job) || {}
21
- Waldit.with_context(context.merge(background_job: job_instance.class.to_s), &block)
20
+ background_job = case job["class"]
21
+ in "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
22
+ job["args"][0]["job_class"]
23
+ in klass
24
+ klass
25
+ end
26
+ Waldit.with_context((deserialize_context(job) || {}).merge(background_job:), &block)
22
27
  end
23
28
 
24
29
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Waldit
4
- VERSION = "0.0.5"
4
+ VERSION = "0.0.7"
5
5
  end
@@ -34,6 +34,7 @@ module Waldit
34
34
  def on_transaction_events(events)
35
35
  record.transaction do
36
36
  @connection = record.connection.raw_connection
37
+ tables = Set.new
37
38
  insert_prepared = false
38
39
  update_prepared = false
39
40
  delete_prepared = false
@@ -43,7 +44,34 @@ module Waldit
43
44
  when CommitTransactionEvent
44
45
  record.where(transaction_id: event.transaction_id).update_all(commited_at: event.timestamp)
45
46
 
47
+ changes = [:old, :new, :diff]
48
+ .map { |diff| [diff, tables.filter { |table| Waldit.store_changes.call(table).include? diff }] }
49
+ .to_h
50
+
51
+ log_new = (changes[:new] || []).map { |table| "'#{table}'" }.join(",")
52
+ log_old = (changes[:old] || []).map { |table| "'#{table}'" }.join(",")
53
+ log_diff = (changes[:diff] || []).map { |table| "'#{table}'" }.join(",")
54
+
55
+ record.where(transaction_id: event.transaction_id, action: "update").update_all(<<~SQL)
56
+ new = CASE WHEN table_name = ANY (ARRAY[#{log_new}]::varchar[]) THEN new ELSE null END,
57
+ old = CASE WHEN table_name = ANY (ARRAY[#{log_old}]::varchar[]) THEN old ELSE null END,
58
+ diff =
59
+ CASE WHEN table_name = ANY (ARRAY[#{log_diff}]::varchar[]) THEN (
60
+ SELECT
61
+ jsonb_object_agg(
62
+ coalesce(old_kv.key, new_kv.key),
63
+ jsonb_build_array(old_kv.value, new_kv.value)
64
+ )
65
+ FROM jsonb_each(old) AS old_kv
66
+ FULL OUTER JOIN jsonb_each(new) AS new_kv ON old_kv.key = new_kv.key
67
+ WHERE old_kv.value IS DISTINCT FROM new_kv.value
68
+ )
69
+ ELSE null
70
+ END
71
+ SQL
72
+
46
73
  when InsertEvent
74
+ tables << event.table
47
75
  unless insert_prepared
48
76
  prepare_insert
49
77
  insert_prepared = true
@@ -51,6 +79,7 @@ module Waldit
51
79
  audit_event(event)
52
80
 
53
81
  when UpdateEvent
82
+ tables << event.table
54
83
  unless update_prepared
55
84
  prepare_update
56
85
  update_prepared = true
@@ -58,6 +87,7 @@ module Waldit
58
87
  audit_event(event)
59
88
 
60
89
  when DeleteEvent
90
+ tables << event.table
61
91
  unless delete_prepared
62
92
  prepare_delete
63
93
  prepare_delete_cleanup
@@ -111,8 +141,8 @@ module Waldit
111
141
 
112
142
  def prepare_delete
113
143
  @connection.prepare("waldit_delete", <<~SQL)
114
- INSERT INTO #{record.table_name} (transaction_id, lsn, table_name, primary_key, action, context, old, new)
115
- VALUES ($1, $2, $3, $4, 'delete'::waldit_action, $5, $6, '{}'::jsonb)
144
+ INSERT INTO #{record.table_name} (transaction_id, lsn, table_name, primary_key, action, context, old)
145
+ VALUES ($1, $2, $3, $4, 'delete'::waldit_action, $5, $6)
116
146
  ON CONFLICT (table_name, primary_key, transaction_id)
117
147
  DO UPDATE SET old = #{record.table_name}.old
118
148
  SQL
data/lib/waldit.rb CHANGED
@@ -21,6 +21,21 @@ module Waldit
21
21
  end
22
22
  end
23
23
 
24
+ attr_reader :store_changes
25
+
26
+ def store_changes=(changes)
27
+ case changes
28
+ when Symbol
29
+ changes = [changes].to_set
30
+ @store_changes = -> table { changes }
31
+ when Array
32
+ changes = changes.map(&:to_sym).to_set
33
+ @store_changes = -> table { changes }
34
+ else
35
+ @store_changes = changes
36
+ end
37
+ end
38
+
24
39
  attr_accessor :ignored_columns
25
40
  attr_accessor :max_transaction_size
26
41
  attr_accessor :model
@@ -36,6 +51,8 @@ module Waldit
36
51
 
37
52
  config.watched_tables = -> table { table != "waldit" }
38
53
 
54
+ config.store_changes = -> table { %i[old new diff] }
55
+
39
56
  config.ignored_columns = -> table { %w[created_at updated_at] }
40
57
 
41
58
  config.max_transaction_size = 10_000
data/rbi/waldit.rbi CHANGED
@@ -2,7 +2,7 @@
2
2
  module Waldit
3
3
  extend T::Sig
4
4
  extend Waldit::Context
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.6"
6
6
 
7
7
  class << self
8
8
  sig { returns(String) }
@@ -11,6 +11,9 @@ module Waldit
11
11
  sig { returns(T.proc.params(table: String).returns(T::Boolean)) }
12
12
  attr_reader :watched_tables
13
13
 
14
+ sig { returns(T.proc.params(table: String).returns(T::Array[Symbol])) }
15
+ attr_reader :store_changes
16
+
14
17
  sig { returns(T.proc.params(table: String).returns(T::Array[String])) }
15
18
  attr_accessor :ignored_columns
16
19
 
@@ -24,6 +27,9 @@ module Waldit
24
27
  sig { params(tables: T.any(T::Array[String], T.proc.params(table: String).returns(T::Boolean))).void }
25
28
  def self.watched_tables=(tables); end
26
29
 
30
+ sig { params(changes: T.any(Symbol, T::Array[Symbol], T.proc.params(table: String).returns(T::Array[Symbol]))).void }
31
+ def self.store_changes=(changes); end
32
+
27
33
  sig { params(block: T.proc.params(config: T.class_of(Waldit)).void).void }
28
34
  def self.configure(&block); end
29
35
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waldit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Navarro
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-08-11 00:00:00.000000000 Z
10
+ date: 2025-08-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: wal
@@ -71,7 +71,6 @@ files:
71
71
  - lib/waldit/version.rb
72
72
  - lib/waldit/watcher.rb
73
73
  - rbi/waldit.rbi
74
- - sig/waldit.rbs
75
74
  homepage: https://github.com/reu/waldit
76
75
  licenses:
77
76
  - MIT
data/sig/waldit.rbs DELETED
@@ -1,73 +0,0 @@
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
- module Waldit::Sidekiq
43
- end
44
-
45
- class Waldit::Waldit::Sidekiq::SaveContext
46
- include ::Sidekiq::ClientMiddleware
47
-
48
- def call: (untyped job_class, untyped job, untyped queue, untyped redis) -> untyped
49
- end
50
-
51
- class Waldit::Waldit::Sidekiq::LoadContext
52
- include ::Sidekiq::ServerMiddleware
53
-
54
- def call: (untyped job_instance, untyped job, untyped queue) { () -> untyped } -> untyped
55
-
56
- def deserialize_context: (untyped job) -> untyped
57
- end
58
-
59
- class Waldit::Watcher < Wal::StreamingWatcher
60
- def audit_event: (InsertEvent | UpdateEvent | DeleteEvent event) -> void
61
-
62
- def on_transaction_events: (::Enumerator[Event] events) -> void
63
-
64
- def should_watch_table?: (String table) -> bool
65
-
66
- def valid_context_prefix?: (String prefix) -> bool
67
-
68
- def ignored_columns: (String table) -> ::Array[String]
69
-
70
- def max_transaction_size: () -> Integer
71
-
72
- def record: () -> singleton(ActiveRecord::Base)
73
- end