pub_sub_model_sync 1.0.beta → 1.0.1
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/.github/workflows/release.yml +43 -0
- data/.rubocop.yml +1 -2
- data/CHANGELOG.md +11 -4
- data/Gemfile.lock +7 -2
- data/README.md +182 -108
- data/docs/notifications-diagram.png +0 -0
- data/lib/pub_sub_model_sync.rb +1 -1
- data/lib/pub_sub_model_sync/base.rb +0 -20
- data/lib/pub_sub_model_sync/config.rb +3 -2
- data/lib/pub_sub_model_sync/initializers/before_commit.rb +23 -0
- data/lib/pub_sub_model_sync/message_processor.rb +32 -9
- data/lib/pub_sub_model_sync/message_publisher.rb +19 -15
- data/lib/pub_sub_model_sync/payload.rb +15 -12
- data/lib/pub_sub_model_sync/{publisher.rb → payload_builder.rb} +16 -11
- data/lib/pub_sub_model_sync/publisher_concern.rb +42 -22
- data/lib/pub_sub_model_sync/railtie.rb +6 -0
- data/lib/pub_sub_model_sync/run_subscriber.rb +17 -13
- data/lib/pub_sub_model_sync/runner.rb +3 -5
- data/lib/pub_sub_model_sync/service_base.rb +5 -32
- data/lib/pub_sub_model_sync/service_google.rb +2 -2
- data/lib/pub_sub_model_sync/service_kafka.rb +2 -2
- data/lib/pub_sub_model_sync/service_rabbit.rb +1 -1
- data/lib/pub_sub_model_sync/subscriber_concern.rb +11 -9
- data/lib/pub_sub_model_sync/transaction.rb +37 -21
- data/lib/pub_sub_model_sync/version.rb +1 -1
- data/samples/README.md +50 -0
- data/samples/app1/Dockerfile +13 -0
- data/samples/app1/Gemfile +37 -0
- data/samples/app1/Gemfile.lock +171 -0
- data/samples/app1/README.md +24 -0
- data/samples/app1/Rakefile +6 -0
- data/samples/app1/app/models/application_record.rb +3 -0
- data/samples/app1/app/models/concerns/.keep +0 -0
- data/samples/app1/app/models/post.rb +19 -0
- data/samples/app1/app/models/user.rb +29 -0
- data/samples/app1/bin/bundle +114 -0
- data/samples/app1/bin/rails +5 -0
- data/samples/app1/bin/rake +5 -0
- data/samples/app1/bin/setup +33 -0
- data/samples/app1/bin/spring +14 -0
- data/samples/app1/config.ru +6 -0
- data/samples/app1/config/application.rb +40 -0
- data/samples/app1/config/boot.rb +4 -0
- data/samples/app1/config/credentials.yml.enc +1 -0
- data/samples/app1/config/database.yml +25 -0
- data/samples/app1/config/environment.rb +5 -0
- data/samples/app1/config/environments/development.rb +63 -0
- data/samples/app1/config/environments/production.rb +105 -0
- data/samples/app1/config/environments/test.rb +57 -0
- data/samples/app1/config/initializers/application_controller_renderer.rb +8 -0
- data/samples/app1/config/initializers/backtrace_silencers.rb +8 -0
- data/samples/app1/config/initializers/cors.rb +16 -0
- data/samples/app1/config/initializers/filter_parameter_logging.rb +6 -0
- data/samples/app1/config/initializers/inflections.rb +16 -0
- data/samples/app1/config/initializers/mime_types.rb +4 -0
- data/samples/app1/config/initializers/pubsub.rb +4 -0
- data/samples/app1/config/initializers/wrap_parameters.rb +14 -0
- data/samples/app1/config/locales/en.yml +33 -0
- data/samples/app1/config/master.key +1 -0
- data/samples/app1/config/puma.rb +43 -0
- data/samples/app1/config/routes.rb +3 -0
- data/samples/app1/config/spring.rb +6 -0
- data/samples/app1/db/migrate/20210513080700_create_users.rb +12 -0
- data/samples/app1/db/migrate/20210513134332_create_posts.rb +11 -0
- data/samples/app1/db/schema.rb +34 -0
- data/samples/app1/db/seeds.rb +7 -0
- data/samples/app1/docker-compose.yml +32 -0
- data/samples/app1/log/.keep +0 -0
- data/samples/app2/Dockerfile +13 -0
- data/samples/app2/Gemfile +37 -0
- data/samples/app2/Gemfile.lock +171 -0
- data/samples/app2/README.md +24 -0
- data/samples/app2/Rakefile +6 -0
- data/samples/app2/app/models/application_record.rb +9 -0
- data/samples/app2/app/models/concerns/.keep +0 -0
- data/samples/app2/app/models/customer.rb +28 -0
- data/samples/app2/app/models/post.rb +10 -0
- data/samples/app2/bin/bundle +114 -0
- data/samples/app2/bin/rails +5 -0
- data/samples/app2/bin/rake +5 -0
- data/samples/app2/bin/setup +33 -0
- data/samples/app2/bin/spring +14 -0
- data/samples/app2/config.ru +6 -0
- data/samples/app2/config/application.rb +40 -0
- data/samples/app2/config/boot.rb +4 -0
- data/samples/app2/config/credentials.yml.enc +1 -0
- data/samples/app2/config/database.yml +25 -0
- data/samples/app2/config/environment.rb +5 -0
- data/samples/app2/config/environments/development.rb +63 -0
- data/samples/app2/config/environments/production.rb +105 -0
- data/samples/app2/config/environments/test.rb +57 -0
- data/samples/app2/config/initializers/application_controller_renderer.rb +8 -0
- data/samples/app2/config/initializers/backtrace_silencers.rb +8 -0
- data/samples/app2/config/initializers/cors.rb +16 -0
- data/samples/app2/config/initializers/filter_parameter_logging.rb +6 -0
- data/samples/app2/config/initializers/inflections.rb +16 -0
- data/samples/app2/config/initializers/mime_types.rb +4 -0
- data/samples/app2/config/initializers/pubsub.rb +4 -0
- data/samples/app2/config/initializers/wrap_parameters.rb +14 -0
- data/samples/app2/config/locales/en.yml +33 -0
- data/samples/app2/config/master.key +1 -0
- data/samples/app2/config/puma.rb +43 -0
- data/samples/app2/config/routes.rb +3 -0
- data/samples/app2/config/spring.rb +6 -0
- data/samples/app2/db/development.sqlite3 +0 -0
- data/samples/app2/db/migrate/20210513080956_create_customers.rb +10 -0
- data/samples/app2/db/migrate/20210513135203_create_posts.rb +10 -0
- data/samples/app2/db/schema.rb +31 -0
- data/samples/app2/db/seeds.rb +7 -0
- data/samples/app2/docker-compose.yml +20 -0
- data/samples/app2/log/.keep +0 -0
- metadata +93 -5
Binary file
|
data/lib/pub_sub_model_sync.rb
CHANGED
@@ -15,7 +15,7 @@ require 'pub_sub_model_sync/connector'
|
|
15
15
|
require 'pub_sub_model_sync/message_processor'
|
16
16
|
require 'pub_sub_model_sync/run_subscriber'
|
17
17
|
|
18
|
-
require 'pub_sub_model_sync/
|
18
|
+
require 'pub_sub_model_sync/payload_builder'
|
19
19
|
require 'pub_sub_model_sync/subscriber'
|
20
20
|
|
21
21
|
require 'pub_sub_model_sync/service_base'
|
@@ -17,25 +17,5 @@ module PubSubModelSync
|
|
17
17
|
config.debug
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
21
|
-
# @param errors (Array(Class|String))
|
22
|
-
def retry_error(errors, qty: 2, &block)
|
23
|
-
retries ||= 0
|
24
|
-
block.call
|
25
|
-
rescue => e
|
26
|
-
retries += 1
|
27
|
-
res = errors.find { |e_type| match_error?(e, e_type) }
|
28
|
-
raise if !res || retries > qty
|
29
|
-
|
30
|
-
sleep(qty * 0.1) && retry
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# @param error (Exception)
|
36
|
-
# @param error_type (Class|String)
|
37
|
-
def match_error?(error, error_type)
|
38
|
-
error_type.is_a?(String) ? error.message.include?(error_type) : error.is_a?(error_type)
|
39
|
-
end
|
40
20
|
end
|
41
21
|
end
|
@@ -8,7 +8,8 @@ module PubSubModelSync
|
|
8
8
|
# customizable callbacks
|
9
9
|
cattr_accessor(:debug) { false }
|
10
10
|
cattr_accessor :logger # LoggerInst
|
11
|
-
cattr_accessor(:
|
11
|
+
cattr_accessor(:transactions_max_buffer) { 100 }
|
12
|
+
cattr_accessor(:enable_rails4_before_commit) { Rails::VERSION::MAJOR == 4 }
|
12
13
|
|
13
14
|
cattr_accessor(:on_before_processing) { ->(_payload, _info) {} } # return :cancel to skip
|
14
15
|
cattr_accessor(:on_success_processing) { ->(_payload, _info) {} }
|
@@ -29,7 +30,7 @@ module PubSubModelSync
|
|
29
30
|
def self.log(msg, kind = :info)
|
30
31
|
msg = "PS_MSYNC ==> #{msg}"
|
31
32
|
if logger == :raise_error
|
32
|
-
kind == :error ? raise(msg) : puts(msg)
|
33
|
+
kind == :error ? raise(StandardError, msg) : puts(msg)
|
33
34
|
else
|
34
35
|
logger ? logger.send(kind, msg) : puts(msg)
|
35
36
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Rails 4 backward compatibility (Add "simple" ps_before_*_commit callbacks)
|
4
|
+
ActiveRecord::ConnectionAdapters::RealTransaction.class_eval do
|
5
|
+
alias_method :commit_without_before_commit, :commit
|
6
|
+
|
7
|
+
def commit
|
8
|
+
call_before_commit_records if PubSubModelSync::Config.enable_rails4_before_commit
|
9
|
+
commit_without_before_commit
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def call_before_commit_records
|
15
|
+
ite = records.uniq
|
16
|
+
ite.each do |record|
|
17
|
+
action = record.previous_changes.include?(:id) ? :create : :update
|
18
|
+
action = :destroy if record.destroyed?
|
19
|
+
callback_name = "ps_before_#{action}_commit".to_sym
|
20
|
+
record.send(callback_name) if record.respond_to?(callback_name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -16,13 +16,17 @@ module PubSubModelSync
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def process!
|
19
|
-
filter_subscribers
|
19
|
+
subscribers = filter_subscribers
|
20
|
+
payload_info = { klass: payload.klass, action: payload.action, mode: payload.mode }
|
21
|
+
log("No subscribers found for #{payload_info}", :warn) if config.debug && subscribers.empty?
|
22
|
+
subscribers.each(&method(:run_subscriber))
|
20
23
|
end
|
21
24
|
|
22
25
|
def process
|
26
|
+
retries ||= 0
|
23
27
|
process!
|
24
28
|
rescue => e
|
25
|
-
notify_error(e)
|
29
|
+
retry_process?(e, retries += 1) ? retry : notify_error(e)
|
26
30
|
end
|
27
31
|
|
28
32
|
private
|
@@ -31,12 +35,10 @@ module PubSubModelSync
|
|
31
35
|
processor = PubSubModelSync::RunSubscriber.new(subscriber, payload)
|
32
36
|
return unless processable?(subscriber)
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
log "processed message with: #{payload.inspect}" if res != :skip_log
|
39
|
-
end
|
38
|
+
log("Processing message #{[subscriber, payload]}...") if config.debug
|
39
|
+
processor.call
|
40
|
+
res = config.on_success_processing.call(payload, { subscriber: subscriber })
|
41
|
+
log "processed message with: #{payload.inspect}" if res != :skip_log
|
40
42
|
end
|
41
43
|
|
42
44
|
def processable?(subscriber)
|
@@ -45,13 +47,34 @@ module PubSubModelSync
|
|
45
47
|
!cancel
|
46
48
|
end
|
47
49
|
|
48
|
-
# @param error (
|
50
|
+
# @param error (StandardError)
|
49
51
|
def notify_error(error)
|
50
52
|
info = [payload, error.message, error.backtrace]
|
51
53
|
res = config.on_error_processing.call(error, { payload: payload })
|
52
54
|
log("Error processing message: #{info}", :error) if res != :skip_log
|
53
55
|
end
|
54
56
|
|
57
|
+
def lost_db_connection?(error)
|
58
|
+
connection_lost_classes = %w[ActiveRecord::ConnectionTimeoutError PG::UnableToSend]
|
59
|
+
connection_lost_classes.include?(error.class.name) || error.message.match?(/lost connection/i)
|
60
|
+
end
|
61
|
+
|
62
|
+
def retry_process?(error, retries) # rubocop:disable Metrics/MethodLength
|
63
|
+
error_payload = [payload, error.message, error.backtrace]
|
64
|
+
return false unless lost_db_connection?(error)
|
65
|
+
|
66
|
+
if retries <= 5
|
67
|
+
sleep(retries)
|
68
|
+
log("Error processing message: (retrying #{retries}/5): #{error_payload}", :error)
|
69
|
+
ActiveRecord::Base.connection.reconnect! rescue nil # rubocop:disable Style/RescueModifier
|
70
|
+
true
|
71
|
+
else
|
72
|
+
log("Retried 5 times and error persists, exiting...: #{error_payload}", :error)
|
73
|
+
Process.exit!(true)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return (Array<PubSubModelSync::Subscriber>)
|
55
78
|
def filter_subscribers
|
56
79
|
config.subscribers.select do |subscriber|
|
57
80
|
subscriber.from_klass == payload.klass && subscriber.action == payload.action && payload.mode == subscriber.mode
|
@@ -22,7 +22,7 @@ module PubSubModelSync
|
|
22
22
|
def transaction(key, settings = {}, &block)
|
23
23
|
t = init_transaction(key, settings)
|
24
24
|
block.call
|
25
|
-
t.
|
25
|
+
t.finish
|
26
26
|
rescue
|
27
27
|
t.rollback
|
28
28
|
raise
|
@@ -31,7 +31,7 @@ module PubSubModelSync
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# Starts a new transaction
|
34
|
-
# @param key (
|
34
|
+
# @param key (String|Nil)
|
35
35
|
# @return (Transaction)
|
36
36
|
def init_transaction(key, settings = {})
|
37
37
|
new_transaction = PubSubModelSync::Transaction.new(key, settings)
|
@@ -47,20 +47,20 @@ module PubSubModelSync
|
|
47
47
|
# @refer PublisherConcern.ps_class_publish
|
48
48
|
# @return Payload
|
49
49
|
def publish_data(klass, data, action, headers: {})
|
50
|
-
|
51
|
-
payload
|
50
|
+
info = { klass: klass.to_s, action: action.to_sym, mode: :klass }
|
51
|
+
log("Building payload for: #{info.inspect}") if config.debug
|
52
|
+
payload = PubSubModelSync::Payload.new(data, info, headers)
|
53
|
+
define_transaction_key(payload)
|
52
54
|
publish(payload)
|
53
55
|
end
|
54
56
|
|
55
|
-
# @param model (ActiveRecord::Base)
|
56
|
-
# @param action (Symbol
|
57
|
-
# @param settings (Hash
|
57
|
+
# @param model (ActiveRecord::Base,PubSubModelSync::PublisherConcern)
|
58
|
+
# @param action (Symbol,String @see PublishConcern::ps_publish)
|
59
|
+
# @param settings (Hash @see PayloadBuilder.settings)
|
58
60
|
def publish_model(model, action, settings = {})
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
payload = publisher.payload
|
63
|
-
|
61
|
+
log("Building payload for: #{[model, action].inspect}") if config.debug
|
62
|
+
payload = PubSubModelSync::PayloadBuilder.new(model, action, settings).call
|
63
|
+
define_transaction_key(payload)
|
64
64
|
transaction(payload.headers[:ordering_key]) do # catch and group all :ps_before_publish syncs
|
65
65
|
publish(payload) { model.ps_after_publish(action, payload) } if ensure_model_publish(model, action, payload)
|
66
66
|
end
|
@@ -80,6 +80,7 @@ module PubSubModelSync
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def connector_publish(payload)
|
83
|
+
log("Publishing message #{[payload]}...") if config.debug
|
83
84
|
connector.publish(payload)
|
84
85
|
log("Published message: #{[payload]}")
|
85
86
|
config.on_after_publish.call(payload)
|
@@ -98,19 +99,18 @@ module PubSubModelSync
|
|
98
99
|
|
99
100
|
def ensure_publish(payload)
|
100
101
|
cancelled = config.on_before_publish.call(payload) == :cancel
|
101
|
-
log("Publish cancelled by config.on_before_publish: #{payload}") if config.debug && cancelled
|
102
|
+
log("Publish cancelled by config.on_before_publish: #{[payload]}") if config.debug && cancelled
|
102
103
|
!cancelled
|
103
104
|
end
|
104
105
|
|
105
106
|
def ordering_key_for(payload)
|
106
|
-
current_transaction&.key ||= payload.headers[:ordering_key]
|
107
107
|
payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]
|
108
108
|
end
|
109
109
|
|
110
110
|
def ensure_model_publish(model, action, payload)
|
111
111
|
res_before = model.ps_before_publish(action, payload)
|
112
112
|
cancelled = res_before == :cancel
|
113
|
-
log("Publish cancelled by model.ps_before_publish: #{payload}") if config.debug && cancelled
|
113
|
+
log("Publish cancelled by model.ps_before_publish: #{[payload]}") if config.debug && cancelled
|
114
114
|
!cancelled
|
115
115
|
end
|
116
116
|
|
@@ -119,6 +119,10 @@ module PubSubModelSync
|
|
119
119
|
res = config.on_error_publish.call(exception, { payload: payload })
|
120
120
|
log("Error publishing: #{info}", :error) if res != :skip_log
|
121
121
|
end
|
122
|
+
|
123
|
+
def define_transaction_key(payload)
|
124
|
+
current_transaction&.key ||= payload.headers[:ordering_key]
|
125
|
+
end
|
122
126
|
end
|
123
127
|
end
|
124
128
|
end
|
@@ -6,30 +6,33 @@ module PubSubModelSync
|
|
6
6
|
attr_reader :data, :info, :headers
|
7
7
|
|
8
8
|
# @param data (Hash: { any value }):
|
9
|
-
# @param info (Hash
|
9
|
+
# @param info (Hash):
|
10
|
+
# klass: (String, required) Notification class name
|
11
|
+
# action: (Symbol, required) Notification action name
|
12
|
+
# mode: (:model|:klass, default :model): :model for instance and :klass for class notifications
|
10
13
|
# @param headers (Hash):
|
11
14
|
# key (String): identifier of the payload, default:
|
12
|
-
# klass/action
|
13
|
-
# klass/action/model.id
|
15
|
+
# <klass/action>: when class message
|
16
|
+
# <klass/action/model.id>: when model message
|
14
17
|
# ordering_key (String): messages with the same key are processed in the same order they
|
15
18
|
# were delivered, default:
|
16
|
-
# klass
|
17
|
-
# klass/id
|
19
|
+
# <klass>: when class message
|
20
|
+
# <klass/id>: when model message
|
18
21
|
# topic_name (String|Array<String>): Specific topic name to be used when delivering the
|
19
22
|
# message (default first topic)
|
20
23
|
# forced_ordering_key (String, optional): Will force to use this value as the ordering_key,
|
21
24
|
# even withing transactions. Default nil.
|
22
25
|
def initialize(data, info, headers = {})
|
23
|
-
@data = data
|
24
|
-
@info =
|
25
|
-
@headers = headers
|
26
|
+
@data = data.deep_symbolize_keys
|
27
|
+
@info = info.deep_symbolize_keys
|
28
|
+
@headers = headers.deep_symbolize_keys
|
26
29
|
build_headers
|
27
30
|
validate!
|
28
31
|
end
|
29
32
|
|
30
33
|
# @return Hash: payload data
|
31
34
|
def to_h
|
32
|
-
{ data: data, info: info, headers: headers }
|
35
|
+
{ data: data.clone, info: info.clone, headers: headers.clone }
|
33
36
|
end
|
34
37
|
|
35
38
|
def klass
|
@@ -41,7 +44,7 @@ module PubSubModelSync
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def mode
|
44
|
-
info[:mode].to_sym
|
47
|
+
(info[:mode] || :model).to_sym
|
45
48
|
end
|
46
49
|
|
47
50
|
# Process payload data
|
@@ -75,8 +78,8 @@ module PubSubModelSync
|
|
75
78
|
# convert payload data into Payload
|
76
79
|
# @param data [Hash]: payload data (:data, :info, :headers)
|
77
80
|
def self.from_payload_data(data)
|
78
|
-
data = data.
|
79
|
-
new(data[:data], data[:info], data[:headers])
|
81
|
+
data = data.symbolize_keys
|
82
|
+
new(data[:data], data[:info] || data[:attributes], data[:headers])
|
80
83
|
end
|
81
84
|
|
82
85
|
private
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PubSubModelSync
|
4
|
-
class
|
4
|
+
class PayloadBuilder < PubSubModelSync::Base
|
5
5
|
attr_accessor :model, :action, :data, :mapping, :headers, :as_klass
|
6
6
|
|
7
|
-
# @param model (ActiveRecord::Base)
|
7
|
+
# @param model (ActiveRecord::Base,PubSubModelSync::PublisherConcern)
|
8
8
|
# @param action (@see PublishConcern::ps_publish)
|
9
9
|
# @param settings (@see PublishConcern::ps_publish): { data:, mapping:, headers:, as_klass: }
|
10
10
|
def initialize(model, action, settings = {})
|
@@ -17,9 +17,9 @@ module PubSubModelSync
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# @return (Payload)
|
20
|
-
def
|
20
|
+
def call
|
21
21
|
values = compute_value(data)
|
22
|
-
values =
|
22
|
+
values = self.class.parse_mapping_for(model, mapping).merge(values)
|
23
23
|
PubSubModelSync::Payload.new(values, settings_data, headers_data)
|
24
24
|
end
|
25
25
|
|
@@ -27,6 +27,18 @@ module PubSubModelSync
|
|
27
27
|
[model.class.name, model.id || SecureRandom.uuid].join('/')
|
28
28
|
end
|
29
29
|
|
30
|
+
# @param model (ActiveRecord::Base)
|
31
|
+
# @param mapping (@see PublishConcern::ps_publish -> mapping)
|
32
|
+
# @return (Hash) Hash with the corresponding values for each attribute
|
33
|
+
# Sample: parse_mapping_for(my_model, %w[id name:full_name])
|
34
|
+
# ==> { id: 10, full_name: 'model.name value' }
|
35
|
+
def self.parse_mapping_for(model, mapping)
|
36
|
+
mapping.map do |prop|
|
37
|
+
source, target = prop.to_s.split(':')
|
38
|
+
[target || source, model.send(source.to_sym)]
|
39
|
+
end.to_h.symbolize_keys
|
40
|
+
end
|
41
|
+
|
30
42
|
private
|
31
43
|
|
32
44
|
def headers_data
|
@@ -46,12 +58,5 @@ module PubSubModelSync
|
|
46
58
|
def settings_data
|
47
59
|
{ klass: as_klass, action: action }
|
48
60
|
end
|
49
|
-
|
50
|
-
def mapping_data
|
51
|
-
mapping.map do |prop|
|
52
|
-
source, target = prop.to_s.split(':')
|
53
|
-
[target || source, model.send(source.to_sym)]
|
54
|
-
end.to_h.symbolize_keys
|
55
|
-
end
|
56
61
|
end
|
57
62
|
end
|
@@ -9,12 +9,6 @@ module PubSubModelSync
|
|
9
9
|
ps_init_transaction_callbacks if self <= ActiveRecord::Base
|
10
10
|
end
|
11
11
|
|
12
|
-
# before preparing data to sync
|
13
|
-
def ps_skip_publish?(_action)
|
14
|
-
false
|
15
|
-
end
|
16
|
-
alias ps_skip_sync? ps_skip_publish? # @deprecated
|
17
|
-
|
18
12
|
# before delivering data (return :cancel to cancel sync)
|
19
13
|
def ps_before_publish(_action, _payload); end
|
20
14
|
alias ps_before_sync ps_before_publish # @deprecated
|
@@ -24,14 +18,14 @@ module PubSubModelSync
|
|
24
18
|
alias ps_after_sync ps_after_publish # @deprecated
|
25
19
|
|
26
20
|
# Delivers a notification via pubsub
|
27
|
-
# @param action (
|
21
|
+
# @param action (Symbol,String) Sample: create|update|save|destroy|<any_other_key>
|
28
22
|
# @param mapping? (Array<String>) If present will generate data using the mapping and added to the payload.
|
29
23
|
# Sample: ["id", "full_name:name"]
|
30
|
-
# @param data? (Hash
|
24
|
+
# @param data? (Hash,Symbol,Proc)
|
31
25
|
# Hash: Data to be added to the payload
|
32
26
|
# Symbol: Method name to be called to retrieve payload data (must return a hash value, receives :action name)
|
33
27
|
# Proc: Block to be called to retrieve payload data
|
34
|
-
# @param headers? (Hash
|
28
|
+
# @param headers? (Hash,Symbol,Proc): (All available attributes in @Payload.headers)
|
35
29
|
# Hash: Data that will be merged with default header values
|
36
30
|
# Symbol: Method name that will be called to retrieve header values (must return a hash, receives :action name)
|
37
31
|
# Proc: Block to be called to retrieve header values
|
@@ -42,6 +36,16 @@ module PubSubModelSync
|
|
42
36
|
end
|
43
37
|
delegate :ps_class_publish, to: :class
|
44
38
|
|
39
|
+
# Permits to perform manually the callback for a specific action
|
40
|
+
# @param action (Symbol, default: :create) Only :create|:update|:destroy
|
41
|
+
def ps_perform_publish(action = :create)
|
42
|
+
items = self.class.ps_cache_publish_callbacks.select { |item| item[:actions].include?(action) }
|
43
|
+
raise(StandardError, "No callback found for action :#{action}") if items.empty?
|
44
|
+
|
45
|
+
items.each { |item| instance_exec(action, &item[:callback]) }
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
45
49
|
module ClassMethods
|
46
50
|
# Publishes a class level notification via pubsub
|
47
51
|
# @param data (Hash): Data of the notification
|
@@ -53,32 +57,48 @@ module PubSubModelSync
|
|
53
57
|
klass.publish_data((as_klass || name).to_s, data, action.to_sym, headers: headers)
|
54
58
|
end
|
55
59
|
|
56
|
-
# @param crud_actions (Symbol
|
60
|
+
# @param crud_actions (Symbol,Array<Symbol>): :create, :update, :destroy
|
57
61
|
# @param method_name (Symbol, optional) method to be called
|
58
|
-
def
|
59
|
-
|
62
|
+
def ps_after_action(crud_actions, method_name = nil, &block)
|
63
|
+
actions = Array(crud_actions).map(&:to_sym)
|
60
64
|
callback = ->(action) { method_name ? send(method_name, action) : instance_exec(action, &block) }
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
65
|
+
ps_cache_publish_callbacks({ actions: actions, callback: callback })
|
66
|
+
actions.each do |action|
|
67
|
+
if action == :destroy
|
68
|
+
after_destroy { instance_exec(action, &callback) }
|
69
|
+
else
|
70
|
+
ps_define_commit_action(action, callback)
|
71
|
+
end
|
66
72
|
end
|
67
73
|
end
|
68
74
|
|
75
|
+
def ps_cache_publish_callbacks(new_value = nil)
|
76
|
+
@ps_cache_publish_callbacks ||= []
|
77
|
+
@ps_cache_publish_callbacks << new_value if new_value
|
78
|
+
@ps_cache_publish_callbacks
|
79
|
+
end
|
80
|
+
|
69
81
|
private
|
70
82
|
|
83
|
+
def ps_define_commit_action(action, callback)
|
84
|
+
if PubSubModelSync::Config.enable_rails4_before_commit # rails 4 compatibility
|
85
|
+
define_method("ps_before_#{action}_commit") { instance_exec(action, &callback) }
|
86
|
+
else
|
87
|
+
commit_name = respond_to?(:before_commit) ? :before_commit : :after_commit
|
88
|
+
send(commit_name, on: action) { instance_exec(action, &callback) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
71
92
|
# Initialize calls to start and end pub_sub transactions and deliver all them in the same order
|
72
93
|
def ps_init_transaction_callbacks
|
73
94
|
start_transaction = lambda do
|
74
|
-
|
75
|
-
@ps_transaction = PubSubModelSync::MessagePublisher.init_transaction(key)
|
95
|
+
@ps_transaction = PubSubModelSync::MessagePublisher.init_transaction(nil)
|
76
96
|
end
|
77
|
-
|
97
|
+
before_create start_transaction, prepend: true
|
78
98
|
before_update start_transaction, prepend: true
|
79
99
|
before_destroy start_transaction, prepend: true
|
80
|
-
after_commit { @ps_transaction
|
81
|
-
after_rollback(prepend: true) { @ps_transaction
|
100
|
+
after_commit { @ps_transaction&.finish }
|
101
|
+
after_rollback(prepend: true) { @ps_transaction&.rollback }
|
82
102
|
end
|
83
103
|
end
|
84
104
|
end
|