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
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class TableSync::Config::CallbackRegistry
|
4
|
-
CALLBACK_KINDS = %i[after_commit before_commit].freeze
|
5
|
-
EVENTS = %i[create update destroy].freeze
|
6
|
-
|
7
|
-
InvalidCallbackKindError = Class.new(ArgumentError)
|
8
|
-
InvalidEventError = Class.new(ArgumentError)
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
@callbacks = CALLBACK_KINDS.map { |event| [event, make_event_hash] }.to_h
|
12
|
-
end
|
13
|
-
|
14
|
-
def register_callback(callback, kind:, event:)
|
15
|
-
validate_callback_kind!(kind)
|
16
|
-
validate_event!(event)
|
17
|
-
|
18
|
-
callbacks.fetch(kind)[event] << callback
|
19
|
-
end
|
20
|
-
|
21
|
-
def get_callbacks(kind:, event:)
|
22
|
-
validate_callback_kind!(kind)
|
23
|
-
validate_event!(event)
|
24
|
-
|
25
|
-
callbacks.fetch(kind).fetch(event, [])
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
attr_reader :callbacks
|
31
|
-
|
32
|
-
def make_event_hash
|
33
|
-
Hash.new { |hsh, key| hsh[key] = [] }
|
34
|
-
end
|
35
|
-
|
36
|
-
def validate_callback_kind!(kind)
|
37
|
-
unless CALLBACK_KINDS.include?(kind)
|
38
|
-
raise(
|
39
|
-
InvalidCallbackKindError,
|
40
|
-
"Invalid callback kind: #{kind.inspect}. Valid kinds are #{CALLBACK_KINDS.inspect}",
|
41
|
-
)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def validate_event!(event)
|
46
|
-
unless EVENTS.include?(event)
|
47
|
-
raise(
|
48
|
-
InvalidEventError,
|
49
|
-
"Invalid event: #{event.inspect}. Valid events are #{EVENTS.inspect}",
|
50
|
-
)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableSync
|
4
|
-
# rubocop:disable Style/MissingRespondToMissing, Style/MethodMissingSuper
|
5
|
-
class ConfigDecorator
|
6
|
-
include ::TableSync::EventActions
|
7
|
-
extend Forwardable
|
8
|
-
|
9
|
-
def_delegators :@config, :on_destroy, :wrap_receiving
|
10
|
-
|
11
|
-
def initialize(config, handler)
|
12
|
-
@config = config
|
13
|
-
@handler = handler
|
14
|
-
end
|
15
|
-
|
16
|
-
def method_missing(name, *args)
|
17
|
-
value = @config.send(name)
|
18
|
-
|
19
|
-
if value.is_a?(Proc)
|
20
|
-
value.call(
|
21
|
-
event: @handler.event,
|
22
|
-
model: @handler.model,
|
23
|
-
version: @handler.version,
|
24
|
-
project_id: @handler.project_id,
|
25
|
-
data: @handler.data,
|
26
|
-
current_row: args.first,
|
27
|
-
)
|
28
|
-
else
|
29
|
-
value
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def allow_event?(event)
|
34
|
-
@config.allow_event?(event)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
# rubocop:enable Style/MissingRespondToMissing, Style/MethodMissingSuper
|
38
|
-
end
|
data/lib/table_sync/dsl.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableSync
|
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:, events: nil, &block)
|
15
|
-
config = ::TableSync::Config.new(
|
16
|
-
model: TableSync.orm.model.new(to_table),
|
17
|
-
events: events,
|
18
|
-
)
|
19
|
-
|
20
|
-
config.instance_exec(&block) if block
|
21
|
-
|
22
|
-
configs[source.to_s] << config
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableSync
|
4
|
-
module EventActions
|
5
|
-
def update(data)
|
6
|
-
data.each_value do |attribute_set|
|
7
|
-
attribute_set.each do |attributes|
|
8
|
-
prevent_incomplete_event!(attributes)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
with_wrapping(DataWrapper::Update.new(data)) do
|
13
|
-
process_upsert(data)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def destroy(data)
|
18
|
-
attributes = data.first || {}
|
19
|
-
target_attributes = attributes.select { |key, _value| target_keys.include?(key) }
|
20
|
-
prevent_incomplete_event!(target_attributes)
|
21
|
-
|
22
|
-
with_wrapping(DataWrapper::Destroy.new(attributes)) do
|
23
|
-
process_destroy(attributes, target_attributes)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def process_upsert(data) # rubocop:disable Metrics/MethodLength
|
28
|
-
model.transaction do
|
29
|
-
args = {
|
30
|
-
data: data,
|
31
|
-
target_keys: target_keys,
|
32
|
-
version_key: version_key,
|
33
|
-
first_sync_time_key: first_sync_time_key,
|
34
|
-
default_values: default_values,
|
35
|
-
}
|
36
|
-
|
37
|
-
@config.callback_registry.get_callbacks(kind: :before_commit, event: :update).each do |cb|
|
38
|
-
cb[data.values.flatten]
|
39
|
-
end
|
40
|
-
|
41
|
-
results = data.reduce([]) do |upserts, (part_model, part_data)|
|
42
|
-
upserts + part_model.upsert(**args, data: part_data)
|
43
|
-
end
|
44
|
-
|
45
|
-
return if results.empty?
|
46
|
-
raise TableSync::UpsertError.new(**args) unless expected_update_result?(results)
|
47
|
-
|
48
|
-
@config.model.after_commit do
|
49
|
-
@config.callback_registry.get_callbacks(kind: :after_commit, event: :update).each do |cb|
|
50
|
-
cb[results]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def process_destroy(attributes, target_attributes)
|
57
|
-
model.transaction do
|
58
|
-
@config.callback_registry.get_callbacks(kind: :before_commit, event: :destroy).each do |cb|
|
59
|
-
cb[attributes]
|
60
|
-
end
|
61
|
-
|
62
|
-
if on_destroy
|
63
|
-
results = on_destroy.call(attributes: attributes, target_keys: target_keys)
|
64
|
-
else
|
65
|
-
results = model.destroy(target_attributes)
|
66
|
-
return if results.empty?
|
67
|
-
raise TableSync::DestroyError.new(target_attributes) if results.size != 1
|
68
|
-
end
|
69
|
-
|
70
|
-
@config.model.after_commit do
|
71
|
-
@config.callback_registry.get_callbacks(kind: :after_commit, event: :destroy).each do |cb|
|
72
|
-
cb[results]
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def with_wrapping(data = {}, &block)
|
79
|
-
if @config.wrap_receiving
|
80
|
-
@config.wrap_receiving.call(data, block)
|
81
|
-
else
|
82
|
-
yield
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def expected_update_result?(query_results)
|
87
|
-
query_results.uniq { |d| d.slice(*target_keys) }.size == query_results.size
|
88
|
-
end
|
89
|
-
|
90
|
-
def prevent_incomplete_event!(attributes)
|
91
|
-
unless target_keys.all?(&attributes.keys.method(:include?))
|
92
|
-
raise TableSync::UnprovidedEventTargetKeysError.new(target_keys, attributes)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class TableSync::EventActions::DataWrapper::Base
|
4
|
-
include Enumerable
|
5
|
-
|
6
|
-
attr_reader :event_data
|
7
|
-
|
8
|
-
def initialize(event_data)
|
9
|
-
@event_data = event_data
|
10
|
-
end
|
11
|
-
|
12
|
-
def type
|
13
|
-
raise NoMethodError # NOTE: for clarity
|
14
|
-
end
|
15
|
-
|
16
|
-
def destroy?
|
17
|
-
false
|
18
|
-
end
|
19
|
-
|
20
|
-
def update?
|
21
|
-
false
|
22
|
-
end
|
23
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class TableSync::EventActions::DataWrapper::Destroy < TableSync::EventActions::DataWrapper::Base
|
4
|
-
def type
|
5
|
-
:destroy
|
6
|
-
end
|
7
|
-
|
8
|
-
def each
|
9
|
-
yield(event_data)
|
10
|
-
end
|
11
|
-
|
12
|
-
def destroy?
|
13
|
-
true
|
14
|
-
end
|
15
|
-
|
16
|
-
def update?
|
17
|
-
false
|
18
|
-
end
|
19
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class TableSync::EventActions::DataWrapper::Update < TableSync::EventActions::DataWrapper::Base
|
4
|
-
def type
|
5
|
-
:update
|
6
|
-
end
|
7
|
-
|
8
|
-
def each
|
9
|
-
event_data.each_pair do |model_klass, changed_models_attrs|
|
10
|
-
yield([model_klass, changed_models_attrs])
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def destroy?
|
15
|
-
false
|
16
|
-
end
|
17
|
-
|
18
|
-
def update?
|
19
|
-
true
|
20
|
-
end
|
21
|
-
end
|
data/lib/table_sync/plugins.rb
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# @api public
|
4
|
-
# @since 2.2.0
|
5
|
-
module TableSync::Plugins
|
6
|
-
require_relative "plugins/registry"
|
7
|
-
require_relative "plugins/access_mixin"
|
8
|
-
require_relative "plugins/abstract"
|
9
|
-
|
10
|
-
# @since 2.2.0
|
11
|
-
@plugin_registry = Registry.new
|
12
|
-
# @since 2.2.0
|
13
|
-
@access_lock = Mutex.new
|
14
|
-
|
15
|
-
class << self
|
16
|
-
# @param plugin_name [Symbol, String]
|
17
|
-
# @return [void]
|
18
|
-
#
|
19
|
-
# @api public
|
20
|
-
# @since 2.2.0
|
21
|
-
def load(plugin_name)
|
22
|
-
thread_safe { plugin_registry[plugin_name].load! }
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [Array<String>]
|
26
|
-
#
|
27
|
-
# @api public
|
28
|
-
# @since 2.2.0
|
29
|
-
def loaded_plugins
|
30
|
-
thread_safe { plugin_registry.loaded.keys }
|
31
|
-
end
|
32
|
-
|
33
|
-
# @return [Array<String>]
|
34
|
-
#
|
35
|
-
# @api public
|
36
|
-
# @since 2.2.0
|
37
|
-
def names
|
38
|
-
thread_safe { plugin_registry.names }
|
39
|
-
end
|
40
|
-
|
41
|
-
# @param plugin_name [Symbol, String]
|
42
|
-
# @return [void]
|
43
|
-
#
|
44
|
-
# @api private
|
45
|
-
# @since 2.2.0
|
46
|
-
def register_plugin(plugin_name, plugin_module)
|
47
|
-
thread_safe { plugin_registry[plugin_name] = plugin_module }
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
# @return [TableSync::Plugins::Registry]
|
53
|
-
#
|
54
|
-
# @api private
|
55
|
-
# @since 2.2.0
|
56
|
-
attr_reader :plugin_registry
|
57
|
-
|
58
|
-
# @return [Mutex]
|
59
|
-
#
|
60
|
-
# @api private
|
61
|
-
# @since 2.2.0
|
62
|
-
attr_reader :access_lock
|
63
|
-
|
64
|
-
# @return [void]
|
65
|
-
#
|
66
|
-
# @api private
|
67
|
-
# @since 2.2.0
|
68
|
-
def thread_safe
|
69
|
-
access_lock.synchronize { yield if block_given? }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# @api private
|
4
|
-
# @since 2.2.0
|
5
|
-
class TableSync::Plugins::Abstract
|
6
|
-
class << self
|
7
|
-
# @param child_klass [Class]
|
8
|
-
# @return [void]
|
9
|
-
#
|
10
|
-
# @api private
|
11
|
-
# @since 2.2.0
|
12
|
-
def inherited(child_klass)
|
13
|
-
child_klass.instance_variable_set(:@__loaded__, false)
|
14
|
-
child_klass.instance_variable_set(:@__lock__, Mutex.new)
|
15
|
-
super
|
16
|
-
end
|
17
|
-
|
18
|
-
# @return [void]
|
19
|
-
#
|
20
|
-
# @api private
|
21
|
-
# @since 2.2.0
|
22
|
-
def load!
|
23
|
-
__thread_safe__ do
|
24
|
-
unless @__loaded__
|
25
|
-
@__loaded__ = true
|
26
|
-
install!
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# @return [Boolean]
|
32
|
-
#
|
33
|
-
# @api private
|
34
|
-
# @since 2.2.0
|
35
|
-
def loaded?
|
36
|
-
__thread_safe__ { @__loaded__ }
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
# @return [void]
|
42
|
-
#
|
43
|
-
# @api private
|
44
|
-
# @since 2.2.0
|
45
|
-
def install!; end
|
46
|
-
|
47
|
-
# @return [Any]
|
48
|
-
#
|
49
|
-
# @api private
|
50
|
-
# @since 2.2.0
|
51
|
-
def __thread_safe__
|
52
|
-
@__lock__.synchronize { yield }
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# @api private
|
4
|
-
# @since 2.2.0
|
5
|
-
module TableSync::Plugins::AccessMixin
|
6
|
-
# @param plugin_name [Symbol, String]
|
7
|
-
# @return [void]
|
8
|
-
#
|
9
|
-
# @see TableSync::Plugins
|
10
|
-
#
|
11
|
-
# @api public
|
12
|
-
# @since 2.2.0
|
13
|
-
def plugin(plugin_name)
|
14
|
-
TableSync::Plugins.load(plugin_name)
|
15
|
-
end
|
16
|
-
alias_method :enable, :plugin
|
17
|
-
alias_method :load, :plugin
|
18
|
-
|
19
|
-
# @return [Array<String>]
|
20
|
-
#
|
21
|
-
# @see TableSync::Plugins
|
22
|
-
#
|
23
|
-
# @api public
|
24
|
-
# @since 2.2.0
|
25
|
-
def plugins
|
26
|
-
TableSync::Plugins.names
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [Hash<String,Class<TableSync::Plugins::Abstract>>]
|
30
|
-
#
|
31
|
-
# @api private
|
32
|
-
# @since 2.2.0
|
33
|
-
def loaded_plugins
|
34
|
-
TableSync::Plugins.loaded_plugins
|
35
|
-
end
|
36
|
-
alias_method :enabled_plugins, :loaded_plugins
|
37
|
-
|
38
|
-
# @param plugin_name [String, Symbol]
|
39
|
-
# @param plugin_klass [Class<TableSync::Plugins::Abstract>]
|
40
|
-
# @return [void]
|
41
|
-
#
|
42
|
-
# @see TableSync::Plugins
|
43
|
-
#
|
44
|
-
# @api public
|
45
|
-
# @since 2.2.0
|
46
|
-
def register_plugin(plugin_name, plugin_klass)
|
47
|
-
TableSync::Plugins.register_plugin(plugin_name, plugin_klass)
|
48
|
-
end
|
49
|
-
end
|