table_sync 5.1.0 → 6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +3 -0
  5. data/docs/publishing/configuration.md +143 -0
  6. data/docs/publishing/manual.md +155 -0
  7. data/docs/publishing/publishers.md +162 -0
  8. data/docs/publishing.md +35 -105
  9. data/lib/table_sync/errors.rb +31 -22
  10. data/lib/table_sync/event.rb +35 -0
  11. data/lib/table_sync/orm_adapter/active_record.rb +25 -0
  12. data/lib/table_sync/orm_adapter/base.rb +92 -0
  13. data/lib/table_sync/orm_adapter/sequel.rb +29 -0
  14. data/lib/table_sync/publishing/batch.rb +53 -0
  15. data/lib/table_sync/publishing/data/objects.rb +50 -0
  16. data/lib/table_sync/publishing/data/raw.rb +27 -0
  17. data/lib/table_sync/publishing/helpers/attributes.rb +63 -0
  18. data/lib/table_sync/publishing/helpers/debounce.rb +134 -0
  19. data/lib/table_sync/publishing/helpers/objects.rb +39 -0
  20. data/lib/table_sync/publishing/message/base.rb +73 -0
  21. data/lib/table_sync/publishing/message/batch.rb +14 -0
  22. data/lib/table_sync/publishing/message/raw.rb +54 -0
  23. data/lib/table_sync/publishing/message/single.rb +13 -0
  24. data/lib/table_sync/publishing/params/base.rb +66 -0
  25. data/lib/table_sync/publishing/params/batch.rb +23 -0
  26. data/lib/table_sync/publishing/params/raw.rb +7 -0
  27. data/lib/table_sync/publishing/params/single.rb +31 -0
  28. data/lib/table_sync/publishing/raw.rb +21 -0
  29. data/lib/table_sync/publishing/single.rb +65 -0
  30. data/lib/table_sync/publishing.rb +20 -5
  31. data/lib/table_sync/receiving/config.rb +6 -4
  32. data/lib/table_sync/receiving/handler.rb +10 -6
  33. data/lib/table_sync/receiving.rb +0 -2
  34. data/lib/table_sync/setup/active_record.rb +22 -0
  35. data/lib/table_sync/setup/base.rb +67 -0
  36. data/lib/table_sync/setup/sequel.rb +26 -0
  37. data/lib/table_sync/version.rb +1 -1
  38. data/lib/table_sync.rb +31 -8
  39. metadata +28 -7
  40. data/lib/table_sync/publishing/base_publisher.rb +0 -110
  41. data/lib/table_sync/publishing/batch_publisher.rb +0 -109
  42. data/lib/table_sync/publishing/orm_adapter/active_record.rb +0 -32
  43. data/lib/table_sync/publishing/orm_adapter/sequel.rb +0 -57
  44. data/lib/table_sync/publishing/publisher.rb +0 -129
@@ -0,0 +1,67 @@
1
+ # frozen-string_literal: true
2
+
3
+ module TableSync::Setup
4
+ class Base
5
+ include Tainbox
6
+
7
+ EVENTS = %i[create update destroy].freeze
8
+ INVALID_EVENT = Class.new(StandardError)
9
+ INVALID_CONDITION = Class.new(StandardError)
10
+
11
+ attribute :object_class
12
+ attribute :debounce_time
13
+ attribute :on
14
+ attribute :if_condition
15
+ attribute :unless_condition
16
+
17
+ def initialize(attrs)
18
+ super(attrs)
19
+
20
+ self.on = Array.wrap(on).map(&:to_sym)
21
+
22
+ self.if_condition ||= proc { true }
23
+ self.unless_condition ||= proc { false }
24
+
25
+ raise INVALID_EVENTS unless valid_events?
26
+ raise INVALID_CONDITION unless valid_conditions?
27
+ end
28
+
29
+ def register_callbacks
30
+ applicable_events.each { |event| define_after_commit(event) }
31
+ end
32
+
33
+ private
34
+
35
+ # VALIDATION
36
+
37
+ def valid_events?
38
+ on.all? { |event| event.in?(EVENTS) }
39
+ end
40
+
41
+ def valid_conditions?
42
+ if_condition.is_a?(Proc) && unless_condition.is_a?(Proc)
43
+ end
44
+
45
+ # EVENTS
46
+
47
+ def applicable_events
48
+ on.presence || EVENTS
49
+ end
50
+
51
+ # CREATING HOOKS
52
+
53
+ # :nocov:
54
+ def define_after_commit(event)
55
+ raise NotImplementedError
56
+ end
57
+ # :nocov:
58
+
59
+ def options_exposed_for_block
60
+ {
61
+ if: if_condition,
62
+ unless: unless_condition,
63
+ debounce_time: debounce_time,
64
+ }
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ # frozen-string_literal: true
2
+
3
+ module TableSync::Setup
4
+ class Sequel < Base
5
+ private
6
+
7
+ def define_after_commit(event)
8
+ options = options_exposed_for_block
9
+
10
+ object_class.define_method("after_#{event}".to_sym) do
11
+ if instance_eval(&options[:if]) && !instance_eval(&options[:unless])
12
+ db.after_commit do
13
+ TableSync::Publishing::Single.new(
14
+ object_class: self.class.name,
15
+ original_attributes: values,
16
+ event: event,
17
+ debounce_time: options[:debounce_time],
18
+ ).publish_later
19
+ end
20
+ end
21
+
22
+ super()
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TableSync
4
- VERSION = "5.1.0"
4
+ VERSION = "6.0"
5
5
  end
data/lib/table_sync.rb CHANGED
@@ -8,39 +8,62 @@ require "active_support/core_ext/object/blank"
8
8
  require "active_support/core_ext/numeric/time"
9
9
 
10
10
  module TableSync
11
+ require_relative "table_sync/event"
11
12
  require_relative "table_sync/utils"
12
13
  require_relative "table_sync/version"
13
14
  require_relative "table_sync/errors"
15
+
14
16
  require_relative "table_sync/instrument"
15
17
  require_relative "table_sync/instrument_adapter/active_support"
18
+
16
19
  require_relative "table_sync/naming_resolver/active_record"
17
20
  require_relative "table_sync/naming_resolver/sequel"
21
+
22
+ require_relative "table_sync/orm_adapter/base"
23
+ require_relative "table_sync/orm_adapter/active_record"
24
+ require_relative "table_sync/orm_adapter/sequel"
25
+
18
26
  require_relative "table_sync/receiving"
19
27
  require_relative "table_sync/publishing"
20
28
 
29
+ require_relative "table_sync/setup/base"
30
+ require_relative "table_sync/setup/active_record"
31
+ require_relative "table_sync/setup/sequel"
32
+
21
33
  class << self
22
- attr_accessor :publishing_job_class_callable
34
+ attr_accessor :raise_on_empty_message
35
+ attr_accessor :single_publishing_job_class_callable
23
36
  attr_accessor :batch_publishing_job_class_callable
24
37
  attr_accessor :routing_key_callable
25
38
  attr_accessor :exchange_name
26
- attr_accessor :routing_metadata_callable
39
+ attr_accessor :headers_callable
27
40
  attr_accessor :notifier
41
+
28
42
  attr_reader :orm
29
43
  attr_reader :publishing_adapter
30
44
  attr_reader :receiving_model
45
+ attr_reader :setup
31
46
 
32
- def sync(klass, **opts)
33
- publishing_adapter.setup_sync(klass, opts)
47
+ def sync(object_class, **options)
48
+ setup.new(
49
+ object_class: object_class,
50
+ on: options[:on],
51
+ if_condition: options[:if],
52
+ unless_condition: options[:unless],
53
+ debounce_time: options[:debounce_time],
54
+ ).register_callbacks
34
55
  end
35
56
 
36
57
  def orm=(val)
37
58
  case val
38
59
  when :active_record
39
- @publishing_adapter = Publishing::ORMAdapter::ActiveRecord
40
- @receiving_model = Receiving::Model::ActiveRecord
60
+ @publishing_adapter = TableSync::ORMAdapter::ActiveRecord
61
+ @receiving_model = Receiving::Model::ActiveRecord
62
+ @setup = TableSync::Setup::ActiveRecord
41
63
  when :sequel
42
- @publishing_adapter = Publishing::ORMAdapter::Sequel
43
- @receiving_model = Receiving::Model::Sequel
64
+ @publishing_adapter = TableSync::ORMAdapter::Sequel
65
+ @receiving_model = Receiving::Model::Sequel
66
+ @setup = TableSync::Setup::Sequel
44
67
  else
45
68
  raise ORMNotSupported.new(val.inspect)
46
69
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: table_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: '6.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Umbrellio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-09 00:00:00.000000000 Z
11
+ date: 2021-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: memery
@@ -271,19 +271,37 @@ files:
271
271
  - docs/message_protocol.md
272
272
  - docs/notifications.md
273
273
  - docs/publishing.md
274
+ - docs/publishing/configuration.md
275
+ - docs/publishing/manual.md
276
+ - docs/publishing/publishers.md
274
277
  - docs/receiving.md
275
278
  - lib/table_sync.rb
276
279
  - lib/table_sync/errors.rb
280
+ - lib/table_sync/event.rb
277
281
  - lib/table_sync/instrument.rb
278
282
  - lib/table_sync/instrument_adapter/active_support.rb
279
283
  - lib/table_sync/naming_resolver/active_record.rb
280
284
  - lib/table_sync/naming_resolver/sequel.rb
285
+ - lib/table_sync/orm_adapter/active_record.rb
286
+ - lib/table_sync/orm_adapter/base.rb
287
+ - lib/table_sync/orm_adapter/sequel.rb
281
288
  - lib/table_sync/publishing.rb
282
- - lib/table_sync/publishing/base_publisher.rb
283
- - lib/table_sync/publishing/batch_publisher.rb
284
- - lib/table_sync/publishing/orm_adapter/active_record.rb
285
- - lib/table_sync/publishing/orm_adapter/sequel.rb
286
- - lib/table_sync/publishing/publisher.rb
289
+ - lib/table_sync/publishing/batch.rb
290
+ - lib/table_sync/publishing/data/objects.rb
291
+ - lib/table_sync/publishing/data/raw.rb
292
+ - lib/table_sync/publishing/helpers/attributes.rb
293
+ - lib/table_sync/publishing/helpers/debounce.rb
294
+ - lib/table_sync/publishing/helpers/objects.rb
295
+ - lib/table_sync/publishing/message/base.rb
296
+ - lib/table_sync/publishing/message/batch.rb
297
+ - lib/table_sync/publishing/message/raw.rb
298
+ - lib/table_sync/publishing/message/single.rb
299
+ - lib/table_sync/publishing/params/base.rb
300
+ - lib/table_sync/publishing/params/batch.rb
301
+ - lib/table_sync/publishing/params/raw.rb
302
+ - lib/table_sync/publishing/params/single.rb
303
+ - lib/table_sync/publishing/raw.rb
304
+ - lib/table_sync/publishing/single.rb
287
305
  - lib/table_sync/receiving.rb
288
306
  - lib/table_sync/receiving/config.rb
289
307
  - lib/table_sync/receiving/config_decorator.rb
@@ -291,6 +309,9 @@ files:
291
309
  - lib/table_sync/receiving/handler.rb
292
310
  - lib/table_sync/receiving/model/active_record.rb
293
311
  - lib/table_sync/receiving/model/sequel.rb
312
+ - lib/table_sync/setup/active_record.rb
313
+ - lib/table_sync/setup/base.rb
314
+ - lib/table_sync/setup/sequel.rb
294
315
  - lib/table_sync/utils.rb
295
316
  - lib/table_sync/utils/interface_checker.rb
296
317
  - lib/table_sync/utils/proc_array.rb
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TableSync::Publishing::BasePublisher
4
- include Memery
5
-
6
- BASE_SAFE_JSON_TYPES = [NilClass, String, TrueClass, FalseClass, Numeric, Symbol].freeze
7
- NOT_MAPPED = Object.new
8
-
9
- private
10
-
11
- attr_accessor :object_class
12
-
13
- # @!method job_callable
14
- # @!method job_callable_error_message
15
- # @!method attrs_for_callables
16
- # @!method attrs_for_routing_key
17
- # @!method attrs_for_metadata
18
- # @!method attributes_for_sync
19
-
20
- memoize def current_time
21
- Time.current
22
- end
23
-
24
- memoize def primary_keys
25
- Array(object_class.primary_key).map(&:to_sym)
26
- end
27
-
28
- memoize def attributes_for_sync_defined?
29
- object_class.method_defined?(:attributes_for_sync)
30
- end
31
-
32
- def resolve_routing_key
33
- routing_key_callable.call(object_class.name, attrs_for_routing_key)
34
- end
35
-
36
- def metadata
37
- TableSync.routing_metadata_callable&.call(object_class.name, attrs_for_metadata)
38
- end
39
-
40
- def confirm?
41
- @confirm
42
- end
43
-
44
- def routing_key_callable
45
- return TableSync.routing_key_callable if TableSync.routing_key_callable
46
- raise "Can't publish, set TableSync.routing_key_callable"
47
- end
48
-
49
- def filter_safe_for_serialization(object)
50
- case object
51
- when Array
52
- object.each_with_object([]) do |value, memo|
53
- value = filter_safe_for_serialization(value)
54
- memo << value if object_mapped?(value)
55
- end
56
- when Hash
57
- object.each_with_object({}) do |(key, value), memo|
58
- key = filter_safe_for_serialization(key)
59
- value = filter_safe_hash_values(value)
60
- memo[key] = value if object_mapped?(key) && object_mapped?(value)
61
- end
62
- when Float::INFINITY
63
- NOT_MAPPED
64
- when *BASE_SAFE_JSON_TYPES
65
- object
66
- else # rubocop:disable Lint/DuplicateBranch
67
- NOT_MAPPED
68
- end
69
- end
70
-
71
- def filter_safe_hash_values(value)
72
- case value
73
- when Symbol
74
- value.to_s
75
- else
76
- filter_safe_for_serialization(value)
77
- end
78
- end
79
-
80
- def object_mapped?(object)
81
- object != NOT_MAPPED
82
- end
83
-
84
- def job_class
85
- job_callable ? job_callable.call : raise(job_callable_error_message)
86
- end
87
-
88
- def publishing_data
89
- {
90
- model: object_class.try(:table_sync_model_name) || object_class.name,
91
- attributes: attributes_for_sync,
92
- version: current_time.to_f,
93
- }
94
- end
95
-
96
- def params
97
- params = {
98
- event: :table_sync,
99
- data: publishing_data,
100
- confirm_select: confirm?,
101
- routing_key: routing_key,
102
- realtime: true,
103
- headers: metadata,
104
- }
105
-
106
- params[:exchange_name] = TableSync.exchange_name if TableSync.exchange_name
107
-
108
- params
109
- end
110
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TableSync::Publishing::BatchPublisher < TableSync::Publishing::BasePublisher
4
- def initialize(object_class, original_attributes_array, **options)
5
- @original_attributes_array = original_attributes_array.map do |hash|
6
- filter_safe_for_serialization(hash.deep_symbolize_keys)
7
- end
8
-
9
- @object_class = object_class.constantize
10
- @confirm = options[:confirm] || true
11
- @routing_key = options[:routing_key] || resolve_routing_key
12
- @push_original_attributes = options[:push_original_attributes] || false
13
- @headers = options[:headers]
14
- @event = options[:event] || :update
15
- end
16
-
17
- def publish
18
- enqueue_job
19
- end
20
-
21
- def publish_now
22
- return unless need_publish?
23
- Rabbit.publish(params)
24
-
25
- model_naming = TableSync.publishing_adapter.model_naming(object_class)
26
- TableSync::Instrument.notify table: model_naming.table, schema: model_naming.schema,
27
- event: event,
28
- count: publishing_data[:attributes].size, direction: :publish
29
- end
30
-
31
- private
32
-
33
- attr_reader :original_attributes_array, :routing_key, :headers, :event
34
-
35
- def push_original_attributes?
36
- @push_original_attributes
37
- end
38
-
39
- def need_publish?
40
- (push_original_attributes? && original_attributes_array.present?) || objects.present?
41
- end
42
-
43
- memoize def objects
44
- needles.map { |needle| TableSync.publishing_adapter.find(object_class, needle) }.compact
45
- end
46
-
47
- def job_callable
48
- TableSync.batch_publishing_job_class_callable
49
- end
50
-
51
- def job_callable_error_message
52
- "Can't publish, set TableSync.batch_publishing_job_class_callable"
53
- end
54
-
55
- def attrs_for_callables
56
- {}
57
- end
58
-
59
- def attrs_for_routing_key
60
- {}
61
- end
62
-
63
- def attrs_for_metadata
64
- {}
65
- end
66
-
67
- def params
68
- {
69
- **super,
70
- headers: headers,
71
- }
72
- end
73
-
74
- def needles
75
- original_attributes_array.map { |original_attributes| original_attributes.slice(*primary_keys) }
76
- end
77
-
78
- def publishing_data
79
- {
80
- **super,
81
- event: event,
82
- metadata: {},
83
- }
84
- end
85
-
86
- def attributes_for_sync
87
- return original_attributes_array if push_original_attributes?
88
-
89
- objects.map do |object|
90
- if attributes_for_sync_defined?
91
- object.attributes_for_sync
92
- else
93
- TableSync.publishing_adapter.attributes(object)
94
- end
95
- end
96
- end
97
-
98
- def enqueue_job
99
- job_class.perform_later(
100
- object_class.name,
101
- original_attributes_array,
102
- enqueue_additional_options,
103
- )
104
- end
105
-
106
- def enqueue_additional_options
107
- { confirm: confirm?, push_original_attributes: push_original_attributes? }
108
- end
109
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableSync::Publishing::ORMAdapter
4
- module ActiveRecord
5
- module_function
6
-
7
- def model_naming(object)
8
- ::TableSync::NamingResolver::ActiveRecord.new(table_name: object.table_name)
9
- end
10
-
11
- def find(dataset, conditions)
12
- dataset.find_by(conditions)
13
- end
14
-
15
- def attributes(object)
16
- object.attributes
17
- end
18
-
19
- def setup_sync(klass, opts)
20
- debounce_time = opts.delete(:debounce_time)
21
-
22
- klass.instance_exec do
23
- { create: :created, update: :updated, destroy: :destroyed }.each do |event, state|
24
- after_commit(on: event, **opts) do
25
- TableSync::Publishing::Publisher.new(self.class.name, attributes,
26
- state: state, debounce_time: debounce_time).publish
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableSync::Publishing::ORMAdapter
4
- module Sequel
5
- module_function
6
-
7
- def model_naming(object)
8
- ::TableSync::NamingResolver::Sequel.new(table_name: object.table_name, db: object.db)
9
- end
10
-
11
- def find(dataset, conditions)
12
- dataset.find(conditions)
13
- end
14
-
15
- def attributes(object)
16
- object.values
17
- end
18
-
19
- def setup_sync(klass, opts)
20
- if_predicate = to_predicate(opts.delete(:if), true)
21
- unless_predicate = to_predicate(opts.delete(:unless), false)
22
- debounce_time = opts.delete(:debounce_time)
23
-
24
- if opts.any?
25
- raise "Only :if, :skip_debounce and :unless options are currently " \
26
- "supported for Sequel hooks"
27
- end
28
-
29
- register_callbacks(klass, if_predicate, unless_predicate, debounce_time)
30
- end
31
-
32
- def to_predicate(val, default)
33
- return val.to_proc if val.respond_to?(:to_proc)
34
-
35
- -> (*) { default }
36
- end
37
-
38
- def register_callbacks(klass, if_predicate, unless_predicate, debounce_time)
39
- { create: :created, update: :updated, destroy: :destroyed }.each do |event, state|
40
- klass.send(:define_method, :"after_#{event}") do
41
- if instance_eval(&if_predicate) && !instance_eval(&unless_predicate)
42
- db.after_commit do
43
- TableSync::Publishing::Publisher.new(
44
- self.class.name,
45
- values,
46
- state: state,
47
- debounce_time: debounce_time,
48
- ).publish
49
- end
50
- end
51
-
52
- super()
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,129 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TableSync::Publishing::Publisher < TableSync::Publishing::BasePublisher
4
- DEBOUNCE_TIME = 1.minute
5
-
6
- # 'original_attributes' are not published, they are used to resolve the routing key
7
- def initialize(object_class, original_attributes, **opts)
8
- @object_class = object_class.constantize
9
- @original_attributes = filter_safe_for_serialization(original_attributes.deep_symbolize_keys)
10
- @confirm = opts.fetch(:confirm, true)
11
- @debounce_time = opts[:debounce_time]&.seconds || DEBOUNCE_TIME
12
- @state = opts.fetch(:state, :updated).to_sym
13
- validate_state
14
- end
15
-
16
- def publish
17
- return enqueue_job if destroyed? || debounce_time.zero?
18
-
19
- sync_time = Rails.cache.read(cache_key) || current_time - debounce_time - 1.second
20
- return if sync_time > current_time
21
-
22
- next_sync_time = sync_time + debounce_time
23
- next_sync_time <= current_time ? enqueue_job : enqueue_job(next_sync_time)
24
- end
25
-
26
- def publish_now
27
- # Update request and object does not exist -> skip publishing
28
- return if !object && !destroyed?
29
-
30
- Rabbit.publish(params)
31
- model_naming = TableSync.publishing_adapter.model_naming(object_class)
32
- TableSync::Instrument.notify table: model_naming.table, schema: model_naming.schema,
33
- event: event, direction: :publish
34
- end
35
-
36
- private
37
-
38
- attr_reader :original_attributes
39
- attr_reader :state
40
- attr_reader :debounce_time
41
-
42
- def attrs_for_callables
43
- attributes_for_sync
44
- end
45
-
46
- def attrs_for_routing_key
47
- if object.respond_to?(:attrs_for_routing_key)
48
- object.attrs_for_routing_key
49
- else
50
- attrs_for_callables
51
- end
52
- end
53
-
54
- def attrs_for_metadata
55
- if object.respond_to?(:attrs_for_metadata)
56
- object.attrs_for_metadata
57
- else
58
- attrs_for_callables
59
- end
60
- end
61
-
62
- def job_callable
63
- TableSync.publishing_job_class_callable
64
- end
65
-
66
- def job_callable_error_message
67
- "Can't publish, set TableSync.publishing_job_class_callable"
68
- end
69
-
70
- def enqueue_job(perform_at = current_time)
71
- job = job_class.set(wait_until: perform_at)
72
- job.perform_later(object_class.name, original_attributes, state: state.to_s, confirm: confirm?)
73
- Rails.cache.write(cache_key, perform_at)
74
- end
75
-
76
- def routing_key
77
- resolve_routing_key
78
- end
79
-
80
- def publishing_data
81
- {
82
- **super,
83
- event: event,
84
- metadata: { created: created? },
85
- }
86
- end
87
-
88
- memoize def attributes_for_sync
89
- if destroyed?
90
- if object_class.respond_to?(:table_sync_destroy_attributes)
91
- object_class.table_sync_destroy_attributes(original_attributes)
92
- else
93
- original_attributes
94
- end
95
- elsif attributes_for_sync_defined?
96
- object.attributes_for_sync
97
- else
98
- TableSync.publishing_adapter.attributes(object)
99
- end
100
- end
101
-
102
- memoize def object
103
- TableSync.publishing_adapter.find(object_class, needle)
104
- end
105
-
106
- def event
107
- destroyed? ? :destroy : :update
108
- end
109
-
110
- def needle
111
- original_attributes.slice(*primary_keys)
112
- end
113
-
114
- def cache_key
115
- "#{object_class}/#{needle}_table_sync_time".delete(" ")
116
- end
117
-
118
- def destroyed?
119
- state == :destroyed
120
- end
121
-
122
- def created?
123
- state == :created
124
- end
125
-
126
- def validate_state
127
- raise "Unknown state: #{state.inspect}" unless %i[created updated destroyed].include?(state)
128
- end
129
- end