table_sync 6.0.4 → 6.1.0

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