table_sync 1.13.1 → 4.0.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.rubocop.yml +26 -1
- data/.travis.yml +6 -6
- data/CHANGELOG.md +74 -1
- data/Gemfile.lock +262 -0
- data/LICENSE.md +1 -1
- data/README.md +4 -1
- data/docs/message_protocol.md +24 -0
- data/docs/notifications.md +45 -0
- data/docs/publishing.md +147 -0
- data/docs/receiving.md +341 -0
- data/lib/table_sync.rb +23 -33
- data/lib/table_sync/errors.rb +60 -22
- data/lib/table_sync/instrument.rb +2 -2
- data/lib/table_sync/publishing.rb +11 -0
- data/lib/table_sync/{base_publisher.rb → publishing/base_publisher.rb} +1 -1
- data/lib/table_sync/{batch_publisher.rb → publishing/batch_publisher.rb} +12 -13
- data/lib/table_sync/{orm_adapter → publishing/orm_adapter}/active_record.rb +4 -8
- data/lib/table_sync/{orm_adapter → publishing/orm_adapter}/sequel.rb +10 -17
- data/lib/table_sync/{publisher.rb → publishing/publisher.rb} +4 -4
- data/lib/table_sync/receiving.rb +14 -0
- data/lib/table_sync/receiving/config.rb +218 -0
- data/lib/table_sync/receiving/config_decorator.rb +27 -0
- data/lib/table_sync/receiving/dsl.rb +28 -0
- data/lib/table_sync/receiving/handler.rb +131 -0
- data/lib/table_sync/{model → receiving/model}/active_record.rb +37 -23
- data/lib/table_sync/{model → receiving/model}/sequel.rb +13 -8
- data/lib/table_sync/utils.rb +9 -0
- data/lib/table_sync/utils/interface_checker.rb +97 -0
- data/lib/table_sync/utils/proc_array.rb +17 -0
- data/lib/table_sync/utils/proc_keywords_resolver.rb +46 -0
- data/lib/table_sync/version.rb +1 -1
- data/table_sync.gemspec +5 -4
- metadata +48 -30
- data/docs/synopsis.md +0 -318
- data/lib/table_sync/config.rb +0 -105
- data/lib/table_sync/config/callback_registry.rb +0 -53
- data/lib/table_sync/config_decorator.rb +0 -38
- data/lib/table_sync/dsl.rb +0 -25
- data/lib/table_sync/event_actions.rb +0 -96
- data/lib/table_sync/event_actions/data_wrapper.rb +0 -7
- data/lib/table_sync/event_actions/data_wrapper/base.rb +0 -23
- data/lib/table_sync/event_actions/data_wrapper/destroy.rb +0 -19
- data/lib/table_sync/event_actions/data_wrapper/update.rb +0 -21
- data/lib/table_sync/receiving_handler.rb +0 -76
@@ -0,0 +1,45 @@
|
|
1
|
+
### Notifications
|
2
|
+
|
3
|
+
#### ActiveSupport adapter
|
4
|
+
|
5
|
+
You can use an already existing ActiveSupport adapter:
|
6
|
+
```ruby
|
7
|
+
TableSync.notifier = TableSync::InstrumentAdapter::ActiveSupport
|
8
|
+
```
|
9
|
+
|
10
|
+
This instrumentation API is provided by Active Support. It allows to subscribe to notifications:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
ActiveSupport::Notifications.subscribe(/tablesync/) do |name, start, finish, id, payload|
|
14
|
+
# do something
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
Types of events available:
|
19
|
+
`"tablesync.receive.update"`, `"tablesync.receive.destroy"`, `"tablesync.publish.update"`
|
20
|
+
and `"tablesync.publish.destroy"`.
|
21
|
+
|
22
|
+
You have access to the payload, which contains `event`, `direction`, `table`, `schema` and `count`.
|
23
|
+
|
24
|
+
```
|
25
|
+
{
|
26
|
+
:event => :update, # one of update / destroy
|
27
|
+
:direction => :publish, # one of publish / receive
|
28
|
+
:table => "users",
|
29
|
+
:schema => "public",
|
30
|
+
:count => 1
|
31
|
+
}
|
32
|
+
```
|
33
|
+
|
34
|
+
See more at https://guides.rubyonrails.org/active_support_instrumentation.html
|
35
|
+
|
36
|
+
|
37
|
+
#### Custom adapters
|
38
|
+
|
39
|
+
You can also create a custom adapter. It is expected to respond to the following method:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
def notify(table:, event:, direction:, count:)
|
43
|
+
# processes data about table_sync event
|
44
|
+
end
|
45
|
+
```
|
data/docs/publishing.md
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# Publishing changes
|
2
|
+
|
3
|
+
Include `TableSync.sync(self)` into a Sequel or ActiveRecord model. `:if` and `:unless` are
|
4
|
+
supported for Sequel and ActiveRecord
|
5
|
+
|
6
|
+
Functioning `Rails.cache` is required
|
7
|
+
|
8
|
+
Example:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class SomeModel < Sequel::Model
|
12
|
+
TableSync.sync(self, { if: -> (*) { some_code } })
|
13
|
+
end
|
14
|
+
```
|
15
|
+
|
16
|
+
#### #attributes_for_sync
|
17
|
+
|
18
|
+
Models can implement `#attributes_for_sync` to override which attributes are published. If not
|
19
|
+
present, all attributes are published
|
20
|
+
|
21
|
+
#### #attrs_for_routing_key
|
22
|
+
|
23
|
+
Models can implement `#attrs_for_routing_key` to override which attributes are given to routing_key_callable. If not present, default attributes are given
|
24
|
+
|
25
|
+
#### #attrs_for_metadata
|
26
|
+
|
27
|
+
Models can implement `#attrs_for_metadata` to override which attributes are given to metadata_callable. If not present, default attributes are given
|
28
|
+
|
29
|
+
#### .table_sync_model_name
|
30
|
+
|
31
|
+
Models can implement `.table_sync_model_name` class method to override the model name used for
|
32
|
+
publishing events. Default is model class name
|
33
|
+
|
34
|
+
#### .table_sync_destroy_attributes(original_attributes)
|
35
|
+
|
36
|
+
Models can implement `.table_sync_destroy_attributes` class method to override the attributes
|
37
|
+
used for publishing destroy events. Default is object's primary key
|
38
|
+
|
39
|
+
## Configuration
|
40
|
+
|
41
|
+
- `TableSync.publishing_job_class_callable` is a callable which should resolve to a ActiveJob
|
42
|
+
subclass that calls TableSync back to actually publish changes (required)
|
43
|
+
|
44
|
+
Example:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class TableSync::Job < ActiveJob::Base
|
48
|
+
def perform(*args)
|
49
|
+
TableSync::Publisher.new(*args).publish_now
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
- `TableSync.batch_publishing_job_class_callable` is a callable which should resolve to a ActiveJob
|
55
|
+
subclass that calls TableSync batch publisher back to actually publish changes (required for batch publisher)
|
56
|
+
|
57
|
+
- `TableSync.routing_key_callable` is a callable which resolves which routing key to use when
|
58
|
+
publishing changes. It receives object class and attributes (required)
|
59
|
+
|
60
|
+
Example:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
TableSync.routing_key_callable = -> (klass, attributes) { klass.gsub('::', '_').tableize }
|
64
|
+
```
|
65
|
+
|
66
|
+
- `TableSync.routing_metadata_callable` is a callable that adds RabbitMQ headers which can be
|
67
|
+
used in routing (optional). One possible way of using it is defining a headers exchange and
|
68
|
+
routing rules based on key-value pairs (which correspond to sent headers)
|
69
|
+
|
70
|
+
Example:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
TableSync.routing_metadata_callable = -> (klass, attributes) { attributes.slice("project_id") }
|
74
|
+
```
|
75
|
+
|
76
|
+
- `TableSync.exchange_name` defines the exchange name used for publishing (optional, falls back
|
77
|
+
to default Rabbit gem configuration).
|
78
|
+
|
79
|
+
- `TableSync.notifier` is a module that provides publish and recieve notifications.
|
80
|
+
|
81
|
+
# Manual publishing
|
82
|
+
|
83
|
+
`TableSync::Publisher.new(object_class, original_attributes, confirm: true, state: :updated, debounce_time: 45)`
|
84
|
+
where state is one of `:created / :updated / :destroyed` and `confirm` is Rabbit's confirm delivery flag and optional param `debounce_time` determines debounce time in seconds, 1 minute by default.
|
85
|
+
|
86
|
+
# Manual publishing with batches
|
87
|
+
|
88
|
+
You can use `TableSync::BatchPublisher` to publish changes in batches (array of hashes in `attributes`).
|
89
|
+
|
90
|
+
When using `TableSync::BatchPublisher`,` TableSync.routing_key_callable` is called as follows:
|
91
|
+
`TableSync.routing_key_callable.call(klass, {})`, i.e. empty hash is passed instead of attributes.
|
92
|
+
And `TableSync.routing_metadata_callable` is not called at all: metadata is set to empty hash.
|
93
|
+
|
94
|
+
`TableSync::BatchPublisher.new(object_class, original_attributes_array, **options)`, where `original_attributes_array` is an array with hash of attributes of published objects and `options` is a hash of options.
|
95
|
+
|
96
|
+
`options` consists of:
|
97
|
+
- `confirm`, which is a flag for RabbitMQ, `true` by default
|
98
|
+
- `routing_key`, which is a custom key used (if given) to override one from `TableSync.routing_key_callable`, `nil` by default
|
99
|
+
- `push_original_attributes` (default value is `false`), if this option is set to `true`,
|
100
|
+
original_attributes_array will be pushed to Rabbit instead of fetching records from database and sending their mapped attributes.
|
101
|
+
- `headers`, which is an option for custom headers (can be used for headers exchanges routes), `nil` by default
|
102
|
+
- `event`, which is an option for event specification (`:destroy` or `:update`), `:update` by default
|
103
|
+
|
104
|
+
Example:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
TableSync::BatchPublisher.new(
|
108
|
+
"SomeClass",
|
109
|
+
[{ id: 1 }, { id: 2 }],
|
110
|
+
confirm: false,
|
111
|
+
routing_key: "custom_routing_key",
|
112
|
+
push_original_attributes: true,
|
113
|
+
headers: { key: :value },
|
114
|
+
event: :destroy,
|
115
|
+
)
|
116
|
+
```
|
117
|
+
|
118
|
+
# Manual publishing with batches (Russian)
|
119
|
+
|
120
|
+
С помощью класса `TableSync::BatchPublisher` вы можете опубликовать изменения батчами (массивом в `attributes`).
|
121
|
+
|
122
|
+
При использовании `TableSync::BatchPublisher`, `TableSync.routing_key_callable` вызывается следующим образом:
|
123
|
+
`TableSync.routing_key_callable.call(klass, {})`, то есть вместо аттрибутов передается пустой хэш.
|
124
|
+
А `TableSync.routing_metadata_callable` не вызывается вовсе: в метадате устанавливается пустой хэш.
|
125
|
+
|
126
|
+
`TableSync::BatchPublisher.new(object_class, original_attributes_array, **options)`, где `original_attributes_array` - массив с аттрибутами публикуемых объектов и `options`- это хэш с дополнительными опциями.
|
127
|
+
|
128
|
+
`options` состоит из:
|
129
|
+
- `confirm`, флаг для RabbitMQ, по умолчанию - `true`
|
130
|
+
- `routing_key`, ключ, который (если указан) замещает ключ, получаемый из `TableSync.routing_key_callable`, по умолчанию - `nil`
|
131
|
+
- `push_original_attributes` (значение по умолчанию `false`), если для этой опции задано значение true, в Rabbit будут отправлены original_attributes_array, вместо получения значений записей из базы непосредственно перед отправкой.
|
132
|
+
- `headers`, опция для задания headers (можно использовать для задания маршрутов в headers exchange'ах), `nil` по умолчанию
|
133
|
+
- `event`, опция для указания типа события (`:destroy` или `:update`), `:update` по умолчанию
|
134
|
+
|
135
|
+
Example:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
TableSync::BatchPublisher.new(
|
139
|
+
"SomeClass",
|
140
|
+
[{ id: 1 }, { id: 2 }],
|
141
|
+
confirm: false,
|
142
|
+
routing_key: "custom_routing_key",
|
143
|
+
push_original_attributes: true,
|
144
|
+
headers: { key: :value },
|
145
|
+
event: :destroy,
|
146
|
+
)
|
147
|
+
```
|
data/docs/receiving.md
ADDED
@@ -0,0 +1,341 @@
|
|
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
|
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 nil
|
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: {}, &receiving_logic|
|
239
|
+
receiving_logic.call
|
240
|
+
return makes no sense
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
default value is `proc { |&block| block.call }`
|
245
|
+
|
246
|
+
#### before_update
|
247
|
+
Perform code before updating data in the database.
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
before_update do |data:, target_keys:, version_key:, default_values:|
|
251
|
+
return makes no sense
|
252
|
+
end
|
253
|
+
|
254
|
+
before_update do |data:, target_keys:, version_key:, default_values:|
|
255
|
+
return makes no sense
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
Сan be defined several times. Execution order guaranteed.
|
260
|
+
|
261
|
+
#### after_commit_on_update
|
262
|
+
Perform code after updated data was committed.
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
after_commit_on_update do |data:, target_keys:, version_key:, default_values:|
|
266
|
+
return makes no sense
|
267
|
+
end
|
268
|
+
|
269
|
+
after_commit_on_update do |data:, target_keys:, version_key:, default_values:|
|
270
|
+
return makes no sense
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
Сan be defined several times. Execution order guaranteed.
|
275
|
+
|
276
|
+
#### before_destroy
|
277
|
+
Perform code before destroying data in database.
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
before_destroy do |data:, target_keys:, version_key:|
|
281
|
+
return makes no sense
|
282
|
+
end
|
283
|
+
|
284
|
+
before_destroy do |data:, target_keys:, version_key:|
|
285
|
+
return makes no sense
|
286
|
+
end
|
287
|
+
```
|
288
|
+
|
289
|
+
Сan be defined several times. Execution order guaranteed.
|
290
|
+
|
291
|
+
#### after_commit_on_destroy
|
292
|
+
Perform code after destroyed data was committed.
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
after_commit_on_destroy do |data:, target_keys:, version_key:|
|
296
|
+
return makes no sense
|
297
|
+
end
|
298
|
+
|
299
|
+
after_commit_on_destroy do |data:, target_keys:, version_key:|
|
300
|
+
return makes no sense
|
301
|
+
end
|
302
|
+
```
|
303
|
+
|
304
|
+
Сan be defined several times. Execution order guaranteed.
|
305
|
+
|
306
|
+
### Custom model
|
307
|
+
You can use custom model for receiving.
|
308
|
+
```
|
309
|
+
class Rabbit::Handler::MainProject::TableSync < TableSync::ReceivingHandler
|
310
|
+
receive "Project", to_model: CustomModel.new
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
This model has to implement next interface:
|
315
|
+
```
|
316
|
+
def columns
|
317
|
+
return all columns from table
|
318
|
+
end
|
319
|
+
|
320
|
+
def primary_keys
|
321
|
+
return primary keys from table
|
322
|
+
end
|
323
|
+
|
324
|
+
def upsert(data: Array, target_keys: Array, version_key: Symbol, default_values: Hash)
|
325
|
+
return array with updated rows
|
326
|
+
end
|
327
|
+
|
328
|
+
def destroy(data: Array, target_keys: Array, version_key: Symbol)
|
329
|
+
return array with delited rows
|
330
|
+
end
|
331
|
+
|
332
|
+
def transaction(&block)
|
333
|
+
block.call
|
334
|
+
return makes no sense
|
335
|
+
end
|
336
|
+
|
337
|
+
def after_commit(&block)
|
338
|
+
block.call
|
339
|
+
return makes no sense
|
340
|
+
end
|
341
|
+
```
|