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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +26 -1
  4. data/.travis.yml +6 -6
  5. data/CHANGELOG.md +74 -1
  6. data/Gemfile.lock +262 -0
  7. data/LICENSE.md +1 -1
  8. data/README.md +4 -1
  9. data/docs/message_protocol.md +24 -0
  10. data/docs/notifications.md +45 -0
  11. data/docs/publishing.md +147 -0
  12. data/docs/receiving.md +341 -0
  13. data/lib/table_sync.rb +23 -33
  14. data/lib/table_sync/errors.rb +60 -22
  15. data/lib/table_sync/instrument.rb +2 -2
  16. data/lib/table_sync/publishing.rb +11 -0
  17. data/lib/table_sync/{base_publisher.rb → publishing/base_publisher.rb} +1 -1
  18. data/lib/table_sync/{batch_publisher.rb → publishing/batch_publisher.rb} +12 -13
  19. data/lib/table_sync/{orm_adapter → publishing/orm_adapter}/active_record.rb +4 -8
  20. data/lib/table_sync/{orm_adapter → publishing/orm_adapter}/sequel.rb +10 -17
  21. data/lib/table_sync/{publisher.rb → publishing/publisher.rb} +4 -4
  22. data/lib/table_sync/receiving.rb +14 -0
  23. data/lib/table_sync/receiving/config.rb +218 -0
  24. data/lib/table_sync/receiving/config_decorator.rb +27 -0
  25. data/lib/table_sync/receiving/dsl.rb +28 -0
  26. data/lib/table_sync/receiving/handler.rb +131 -0
  27. data/lib/table_sync/{model → receiving/model}/active_record.rb +37 -23
  28. data/lib/table_sync/{model → receiving/model}/sequel.rb +13 -8
  29. data/lib/table_sync/utils.rb +9 -0
  30. data/lib/table_sync/utils/interface_checker.rb +97 -0
  31. data/lib/table_sync/utils/proc_array.rb +17 -0
  32. data/lib/table_sync/utils/proc_keywords_resolver.rb +46 -0
  33. data/lib/table_sync/version.rb +1 -1
  34. data/table_sync.gemspec +5 -4
  35. metadata +48 -30
  36. data/docs/synopsis.md +0 -318
  37. data/lib/table_sync/config.rb +0 -105
  38. data/lib/table_sync/config/callback_registry.rb +0 -53
  39. data/lib/table_sync/config_decorator.rb +0 -38
  40. data/lib/table_sync/dsl.rb +0 -25
  41. data/lib/table_sync/event_actions.rb +0 -96
  42. data/lib/table_sync/event_actions/data_wrapper.rb +0 -7
  43. data/lib/table_sync/event_actions/data_wrapper/base.rb +0 -23
  44. data/lib/table_sync/event_actions/data_wrapper/destroy.rb +0 -19
  45. data/lib/table_sync/event_actions/data_wrapper/update.rb +0 -21
  46. data/lib/table_sync/receiving_handler.rb +0 -76
@@ -1,61 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "memery"
4
+ require "self_data"
4
5
  require "rabbit_messaging"
5
6
  require "rabbit/event_handler" # NOTE: from rabbit_messaging"
6
7
  require "active_support/core_ext/object/blank"
7
8
  require "active_support/core_ext/numeric/time"
8
9
 
9
10
  module TableSync
10
- require_relative "./table_sync/version"
11
- require_relative "./table_sync/errors"
12
- require_relative "./table_sync/event_actions"
13
- require_relative "./table_sync/event_actions/data_wrapper"
14
- require_relative "./table_sync/config"
15
- require_relative "./table_sync/config/callback_registry"
16
- require_relative "./table_sync/config_decorator"
17
- require_relative "./table_sync/dsl"
18
- require_relative "./table_sync/receiving_handler"
19
- require_relative "./table_sync/base_publisher"
20
- require_relative "./table_sync/publisher"
21
- require_relative "./table_sync/batch_publisher"
22
- require_relative "./table_sync/orm_adapter/active_record"
23
- require_relative "./table_sync/orm_adapter/sequel"
24
- require_relative "./table_sync/model/active_record"
25
- require_relative "./table_sync/model/sequel"
26
- require_relative "./table_sync/instrument"
27
- require_relative "./table_sync/instrument_adapter/active_support"
28
- require_relative "./table_sync/naming_resolver/active_record"
29
- require_relative "./table_sync/naming_resolver/sequel"
11
+ require_relative "table_sync/utils"
12
+ require_relative "table_sync/version"
13
+ require_relative "table_sync/errors"
14
+ require_relative "table_sync/instrument"
15
+ require_relative "table_sync/instrument_adapter/active_support"
16
+ require_relative "table_sync/naming_resolver/active_record"
17
+ require_relative "table_sync/naming_resolver/sequel"
18
+ require_relative "table_sync/receiving"
19
+ require_relative "table_sync/publishing"
30
20
 
31
21
  class << self
32
- include Memery
33
-
34
22
  attr_accessor :publishing_job_class_callable
35
23
  attr_accessor :batch_publishing_job_class_callable
36
24
  attr_accessor :routing_key_callable
37
25
  attr_accessor :exchange_name
38
26
  attr_accessor :routing_metadata_callable
39
27
  attr_accessor :notifier
28
+ attr_reader :orm
29
+ attr_reader :publishing_adapter
30
+ attr_reader :receiving_model
40
31
 
41
- def sync(*args)
42
- orm.setup_sync(*args)
32
+ def sync(klass, **opts)
33
+ publishing_adapter.setup_sync(klass, opts)
43
34
  end
44
35
 
45
36
  def orm=(val)
46
- clear_memery_cache!
47
- @orm = val
48
- end
49
-
50
- memoize def orm
51
- case @orm
37
+ case val
52
38
  when :active_record
53
- ORMAdapter::ActiveRecord
39
+ @publishing_adapter = Publishing::ORMAdapter::ActiveRecord
40
+ @receiving_model = Receiving::Model::ActiveRecord
54
41
  when :sequel
55
- ORMAdapter::Sequel
42
+ @publishing_adapter = Publishing::ORMAdapter::Sequel
43
+ @receiving_model = Receiving::Model::Sequel
56
44
  else
57
- raise "ORM not supported: #{@orm.inspect}"
45
+ raise ORMNotSupported.new(val.inspect)
58
46
  end
47
+
48
+ @orm = val
59
49
  end
60
50
  end
61
51
  end
@@ -4,38 +4,76 @@ module TableSync
4
4
  Error = Class.new(StandardError)
5
5
 
6
6
  class UpsertError < Error
7
- def initialize(data:, target_keys:, version_key:, first_sync_time_key:, default_values:)
8
- super <<~MSG
9
- Upsert has changed more than 1 row;
10
- data: #{data.inspect}
11
- target_keys: #{target_keys.inspect}
12
- version_key: #{version_key.inspect}
13
- first_sync_time_key: #{first_sync_time_key.inspect}
14
- default_values: #{default_values.inspect}
7
+ def initialize(data:, target_keys:, result:)
8
+ super("data: #{data.inspect}, target_keys: #{target_keys.inspect}, result: #{result.inspect}")
9
+ end
10
+ end
11
+
12
+ class DestroyError < Error
13
+ def initialize(data:, target_keys:, result:)
14
+ super("data: #{data.inspect}, target_keys: #{target_keys.inspect}, result: #{result.inspect}")
15
+ end
16
+ end
17
+
18
+ class DataError < Error
19
+ def initialize(data, target_keys, description)
20
+ super(<<~MSG.squish)
21
+ #{description}
22
+ target_keys: #{target_keys}
23
+ data: #{data}
15
24
  MSG
16
25
  end
17
26
  end
18
27
 
19
- class UndefinedConfig < Error
20
- def initialize(model)
21
- super("Config not defined for model; model: #{model.inspect}")
28
+ # @api public
29
+ # @since 2.2.0
30
+ PluginError = Class.new(Error)
31
+
32
+ # @api public
33
+ # @since 2.2.0
34
+ class UnregisteredPluginError < PluginError
35
+ # @param plugin_name [Any]
36
+ def initialize(plugin_name)
37
+ super("#{plugin_name} plugin is not registered")
22
38
  end
23
39
  end
24
40
 
25
- class DestroyError < Error
26
- def initialize(data)
27
- super("Destroy has changed more than 1 row; data: #{data.inspect}")
41
+ # @api public
42
+ # @since 2.2.0
43
+ class AlreadyRegisteredPluginError < PluginError
44
+ # @param plugin_name [Any]
45
+ def initialize(plugin_name)
46
+ super("#{plugin_name} plugin already exists")
28
47
  end
29
48
  end
30
49
 
31
- class UnprovidedEventTargetKeysError < Error
32
- # @param target_keys [Array<Symbol,String>]
33
- # @param target_attributes [Hash<Symbol|String,Any>]
34
- def initialize(target_keys, target_attributes)
35
- super(<<~MSG.squish)
36
- Some target keys not found in received attributes!
37
- (Expects: #{target_keys}, Actual: #{target_attributes.keys})
38
- MSG
50
+ class InterfaceError < Error
51
+ def initialize(object, method_name, parameters, description)
52
+ parameters = parameters.map do |parameter|
53
+ type, name = parameter
54
+
55
+ case type
56
+ when :req
57
+ name.to_s
58
+ when :keyreq
59
+ "#{name}:"
60
+ when :block
61
+ "&#{name}"
62
+ end
63
+ end
64
+
65
+ signature = "#{method_name}(#{parameters.join(", ")})"
66
+
67
+ super("#{object} has to implement method `#{signature}`\n#{description}")
68
+ end
69
+ end
70
+
71
+ UndefinedEvent = Class.new(Error)
72
+ ORMNotSupported = Class.new(Error)
73
+
74
+ class WrongOptionValue < Error
75
+ def initialize(model, option, value)
76
+ super("TableSync config for #{model.inspect} can't contain #{value.inspect} as #{option}")
39
77
  end
40
78
  end
41
79
  end
@@ -3,7 +3,7 @@
3
3
  module TableSync::Instrument
4
4
  module_function
5
5
 
6
- def notify(*args)
7
- TableSync.notifier&.notify(*args)
6
+ def notify(**args)
7
+ TableSync.notifier&.notify(**args)
8
8
  end
9
9
  end
@@ -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::BasePublisher
3
+ class TableSync::Publishing::BasePublisher
4
4
  include Memery
5
5
 
6
6
  BASE_SAFE_JSON_TYPES = [NilClass, String, TrueClass, FalseClass, Numeric, Symbol].freeze
@@ -1,14 +1,17 @@
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
- @object_class = object_class.constantize
6
5
  @original_attributes_array = original_attributes_array.map do |hash|
7
6
  filter_safe_for_serialization(hash.deep_symbolize_keys)
8
7
  end
9
- @confirm = options[:confirm] || true
10
- @routing_key = options[:routing_key] || resolve_routing_key
8
+
9
+ @object_class = object_class.constantize
10
+ @confirm = options[:confirm] || true
11
+ @routing_key = options[:routing_key] || resolve_routing_key
11
12
  @push_original_attributes = options[:push_original_attributes] || false
13
+ @headers = options[:headers]
14
+ @event = options[:event] || :update
12
15
  end
13
16
 
14
17
  def publish
@@ -19,7 +22,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
19
22
  return unless need_publish?
20
23
  Rabbit.publish(params)
21
24
 
22
- model_naming = TableSync.orm.model_naming(object_class)
25
+ model_naming = TableSync.publishing_adapter.model_naming(object_class)
23
26
  TableSync::Instrument.notify table: model_naming.table, schema: model_naming.schema,
24
27
  event: event,
25
28
  count: publishing_data[:attributes].size, direction: :publish
@@ -27,7 +30,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
27
30
 
28
31
  private
29
32
 
30
- attr_reader :original_attributes_array, :routing_key
33
+ attr_reader :original_attributes_array, :routing_key, :headers, :event
31
34
 
32
35
  def push_original_attributes?
33
36
  @push_original_attributes
@@ -38,7 +41,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
38
41
  end
39
42
 
40
43
  memoize def objects
41
- needles.map { |needle| TableSync.orm.find(object_class, needle) }.compact
44
+ needles.map { |needle| TableSync.publishing_adapter.find(object_class, needle) }.compact
42
45
  end
43
46
 
44
47
  def job_callable
@@ -64,7 +67,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
64
67
  def params
65
68
  {
66
69
  **super,
67
- headers: nil,
70
+ headers: headers,
68
71
  }
69
72
  end
70
73
 
@@ -80,10 +83,6 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
80
83
  }
81
84
  end
82
85
 
83
- def event
84
- :update
85
- end
86
-
87
86
  def attributes_for_sync
88
87
  return original_attributes_array if push_original_attributes?
89
88
 
@@ -91,7 +90,7 @@ class TableSync::BatchPublisher < TableSync::BasePublisher
91
90
  if attributes_for_sync_defined?
92
91
  object.attributes_for_sync
93
92
  else
94
- TableSync.orm.attributes(object)
93
+ TableSync.publishing_adapter.attributes(object)
95
94
  end
96
95
  end
97
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
@@ -20,14 +16,14 @@ module TableSync::ORMAdapter
20
16
  object.attributes
21
17
  end
22
18
 
23
- def setup_sync(klass, **opts)
19
+ def setup_sync(klass, opts)
24
20
  debounce_time = opts.delete(:debounce_time)
25
21
 
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
- state: state, debounce_time: debounce_time).publish
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
@@ -20,7 +16,7 @@ module TableSync::ORMAdapter
20
16
  object.values
21
17
  end
22
18
 
23
- def setup_sync(klass, **opts)
19
+ def setup_sync(klass, opts)
24
20
  if_predicate = to_predicate(opts.delete(:if), true)
25
21
  unless_predicate = to_predicate(opts.delete(:unless), false)
26
22
  debounce_time = opts.delete(:debounce_time)
@@ -40,24 +36,21 @@ module TableSync::ORMAdapter
40
36
  end
41
37
 
42
38
  def register_callbacks(klass, if_predicate, unless_predicate, debounce_time)
43
- { create: :created, update: :updated }.each do |event, state|
39
+ { create: :created, update: :updated, destroy: :destroyed }.each do |event, state|
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(self.class.name, values,
48
- state: state, debounce_time: debounce_time).publish
43
+ TableSync::Publishing::Publisher.new(
44
+ self.class.name,
45
+ values,
46
+ state: state,
47
+ debounce_time: debounce_time,
48
+ ).publish
49
49
  end
50
50
  end
51
- super()
52
- end
53
- end
54
51
 
55
- klass.send(:define_method, :after_destroy) do
56
- # publish anyway
57
- db.after_commit do
58
- TableSync::Publisher.new(self.class.name, values, state: :destroyed).publish
52
+ super()
59
53
  end
60
- super()
61
54
  end
62
55
  end
63
56
  end
@@ -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.orm.model_naming(object_class)
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.orm.attributes(object)
98
+ TableSync.publishing_adapter.attributes(object)
99
99
  end
100
100
  end
101
101
 
102
102
  memoize def object
103
- TableSync.orm.find(object_class, needle)
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