pub_sub_model_sync 0.6.0 → 1.0.beta
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 +2 -0
- data/CHANGELOG.md +18 -2
- data/Dockerfile +4 -4
- data/Gemfile.lock +144 -136
- data/README.md +227 -203
- data/docker-compose.yaml +1 -1
- data/docs/notifications-diagram.png +0 -0
- data/lib/pub_sub_model_sync.rb +2 -0
- data/lib/pub_sub_model_sync/base.rb +5 -1
- data/lib/pub_sub_model_sync/config.rb +15 -7
- data/lib/pub_sub_model_sync/message_processor.rb +4 -5
- data/lib/pub_sub_model_sync/message_publisher.rb +50 -60
- data/lib/pub_sub_model_sync/payload.rb +14 -10
- data/lib/pub_sub_model_sync/publisher.rb +38 -32
- data/lib/pub_sub_model_sync/publisher_concern.rb +45 -52
- data/lib/pub_sub_model_sync/run_subscriber.rb +104 -0
- data/lib/pub_sub_model_sync/service_base.rb +6 -6
- data/lib/pub_sub_model_sync/service_google.rb +2 -1
- data/lib/pub_sub_model_sync/service_kafka.rb +7 -3
- data/lib/pub_sub_model_sync/service_rabbit.rb +2 -1
- data/lib/pub_sub_model_sync/subscriber.rb +15 -69
- data/lib/pub_sub_model_sync/subscriber_concern.rb +21 -26
- data/lib/pub_sub_model_sync/transaction.rb +57 -0
- data/lib/pub_sub_model_sync/version.rb +1 -1
- metadata +6 -4
data/docker-compose.yaml
CHANGED
Binary file
|
data/lib/pub_sub_model_sync.rb
CHANGED
@@ -10,8 +10,10 @@ require 'pub_sub_model_sync/subscriber_concern'
|
|
10
10
|
require 'pub_sub_model_sync/message_publisher'
|
11
11
|
require 'pub_sub_model_sync/publisher_concern'
|
12
12
|
require 'pub_sub_model_sync/runner'
|
13
|
+
require 'pub_sub_model_sync/transaction'
|
13
14
|
require 'pub_sub_model_sync/connector'
|
14
15
|
require 'pub_sub_model_sync/message_processor'
|
16
|
+
require 'pub_sub_model_sync/run_subscriber'
|
15
17
|
|
16
18
|
require 'pub_sub_model_sync/publisher'
|
17
19
|
require 'pub_sub_model_sync/subscriber'
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module PubSubModelSync
|
4
4
|
class Base
|
5
|
-
delegate :config, :log, to: self
|
5
|
+
delegate :config, :log, :debug?, to: self
|
6
6
|
|
7
7
|
class << self
|
8
8
|
def config
|
@@ -12,6 +12,10 @@ module PubSubModelSync
|
|
12
12
|
def log(message, kind = :info)
|
13
13
|
config.log message, kind
|
14
14
|
end
|
15
|
+
|
16
|
+
def debug?
|
17
|
+
config.debug
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
# @param errors (Array(Class|String))
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module PubSubModelSync
|
4
4
|
class Config
|
5
5
|
cattr_accessor(:subscribers) { [] }
|
6
|
-
cattr_accessor(:publishers) { [] }
|
7
6
|
cattr_accessor(:service_name) { :google }
|
8
7
|
|
9
8
|
# customizable callbacks
|
10
9
|
cattr_accessor(:debug) { false }
|
11
10
|
cattr_accessor :logger # LoggerInst
|
11
|
+
cattr_accessor(:transactions_use_buffer) { true }
|
12
12
|
|
13
13
|
cattr_accessor(:on_before_processing) { ->(_payload, _info) {} } # return :cancel to skip
|
14
14
|
cattr_accessor(:on_success_processing) { ->(_payload, _info) {} }
|
@@ -16,16 +16,15 @@ module PubSubModelSync
|
|
16
16
|
cattr_accessor(:on_before_publish) { ->(_payload) {} } # return :cancel to skip
|
17
17
|
cattr_accessor(:on_after_publish) { ->(_payload) {} }
|
18
18
|
cattr_accessor(:on_error_publish) { ->(_exception, _info) {} }
|
19
|
-
cattr_accessor(:disabled_callback_publisher) { ->(_model, _action) { false } }
|
20
19
|
|
21
20
|
# google service
|
22
|
-
cattr_accessor :project, :credentials, :topic_name, :subscription_name
|
21
|
+
cattr_accessor :project, :credentials, :topic_name, :subscription_name, :default_topic_name
|
23
22
|
|
24
23
|
# rabbitmq service
|
25
|
-
cattr_accessor :bunny_connection, :topic_name, :subscription_name
|
24
|
+
cattr_accessor :bunny_connection, :topic_name, :subscription_name, :default_topic_name
|
26
25
|
|
27
26
|
# kafka service
|
28
|
-
cattr_accessor :kafka_connection, :topic_name, :subscription_name
|
27
|
+
cattr_accessor :kafka_connection, :topic_name, :subscription_name, :default_topic_name
|
29
28
|
|
30
29
|
def self.log(msg, kind = :info)
|
31
30
|
msg = "PS_MSYNC ==> #{msg}"
|
@@ -37,8 +36,17 @@ module PubSubModelSync
|
|
37
36
|
end
|
38
37
|
|
39
38
|
def self.subscription_key
|
40
|
-
|
41
|
-
|
39
|
+
klass = Rails.application.class
|
40
|
+
app_name = klass.respond_to?(:module_parent_name) ? klass.module_parent_name : klass.parent_name
|
41
|
+
subscription_name || app_name
|
42
|
+
end
|
43
|
+
|
44
|
+
class << self
|
45
|
+
alias default_topic_name_old default_topic_name
|
46
|
+
|
47
|
+
def default_topic_name
|
48
|
+
default_topic_name_old || Array(topic_name).first
|
49
|
+
end
|
42
50
|
end
|
43
51
|
end
|
44
52
|
end
|
@@ -28,12 +28,12 @@ module PubSubModelSync
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def run_subscriber(subscriber)
|
31
|
-
|
31
|
+
processor = PubSubModelSync::RunSubscriber.new(subscriber, payload)
|
32
32
|
return unless processable?(subscriber)
|
33
33
|
|
34
34
|
errors = [ActiveRecord::ConnectionTimeoutError, 'deadlock detected', 'could not serialize access']
|
35
|
-
retry_error(errors, qty:
|
36
|
-
|
35
|
+
retry_error(errors, qty: 5) do
|
36
|
+
processor.call
|
37
37
|
res = config.on_success_processing.call(payload, { subscriber: subscriber })
|
38
38
|
log "processed message with: #{payload.inspect}" if res != :skip_log
|
39
39
|
end
|
@@ -54,8 +54,7 @@ module PubSubModelSync
|
|
54
54
|
|
55
55
|
def filter_subscribers
|
56
56
|
config.subscribers.select do |subscriber|
|
57
|
-
subscriber.
|
58
|
-
subscriber.settings[:from_action].to_s == payload.action.to_s
|
57
|
+
subscriber.from_klass == payload.klass && subscriber.action == payload.action && payload.mode == subscriber.mode
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
@@ -4,7 +4,7 @@ module PubSubModelSync
|
|
4
4
|
class MessagePublisher < PubSubModelSync::Base
|
5
5
|
class << self
|
6
6
|
class MissingPublisher < StandardError; end
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :current_transaction
|
8
8
|
|
9
9
|
def connector
|
10
10
|
@connector ||= PubSubModelSync::Connector.new
|
@@ -16,70 +16,53 @@ module PubSubModelSync
|
|
16
16
|
# be processed in the correct order, despite of the multiple threads. This thanks to the fact
|
17
17
|
# that Pub/Sub services will always send messages with the same `ordering_key` into the same
|
18
18
|
# worker/thread.
|
19
|
-
# @
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
# @see Transaction.new(...)
|
20
|
+
# @param key (String|Nil)
|
21
|
+
# @param block (Yield) block to be executed
|
22
|
+
def transaction(key, settings = {}, &block)
|
23
|
+
t = init_transaction(key, settings)
|
24
|
+
block.call
|
25
|
+
t.deliver_all
|
26
|
+
rescue
|
27
|
+
t.rollback
|
28
|
+
raise
|
29
|
+
ensure
|
30
|
+
t.clean_publisher
|
28
31
|
end
|
29
32
|
|
30
33
|
# Starts a new transaction
|
31
|
-
# @
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
# @param key (@transaction_key)
|
35
|
+
# @return (Transaction)
|
36
|
+
def init_transaction(key, settings = {})
|
37
|
+
new_transaction = PubSubModelSync::Transaction.new(key, settings)
|
38
|
+
if current_transaction
|
39
|
+
current_transaction.add_transaction(new_transaction)
|
40
|
+
else
|
41
|
+
self.current_transaction = new_transaction
|
42
|
+
end
|
43
|
+
new_transaction
|
41
44
|
end
|
42
45
|
|
43
|
-
# Publishes
|
44
|
-
# @
|
45
|
-
# @param data (Hash): Data to be delivered
|
46
|
-
# @param action (:symbol): action name
|
47
|
-
# @param headers (Hash, optional): header settings (More in Payload.headers)
|
46
|
+
# Publishes a class level notification via pubsub
|
47
|
+
# @refer PublisherConcern.ps_class_publish
|
48
48
|
# @return Payload
|
49
49
|
def publish_data(klass, data, action, headers: {})
|
50
|
-
attrs = { klass: klass.to_s, action: action.to_sym }
|
50
|
+
attrs = { klass: klass.to_s, action: action.to_sym, mode: :klass }
|
51
51
|
payload = PubSubModelSync::Payload.new(data, attrs, headers)
|
52
52
|
publish(payload)
|
53
53
|
end
|
54
54
|
|
55
|
-
#
|
56
|
-
# @param
|
57
|
-
# @param
|
58
|
-
|
59
|
-
|
60
|
-
# @param headers (Hash, optional): header settings (More in Payload.headers)
|
61
|
-
# @return Payload
|
62
|
-
def publish_model_data(model, data, action, as_klass: nil, headers: {})
|
63
|
-
headers = PubSubModelSync::Publisher.headers_for(model, action).merge(headers)
|
64
|
-
publish_data(as_klass || model.class.name, data, action, headers: headers)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Publishes model info to pubsub
|
68
|
-
# @param model (ActiveRecord model)
|
69
|
-
# @param action (Sym): Action name
|
70
|
-
# @param custom_data (Hash, optional): If present custom_data will be used as the payload data.
|
71
|
-
# @param custom_headers (Hash): Refer Payload.headers
|
72
|
-
# @return Payload
|
73
|
-
def publish_model(model, action, custom_data: nil, custom_headers: {})
|
74
|
-
return if model.ps_skip_sync?(action)
|
55
|
+
# @param model (ActiveRecord::Base)
|
56
|
+
# @param action (Symbol: @see PublishConcern::ps_publish)
|
57
|
+
# @param settings (Hash: @see Publisher.new.settings)
|
58
|
+
def publish_model(model, action, settings = {})
|
59
|
+
return if model.ps_skip_publish?(action)
|
75
60
|
|
76
|
-
publisher =
|
77
|
-
|
78
|
-
raise(MissingPublisher, error_msg) unless publisher
|
61
|
+
publisher = PubSubModelSync::Publisher.new(model, action, settings)
|
62
|
+
payload = publisher.payload
|
79
63
|
|
80
|
-
payload
|
81
|
-
|
82
|
-
publish(payload) { model.ps_after_sync(action, payload) } if ensure_model_publish(model, action, payload)
|
64
|
+
transaction(payload.headers[:ordering_key]) do # catch and group all :ps_before_publish syncs
|
65
|
+
publish(payload) { model.ps_after_publish(action, payload) } if ensure_model_publish(model, action, payload)
|
83
66
|
end
|
84
67
|
end
|
85
68
|
|
@@ -88,15 +71,20 @@ module PubSubModelSync
|
|
88
71
|
# @return Payload
|
89
72
|
# Raises error if exist
|
90
73
|
def publish!(payload, &block)
|
74
|
+
payload.headers[:ordering_key] = ordering_key_for(payload)
|
91
75
|
return unless ensure_publish(payload)
|
92
76
|
|
93
|
-
|
94
|
-
connector.publish(payload)
|
95
|
-
config.on_after_publish.call(payload)
|
77
|
+
current_transaction ? current_transaction.add_payload(payload) : connector_publish(payload)
|
96
78
|
block&.call
|
97
79
|
payload
|
98
80
|
end
|
99
81
|
|
82
|
+
def connector_publish(payload)
|
83
|
+
connector.publish(payload)
|
84
|
+
log("Published message: #{[payload]}")
|
85
|
+
config.on_after_publish.call(payload)
|
86
|
+
end
|
87
|
+
|
100
88
|
# Similar to :publish! method
|
101
89
|
# Notifies error via :on_error_publish instead of raising error
|
102
90
|
# @return Payload
|
@@ -109,18 +97,20 @@ module PubSubModelSync
|
|
109
97
|
private
|
110
98
|
|
111
99
|
def ensure_publish(payload)
|
112
|
-
payload.headers[:ordering_key] = @transaction_key if @transaction_key.present?
|
113
|
-
forced_ordering_key = payload.headers[:forced_ordering_key]
|
114
|
-
payload.headers[:ordering_key] = forced_ordering_key if forced_ordering_key
|
115
100
|
cancelled = config.on_before_publish.call(payload) == :cancel
|
116
101
|
log("Publish cancelled by config.on_before_publish: #{payload}") if config.debug && cancelled
|
117
102
|
!cancelled
|
118
103
|
end
|
119
104
|
|
105
|
+
def ordering_key_for(payload)
|
106
|
+
current_transaction&.key ||= payload.headers[:ordering_key]
|
107
|
+
payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]
|
108
|
+
end
|
109
|
+
|
120
110
|
def ensure_model_publish(model, action, payload)
|
121
|
-
res_before = model.
|
111
|
+
res_before = model.ps_before_publish(action, payload)
|
122
112
|
cancelled = res_before == :cancel
|
123
|
-
log("Publish cancelled by model.
|
113
|
+
log("Publish cancelled by model.ps_before_publish: #{payload}") if config.debug && cancelled
|
124
114
|
!cancelled
|
125
115
|
end
|
126
116
|
|
@@ -3,10 +3,10 @@
|
|
3
3
|
module PubSubModelSync
|
4
4
|
class Payload
|
5
5
|
class MissingInfo < StandardError; end
|
6
|
-
attr_reader :data, :
|
6
|
+
attr_reader :data, :info, :headers
|
7
7
|
|
8
8
|
# @param data (Hash: { any value }):
|
9
|
-
# @param
|
9
|
+
# @param info (Hash: { klass*: string, action*: :sym, mode?: :klass|:model }):
|
10
10
|
# @param headers (Hash):
|
11
11
|
# key (String): identifier of the payload, default:
|
12
12
|
# klass/action: when class message
|
@@ -19,9 +19,9 @@ module PubSubModelSync
|
|
19
19
|
# message (default first topic)
|
20
20
|
# forced_ordering_key (String, optional): Will force to use this value as the ordering_key,
|
21
21
|
# even withing transactions. Default nil.
|
22
|
-
def initialize(data,
|
22
|
+
def initialize(data, info, headers = {})
|
23
23
|
@data = data
|
24
|
-
@
|
24
|
+
@info = { mode: :model }.merge(info)
|
25
25
|
@headers = headers
|
26
26
|
build_headers
|
27
27
|
validate!
|
@@ -29,15 +29,19 @@ module PubSubModelSync
|
|
29
29
|
|
30
30
|
# @return Hash: payload data
|
31
31
|
def to_h
|
32
|
-
{ data: data,
|
32
|
+
{ data: data, info: info, headers: headers }
|
33
33
|
end
|
34
34
|
|
35
35
|
def klass
|
36
|
-
|
36
|
+
info[:klass].to_s
|
37
37
|
end
|
38
38
|
|
39
39
|
def action
|
40
|
-
|
40
|
+
info[:action].to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
def mode
|
44
|
+
info[:mode].to_sym
|
41
45
|
end
|
42
46
|
|
43
47
|
# Process payload data
|
@@ -69,10 +73,10 @@ module PubSubModelSync
|
|
69
73
|
end
|
70
74
|
|
71
75
|
# convert payload data into Payload
|
72
|
-
# @param data [Hash]: payload data (:data, :
|
76
|
+
# @param data [Hash]: payload data (:data, :info, :headers)
|
73
77
|
def self.from_payload_data(data)
|
74
78
|
data = data.deep_symbolize_keys
|
75
|
-
new(data[:data], data[:
|
79
|
+
new(data[:data], data[:info], data[:headers])
|
76
80
|
end
|
77
81
|
|
78
82
|
private
|
@@ -85,7 +89,7 @@ module PubSubModelSync
|
|
85
89
|
end
|
86
90
|
|
87
91
|
def validate!
|
88
|
-
raise MissingInfo if !
|
92
|
+
raise MissingInfo if !info[:klass] || !info[:action]
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
@@ -1,31 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PubSubModelSync
|
4
|
-
class Publisher
|
5
|
-
attr_accessor :
|
6
|
-
|
7
|
-
# @param
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
4
|
+
class Publisher < PubSubModelSync::Base
|
5
|
+
attr_accessor :model, :action, :data, :mapping, :headers, :as_klass
|
6
|
+
|
7
|
+
# @param model (ActiveRecord::Base)
|
8
|
+
# @param action (@see PublishConcern::ps_publish)
|
9
|
+
# @param settings (@see PublishConcern::ps_publish): { data:, mapping:, headers:, as_klass: }
|
10
|
+
def initialize(model, action, settings = {})
|
11
|
+
@model = model
|
12
|
+
@action = action
|
13
|
+
@data = settings[:data] || {}
|
14
|
+
@mapping = settings[:mapping] || []
|
15
|
+
@headers = settings[:headers] || {}
|
16
|
+
@as_klass = settings[:as_klass] || model.class.name
|
14
17
|
end
|
15
18
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
.merge(custom_headers)
|
22
|
-
data = custom_data || payload_data(model)
|
23
|
-
PubSubModelSync::Payload.new(data, payload_attrs(model, action), payload_headers)
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.headers_for(model, action)
|
27
|
-
key = [model.class.name, action, model.id].join('/')
|
28
|
-
{ ordering_key: ordering_key_for(model), key: key }
|
19
|
+
# @return (Payload)
|
20
|
+
def payload
|
21
|
+
values = compute_value(data)
|
22
|
+
values = mapping_data.merge(values)
|
23
|
+
PubSubModelSync::Payload.new(values, settings_data, headers_data)
|
29
24
|
end
|
30
25
|
|
31
26
|
def self.ordering_key_for(model)
|
@@ -34,18 +29,29 @@ module PubSubModelSync
|
|
34
29
|
|
35
30
|
private
|
36
31
|
|
37
|
-
def
|
38
|
-
|
32
|
+
def headers_data
|
33
|
+
klass_name = model.class.name
|
34
|
+
key = [klass_name, action, model.id || SecureRandom.uuid].join('/')
|
35
|
+
def_data = { ordering_key: self.class.ordering_key_for(model), key: key }
|
36
|
+
def_data.merge(compute_value(headers))
|
37
|
+
end
|
38
|
+
|
39
|
+
def compute_value(value)
|
40
|
+
res = value
|
41
|
+
res = model.send(value, action) if value.is_a?(Symbol) # method name
|
42
|
+
res = value.call(model, action) if value.is_a?(Proc)
|
43
|
+
res
|
44
|
+
end
|
45
|
+
|
46
|
+
def settings_data
|
47
|
+
{ klass: as_klass, action: action }
|
48
|
+
end
|
49
|
+
|
50
|
+
def mapping_data
|
51
|
+
mapping.map do |prop|
|
39
52
|
source, target = prop.to_s.split(':')
|
40
53
|
[target || source, model.send(source.to_sym)]
|
41
54
|
end.to_h.symbolize_keys
|
42
55
|
end
|
43
|
-
|
44
|
-
def payload_attrs(model, action)
|
45
|
-
{
|
46
|
-
klass: (as_klass || model.class.name).to_s,
|
47
|
-
action: action.to_sym
|
48
|
-
}
|
49
|
-
end
|
50
56
|
end
|
51
57
|
end
|
@@ -2,90 +2,83 @@
|
|
2
2
|
|
3
3
|
module PubSubModelSync
|
4
4
|
module PublisherConcern
|
5
|
-
|
6
|
-
base.extend(ClassMethods)
|
7
|
-
base.send(:ps_init_transaction_callbacks)
|
8
|
-
end
|
5
|
+
extend ActiveSupport::Concern
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
included do
|
8
|
+
extend ClassMethods
|
9
|
+
ps_init_transaction_callbacks if self <= ActiveRecord::Base
|
13
10
|
end
|
14
11
|
|
15
12
|
# before preparing data to sync
|
16
|
-
def
|
13
|
+
def ps_skip_publish?(_action)
|
17
14
|
false
|
18
15
|
end
|
16
|
+
alias ps_skip_sync? ps_skip_publish? # @deprecated
|
19
17
|
|
20
18
|
# before delivering data (return :cancel to cancel sync)
|
21
|
-
def
|
19
|
+
def ps_before_publish(_action, _payload); end
|
20
|
+
alias ps_before_sync ps_before_publish # @deprecated
|
22
21
|
|
23
22
|
# after delivering data
|
24
|
-
def
|
23
|
+
def ps_after_publish(_action, _payload); end
|
24
|
+
alias ps_after_sync ps_after_publish # @deprecated
|
25
25
|
|
26
|
-
#
|
27
|
-
# @param action (Sym):
|
28
|
-
# @param
|
29
|
-
#
|
30
|
-
# @param
|
31
|
-
|
26
|
+
# Delivers a notification via pubsub
|
27
|
+
# @param action (Sym|String) Sample: create|update|save|destroy|<any_other_key>
|
28
|
+
# @param mapping? (Array<String>) If present will generate data using the mapping and added to the payload.
|
29
|
+
# Sample: ["id", "full_name:name"]
|
30
|
+
# @param data? (Hash|Symbol|Proc)
|
31
|
+
# Hash: Data to be added to the payload
|
32
|
+
# Symbol: Method name to be called to retrieve payload data (must return a hash value, receives :action name)
|
33
|
+
# Proc: Block to be called to retrieve payload data
|
34
|
+
# @param headers? (Hash|Symbol|Proc): (All available attributes in Payload.headers)
|
35
|
+
# Hash: Data that will be merged with default header values
|
36
|
+
# Symbol: Method name that will be called to retrieve header values (must return a hash, receives :action name)
|
37
|
+
# Proc: Block to be called to retrieve header values
|
38
|
+
# @param as_klass? (String): Output class name used instead of current class name
|
39
|
+
def ps_publish(action, data: {}, mapping: [], headers: {}, as_klass: self.class.name)
|
32
40
|
p_klass = PubSubModelSync::MessagePublisher
|
33
|
-
p_klass.publish_model(self, action,
|
41
|
+
p_klass.publish_model(self, action, data: data, mapping: mapping, headers: headers, as_klass: as_klass)
|
34
42
|
end
|
43
|
+
delegate :ps_class_publish, to: :class
|
35
44
|
|
36
45
|
module ClassMethods
|
37
|
-
#
|
38
|
-
# @param
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
PubSubModelSync::Config.publishers << publisher
|
43
|
-
actions.each do |action|
|
44
|
-
ps_register_callback(action.to_sym)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Klass level notification
|
49
|
-
# @deprecated this method was deprecated in favor of:
|
50
|
-
# PubSubModelSync::MessagePublisher.publish_data(...)
|
46
|
+
# Publishes a class level notification via pubsub
|
47
|
+
# @param data (Hash): Data of the notification
|
48
|
+
# @param action (Symbol): action name of the notification
|
49
|
+
# @param as_klass (String, default current class name): Class name of the notification
|
50
|
+
# @param headers (Hash, optional): header settings (More in Payload.headers)
|
51
51
|
def ps_class_publish(data, action:, as_klass: nil, headers: {})
|
52
52
|
klass = PubSubModelSync::MessagePublisher
|
53
53
|
klass.publish_data((as_klass || name).to_s, data, action.to_sym, headers: headers)
|
54
54
|
end
|
55
55
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
# @param crud_actions (Symbol|Array<Symbol>): :create, :update, :destroy
|
57
|
+
# @param method_name (Symbol, optional) method to be called
|
58
|
+
def ps_on_crud_event(crud_actions, method_name = nil, &block)
|
59
|
+
crud_actions = Array(crud_actions)
|
60
|
+
callback = ->(action) { method_name ? send(method_name, action) : instance_exec(action, &block) }
|
61
|
+
commit_name = respond_to?(:before_commit) ? :before_commit : :after_commit
|
62
|
+
crud_actions.each do |action|
|
63
|
+
send(commit_name, on: :create) { instance_exec(action, &callback) } if action == :create
|
64
|
+
send(commit_name, on: :update) { instance_exec(action, &callback) } if action == :update
|
65
|
+
after_destroy { instance_exec(action, &callback) } if action == :destroy
|
60
66
|
end
|
61
67
|
end
|
62
68
|
|
63
69
|
private
|
64
70
|
|
65
|
-
# TODO: skip all enqueued notifications after_rollback (when failed)
|
66
71
|
# Initialize calls to start and end pub_sub transactions and deliver all them in the same order
|
67
72
|
def ps_init_transaction_callbacks
|
68
73
|
start_transaction = lambda do
|
69
|
-
key = PubSubModelSync::Publisher.ordering_key_for(self)
|
70
|
-
@
|
74
|
+
key = id ? PubSubModelSync::Publisher.ordering_key_for(self) : nil
|
75
|
+
@ps_transaction = PubSubModelSync::MessagePublisher.init_transaction(key)
|
71
76
|
end
|
72
|
-
end_transaction = -> { PubSubModelSync::MessagePublisher.end_transaction(@ps_old_transaction_key) }
|
73
77
|
after_create start_transaction, prepend: true # wait for ID
|
74
78
|
before_update start_transaction, prepend: true
|
75
79
|
before_destroy start_transaction, prepend: true
|
76
|
-
after_commit
|
77
|
-
after_rollback
|
78
|
-
end
|
79
|
-
|
80
|
-
# Configure specific callback and execute publisher when called callback
|
81
|
-
def ps_register_callback(action)
|
82
|
-
after_commit(on: action) do |model|
|
83
|
-
disabled = PubSubModelSync::Config.disabled_callback_publisher.call(model, action)
|
84
|
-
if !disabled && !model.ps_skip_callback?(action)
|
85
|
-
klass = PubSubModelSync::MessagePublisher
|
86
|
-
klass.publish_model(model, action.to_sym)
|
87
|
-
end
|
88
|
-
end
|
80
|
+
after_commit { @ps_transaction.deliver_all }
|
81
|
+
after_rollback(prepend: true) { @ps_transaction.rollback }
|
89
82
|
end
|
90
83
|
end
|
91
84
|
end
|