pub_sub_model_sync 1.0.beta2 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +43 -0
- data/CHANGELOG.md +7 -4
- data/Gemfile.lock +3 -1
- data/README.md +174 -105
- 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 +1 -1
- data/lib/pub_sub_model_sync/initializers/before_commit.rb +3 -3
- data/lib/pub_sub_model_sync/message_processor.rb +32 -9
- data/lib/pub_sub_model_sync/message_publisher.rb +14 -10
- data/lib/pub_sub_model_sync/payload.rb +15 -12
- data/lib/pub_sub_model_sync/{publisher.rb → payload_builder.rb} +15 -10
- data/lib/pub_sub_model_sync/publisher_concern.rb +27 -16
- data/lib/pub_sub_model_sync/run_subscriber.rb +17 -13
- data/lib/pub_sub_model_sync/service_base.rb +5 -32
- data/lib/pub_sub_model_sync/service_google.rb +1 -1
- data/lib/pub_sub_model_sync/subscriber_concern.rb +6 -4
- data/lib/pub_sub_model_sync/transaction.rb +6 -2
- data/lib/pub_sub_model_sync/version.rb +1 -1
- data/samples/README.md +50 -0
- data/samples/app1/.gitattributes +8 -0
- data/samples/app1/.gitignore +28 -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/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/.gitattributes +8 -0
- data/samples/app2/.gitignore +28 -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/puma.rb +43 -0
- data/samples/app2/config/routes.rb +3 -0
- data/samples/app2/config/spring.rb +6 -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
|
@@ -30,7 +30,7 @@ module PubSubModelSync
|
|
30
30
|
def self.log(msg, kind = :info)
|
31
31
|
msg = "PS_MSYNC ==> #{msg}"
|
32
32
|
if logger == :raise_error
|
33
|
-
kind == :error ? raise(msg) : puts(msg)
|
33
|
+
kind == :error ? raise(StandardError, msg) : puts(msg)
|
34
34
|
else
|
35
35
|
logger ? logger.send(kind, msg) : puts(msg)
|
36
36
|
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
# Rails 4 backward compatibility (Add "simple" ps_before_*_commit callbacks)
|
4
4
|
ActiveRecord::ConnectionAdapters::RealTransaction.class_eval do
|
5
|
-
alias_method :
|
5
|
+
alias_method :commit_without_before_commit, :commit
|
6
6
|
|
7
7
|
def commit
|
8
|
-
call_before_commit_records if
|
9
|
-
|
8
|
+
call_before_commit_records if PubSubModelSync::Config.enable_rails4_before_commit
|
9
|
+
commit_without_before_commit
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
@@ -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
|
@@ -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
57
|
# @param model (ActiveRecord::Base)
|
56
58
|
# @param action (Symbol: @see PublishConcern::ps_publish)
|
57
|
-
# @param settings (Hash: @see
|
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.inspect}...") if config.debug
|
83
84
|
connector.publish(payload)
|
84
85
|
log("Published message: #{[payload]}")
|
85
86
|
config.on_after_publish.call(payload)
|
@@ -103,7 +104,6 @@ module PubSubModelSync
|
|
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
|
|
@@ -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,7 +1,7 @@
|
|
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
7
|
# @param model (ActiveRecord::Base)
|
@@ -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
|
@@ -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
|
@@ -55,24 +59,32 @@ module PubSubModelSync
|
|
55
59
|
|
56
60
|
# @param crud_actions (Symbol|Array<Symbol>): :create, :update, :destroy
|
57
61
|
# @param method_name (Symbol, optional) method to be called
|
58
|
-
def
|
62
|
+
def ps_after_action(crud_actions, method_name = nil, &block)
|
63
|
+
actions = Array(crud_actions).map(&:to_sym)
|
59
64
|
callback = ->(action) { method_name ? send(method_name, action) : instance_exec(action, &block) }
|
60
|
-
|
65
|
+
ps_cache_publish_callbacks({ actions: actions, callback: callback })
|
66
|
+
actions.each do |action|
|
61
67
|
if action == :destroy
|
62
68
|
after_destroy { instance_exec(action, &callback) }
|
63
69
|
else
|
64
|
-
|
70
|
+
ps_define_commit_action(action, callback)
|
65
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
|
|
71
|
-
def
|
72
|
-
commit_name = respond_to?(:before_commit) ? :before_commit : :after_commit
|
83
|
+
def ps_define_commit_action(action, callback)
|
73
84
|
if PubSubModelSync::Config.enable_rails4_before_commit # rails 4 compatibility
|
74
85
|
define_method("ps_before_#{action}_commit") { instance_exec(action, &callback) }
|
75
86
|
else
|
87
|
+
commit_name = respond_to?(:before_commit) ? :before_commit : :after_commit
|
76
88
|
send(commit_name, on: action) { instance_exec(action, &callback) }
|
77
89
|
end
|
78
90
|
end
|
@@ -80,14 +92,13 @@ module PubSubModelSync
|
|
80
92
|
# Initialize calls to start and end pub_sub transactions and deliver all them in the same order
|
81
93
|
def ps_init_transaction_callbacks
|
82
94
|
start_transaction = lambda do
|
83
|
-
|
84
|
-
@ps_transaction = PubSubModelSync::MessagePublisher.init_transaction(key)
|
95
|
+
@ps_transaction = PubSubModelSync::MessagePublisher.init_transaction(nil)
|
85
96
|
end
|
86
|
-
|
97
|
+
before_create start_transaction, prepend: true
|
87
98
|
before_update start_transaction, prepend: true
|
88
99
|
before_destroy start_transaction, prepend: true
|
89
|
-
after_commit { @ps_transaction
|
90
|
-
after_rollback(prepend: true) { @ps_transaction
|
100
|
+
after_commit { @ps_transaction&.finish }
|
101
|
+
after_rollback(prepend: true) { @ps_transaction&.rollback }
|
91
102
|
end
|
92
103
|
end
|
93
104
|
end
|
@@ -26,7 +26,7 @@ module PubSubModelSync
|
|
26
26
|
def run_class_message
|
27
27
|
model_class = subscriber.klass.constantize
|
28
28
|
model_class.ps_processing_payload = payload # TODO: review for parallel notifications
|
29
|
-
call_action(model_class
|
29
|
+
call_action(model_class) if ensure_sync(model_class)
|
30
30
|
end
|
31
31
|
|
32
32
|
# support for: create, update, destroy
|
@@ -48,16 +48,17 @@ module PubSubModelSync
|
|
48
48
|
res
|
49
49
|
end
|
50
50
|
|
51
|
-
def call_action(object
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
51
|
+
def call_action(object)
|
52
|
+
callback = settings[:to_action]
|
53
|
+
callback.is_a?(Proc) ? object.instance_exec(payload.data, &callback) : call_action_method(object)
|
54
|
+
end
|
55
|
+
|
56
|
+
def call_action_method(object)
|
57
|
+
method_name = settings[:to_action]
|
58
|
+
method_name = :save! if %i[create update].include?(method_name.to_sym)
|
59
|
+
method_name = :destroy! if method_name.to_sym == :destroy
|
60
|
+
is_crud_action = %i[save! destroy!].include?(method_name)
|
61
|
+
is_crud_action ? object.send(method_name) : object.send(method_name, payload.data)
|
61
62
|
end
|
62
63
|
|
63
64
|
def parse_condition(condition, object)
|
@@ -74,10 +75,13 @@ module PubSubModelSync
|
|
74
75
|
model_class = subscriber.klass.constantize
|
75
76
|
return model_class.ps_find_model(payload.data) if model_class.respond_to?(:ps_find_model)
|
76
77
|
|
78
|
+
error_msg = 'No values provided for identifiers:'
|
79
|
+
raise(StandardError, "#{error_msg} #{[settings[:id], payload]}") if model_identifiers.empty?
|
80
|
+
|
77
81
|
model_class.where(model_identifiers).first_or_initialize
|
78
82
|
end
|
79
83
|
|
80
|
-
# @param mappings (Array<String>) supports aliasing, sample: ["id", "full_name:name"]
|
84
|
+
# @param mappings (Array<String,Symbol>) supports aliasing, sample: ["id", "full_name:name"]
|
81
85
|
# @return (Hash) hash with the correct attr names and its values
|
82
86
|
def parse_mapping(mappings)
|
83
87
|
mappings.map do |prop|
|
@@ -91,7 +95,7 @@ module PubSubModelSync
|
|
91
95
|
|
92
96
|
# @return (Hash) hash including identifiers and its values
|
93
97
|
def model_identifiers
|
94
|
-
@model_identifiers ||= parse_mapping(Array(settings[:id])
|
98
|
+
@model_identifiers ||= parse_mapping(Array(settings[:id]))
|
95
99
|
end
|
96
100
|
|
97
101
|
def populate_model
|