pub_sub_model_sync 0.5.0.1 → 0.5.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f08249ef28730afdc2a0f3b025ef66d5117694fdc4ed995e9eb907e37e60defb
4
- data.tar.gz: cc8d337de6952db6e9747912178ce7fd09f93c3a600d1d4756671fb6bcefadad
3
+ metadata.gz: 57e8d23c9e514734a8a45664f641378bbb46e8de5315c536a0f692efcbe29dd2
4
+ data.tar.gz: d0f290dac2b0563ffe5412f85e1a6a6e340e281049755b26dc732a3661501b92
5
5
  SHA512:
6
- metadata.gz: cdca1e7397fa5fae46198fce8ae24036318d619d53cff5c06e8e8c266423a5681e48588eddcced3271747242a35fc77b1c75bf567d5461c4fa7f573ae29c4c15
7
- data.tar.gz: 0c48d83f3904c5872cdc3b48f2d341be84cbe84899a3fdc0738c9ad290e5e6c1c49ca2ab226e12df4f097d6f927e603b4691f64760a8d4dd9c703431711e71ec
6
+ metadata.gz: 351901f98e6c806e7a4efe20abdac65de5bf8f716dc57e1889302eab02d33b3f2f4d449cd0d351fd5ae882d16abe79a77791bea8ad956c52cda47829319eb858
7
+ data.tar.gz: e906d601a8c37b999a8b6d550e605e9c04b67db692cec4b60220ae286bef9adde7132d8614cee4ca7dab9ef43538ae2baede3f07e402c4c21a189b79a9ba6873
@@ -1,5 +1,27 @@
1
1
  # Change Log
2
2
 
3
+ # 0.5.4 (January 8, 2021)
4
+ - fix: exclude identifiers when syncing model
5
+ - feat: callbacks support for future extra params
6
+ - feat: make connectors configurable
7
+ - feat: add :process! and :process, :publish!, :publish methods to payload
8
+ - feat: auto retry 2 times when "could not obtain a database connection within 5.000 seconds..." error occurs
9
+
10
+ # 0.5.3 (December 30, 2020)
11
+ - fix: kafka consume all messages from different apps
12
+ - style: use the correct consumer key
13
+
14
+ # 0.5.2 (December 30, 2020)
15
+ - fix: rabbitmq deliver messages to all subscribers
16
+ - fix: rabbitmq persist messages to recover after restarting
17
+
18
+ # 0.5.1.1 (December 29, 2020)
19
+ - Hotfix: auto convert class name into string
20
+
21
+ # 0.5.1 (December 24, 2020)
22
+ - feat: rename publisher callbacks to be more understandable
23
+ - feat: add callbacks to listen when processing a message (before saving sync)
24
+
3
25
  # 0.5.0.1 (December 22, 2020)
4
26
  - fix: add missing rabbit mock method
5
27
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (0.5.0.1)
4
+ pub_sub_model_sync (0.5.4)
5
5
  rails
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -28,10 +28,9 @@ And then execute: $ bundle install
28
28
  ```ruby
29
29
  # initializers/pub_sub_config.rb
30
30
  PubSubModelSync::Config.service_name = :google
31
- PubSubModelSync::Config.project = 'project-id'
31
+ PubSubModelSync::Config.project = 'google-project-id'
32
32
  PubSubModelSync::Config.credentials = 'path-to-the-config'
33
33
  PubSubModelSync::Config.topic_name = 'sample-topic'
34
- PubSubModelSync::Config.subscription_name = 'p1-subscriber'
35
34
  ```
36
35
  See details here:
37
36
  https://github.com/googleapis/google-cloud-ruby/tree/master/google-cloud-pubsub
@@ -40,7 +39,7 @@ And then execute: $ bundle install
40
39
  ```ruby
41
40
  PubSubModelSync::Config.service_name = :rabbitmq
42
41
  PubSubModelSync::Config.bunny_connection = 'amqp://guest:guest@localhost'
43
- PubSubModelSync::Config.queue_name = ''
42
+ PubSubModelSync::Config.queue_name = 'model-sync'
44
43
  PubSubModelSync::Config.topic_name = 'sample-topic'
45
44
  ```
46
45
  See details here: https://github.com/ruby-amqp/bunny
@@ -48,7 +47,7 @@ And then execute: $ bundle install
48
47
  - configuration for Apache Kafka (You need kafka installed)
49
48
  ```ruby
50
49
  PubSubModelSync::Config.service_name = :kafka
51
- PubSubModelSync::Config.kafka_connection = [["kafka1:9092", "localhost:2121"], logger: Rails.logger]
50
+ PubSubModelSync::Config.kafka_connection = [["kafka1:9092", "localhost:2121"], { logger: Rails.logger }]
52
51
  PubSubModelSync::Config.topic_name = 'sample-topic'
53
52
  ```
54
53
  See details here: https://github.com/zendesk/ruby-kafka
@@ -59,7 +58,13 @@ And then execute: $ bundle install
59
58
  ```ruby
60
59
  rake pub_sub_model_sync:start
61
60
  ```
62
- Note: Publishers do not need todo this
61
+ Note: Publishers do not need todo this
62
+ Note2 (Rails 6+): Due to Zeitwerk, you need to load listeners manually when syncing outside ```rake pub_sub_model_sync:start```
63
+ ```ruby
64
+ # PubSubModelSync::Config.subscribers ==> []
65
+ Rails.application.try(:eager_load!)
66
+ # PubSubModelSync::Config.subscribers ==> [#<PubSubModelSync::Subscriber:0x000.. @klass="Article", @action=:create..., ....]
67
+ ```
63
68
 
64
69
  - Check the service status with:
65
70
  ```PubSubModelSync::MessagePublisher.publish_data('Test message', {sample_value: 10}, :create)```
@@ -163,6 +168,9 @@ Note: Be careful with collision of names
163
168
  ```.ps_subscriber_changed?(data)```
164
169
  By default: ```model.changed?```
165
170
 
171
+ - Permit to perform custom actions before saving sync of the model (:cancel can be returned to skip sync)
172
+ ```.ps_before_save_sync(payload)```
173
+
166
174
  ### Publishers
167
175
  - Permit to configure crud publishers
168
176
  ```ps_publish(attrs, actions: nil, as_klass: nil)```
@@ -198,10 +206,13 @@ Note: Be careful with collision of names
198
206
  * action_name: (required, :sim) Action name
199
207
  * as_klass: (optional, :string) Custom class name (Default current model name)
200
208
 
201
- - Publish a class level notification (Same as above: manual call)
209
+ - Payload actions
202
210
  ```ruby
203
211
  payload = PubSubModelSync::Payload.new({ title: 'hello' }, { action: :greeting, klass: 'User' })
204
- payload.publish!
212
+ payload.publish! # publishes notification data. It raises exception if fails and does not call ```:on_error_publishing``` callback
213
+ payload.publish # publishes notification data. On error does not raise exception but calls ```:on_error_publishing``` callback
214
+ payload.process! # process a notification data. It raises exception if fails and does not call ```.on_error_processing``` callback
215
+ payload.publish # process a notification data. It does not raise exception if fails but calls ```.on_error_processing``` callback
205
216
  ```
206
217
 
207
218
  - Get crud publisher configured for the class
@@ -274,22 +285,26 @@ config = PubSubModelSync::Config
274
285
  config.debug = true
275
286
  ```
276
287
 
277
- - ```debug = true```
288
+ - ```.subscription_name = 'app-2'```
289
+ Permit to define a custom consumer identifier (Default: Rails application name)
290
+ - ```.debug = true```
278
291
  (true/false*) => show advanced log messages
279
- - ```logger = Rails.logger```
292
+ - ```.logger = Rails.logger```
280
293
  (Logger) => define custom logger
281
- - ```disabled = true```
282
- (true/false*) => if true, does not publish model messages (Create/Update/Destroy)
283
- - ```on_process_success = ->(payload, subscriber) { puts payload }```
294
+ - ```.disabled_callback_publisher = ->(_model, _action) { false }```
295
+ (true/false*) => if true, does not listen model callbacks for auto sync (Create/Update/Destroy)
296
+ - ```.on_before_processing = ->(payload, {subscriber:}) { puts payload }```
297
+ (Proc) => called before processing received message (:cancel can be returned to skip processing)
298
+ - ```.on_success_processing = ->(payload, {subscriber:}) { puts payload }```
284
299
  (Proc) => called when a message was successfully processed
285
- - ```on_process_error = ->(exception, payload) { sleep 1; payload.process! }```
286
- (Proc) => called when a message failed when processing
287
- - ```on_before_publish = ->(payload) { puts payload }```
288
- (Proc) => called before publishing a message
289
- - ```on_after_publish = ->(payload) { puts payload }```
300
+ - ```.on_error_processing = ->(exception, {payload:, subscriber:}) { payload.delay(...).process! }```
301
+ (Proc) => called when a message failed when processing (delayed_job or similar can be used for retrying)
302
+ - ```.on_before_publish = ->(payload) { puts payload }```
303
+ (Proc) => called before publishing a message (:cancel can be returned to skip publishing)
304
+ - ```.on_after_publish = ->(payload) { puts payload }```
290
305
  (Proc) => called after publishing a message
291
- - ```on_publish_error = ->(exception, payload) { sleep 1; payload.publish! }```
292
- (Proc) => called when failed publishing a message
306
+ - ```.on_error_publish = ->(exception, {payload:}) { payload.delay(...).publish! }```
307
+ (Proc) => called when failed publishing a message (delayed_job or similar can be used for retrying)
293
308
 
294
309
  ## TODO
295
310
  - Add alias attributes when subscribing (similar to publisher)
@@ -13,5 +13,12 @@ module PubSubModelSync
13
13
  config.log message, kind
14
14
  end
15
15
  end
16
+
17
+ def retry_error(error_klass, qty: 2, &block)
18
+ @retries ||= 0
19
+ block.call
20
+ rescue error_klass => _e
21
+ (@retries += 1) <= qty ? retry : raise
22
+ end
16
23
  end
17
24
  end
@@ -10,12 +10,13 @@ module PubSubModelSync
10
10
  cattr_accessor(:debug) { false }
11
11
  cattr_accessor :logger # LoggerInst
12
12
 
13
- cattr_accessor(:on_process_success) { ->(_payload, _subscriber) {} }
14
- cattr_accessor(:on_process_error) { ->(_exception, _payload) {} }
15
- cattr_accessor(:on_before_publish) { ->(_payload) {} }
13
+ cattr_accessor(:on_before_processing) { ->(_payload, _info) {} } # return :cancel to skip
14
+ cattr_accessor(:on_success_processing) { ->(_payload, _info) {} }
15
+ cattr_accessor(:on_error_processing) { ->(_exception, _info) {} }
16
+ cattr_accessor(:on_before_publish) { ->(_payload) {} } # return :cancel to skip
16
17
  cattr_accessor(:on_after_publish) { ->(_payload) {} }
17
- cattr_accessor(:on_publish_error) { ->(_exception, _payload) {} }
18
- cattr_accessor(:disabled) { false }
18
+ cattr_accessor(:on_error_publish) { ->(_exception, _info) {} }
19
+ cattr_accessor(:disabled_callback_publisher) { ->(_model, _action) { false } }
19
20
 
20
21
  # google service
21
22
  cattr_accessor :project, :credentials, :topic_name, :subscription_name
@@ -2,7 +2,7 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  class MessageProcessor < PubSubModelSync::Base
5
- attr_accessor :payload
5
+ attr_accessor :payload, :raise_error
6
6
 
7
7
  # @param payload (Payload): payload to be delivered
8
8
  # @Deprecated: def initialize(data, klass, action)
@@ -22,17 +22,27 @@ module PubSubModelSync
22
22
  private
23
23
 
24
24
  def run_subscriber(subscriber)
25
- subscriber.eval_message(payload.data)
26
- config.on_process_success.call(payload, subscriber)
27
- log "processed message with: #{payload}"
25
+ return unless processable?(subscriber)
26
+
27
+ retry_error(ActiveRecord::ConnectionTimeoutError, qty: 2) do
28
+ subscriber.process!(payload)
29
+ res = config.on_success_processing.call(payload, { subscriber: subscriber })
30
+ log "processed message with: #{payload.inspect}" if res != :skip_log
31
+ end
28
32
  rescue => e
29
- print_subscriber_error(e)
33
+ raise_error ? raise : print_subscriber_error(e, subscriber)
34
+ end
35
+
36
+ def processable?(subscriber)
37
+ cancel = config.on_before_processing.call(payload, { subscriber: subscriber }) == :cancel
38
+ log("process message cancelled: #{payload}") if cancel && config.debug
39
+ !cancel
30
40
  end
31
41
 
32
42
  # @param error (Error)
33
- def print_subscriber_error(error)
43
+ def print_subscriber_error(error, subscriber)
34
44
  info = [payload, error.message, error.backtrace]
35
- res = config.on_process_error.call(error, payload)
45
+ res = config.on_error_processing.call(error, { payload: payload, subscriber: subscriber })
36
46
  log("Error processing message: #{info}", :error) if res != :skip_log
37
47
  end
38
48
 
@@ -8,7 +8,7 @@ module PubSubModelSync
8
8
  end
9
9
 
10
10
  def publish_data(klass, data, action)
11
- payload = PubSubModelSync::Payload.new(data, { klass: klass, action: action.to_sym })
11
+ payload = PubSubModelSync::Payload.new(data, { klass: klass.to_s, action: action.to_sym })
12
12
  publish(payload)
13
13
  end
14
14
 
@@ -19,8 +19,7 @@ module PubSubModelSync
19
19
  return if model.ps_skip_sync?(action)
20
20
 
21
21
  publisher ||= model.class.ps_publisher(action)
22
- payload_info = publisher.payload(model, action)
23
- payload = PubSubModelSync::Payload.new(payload_info[:data], payload_info[:attrs])
22
+ payload = publisher.payload(model, action)
24
23
  res_before = model.ps_before_sync(action, payload.data)
25
24
  return if res_before == :cancel
26
25
 
@@ -28,20 +27,24 @@ module PubSubModelSync
28
27
  model.ps_after_sync(action, payload.data)
29
28
  end
30
29
 
31
- def publish(payload)
32
- log("Publishing message: #{[payload]}") if config.debug
33
- config.on_before_publish.call(payload)
30
+ def publish(payload, raise_error: false)
31
+ if config.on_before_publish.call(payload) == :cancel
32
+ log("Publish message cancelled: #{payload}") if config.debug
33
+ return
34
+ end
35
+
36
+ log("Publishing message: #{[payload]}")
34
37
  connector.publish(payload)
35
38
  config.on_after_publish.call(payload)
36
39
  rescue => e
37
- notify_error(e, payload)
40
+ raise_error ? raise : notify_error(e, payload)
38
41
  end
39
42
 
40
43
  private
41
44
 
42
45
  def notify_error(exception, payload)
43
46
  info = [payload, exception.message, exception.backtrace]
44
- res = config.on_publish_error.call(exception, payload)
47
+ res = config.on_error_publish.call(exception, { payload: payload })
45
48
  log("Error publishing: #{info}", :error) if res != :skip_log
46
49
  end
47
50
  end
@@ -26,11 +26,23 @@ module PubSubModelSync
26
26
  end
27
27
 
28
28
  def process!
29
+ process do |publisher|
30
+ publisher.raise_error = true
31
+ end
32
+ end
33
+
34
+ def process
29
35
  publisher = PubSubModelSync::MessageProcessor.new(self)
36
+ yield(publisher) if block_given?
30
37
  publisher.process
31
38
  end
32
39
 
33
40
  def publish!
41
+ klass = PubSubModelSync::MessagePublisher
42
+ klass.publish(self, raise_error: true)
43
+ end
44
+
45
+ def publish
34
46
  klass = PubSubModelSync::MessagePublisher
35
47
  klass.publish(self)
36
48
  end
@@ -12,7 +12,7 @@ module PubSubModelSync
12
12
  end
13
13
 
14
14
  def payload(model, action)
15
- { data: payload_data(model), attrs: payload_attrs(model, action) }
15
+ PubSubModelSync::Payload.new(payload_data(model), payload_attrs(model, action))
16
16
  end
17
17
 
18
18
  private
@@ -11,13 +11,12 @@ module PubSubModelSync
11
11
  false
12
12
  end
13
13
 
14
- # TODO: make it using respond_to?(:ps_skip_sync?)
15
14
  # before preparing data to sync
16
15
  def ps_skip_sync?(_action)
17
16
  false
18
17
  end
19
18
 
20
- # before delivering data
19
+ # before delivering data (return :cancel to cancel sync)
21
20
  def ps_before_sync(_action, _data); end
22
21
 
23
22
  # after delivering data
@@ -64,7 +63,7 @@ module PubSubModelSync
64
63
 
65
64
  def ps_register_callback(action, publisher)
66
65
  after_commit(on: action) do |model|
67
- disabled = PubSubModelSync::Config.disabled
66
+ disabled = PubSubModelSync::Config.disabled_callback_publisher.call(model, action)
68
67
  if !disabled && !model.ps_skip_callback?(action)
69
68
  klass = PubSubModelSync::MessagePublisher
70
69
  klass.publish_model(model, action.to_sym, publisher)
@@ -4,6 +4,8 @@ require 'pub_sub_model_sync/payload'
4
4
  module PubSubModelSync
5
5
  class ServiceBase < PubSubModelSync::Base
6
6
  SERVICE_KEY = 'service_model_sync'
7
+ PUBLISH_SETTINGS = {}.freeze
8
+ LISTEN_SETTINGS = {}.freeze
7
9
 
8
10
  def listen_messages
9
11
  raise 'method :listen_messages must be defined in service'
@@ -18,7 +18,7 @@ module PubSubModelSync
18
18
 
19
19
  def listen_messages
20
20
  @subscription = subscribe_to_topic
21
- @subscriber = subscription.listen(&method(:process_message))
21
+ @subscriber = subscription.listen(LISTEN_SETTINGS, &method(:process_message))
22
22
  log('Listener starting...')
23
23
  subscriber.start
24
24
  log('Listener started')
@@ -28,7 +28,7 @@ module PubSubModelSync
28
28
  end
29
29
 
30
30
  def publish(payload)
31
- topic.publish(payload.to_json, { SERVICE_KEY => true })
31
+ topic.publish(payload.to_json, { SERVICE_KEY => true }.merge(PUBLISH_SETTINGS))
32
32
  end
33
33
 
34
34
  def stop
@@ -39,8 +39,8 @@ module PubSubModelSync
39
39
  private
40
40
 
41
41
  def subscribe_to_topic
42
- topic.subscription(config.subscription_name) ||
43
- topic.subscribe(config.subscription_name)
42
+ topic.subscription(config.subscription_key) ||
43
+ topic.subscribe(config.subscription_key)
44
44
  end
45
45
 
46
46
  def process_message(received_message)
@@ -10,17 +10,17 @@ module PubSubModelSync
10
10
  cattr_accessor :producer
11
11
  attr_accessor :config, :service, :consumer
12
12
 
13
- CONSUMER_GROUP = 'service_model_sync'
14
-
15
13
  def initialize
16
14
  @config = PubSubModelSync::Config
17
- @service = Kafka.new(*config.kafka_connection)
15
+ settings = config.kafka_connection
16
+ settings[1][:client_id] ||= config.subscription_key
17
+ @service = Kafka.new(*settings)
18
18
  end
19
19
 
20
20
  def listen_messages
21
21
  log('Listener starting...')
22
22
  start_consumer
23
- consumer.each_message(&method(:process_message))
23
+ consumer.each_message(LISTEN_SETTINGS, &method(:process_message))
24
24
  rescue PubSubModelSync::Runner::ShutDown
25
25
  log('Listener stopped')
26
26
  rescue => e
@@ -28,7 +28,11 @@ module PubSubModelSync
28
28
  end
29
29
 
30
30
  def publish(payload)
31
- producer.produce(payload.to_json, message_settings)
31
+ settings = {
32
+ topic: config.topic_name,
33
+ headers: { SERVICE_KEY => true }
34
+ }.merge(PUBLISH_SETTINGS)
35
+ producer.produce(payload.to_json, settings)
32
36
  producer.deliver_messages
33
37
  end
34
38
 
@@ -39,12 +43,8 @@ module PubSubModelSync
39
43
 
40
44
  private
41
45
 
42
- def message_settings
43
- { topic: config.topic_name, headers: { SERVICE_KEY => true } }
44
- end
45
-
46
46
  def start_consumer
47
- @consumer = service.consumer(group_id: CONSUMER_GROUP)
47
+ @consumer = service.consumer(group_id: config.subscription_key)
48
48
  consumer.subscribe(config.topic_name)
49
49
  end
50
50
 
@@ -40,6 +40,7 @@ module PubSubModelSync
40
40
 
41
41
  def stop
42
42
  log('Listener stopping...')
43
+ channel&.close
43
44
  service.close
44
45
  end
45
46
 
@@ -48,12 +49,17 @@ module PubSubModelSync
48
49
  def message_settings
49
50
  {
50
51
  routing_key: queue.name,
51
- type: SERVICE_KEY
52
- }
52
+ type: SERVICE_KEY,
53
+ persistent: true
54
+ }.merge(PUBLISH_SETTINGS)
55
+ end
56
+
57
+ def queue_settings
58
+ { durable: true, auto_delete: false }
53
59
  end
54
60
 
55
61
  def subscribe_settings
56
- { manual_ack: false }
62
+ { manual_ack: false }.merge(LISTEN_SETTINGS)
57
63
  end
58
64
 
59
65
  def process_message(_delivery_info, meta_info, payload)
@@ -65,8 +71,7 @@ module PubSubModelSync
65
71
  def subscribe_to_queue
66
72
  service.start
67
73
  @channel = service.create_channel
68
- queue_settings = { durable: true, auto_delete: false }
69
- @queue = channel.queue(config.queue_name, queue_settings)
74
+ @queue = channel.queue(config.subscription_key, queue_settings)
70
75
  subscribe_to_exchange
71
76
  end
72
77
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  class Subscriber
5
- attr_accessor :klass, :action, :attrs, :settings
5
+ attr_accessor :klass, :action, :attrs, :settings, :identifiers
6
+ attr_reader :payload
6
7
 
7
8
  # @param settings: (Hash) { id: :id, direct_mode: false,
8
9
  # from_klass: klass, from_action: action }
@@ -13,50 +14,53 @@ module PubSubModelSync
13
14
  @action = action
14
15
  @attrs = attrs
15
16
  @settings = def_settings.merge(settings)
17
+ @identifiers = Array(settings[:id]).map(&:to_sym)
16
18
  end
17
19
 
18
- def eval_message(message)
20
+ def process!(payload)
21
+ @payload = payload
19
22
  if settings[:direct_mode]
20
- run_class_message(message)
23
+ run_class_message
21
24
  else
22
- run_model_message(message)
25
+ run_model_message
23
26
  end
24
27
  end
25
28
 
26
29
  private
27
30
 
28
- def run_class_message(message)
31
+ def run_class_message
29
32
  model_class = klass.constantize
30
- model_class.send(action, message)
33
+ model_class.send(action, payload.data)
31
34
  end
32
35
 
33
36
  # support for: create, update, destroy
34
- def run_model_message(message)
35
- model = find_model(message)
37
+ def run_model_message
38
+ model = find_model
39
+ return if model.ps_before_save_sync(payload) == :cancel
40
+
36
41
  if action == :destroy
37
42
  model.destroy!
38
43
  else
39
- populate_model(model, message)
40
- return if action == :update && !model.ps_subscriber_changed?(message)
44
+ populate_model(model)
45
+ return if action == :update && !model.ps_subscriber_changed?(payload.data)
41
46
 
42
47
  model.save!
43
48
  end
44
49
  end
45
50
 
46
- def find_model(message)
51
+ def find_model
47
52
  model_class = klass.constantize
48
- return model_class.ps_find_model(message) if model_class.respond_to?(:ps_find_model)
53
+ return model_class.ps_find_model(payload.data) if model_class.respond_to?(:ps_find_model)
49
54
 
50
- model_class.where(model_identifiers(message)).first_or_initialize
55
+ model_class.where(model_identifiers).first_or_initialize
51
56
  end
52
57
 
53
- def model_identifiers(message)
54
- identifiers = Array(settings[:id])
55
- identifiers.map { |key| [key, message[key.to_sym]] }.to_h
58
+ def model_identifiers
59
+ identifiers.map { |key| [key, payload.data[key.to_sym]] }.to_h
56
60
  end
57
61
 
58
- def populate_model(model, message)
59
- values = message.slice(*attrs)
62
+ def populate_model(model)
63
+ values = payload.data.slice(*attrs).except(*identifiers)
60
64
  values.each do |attr, value|
61
65
  model.send("#{attr}=", value)
62
66
  end
@@ -12,6 +12,10 @@ module PubSubModelSync
12
12
  changed?
13
13
  end
14
14
 
15
+ # permit to apply custom actions before applying sync
16
+ # @return (nil|:cancel): nil to continue sync OR :cancel to skip sync
17
+ def ps_before_save_sync(_payload); end
18
+
15
19
  module ClassMethods
16
20
  def ps_subscribe(attrs, actions: nil, from_klass: name, id: :id)
17
21
  settings = { id: id, from_klass: from_klass }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '0.5.0.1'
4
+ VERSION = '0.5.4'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pub_sub_model_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.1
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-22 00:00:00.000000000 Z
11
+ date: 2021-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails