wal 0.0.20 → 0.0.22

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: 823832e9e8ecbc21c1369c093e21da2f6feefc9d9ef0eb44ec73c38048075322
4
- data.tar.gz: 23d82a6e7b0d7630fef5e34fc46db32bfb7786300c9a8b2ebbaad44098a1dc8f
3
+ metadata.gz: e7cd3acf2bcf902cb1bbc73a89063a6f360b73bacb52e4764900ece3eaa5e08d
4
+ data.tar.gz: 67cf46933f34837e0d7b9730ff87cee2e785e4057adb9fa519f1e80150513a88
5
5
  SHA512:
6
- metadata.gz: 3398ab723fab693c7c9ffa653fa7a3f399431b2cf05106dc56127a31240b4d72f158e515a402312553db4729fe8f39937e490a0f338aa9bf94afc7c3984cad22
7
- data.tar.gz: cd5d54cb4ca4351ccff83090dbfd78b5d64a17b4119d6c9b49eb1c31da39d1ce6c7cb577f60137f1c88ad6222436b39b85b2ae623e3319c84e57cf44072cae71
6
+ metadata.gz: a8ecd8b4dde1dfb4867c6532da43714d53662482b86f8a76b5adda396edba042d46cd1d1bdb2ed86590efbfb3f29c8946b37094621176c19690b738939affa97
7
+ data.tar.gz: ccb32bab9c54f998451f3b10ff1a0895ac06b07f6446ae8aafeb24cc32c10bca2672622790276ac9895b26ca9616a061fad359cf8ac7ac9835366138b248f091
data/LICENSE.txt CHANGED
@@ -1,21 +1,28 @@
1
- The MIT License (MIT)
1
+ BSD 3-Clause License
2
2
 
3
- Copyright (c) 2025 Rodrigo Navarro
3
+ Copyright (c) 2025-present, Rodrigo Navarro. All rights reserved.
4
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:
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
11
7
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
8
+ * Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
14
10
 
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.
11
+ * Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ * Neither the name of wsrv.nl and images.weserv.nl nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -41,47 +41,46 @@ module Wal
41
41
 
42
42
  def self.inherited(subclass)
43
43
  super
44
- @@change_callbacks = Hash.new { |hash, key| hash[key] = [] }
45
- @@delete_callbacks = Hash.new { |hash, key| hash[key] = [] }
44
+ subclass.class_eval do
45
+ def self.change_callbacks
46
+ @change_callbacks ||= Hash.new { |hash, key| hash[key] = [] }
47
+ end
48
+
49
+ def self.delete_callbacks
50
+ @delete_callbacks ||= Hash.new { |hash, key| hash[key] = [] }
51
+ end
52
+ end
46
53
  end
47
54
 
48
55
  def self.on_insert(table, &block)
49
56
  table = table.is_a?(String) ? table : table.table_name
50
- @@change_callbacks[table].push(only: [:create], block: block)
57
+ change_callbacks[table].push(only: [:create], block: block)
51
58
  end
52
59
 
53
60
  def self.on_update(table, changed: nil, &block)
54
61
  table = table.is_a?(String) ? table : table.table_name
55
- @@change_callbacks[table].push(only: [:update], changed: changed&.map(&:to_s), block: block)
62
+ change_callbacks[table].push(only: [:update], changed: changed&.map(&:to_s), block: block)
56
63
  end
57
64
 
58
65
  def self.on_save(table, changed: nil, &block)
59
66
  table = table.is_a?(String) ? table : table.table_name
60
- @@change_callbacks[table].push(only: [:create, :update], changed: changed&.map(&:to_s), block: block)
67
+ change_callbacks[table].push(only: [:create, :update], changed: changed&.map(&:to_s), block: block)
61
68
  end
62
69
 
63
70
  def self.on_delete(table, &block)
64
71
  table = table.is_a?(String) ? table : table.table_name
65
- @@delete_callbacks[table].push(block: block)
66
- end
67
-
68
- def self.change_callbacks
69
- @@change_callbacks
70
- end
71
-
72
- def self.delete_callbacks
73
- @@delete_callbacks
72
+ delete_callbacks[table].push(block: block)
74
73
  end
75
74
 
76
75
  def on_record_changed(event)
77
76
  case event
78
77
  when InsertEvent
79
- @@change_callbacks[event.table]
78
+ self.class.change_callbacks[event.full_table_name]
80
79
  .filter { |callback| callback[:only].include? :create }
81
80
  .each { |callback| instance_exec(event, &callback[:block]) }
82
81
 
83
82
  when UpdateEvent
84
- @@change_callbacks[event.table]
83
+ self.class.change_callbacks[event.full_table_name]
85
84
  .filter { |callback| callback[:only].include? :update }
86
85
  .each do |callback|
87
86
  if (attributes = callback[:changed])
@@ -92,14 +91,14 @@ module Wal
92
91
  end
93
92
 
94
93
  when DeleteEvent
95
- @@delete_callbacks[event.table].each do |callback|
94
+ self.class.delete_callbacks[event.full_table_name].each do |callback|
96
95
  instance_exec(event, &callback[:block])
97
96
  end
98
97
  end
99
98
  end
100
99
 
101
100
  def should_watch_table?(table)
102
- (@@change_callbacks.keys | @@delete_callbacks.keys).include? table
101
+ (self.class.change_callbacks.keys | self.class.delete_callbacks.keys).include? table
103
102
  end
104
103
 
105
104
  # `RecordWatcher` supports three processing strategies:
@@ -184,7 +183,7 @@ module Wal
184
183
  def on_update(event)
185
184
  if (id = event.primary_key)
186
185
  @records ||= {}
187
- @records[[event.table, id]] = case (existing_event = @records[[event.table, id]])
186
+ @records[[event.full_table_name, id]] = case (existing_event = @records[[event.full_table_name, id]])
188
187
  when InsertEvent
189
188
  # A record inserted on this transaction is being updated, which means it should still reflect as a insert
190
189
  # event, we just change the information to reflect the most current data that was just updated.
@@ -204,7 +203,7 @@ module Wal
204
203
  def on_delete(event)
205
204
  if (id = event.primary_key)
206
205
  @records ||= {}
207
- @records[[event.table, id]] = case (existing_event = @records[[event.table, id]])
206
+ @records[[event.full_table_name, id]] = case (existing_event = @records[[event.full_table_name, id]])
208
207
  when InsertEvent
209
208
  # We are removing a record that was inserted on this transaction, we should not even report this change, as
210
209
  # this record never existed outside this transaction anyways.
@@ -304,9 +303,9 @@ module Wal
304
303
  end
305
304
 
306
305
  def on_delete(event)
307
- case @table.where(table_name: event.table, primary_key: event.primary_key).pluck(:action, :old).first
306
+ case @table.where(table_name: event.full_table_name, primary_key: event.primary_key).pluck(:action, :old).first
308
307
  in ["insert", _]
309
- @table.where(table_name: event.table, primary_key: event.primary_key).delete_all
308
+ @table.where(table_name: event.full_table_name, primary_key: event.primary_key).delete_all
310
309
  in ["update", old]
311
310
  @table.upsert(serialize(event).merge(old:))
312
311
  in ["delete", _]
@@ -326,7 +325,7 @@ module Wal
326
325
  serialized = {
327
326
  transaction_id: event.transaction_id,
328
327
  lsn: event.lsn,
329
- table_name: event.table,
328
+ table_name: event.full_table_name,
330
329
  primary_key: event.primary_key,
331
330
  context: event.context,
332
331
  }
@@ -343,10 +342,12 @@ module Wal
343
342
  end
344
343
 
345
344
  def deserialize(persisted_event)
345
+ table, schema = persisted_event.table_name.split(".", 2)
346
346
  deserialized = {
347
347
  transaction_id: persisted_event.transaction_id,
348
348
  lsn: persisted_event.lsn,
349
- table: persisted_event.table_name,
349
+ schema: schema || "public",
350
+ table: table,
350
351
  primary_key: persisted_event.primary_key,
351
352
  context: persisted_event.context,
352
353
  }
@@ -47,10 +47,11 @@ module Wal
47
47
 
48
48
  watch_conn.start_pgoutput_replication_slot(@replication_slot, publications, messages: true).filter_map do |msg|
49
49
  case msg
50
- in XLogData(data: PG::Replication::PGOutput::Relation(oid:, name:, columns:))
50
+ in XLogData(data: PG::Replication::PGOutput::Relation(oid:, name:, columns:, namespace:))
51
51
  tables[oid] = Table.new(
52
52
  # TODO: for now we are forcing an id column here, but that is not really correct
53
53
  primary_key_colums: columns.any? { |col| col.name == "id" } ? ["id"] : [],
54
+ schema: namespace,
54
55
  name:,
55
56
  columns: columns.map do |col|
56
57
  Column.new(
@@ -100,7 +101,7 @@ module Wal
100
101
 
101
102
  in XLogData(lsn:, data: PG::Replication::PGOutput::Insert(oid:, new:))
102
103
  table = tables[oid]
103
- next unless watcher.should_watch_table? table.name
104
+ next unless watcher.should_watch_table? table.full_table_name
104
105
  new_data = table.decode_row(new)
105
106
  record_id = table.primary_key(new_data)
106
107
  next unless record_id
@@ -109,6 +110,7 @@ module Wal
109
110
  transaction_id:,
110
111
  lsn:,
111
112
  context:,
113
+ schema: table.schema,
112
114
  table: table.name,
113
115
  primary_key: record_id,
114
116
  new: new_data,
@@ -116,7 +118,7 @@ module Wal
116
118
 
117
119
  in XLogData(lsn:, data: PG::Replication::PGOutput::Update(oid:, new:, old:))
118
120
  table = tables[oid]
119
- next unless watcher.should_watch_table? table.name
121
+ next unless watcher.should_watch_table? table.full_table_name
120
122
  old_data = table.decode_row(old)
121
123
  new_data = table.decode_row(new)
122
124
  record_id = table.primary_key(new_data)
@@ -126,6 +128,7 @@ module Wal
126
128
  transaction_id:,
127
129
  lsn:,
128
130
  context:,
131
+ schema: table.schema,
129
132
  table: table.name,
130
133
  primary_key: record_id,
131
134
  old: old_data,
@@ -134,7 +137,7 @@ module Wal
134
137
 
135
138
  in XLogData(lsn:, data: PG::Replication::PGOutput::Delete(oid:, old:, key:))
136
139
  table = tables[oid]
137
- next unless watcher.should_watch_table? table.name
140
+ next unless watcher.should_watch_table? table.full_table_name
138
141
  old_data = table.decode_row(old.presence || key)
139
142
  record_id = table.primary_key(old_data)
140
143
  next unless record_id
@@ -143,6 +146,7 @@ module Wal
143
146
  transaction_id:,
144
147
  lsn:,
145
148
  context:,
149
+ schema: table.schema,
146
150
  table: table.name,
147
151
  primary_key: record_id,
148
152
  old: old_data,
@@ -160,7 +164,16 @@ module Wal
160
164
  end
161
165
  end
162
166
 
163
- class Table < Data.define(:name, :primary_key_colums, :columns)
167
+ class Table < Data.define(:schema, :name, :primary_key_colums, :columns)
168
+ def full_table_name
169
+ case schema
170
+ in "public"
171
+ name
172
+ else
173
+ "#{schema}.#{name}"
174
+ end
175
+ end
176
+
164
177
  def primary_key(decoded_row)
165
178
  case primary_key_colums
166
179
  in [key]
data/lib/wal/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wal
4
- VERSION = "0.0.20"
4
+ VERSION = "0.0.22"
5
5
  end
data/lib/wal.rb CHANGED
@@ -29,6 +29,17 @@ module Wal
29
29
  class CommitTransactionEvent < Data.define(:transaction_id, :lsn, :context, :timestamp)
30
30
  end
31
31
 
32
+ module TableName
33
+ def full_table_name
34
+ case schema
35
+ in "public"
36
+ table
37
+ else
38
+ "#{schema}.#{table}"
39
+ end
40
+ end
41
+ end
42
+
32
43
  module ChangeEvent
33
44
  def diff
34
45
  {}
@@ -55,7 +66,8 @@ module Wal
55
66
  end
56
67
  end
57
68
 
58
- class InsertEvent < Data.define(:transaction_id, :lsn, :context, :table, :primary_key, :new)
69
+ class InsertEvent < Data.define(:transaction_id, :lsn, :context, :schema, :table, :primary_key, :new)
70
+ include ::Wal::TableName
59
71
  include ::Wal::ChangeEvent
60
72
 
61
73
  def diff
@@ -63,7 +75,8 @@ module Wal
63
75
  end
64
76
  end
65
77
 
66
- class UpdateEvent < Data.define(:transaction_id, :lsn, :context, :table, :primary_key, :old, :new)
78
+ class UpdateEvent < Data.define(:transaction_id, :lsn, :context, :schema, :table, :primary_key, :old, :new)
79
+ include ::Wal::TableName
67
80
  include ::Wal::ChangeEvent
68
81
 
69
82
  def diff
@@ -73,7 +86,8 @@ module Wal
73
86
  end
74
87
  end
75
88
 
76
- class DeleteEvent < Data.define(:transaction_id, :lsn, :context, :table, :primary_key, :old)
89
+ class DeleteEvent < Data.define(:transaction_id, :lsn, :context, :schema, :table, :primary_key, :old)
90
+ include ::Wal::TableName
77
91
  include ::Wal::ChangeEvent
78
92
 
79
93
  def diff
data/rbi/wal.rbi CHANGED
@@ -7,7 +7,7 @@ module Wal
7
7
  UpdateEvent,
8
8
  DeleteEvent,
9
9
  ) }
10
- VERSION = "0.0.20"
10
+ VERSION = "0.0.21"
11
11
 
12
12
  class << self
13
13
  sig { returns(T.class_of(Logger)) }
@@ -37,6 +37,13 @@ module Wal
37
37
 
38
38
  end
39
39
 
40
+ module TableName
41
+ extend T::Sig
42
+
43
+ sig { returns(String) }
44
+ def full_table_name; end
45
+ end
46
+
40
47
  module ChangeEvent
41
48
  extend T::Sig
42
49
 
@@ -60,10 +67,12 @@ module Wal
60
67
  prop :transaction_id, Integer, immutable: true
61
68
  prop :lsn, Integer, immutable: true
62
69
  prop :context, T::Hash[String, T.untyped], immutable: true
70
+ prop :schema, String, immutable: true
63
71
  prop :table, String, immutable: true
64
72
  prop :primary_key, T.untyped, immutable: true
65
73
  prop :new, T::Hash[String, T.untyped], immutable: true
66
74
 
75
+ include ::Wal::TableName
67
76
  include ::Wal::ChangeEvent
68
77
  extend T::Sig
69
78
 
@@ -75,11 +84,13 @@ module Wal
75
84
  prop :transaction_id, Integer, immutable: true
76
85
  prop :lsn, Integer, immutable: true
77
86
  prop :context, T::Hash[String, T.untyped], immutable: true
87
+ prop :schema, String, immutable: true
78
88
  prop :table, String, immutable: true
79
89
  prop :primary_key, T.untyped, immutable: true
80
90
  prop :old, T::Hash[String, T.untyped], immutable: true
81
91
  prop :new, T::Hash[String, T.untyped], immutable: true
82
92
 
93
+ include ::Wal::TableName
83
94
  include ::Wal::ChangeEvent
84
95
  extend T::Sig
85
96
 
@@ -91,10 +102,12 @@ module Wal
91
102
  prop :transaction_id, Integer, immutable: true
92
103
  prop :lsn, Integer, immutable: true
93
104
  prop :context, T::Hash[String, T.untyped], immutable: true
105
+ prop :schema, String, immutable: true
94
106
  prop :table, String, immutable: true
95
107
  prop :primary_key, T.untyped, immutable: true
96
108
  prop :old, T::Hash[String, T.untyped], immutable: true
97
109
 
110
+ include ::Wal::TableName
98
111
  include ::Wal::ChangeEvent
99
112
  extend T::Sig
100
113
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.20
4
+ version: 0.0.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Navarro
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-09-20 00:00:00.000000000 Z
10
+ date: 2025-10-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: pg
@@ -105,7 +105,7 @@ files:
105
105
  - lib/wal/watcher.rb
106
106
  - rbi/wal.rbi
107
107
  licenses:
108
- - MIT
108
+ - BSD-3-Clause
109
109
  metadata:
110
110
  homepage_uri: https://github.com/reu/wal
111
111
  source_code_uri: https://github.com/reu/wal