table_sync 6.0.4 → 6.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.
@@ -1,162 +0,0 @@
1
- # Publishers
2
-
3
- There are three publishers you can use to send data.
4
-
5
- - `TableSync::Publishing::Single` - sends one row with initialization.
6
- - `TableSync::Publishing::Batch` - sends a batch of rows with initialization.
7
- - `TableSync::Publishing::Raw` - sends raw data without checks.
8
-
9
- ## Single
10
-
11
- `TableSync::Publishing::Single` - sends one row with initialization.
12
-
13
- This is a publisher called by `TableSync.sync(self)`.
14
-
15
- ### Expected parameters:
16
-
17
- - `object_class` - class (model) used to initialize published object
18
- - `original_attributes` - attributes used to initialize `object_class` with
19
- - `debounce_time` - minimum allowed time between delayed publishings
20
- - `event` - type of event that happened to the published object (`create`, `update`, `destroy`); `update` by default
21
-
22
- ### What it does (when uses `#publish_now`):
23
- - takes in the `original_attributes`, `object_class`, `event`
24
- - constantizes `object_class`
25
- - extracts the primary key (`needle`) of the `object_class` from the `original_attributes`
26
- - queries the database for the object with the `needle` (for `update` and `create`) or initializes the `object_class` with `original_attributes` (for `destroy`)
27
- - constructs routing_key using `routing_key_callable` and `#attributes_for_routing_key` (if defined)
28
- - constructs headers using `headers_callable` and `#attributes_for_headers` (if defined)
29
- - publishes Rabbit message (uses attributes from queried/initialized object as data)
30
- - sends notification (if set up)
31
-
32
- ### What it does (when uses `#publish_later`):
33
- - takes in the `original_attributes`, `object_class`, `debounce_time`, `event`
34
- - serializes the `original_attributes`, silently filters out unserializable keys/values
35
- - enqueues (or skips) the job with the `serialized_original_attributes` to be performed in time according to debounce
36
- - job (if enqueued) calls `TableSync::Publishing::Single#publish_now` with `serialized_original_attributes` and the same `object_class`, `debounce_time`, `event`
37
-
38
- ### Serialization
39
-
40
- Currently allowed key/values are:
41
- `NilClass`, `String`, `TrueClass`, `FalseClass`, `Numeric`, `Symbol`.
42
-
43
- ### Job
44
-
45
- Job is defined in `TableSync.single_publishing_job_class_callable` as a proc. Read more in [Configuration](docs/publishing/configuration.md).
46
-
47
- ### Example #1 (send right now)
48
-
49
- ```ruby
50
- TableSync::Publishing::Single.new(
51
- object_class: "User",
52
- original_attributes: { id: 1, name: "Mark" },
53
- debounce_time: 60, # useless for #publish _now, can be skipped
54
- event: :create,
55
- ).publish_now
56
- ```
57
-
58
- ### Example #2 (enqueue job)
59
-
60
- ```ruby
61
- TableSync::Publishing::Single.new(
62
- object_class: "User",
63
- original_attributes: { id: 1, name: "Mark" }, # will be serialized!
64
- debounce_time: 60,
65
- event: :update,
66
- ).publish_later
67
- ```
68
-
69
- ## Batch
70
-
71
- - `TableSync::Publishing::Batch` - sends a batch of rows with initialization.
72
-
73
- ### Expected parameters:
74
-
75
- - `object_class` - class (model) used to initialize published objects
76
- - `original_attributes` - array of attributes used to initialize `object_class` with
77
- - `event` - type of event that happened to the published objects (`create`, `update`, `destroy`); `update` by default
78
- - `routing_key` - custom routing_key
79
- - `headers` - custom headers
80
-
81
- ### What it does (when uses `#publish_now`):
82
- - takes in the `original_attributes`, `object_class`, `event`, `routing_key`, `headers`
83
- - constantizes `object_class`
84
- - extracts primary keys (`needles`) of the `object_class` from the array of `original_attributes`
85
- - queries the database for the objects with `needles` (for `update` and `create`) or initializes the `object_class` with `original_attributes` (for `destroy`)
86
- - constructs routing_key using `routing_key_callable` (ignores `#attributes_for_routing_key`) or uses `routing_key` if given
87
- - constructs headers using `headers_callable` (ignores `#attributes_for_headers`) or uses `headers` if given
88
- - publishes Rabbit message (uses attributes from queried/initialized objects as data)
89
- - sends notification (if set up)
90
-
91
- ### What it does (when uses `#publish_later`):
92
- - takes in the `original_attributes`, `object_class`, `event`, `routing_key`, `headers`
93
- - serializes the array of `original_attributes`, silently filters out unserializable keys/values
94
- - enqueues the job with the `serialized_original_attributes`
95
- - job calls `TableSync::Publishing::Batch#publish_now` with `serialized_original_attributes` and the same `object_class`, `event`, `routing_key`, `headers`
96
-
97
- ### Serialization
98
-
99
- Currently allowed key/values are:
100
- `NilClass`, `String`, `TrueClass`, `FalseClass`, `Numeric`, `Symbol`.
101
-
102
- ### Job
103
-
104
- Job is defined in `TableSync.batch_publishing_job_class_callable` as a proc. Read more in [Configuration](docs/publishing/configuration.md).
105
-
106
- ### Example #1 (send right now)
107
-
108
- ```ruby
109
- TableSync::Publishing::Batch.new(
110
- object_class: "User",
111
- original_attributes: [{ id: 1, name: "Mark" }, { id: 2, name: "Bob" }],
112
- event: :create,
113
- routing_key: :custom_key, # optional
114
- headers: { type: "admin" }, # optional
115
- ).publish_now
116
- ```
117
-
118
- ### Example #2 (enqueue job)
119
-
120
- ```ruby
121
- TableSync::Publishing::Batch.new(
122
- object_class: "User",
123
- original_attributes: [{ id: 1, name: "Mark" }, { id: 2, name: "Bob" }],
124
- event: :create,
125
- routing_key: :custom_key, # optional
126
- headers: { type: "admin" }, # optional
127
- ).publish_later
128
- ```
129
-
130
- ## Raw
131
- - `TableSync::Publishing::Raw` - sends raw data without checks.
132
-
133
- Be carefull with this publisher. There are no checks for the data sent.
134
- You can send anything.
135
-
136
- ### Expected parameters:
137
-
138
- - `object_class` - model
139
- - `original_attributes` - raw data that will be sent
140
- - `event` - type of event that happened to the published objects (`create`, `update`, `destroy`); `update` by default
141
- - `routing_key` - custom routing_key
142
- - `headers` - custom headers
143
-
144
- ### What it does (when uses `#publish_now`):
145
- - takes in the `original_attributes`, `object_class`, `event`, `routing_key`, `headers`
146
- - constantizes `object_class`
147
- - constructs routing_key using `routing_key_callable` (ignores `#attributes_for_routing_key`) or uses `routing_key` if given
148
- - constructs headers using `headers_callable` (ignores `#attributes_for_headers`) or uses `headers` if given
149
- - publishes Rabbit message (uses `original_attributes` as is)
150
- - sends notification (if set up)
151
-
152
- ### Example
153
-
154
- ```ruby
155
- TableSync::Publishing::Raw.new(
156
- object_class: "User",
157
- original_attributes: [{ id: 1, name: "Mark" }, { id: 2, name: "Bob" }],
158
- event: :create,
159
- routing_key: :custom_key, # optional
160
- headers: { type: "admin" }, # optional
161
- ).publish_now
162
- ```
data/docs/publishing.md DELETED
@@ -1,80 +0,0 @@
1
- # Publishing
2
-
3
- TableSync can be used to send data using RabbitMQ.
4
-
5
- You can do in two ways. Automatic and manual.
6
- Each one has its own pros and cons.
7
-
8
- Automatic is used to publish changes in realtime, as soon as the tracked entity changes.
9
- Usually syncs one entity at a time.
10
-
11
- Manual allows to sync a lot of entities per message.
12
- But demands greater amount of work and data preparation.
13
-
14
- ## Automatic
15
-
16
- Include `TableSync.sync(self)` into a Sequel or ActiveRecord model.
17
-
18
- Options:
19
-
20
- - `if:` and `unless:` - Runs given proc in the scope of an instance. Skips sync on `false` for `if:` and on `true` for `unless:`.
21
- - `on:` - specify events (`create`, `update`, `destroy`) to trigger sync on. Triggered for all of them without this option.
22
- - `debounce_time` - min time period allowed between synchronizations.
23
-
24
- Functioning `Rails.cache` is required.
25
-
26
- How it works:
27
-
28
- - `TableSync.sync(self)` - registers new callbacks (for `create`, `update`, `destroy`) for ActiveRecord model, and defines `after_create`, `after_update` and `after_destroy` callback methods for Sequel model.
29
-
30
- - Callbacks call `TableSync::Publishing::Single#publish_later` with given options and object attributes. It enqueues a job which then publishes a message.
31
-
32
- Example:
33
-
34
- ```ruby
35
- class SomeModel < Sequel::Model
36
- TableSync.sync(self, { if: -> (*) { some_code }, unless: -> (*) { some_code }, on: [:create, :update] })
37
- end
38
-
39
- class SomeOtherModel < Sequel::Model
40
- TableSync.sync(self)
41
- end
42
- ```
43
-
44
- ### Non persisted record destruction
45
-
46
- Sometimes destroy event can happen for non persisted record. In this case we can expect the following:
47
-
48
- For Sequel: 'Sequel::NoExistingObject' is raised. (This is default Sequel behaviour)
49
- For Active Record: Publishing is skipped.
50
-
51
- Example:
52
-
53
- ```ruby
54
- # ActiveRecord
55
- user = User.new.destroy! # Publishing is skipped.
56
-
57
- # Sequel
58
- user = User.new.destroy! # raise Sequel::NoExistingObject
59
- ```
60
-
61
- ## Manual
62
-
63
- Directly call one of the publishers. It's the best if you need to sync a lot of data.
64
- This way you don't even need for the changes to occur.
65
-
66
- Example:
67
-
68
- ```ruby
69
- TableSync::Publishing::Batch.new(
70
- object_class: "User",
71
- original_attributes: [{ id: 1 }, { id: 2 }],
72
- event: :update,
73
- ).publish_now
74
- ```
75
-
76
- ## Read More
77
-
78
- - [Publishers](publishing/publishers.md)
79
- - [Configuration](publishing/configuration.md)
80
- - [Manual Sync (examples)](publishing/manual.md)
data/docs/receiving.md DELETED
@@ -1,346 +0,0 @@
1
- # Receiving changes
2
-
3
- Naming convention for receiving handlers is `Rabbit::Handler::GROUP_ID::TableSync`,
4
- where `GROUP_ID` represents first part of source exchange name.
5
- Define handler class inherited from `TableSync::ReceivingHandler`
6
- and named according to described convention.
7
- You should use DSL inside the class.
8
- Suppose we will synchronize models {Project, News, User} project {MainProject}, then:
9
-
10
- ```ruby
11
- class Rabbit::Handler::MainProject::TableSync < TableSync::ReceivingHandler
12
- queue_as :custom_queue
13
-
14
- receive "Project", to_table: :projects
15
-
16
- receive "News", to_table: :news, events: :update do
17
- after_commit_on_update do
18
- NewsCache.reload
19
- end
20
- end
21
-
22
- receive "User", to_table: :clients, events: %i[update destroy] do
23
- mapping_overrides email: :project_user_email, id: :project_user_id
24
-
25
- only :project_user_email, :project_user_id, :project_id
26
- target_keys :project_id, :project_user_id
27
- rest_key :project_user_rest
28
- version_key :project_user_version
29
-
30
- additional_data do |project_id:|
31
- { project_id: project_id }
32
- end
33
-
34
- default_values do
35
- { created_at: Time.current }
36
- end
37
- end
38
-
39
- receive "User", to_model: CustomModel.new(:users) do
40
- rest_key false
41
- end
42
- end
43
- ```
44
-
45
- ### Handler class (`Rabbit::Handler::MainProject::TableSync`)
46
-
47
- In this case:
48
- - `TableSync` - RabbitMQ event type.
49
- - `MainProject` - event source.
50
- - `Rabbit::Handler` - module for our handlers of events from RabbitMQ (there might be others)
51
-
52
- Method `queue_as` allows you to set custom queue.
53
-
54
- ### Recieving handler batch processing
55
-
56
- Receiving handler supports array of attributes in a single update or destroy event. Corresponding
57
- upsert-style logic in ActiveRecord and Sequel orm handlers are provided.
58
-
59
- ### Config
60
- ```ruby
61
- receive source, [to_table:, to_model:, events:, &block]
62
- ```
63
-
64
- The method receives following arguments
65
- - `source` - string, name of source model (required)
66
- - `to_table` - destination table name (required if not set to_model)
67
- - `to_model` - destination model (required if not set to_table)
68
- - `events` - array of supported events (optional)
69
- - `block` - configuration block with options (optional)
70
-
71
- This method implements logic of mapping `source` to `to_table` (or to `to_model`) and allows customizing
72
- the event handling logic with provided block.
73
- You can use one `source` for a lot of `to_table` or `to_moel`.
74
-
75
- ### Options:
76
-
77
- Most of the options can be set as computed value or as a process.
78
-
79
- ```ruby
80
- option(value)
81
- ```
82
-
83
- ```ruby
84
- option do |key params|
85
- value
86
- end
87
- ```
88
-
89
- Each of options can receive static value or code block which will be called for each event with the following arguments:
90
- - `event` - type of event (`:update` or `:destroy`)
91
- - `model` - source model (`Project`, `News`, `User` in example)
92
- - `version` - version of the data
93
- - `project_id` - id of project which is used in RabbitMQ
94
- - `raw_data` - raw data from event (before applying `mapping_overrides`, `only`, etc.)
95
-
96
- Blocks can receive any number of parameters from the list.
97
-
98
- All specific key params will be explained in examples for each option.
99
-
100
- #### only
101
- Whitelist for receiving attributes.
102
-
103
- ```ruby
104
- only(instance of Array)
105
- ```
106
-
107
- ```ruby
108
- only do |row:|
109
- return instance of Array
110
- end
111
- ```
112
-
113
- default value is taken through the call `model.columns`
114
-
115
- #### target_keys
116
- Primary keys or unique keys.
117
-
118
- ```ruby
119
- target_keys(instance of Array)
120
- ```
121
-
122
- ```ruby
123
- target_keys do |data:|
124
- return instance of Array
125
- end
126
- ```
127
-
128
- default value is taken through the call `model.primary_keys`
129
-
130
- #### rest_key
131
- Name of jsonb column for attributes which are not included in the whitelist.
132
- You can set the `rest_key(false)` if you won't need the rest data.
133
-
134
- ```ruby
135
- rest_key(instance of Symbol)
136
- ```
137
-
138
- ```ruby
139
- rest_key do |row:, rest:|
140
- return instance of Symbol
141
- end
142
- ```
143
- default value is `:rest`
144
-
145
- #### version_key
146
- Name of version column.
147
-
148
- ```ruby
149
- version_key(instance of Symbol)
150
- ```
151
-
152
- ```ruby
153
- version_key do |data:|
154
- return instance of Symbol
155
- end
156
- ```
157
- default value is `:version`
158
-
159
- #### except
160
- Blacklist for receiving attributes.
161
-
162
- ```ruby
163
- except(instance of Array)
164
- ```
165
-
166
- ```ruby
167
- except do |row:|
168
- return instance of Array
169
- end
170
- ```
171
-
172
- default value is `[]`
173
-
174
- #### mapping_overrides
175
- Map for overriding receiving columns.
176
-
177
- ```ruby
178
- mapping_overrides(instance of Hash)
179
- ```
180
-
181
- ```ruby
182
- mapping_overrides do |row:|
183
- return instance of Hash
184
- end
185
- ```
186
-
187
- default value is `{}`
188
-
189
- #### additional_data
190
- Additional data for insert or update (e.g. `project_id`).
191
-
192
- ```ruby
193
- additional_data(instance of Hash)
194
- ```
195
-
196
- ```ruby
197
- additional_data do |row:|
198
- return instance of Hash
199
- end
200
- ```
201
-
202
- default value is `{}`
203
-
204
- #### default_values
205
- Values for insert if a row is not found.
206
-
207
- ```ruby
208
- default_values(instance of Hash)
209
- ```
210
-
211
- ```ruby
212
- default_values do |data:|
213
- return instance of Hash
214
- end
215
- ```
216
-
217
- default value is `{}`
218
-
219
- #### skip
220
- Return truthy value to skip the row.
221
-
222
- ```ruby
223
- skip(instance of TrueClass or FalseClass)
224
- ```
225
-
226
- ```ruby
227
- skip do |data:|
228
- return instance of TrueClass or FalseClass
229
- end
230
- ```
231
-
232
- default value is `false`
233
-
234
- #### wrap_receiving
235
- Proc that is used to wrap the receiving logic by custom block of code.
236
-
237
- ```ruby
238
- wrap_receiving do |data:, target_keys:, version_key:, default_values: {}, event:, &receiving_logic|
239
- receiving_logic.call
240
- return makes no sense
241
- end
242
- ```
243
-
244
- event option is current fired event
245
- default value is `proc { |&block| block.call }`
246
-
247
- #### before_update
248
- Perform code before updating data in the database.
249
-
250
- ```ruby
251
- before_update do |data:, target_keys:, version_key:, default_values:|
252
- return makes no sense
253
- end
254
-
255
- before_update do |data:, target_keys:, version_key:, default_values:|
256
- return makes no sense
257
- end
258
- ```
259
-
260
- Сan be defined several times. Execution order guaranteed.
261
-
262
- #### after_commit_on_update
263
- Perform code after updated data was committed.
264
-
265
- ```ruby
266
- after_commit_on_update do |data:, target_keys:, version_key:, default_values:, results:|
267
- return makes no sense
268
- end
269
-
270
- after_commit_on_update do |data:, target_keys:, version_key:, default_values:, results:|
271
- return makes no sense
272
- end
273
- ```
274
-
275
- - `results` - returned value from `model.upsert`
276
-
277
- Сan be defined several times. Execution order guaranteed.
278
-
279
- #### before_destroy
280
- Perform code before destroying data in database.
281
-
282
- ```ruby
283
- before_destroy do |data:, target_keys:, version_key:|
284
- return makes no sense
285
- end
286
-
287
- before_destroy do |data:, target_keys:, version_key:|
288
- return makes no sense
289
- end
290
- ```
291
-
292
- Сan be defined several times. Execution order guaranteed.
293
-
294
- #### after_commit_on_destroy
295
- Perform code after destroyed data was committed.
296
-
297
- ```ruby
298
- after_commit_on_destroy do |data:, target_keys:, version_key:, results:|
299
- return makes no sense
300
- end
301
-
302
- after_commit_on_destroy do |data:, target_keys:, version_key:, results:|
303
- return makes no sense
304
- end
305
- ```
306
-
307
- - `results` - returned value from `model.destroy`
308
-
309
- Сan be defined several times. Execution order guaranteed.
310
-
311
- ### Custom model
312
- You can use custom model for receiving.
313
- ```
314
- class Rabbit::Handler::MainProject::TableSync < TableSync::ReceivingHandler
315
- receive "Project", to_model: CustomModel.new
316
- end
317
- ```
318
-
319
- This model has to implement next interface:
320
- ```
321
- def columns
322
- return all columns from table
323
- end
324
-
325
- def primary_keys
326
- return primary keys from table
327
- end
328
-
329
- def upsert(data: Array, target_keys: Array, version_key: Symbol, default_values: Hash)
330
- return array with updated rows
331
- end
332
-
333
- def destroy(data: Array, target_keys: Array, version_key: Symbol)
334
- return array with delited rows
335
- end
336
-
337
- def transaction(&block)
338
- block.call
339
- return makes no sense
340
- end
341
-
342
- def after_commit(&block)
343
- block.call
344
- return makes no sense
345
- end
346
- ```