table_sync 2.3.0 → 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/.rubocop.yml +21 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile.lock +82 -77
- data/README.md +4 -2
- 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 +16 -31
- data/lib/table_sync/errors.rb +39 -23
- 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} +4 -4
- data/lib/table_sync/{orm_adapter → publishing/orm_adapter}/active_record.rb +3 -7
- data/lib/table_sync/{orm_adapter → publishing/orm_adapter}/sequel.rb +2 -6
- 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 +36 -22
- 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 +2 -1
- metadata +42 -30
- data/docs/development.md +0 -43
- data/docs/synopsis.md +0 -336
- 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/plugins.rb +0 -72
- data/lib/table_sync/plugins/abstract.rb +0 -55
- data/lib/table_sync/plugins/access_mixin.rb +0 -49
- data/lib/table_sync/plugins/registry.rb +0 -153
- data/lib/table_sync/receiving_handler.rb +0 -76
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableSync
|
4
|
+
module Publishing
|
5
|
+
require_relative "publishing/base_publisher"
|
6
|
+
require_relative "publishing/publisher"
|
7
|
+
require_relative "publishing/batch_publisher"
|
8
|
+
require_relative "publishing/orm_adapter/active_record"
|
9
|
+
require_relative "publishing/orm_adapter/sequel"
|
10
|
+
end
|
11
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class TableSync::BatchPublisher < TableSync::BasePublisher
|
3
|
+
class TableSync::Publishing::BatchPublisher < TableSync::Publishing::BasePublisher
|
4
4
|
def initialize(object_class, original_attributes_array, **options)
|
5
5
|
@original_attributes_array = original_attributes_array.map do |hash|
|
6
6
|
filter_safe_for_serialization(hash.deep_symbolize_keys)
|
@@ -22,7 +22,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
|
|
22
22
|
return unless need_publish?
|
23
23
|
Rabbit.publish(params)
|
24
24
|
|
25
|
-
model_naming = TableSync.
|
25
|
+
model_naming = TableSync.publishing_adapter.model_naming(object_class)
|
26
26
|
TableSync::Instrument.notify table: model_naming.table, schema: model_naming.schema,
|
27
27
|
event: event,
|
28
28
|
count: publishing_data[:attributes].size, direction: :publish
|
@@ -41,7 +41,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
|
|
41
41
|
end
|
42
42
|
|
43
43
|
memoize def objects
|
44
|
-
needles.map { |needle| TableSync.
|
44
|
+
needles.map { |needle| TableSync.publishing_adapter.find(object_class, needle) }.compact
|
45
45
|
end
|
46
46
|
|
47
47
|
def job_callable
|
@@ -90,7 +90,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
|
|
90
90
|
if attributes_for_sync_defined?
|
91
91
|
object.attributes_for_sync
|
92
92
|
else
|
93
|
-
TableSync.
|
93
|
+
TableSync.publishing_adapter.attributes(object)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
@@ -1,13 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module TableSync::ORMAdapter
|
3
|
+
module TableSync::Publishing::ORMAdapter
|
4
4
|
module ActiveRecord
|
5
5
|
module_function
|
6
6
|
|
7
|
-
def model
|
8
|
-
::TableSync::Model::ActiveRecord
|
9
|
-
end
|
10
|
-
|
11
7
|
def model_naming(object)
|
12
8
|
::TableSync::NamingResolver::ActiveRecord.new(table_name: object.table_name)
|
13
9
|
end
|
@@ -26,8 +22,8 @@ module TableSync::ORMAdapter
|
|
26
22
|
klass.instance_exec do
|
27
23
|
{ create: :created, update: :updated, destroy: :destroyed }.each do |event, state|
|
28
24
|
after_commit(on: event, **opts) do
|
29
|
-
TableSync::Publisher.new(self.class.name, attributes,
|
30
|
-
|
25
|
+
TableSync::Publishing::Publisher.new(self.class.name, attributes,
|
26
|
+
state: state, debounce_time: debounce_time).publish
|
31
27
|
end
|
32
28
|
end
|
33
29
|
end
|
@@ -1,13 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module TableSync::ORMAdapter
|
3
|
+
module TableSync::Publishing::ORMAdapter
|
4
4
|
module Sequel
|
5
5
|
module_function
|
6
6
|
|
7
|
-
def model
|
8
|
-
::TableSync::Model::Sequel
|
9
|
-
end
|
10
|
-
|
11
7
|
def model_naming(object)
|
12
8
|
::TableSync::NamingResolver::Sequel.new(table_name: object.table_name, db: object.db)
|
13
9
|
end
|
@@ -44,7 +40,7 @@ module TableSync::ORMAdapter
|
|
44
40
|
klass.send(:define_method, :"after_#{event}") do
|
45
41
|
if instance_eval(&if_predicate) && !instance_eval(&unless_predicate)
|
46
42
|
db.after_commit do
|
47
|
-
TableSync::Publisher.new(
|
43
|
+
TableSync::Publishing::Publisher.new(
|
48
44
|
self.class.name,
|
49
45
|
values,
|
50
46
|
state: state,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class TableSync::Publisher < TableSync::BasePublisher
|
3
|
+
class TableSync::Publishing::Publisher < TableSync::Publishing::BasePublisher
|
4
4
|
DEBOUNCE_TIME = 1.minute
|
5
5
|
|
6
6
|
# 'original_attributes' are not published, they are used to resolve the routing key
|
@@ -34,7 +34,7 @@ class TableSync::Publisher < TableSync::BasePublisher
|
|
34
34
|
return if !object && !destroyed?
|
35
35
|
|
36
36
|
Rabbit.publish(params)
|
37
|
-
model_naming = TableSync.
|
37
|
+
model_naming = TableSync.publishing_adapter.model_naming(object_class)
|
38
38
|
TableSync::Instrument.notify table: model_naming.table, schema: model_naming.schema,
|
39
39
|
event: event, direction: :publish
|
40
40
|
end
|
@@ -95,12 +95,12 @@ class TableSync::Publisher < TableSync::BasePublisher
|
|
95
95
|
elsif attributes_for_sync_defined?
|
96
96
|
object.attributes_for_sync
|
97
97
|
else
|
98
|
-
TableSync.
|
98
|
+
TableSync.publishing_adapter.attributes(object)
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
102
|
memoize def object
|
103
|
-
TableSync.
|
103
|
+
TableSync.publishing_adapter.find(object_class, needle)
|
104
104
|
end
|
105
105
|
|
106
106
|
def event
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableSync
|
4
|
+
module Receiving
|
5
|
+
AVAILABLE_EVENTS = [:update, :destroy].freeze
|
6
|
+
|
7
|
+
require_relative "receiving/config"
|
8
|
+
require_relative "receiving/config_decorator"
|
9
|
+
require_relative "receiving/dsl"
|
10
|
+
require_relative "receiving/handler"
|
11
|
+
require_relative "receiving/model/active_record"
|
12
|
+
require_relative "receiving/model/sequel"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableSync::Receiving
|
4
|
+
class Config
|
5
|
+
attr_reader :model, :events
|
6
|
+
|
7
|
+
def initialize(model:, events: AVAILABLE_EVENTS)
|
8
|
+
@model = model
|
9
|
+
|
10
|
+
@events = [events].flatten.map(&:to_sym)
|
11
|
+
|
12
|
+
unless @events.all? { |event| AVAILABLE_EVENTS.include?(event) }
|
13
|
+
raise TableSync::UndefinedEvent.new(events)
|
14
|
+
end
|
15
|
+
|
16
|
+
self.class.default_values_for_options.each do |ivar, default_value_generator|
|
17
|
+
instance_variable_set(ivar, default_value_generator.call(self))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_reader :default_values_for_options
|
23
|
+
|
24
|
+
# In a configs this options are requested as they are
|
25
|
+
# config.option - get value
|
26
|
+
# config.option(args) - set static value
|
27
|
+
# config.option { ... } - set proc as value
|
28
|
+
#
|
29
|
+
# In `Receiving::Handler` or `Receiving::EventActions` this options are requested
|
30
|
+
# through `Receiving::ConfigDecorator#method_missing` which always executes `config.option`
|
31
|
+
|
32
|
+
def add_option(name, value_setter_wrapper:, value_as_proc_setter_wrapper:, default:)
|
33
|
+
ivar = "@#{name}".to_sym
|
34
|
+
|
35
|
+
@default_values_for_options ||= {}
|
36
|
+
@default_values_for_options[ivar] = default
|
37
|
+
|
38
|
+
define_method(name) do |*value, &value_as_proc|
|
39
|
+
return instance_variable_get(ivar) if value.empty? && value_as_proc.nil?
|
40
|
+
|
41
|
+
value = value.first if value.size == 1
|
42
|
+
|
43
|
+
if value_as_proc.present?
|
44
|
+
new_value = TableSync::Utils.proc_keywords_resolver(&value_as_proc)
|
45
|
+
setter_wrapper = value_as_proc_setter_wrapper
|
46
|
+
else
|
47
|
+
new_value = value
|
48
|
+
setter_wrapper = value_setter_wrapper
|
49
|
+
end
|
50
|
+
|
51
|
+
old_value = instance_variable_get(ivar)
|
52
|
+
result_value = instance_exec(name, new_value, old_value, &setter_wrapper)
|
53
|
+
instance_variable_set(ivar, result_value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def allow_event?(name)
|
59
|
+
events.include?(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Helpers:
|
65
|
+
|
66
|
+
column_key = proc do |option_name, new_value|
|
67
|
+
unless model.columns.include?(new_value)
|
68
|
+
raise TableSync::WrongOptionValue.new(model, option_name, new_value)
|
69
|
+
end
|
70
|
+
new_value
|
71
|
+
end
|
72
|
+
|
73
|
+
exactly_symbol = proc do |option_name, new_value|
|
74
|
+
unless new_value.is_a? Symbol
|
75
|
+
raise TableSync::WrongOptionValue.new(model, option_name, new_value)
|
76
|
+
end
|
77
|
+
new_value
|
78
|
+
end
|
79
|
+
|
80
|
+
to_array = proc do |block|
|
81
|
+
proc do |option_name, new_value|
|
82
|
+
new_value = [new_value].flatten
|
83
|
+
new_value.map { |value| instance_exec(option_name, value, &block) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
exactly_not_array = proc do |block|
|
88
|
+
proc do |option_name, new_value|
|
89
|
+
if new_value.is_a? Array
|
90
|
+
raise TableSync::WrongOptionValue.new(model, option_name, new_value)
|
91
|
+
end
|
92
|
+
instance_exec(option_name, new_value, &block)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
exactly_hash = proc do |block_for_keys, block_for_values|
|
97
|
+
proc do |option_name, new_value|
|
98
|
+
unless new_value.is_a? Hash
|
99
|
+
raise TableSync::WrongOptionValue.new(model, option_name, new_value)
|
100
|
+
end
|
101
|
+
|
102
|
+
new_value.to_a.map do |key, value|
|
103
|
+
[
|
104
|
+
instance_exec("#{option_name} keys", key, &block_for_keys),
|
105
|
+
instance_exec("#{option_name} values", value, &block_for_values),
|
106
|
+
]
|
107
|
+
end.to_h
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
any_value = proc do |_option_name, new_value|
|
112
|
+
new_value
|
113
|
+
end
|
114
|
+
|
115
|
+
raise_error = proc do |option_name, new_value|
|
116
|
+
raise TableSync::WrongOptionValue.new(model, option_name, new_value)
|
117
|
+
end
|
118
|
+
|
119
|
+
exactly_boolean = proc do |option_name, new_value|
|
120
|
+
unless new_value.is_a?(TrueClass) || new_value.is_a?(FalseClass)
|
121
|
+
raise TableSync::WrongOptionValue.new(model, option_name, new_value)
|
122
|
+
end
|
123
|
+
new_value
|
124
|
+
end
|
125
|
+
|
126
|
+
allow_false = proc do |block|
|
127
|
+
proc do |option_name, new_value|
|
128
|
+
next false if new_value.is_a? FalseClass
|
129
|
+
instance_exec(option_name, new_value, &block)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
proc_result = proc do |block|
|
134
|
+
proc do |option_name, new_value|
|
135
|
+
proc do |*args, &b|
|
136
|
+
result = new_value.call(*args, &b)
|
137
|
+
instance_exec(option_name, result, &block)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
accumulate_procs = proc do |block|
|
143
|
+
proc do |option_name, new_value, old_value|
|
144
|
+
old_value.push(&instance_exec(option_name, new_value, &block))
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Options:
|
149
|
+
|
150
|
+
# rubocop:disable Layout/ArgumentAlignment
|
151
|
+
|
152
|
+
TableSync::Receiving::Config.add_option :only,
|
153
|
+
value_setter_wrapper: to_array[column_key],
|
154
|
+
value_as_proc_setter_wrapper: proc_result[to_array[column_key]],
|
155
|
+
default: proc { |config| config.model.columns }
|
156
|
+
|
157
|
+
TableSync::Receiving::Config.add_option :target_keys,
|
158
|
+
value_setter_wrapper: to_array[column_key],
|
159
|
+
value_as_proc_setter_wrapper: proc_result[to_array[column_key]],
|
160
|
+
default: proc { |config| Array.wrap(config.model.primary_keys) }
|
161
|
+
|
162
|
+
TableSync::Receiving::Config.add_option :rest_key,
|
163
|
+
value_setter_wrapper: exactly_not_array[allow_false[column_key]],
|
164
|
+
value_as_proc_setter_wrapper: proc_result[exactly_not_array[allow_false[column_key]]],
|
165
|
+
default: proc { :rest }
|
166
|
+
|
167
|
+
TableSync::Receiving::Config.add_option :version_key,
|
168
|
+
value_setter_wrapper: exactly_not_array[column_key],
|
169
|
+
value_as_proc_setter_wrapper: proc_result[exactly_not_array[column_key]],
|
170
|
+
default: proc { :version }
|
171
|
+
|
172
|
+
TableSync::Receiving::Config.add_option :except,
|
173
|
+
value_setter_wrapper: to_array[exactly_symbol],
|
174
|
+
value_as_proc_setter_wrapper: proc_result[to_array[exactly_symbol]],
|
175
|
+
default: proc { [] }
|
176
|
+
|
177
|
+
TableSync::Receiving::Config.add_option :mapping_overrides,
|
178
|
+
value_setter_wrapper: exactly_hash[exactly_symbol, exactly_symbol],
|
179
|
+
value_as_proc_setter_wrapper: proc_result[exactly_hash[exactly_symbol, exactly_symbol]],
|
180
|
+
default: proc { {} }
|
181
|
+
|
182
|
+
TableSync::Receiving::Config.add_option :additional_data,
|
183
|
+
value_setter_wrapper: exactly_hash[exactly_symbol, any_value],
|
184
|
+
value_as_proc_setter_wrapper: proc_result[exactly_hash[exactly_symbol, any_value]],
|
185
|
+
default: proc { {} }
|
186
|
+
|
187
|
+
TableSync::Receiving::Config.add_option :default_values,
|
188
|
+
value_setter_wrapper: exactly_hash[exactly_symbol, any_value],
|
189
|
+
value_as_proc_setter_wrapper: proc_result[exactly_hash[exactly_symbol, any_value]],
|
190
|
+
default: proc { {} }
|
191
|
+
|
192
|
+
TableSync::Receiving::Config.add_option :skip,
|
193
|
+
value_setter_wrapper: exactly_boolean,
|
194
|
+
value_as_proc_setter_wrapper: proc_result[exactly_boolean],
|
195
|
+
default: proc { false }
|
196
|
+
|
197
|
+
TableSync::Receiving::Config.add_option :wrap_receiving,
|
198
|
+
value_setter_wrapper: raise_error,
|
199
|
+
value_as_proc_setter_wrapper: any_value,
|
200
|
+
default: proc { proc { |&block| block.call } }
|
201
|
+
|
202
|
+
%i[
|
203
|
+
before_update
|
204
|
+
after_commit_on_update
|
205
|
+
before_destroy
|
206
|
+
after_commit_on_destroy
|
207
|
+
].each do |option|
|
208
|
+
TableSync::Receiving::Config.add_option option,
|
209
|
+
value_setter_wrapper: raise_error,
|
210
|
+
value_as_proc_setter_wrapper: accumulate_procs[any_value],
|
211
|
+
default: (proc do |_config|
|
212
|
+
TableSync::Utils::ProcArray.new do |array, args, &block|
|
213
|
+
array.map { |pr| pr.call(*args, &block) }
|
214
|
+
end
|
215
|
+
end)
|
216
|
+
end
|
217
|
+
|
218
|
+
# rubocop:enable Layout/ArgumentAlignment
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableSync::Receiving
|
4
|
+
class ConfigDecorator
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@config, :allow_event?
|
8
|
+
# rubocop:disable Metrics/ParameterLists
|
9
|
+
def initialize(config:, event:, model:, version:, project_id:, raw_data:)
|
10
|
+
@config = config
|
11
|
+
|
12
|
+
@default_params = {
|
13
|
+
event: event,
|
14
|
+
model: model,
|
15
|
+
version: version,
|
16
|
+
project_id: project_id,
|
17
|
+
raw_data: raw_data,
|
18
|
+
}
|
19
|
+
end
|
20
|
+
# rubocop:enable Metrics/ParameterLists
|
21
|
+
|
22
|
+
def method_missing(name, **additional_params, &block)
|
23
|
+
value = @config.send(name)
|
24
|
+
value.is_a?(Proc) ? value.call(@default_params.merge(additional_params), &block) : value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableSync::Receiving
|
4
|
+
module DSL
|
5
|
+
def inherited(klass)
|
6
|
+
klass.instance_variable_set(:@configs, configs.deep_dup)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def configs
|
11
|
+
@configs ||= Hash.new { |hash, key| hash[key] = [] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive(source, to_table: nil, to_model: nil, events: [:update, :destroy], &block)
|
15
|
+
model = to_table ? TableSync.receiving_model.new(to_table) : to_model
|
16
|
+
|
17
|
+
TableSync::Utils::InterfaceChecker.new(model).implements(:receiving_model)
|
18
|
+
|
19
|
+
config = ::TableSync::Receiving::Config.new(model: model, events: events)
|
20
|
+
|
21
|
+
config.instance_exec(&block) if block
|
22
|
+
|
23
|
+
configs[source.to_s] << config
|
24
|
+
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TableSync::Receiving::Handler < Rabbit::EventHandler
|
4
|
+
extend TableSync::Receiving::DSL
|
5
|
+
|
6
|
+
# Rabbit::EventHandler uses Tainbox and performs `handler.new(message).call`
|
7
|
+
attribute :event
|
8
|
+
attribute :model
|
9
|
+
attribute :version
|
10
|
+
|
11
|
+
def call
|
12
|
+
configs.each do |config|
|
13
|
+
next unless config.allow_event?(event)
|
14
|
+
|
15
|
+
data = processed_data(config)
|
16
|
+
|
17
|
+
next if data.empty?
|
18
|
+
|
19
|
+
version_key = config.version_key(data: data)
|
20
|
+
data.each { |row| row[version_key] = version }
|
21
|
+
|
22
|
+
target_keys = config.target_keys(data: data)
|
23
|
+
|
24
|
+
validate_data(data, target_keys: target_keys)
|
25
|
+
|
26
|
+
params = { data: data, target_keys: target_keys, version_key: version_key }
|
27
|
+
|
28
|
+
if event == :update
|
29
|
+
params[:default_values] = config.default_values(data: data)
|
30
|
+
end
|
31
|
+
|
32
|
+
config.wrap_receiving(**params) do
|
33
|
+
perform(config, params)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# redefine setter from Rabbit::EventHandler
|
41
|
+
def data=(data)
|
42
|
+
super(Array.wrap(data[:attributes]))
|
43
|
+
end
|
44
|
+
|
45
|
+
def event=(name)
|
46
|
+
name = name.to_sym
|
47
|
+
raise TableSync::UndefinedEvent.new(event) unless %i[update destroy].include?(name)
|
48
|
+
super(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def model=(name)
|
52
|
+
super(name.to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
def configs
|
56
|
+
@configs ||= self.class.configs[model]&.map do |config|
|
57
|
+
::TableSync::Receiving::ConfigDecorator.new(
|
58
|
+
config: config,
|
59
|
+
# next parameters will be send to each proc-options from config
|
60
|
+
event: event,
|
61
|
+
model: model,
|
62
|
+
version: version,
|
63
|
+
project_id: project_id,
|
64
|
+
raw_data: data,
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def processed_data(config)
|
70
|
+
data.map do |row|
|
71
|
+
next if config.skip(row: row)
|
72
|
+
|
73
|
+
row = row.dup
|
74
|
+
|
75
|
+
config.mapping_overrides(row: row).each do |before, after|
|
76
|
+
row[after] = row.delete(before)
|
77
|
+
end
|
78
|
+
|
79
|
+
config.except(row: row).each(&row.method(:delete))
|
80
|
+
|
81
|
+
row.merge!(config.additional_data(row: row))
|
82
|
+
|
83
|
+
only = config.only(row: row)
|
84
|
+
row, rest = row.partition { |key, _| key.in?(only) }.map(&:to_h)
|
85
|
+
|
86
|
+
rest_key = config.rest_key(row: row, rest: rest)
|
87
|
+
(row[rest_key] ||= {}).merge!(rest) if rest_key
|
88
|
+
|
89
|
+
row
|
90
|
+
end.compact
|
91
|
+
end
|
92
|
+
|
93
|
+
def validate_data(data, target_keys:)
|
94
|
+
data.each do |row|
|
95
|
+
next if target_keys.all?(&row.keys.method(:include?))
|
96
|
+
raise TableSync::DataError.new(
|
97
|
+
data, target_keys, "Some target keys not found in received attributes!"
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
if data.uniq { |row| row.slice(*target_keys) }.size != data.size
|
102
|
+
raise TableSync::DataError.new(
|
103
|
+
data, target_keys, "Duplicate rows found!"
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def perform(config, params)
|
109
|
+
model = config.model
|
110
|
+
|
111
|
+
model.transaction do
|
112
|
+
if event == :update
|
113
|
+
config.before_update(**params)
|
114
|
+
|
115
|
+
results = model.upsert(**params)
|
116
|
+
|
117
|
+
model.after_commit do
|
118
|
+
config.after_commit_on_update(**params.merge(results: results))
|
119
|
+
end
|
120
|
+
else
|
121
|
+
config.before_destroy(**params)
|
122
|
+
|
123
|
+
results = model.destroy(**params)
|
124
|
+
|
125
|
+
model.after_commit do
|
126
|
+
config.after_commit_on_destroy(**params.merge(results: results))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|