pub_sub_model_sync 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ba16c2e3129caa85cf184030955e6a1de130b23b9b552a620c1354234eb5376
4
- data.tar.gz: a4b3eb7514efe123a38fb46ed933bf9ac0b1f418f1675d73b13e2cee25e6fe03
3
+ metadata.gz: 3016ebc69eba9ccb8b168734db1b947b77a4c9400f48d8228f979a7c1ec9eab8
4
+ data.tar.gz: 82187606c0411ece3eb0d78db59a8794019e80acd5374ffd92b95b0eee4368f7
5
5
  SHA512:
6
- metadata.gz: b55d2b5f1bdd763061e0c2ea60f9fe080bdb1db26e5cdb20adf33973524612d2041b0c4dab1ec1afc90e4f7a91ea8327b41ef8c9ecf53ba8bafe543b92dd74e9
7
- data.tar.gz: 218f2d41a1fcb04d240a4c3e91daa58b989555f9a97dc488fbc0bd627c0db412e3de224afc31a02c73bfa123f8817705811da1f3e7d20d240036f00de4ccb11d
6
+ metadata.gz: f4335695f26d69f091a2ea1726221aab4b07b712827318adfe16f2d1275cde21a0860ce7562ed266f6a0661eb2b8d634abea0956a4478ad55f70e6c199c8a364
7
+ data.tar.gz: 9252e45ab27b332d9b50afc93da62a0a039c707a1bb3b219ff4e60c1c4f3dae8eb7a85024fc3d1dab87c006e448e60cf3d1f3d953dad8cc8fced24bd375b47cd
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ # 0.2.3 (April 15, 2020)
4
+ - Improve helper names
5
+ - feat: perform manual sync with custom settings
6
+ - fix for "IO timeout when reading 7 bytes" error (Rabbit)
7
+ - style: do not print processed message when failed
8
+ - feat: retry delivery message when failed (RabbitMQ)
9
+
10
+
3
11
  # 0.2.2 (March 27, 2020)
4
12
  - fix default value for cattr_accessor in ror < 5.2
5
13
  - add callbacks when publishing a message
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (0.2.2)
4
+ pub_sub_model_sync (0.2.3)
5
5
  activesupport
6
6
  rails
7
7
 
data/README.md CHANGED
@@ -97,9 +97,13 @@ class User < ActiveRecord::Base
97
97
  include PubSubModelSync::PublisherConcern
98
98
  ps_publish(%i[name:full_name email], actions: %i[update], as_klass: 'Client', id: :client_id)
99
99
 
100
- def ps_skip_for?(_action)
100
+ def ps_skip_callback?(_action)
101
101
  false # here logic with action to skip push message
102
102
  end
103
+
104
+ def ps_skip_sync?(_action)
105
+ false # here logic with action to skip push message
106
+ end
103
107
  end
104
108
 
105
109
  # App 2 (Subscriber)
@@ -115,18 +119,38 @@ class User < ActiveRecord::Base
115
119
  end
116
120
  ```
117
121
 
122
+ Note: Be careful with collision of names
123
+ ```
124
+ class User
125
+ # ps_publish %i[name_data:name name:key] # key will be replaced with name_data
126
+ ps_publish %i[name_data:name key_data:key] # use alias to avoid collision
127
+
128
+ def key_data
129
+ name
130
+ end
131
+ end
132
+ ```
133
+
118
134
  ## API
119
- - To perform a callback before publishing message (CRUD)
135
+ - Permit to cancel sync called after create/update/destroy (Before initializing sync service)
136
+ ```model.ps_skip_callback?(action)```
137
+ Note: Return true to cancel sync
138
+
139
+ - Callback called before preparing data for sync (Permit to stop sync)
140
+ ```model.ps_skip_sync?(action)```
141
+ Note: return true to cancel sync
142
+
143
+ - Callback called before sync (After preparing data)
120
144
  ```model.ps_before_sync(action, data_to_deliver)```
121
- Note: If the method returns ```false```, the message will not be published
145
+ Note: If the method returns ```:cancel```, the sync will be stopped (message will not be published)
122
146
 
123
- - To perform a callback after publishing message (CRUD)
124
- ```model.ps_after_sync(action, data_to_deliver)```
125
- Note: If the method returns ```false```, the message will not be published
147
+ - Callback called after sync
148
+ ```model.ps_after_sync(action, data_delivered)```
126
149
 
127
150
  - Perform sync on demand (:create, :update, :destroy):
128
151
  The target model will receive a notification to perform the indicated action
129
- ```my_model.ps_perform_sync(action_name)```
152
+ ```my_model.ps_perform_sync(action_name, custom_settings = {})```
153
+ * custom_settings: override default settings defined for action_name ({ attrs: [], as_klass: nil, id: nil })
130
154
 
131
155
  - Class level notification:
132
156
  ```User.ps_class_publish(data, action: action_name, as_klass: custom_klass_name)```
@@ -13,10 +13,13 @@ module PubSubModelSync
13
13
  end
14
14
 
15
15
  def process
16
+ @failed = false
16
17
  log 'processing message'
17
18
  listeners = filter_listeners
18
- eval_message(listeners) if listeners.any?
19
- log 'processed message'
19
+ return log 'Skipped: No listeners' unless listeners.any?
20
+
21
+ eval_message(listeners)
22
+ log 'processed message' unless @failed
20
23
  end
21
24
 
22
25
  private
@@ -36,6 +39,7 @@ module PubSubModelSync
36
39
  model_class.send(listener[:action], data)
37
40
  rescue => e
38
41
  log("Error listener (#{listener}): #{e.message}", :error)
42
+ @failed = true
39
43
  end
40
44
 
41
45
  # support for: create, update, destroy
@@ -49,13 +53,13 @@ module PubSubModelSync
49
53
  end
50
54
  rescue => e
51
55
  log("Error listener (#{listener}): #{e.message}", :error)
56
+ @failed = true
52
57
  end
53
58
 
54
59
  def find_model(listener)
55
60
  model_class = listener[:klass].constantize
56
61
  identifier = listener[:settings][:id] || :id
57
- model_class.where(identifier => attrs[:id]).first ||
58
- model_class.new(identifier => attrs[:id])
62
+ model_class.where(identifier => attrs[:id]).first_or_initialize
59
63
  end
60
64
 
61
65
  def populate_model(model, listener)
@@ -30,6 +30,10 @@ module PubSubModelSync
30
30
  def topic(*_args)
31
31
  @topic ||= MockTopic.new
32
32
  end
33
+
34
+ def close
35
+ true
36
+ end
33
37
  end
34
38
 
35
39
  def create_channel(*_args)
@@ -14,12 +14,14 @@ module PubSubModelSync
14
14
 
15
15
  # @param settings (Hash): { attrs: [], as_klass: nil, id: nil }
16
16
  def publish_model(model, action, settings = nil)
17
+ return if model.ps_skip_sync?(action)
18
+
17
19
  settings ||= model.class.ps_publisher_info(action)
18
20
  attributes = build_model_attrs(model, action, settings)
19
21
  data = {}
20
22
  data = build_model_data(model, settings[:attrs]) if action != :destroy
21
23
  res_before = model.ps_before_sync(action, data)
22
- return if res_before == false
24
+ return if res_before == :cancel
23
25
 
24
26
  connector.publish(data.symbolize_keys, attributes)
25
27
  model.ps_after_sync(action, data)
@@ -6,18 +6,28 @@ module PubSubModelSync
6
6
  base.extend(ClassMethods)
7
7
  end
8
8
 
9
- # Permit to skip a publish callback
10
- def ps_skip_for?(_action)
9
+ # Before initializing sync service (callbacks: after create/update/destroy)
10
+ def ps_skip_callback?(_action)
11
11
  false
12
12
  end
13
13
 
14
+ # before preparing data to sync
15
+ def ps_skip_sync?(_action)
16
+ false
17
+ end
18
+
19
+ # before delivering data
14
20
  def ps_before_sync(_action, _data); end
15
21
 
22
+ # after delivering data
16
23
  def ps_after_sync(_action, _data); end
17
24
 
18
- def ps_perform_sync(action = :create)
25
+ # To perform sync on demand
26
+ # @param custom_settings (Hash): { attrs: [], as_klass: nil, id: nil }
27
+ def ps_perform_sync(action = :create, custom_settings = {})
19
28
  service = self.class.ps_publisher_service
20
- service.publish_model(self, action, self.class.ps_publisher_info(action))
29
+ model_settings = self.class.ps_publisher_info(action) || {}
30
+ service.publish_model(self, action, model_settings.merge(custom_settings))
21
31
  end
22
32
 
23
33
  module ClassMethods
@@ -32,12 +42,14 @@ module PubSubModelSync
32
42
  end
33
43
  end
34
44
 
45
+ # Publisher info for specific action
35
46
  def ps_publisher_info(action = :create)
36
47
  PubSubModelSync::Config.publishers.select do |listener|
37
48
  listener[:klass] == name && listener[:action] == action
38
49
  end.last
39
50
  end
40
51
 
52
+ # On demand class level publisher
41
53
  def ps_class_publish(data, action:, as_klass: nil)
42
54
  as_klass = (as_klass || name).to_s
43
55
  ps_publisher_service.publish_data(as_klass, data, action.to_sym)
@@ -51,7 +63,7 @@ module PubSubModelSync
51
63
 
52
64
  def ps_register_callback(action, info)
53
65
  after_commit(on: action) do |model|
54
- unless model.ps_skip_for?(action)
66
+ unless model.ps_skip_callback?(action)
55
67
  service = model.class.ps_publisher_service
56
68
  service.publish_model(model, action.to_sym, info)
57
69
  end
@@ -28,9 +28,12 @@ module PubSubModelSync
28
28
 
29
29
  def publish(data, attributes)
30
30
  log("Publishing: #{[data, attributes]}")
31
- subscribe_to_queue
32
- payload = { data: data, attributes: attributes }
33
- topic.publish(payload.to_json, message_settings)
31
+ deliver_data(data, attributes)
32
+ # TODO: max retry
33
+ rescue Timeout::Error => e
34
+ log("Error publishing (retrying....): #{e.message}", :error)
35
+ initialize
36
+ retry
34
37
  rescue => e
35
38
  info = [data, attributes, e.message, e.backtrace]
36
39
  log("Error publishing: #{info}", :error)
@@ -72,5 +75,16 @@ module PubSubModelSync
72
75
  def log(msg, kind = :info)
73
76
  config.log("Rabbit Service ==> #{msg}", kind)
74
77
  end
78
+
79
+ def deliver_data(data, attributes)
80
+ subscribe_to_queue
81
+ payload = { data: data, attributes: attributes }
82
+ topic.publish(payload.to_json, message_settings)
83
+
84
+ # Ugly fix: "IO timeout when reading 7 bytes"
85
+ # https://stackoverflow.com/questions/39039129/rabbitmq-timeouterror-io-timeout-when-reading-7-bytes
86
+ channel.close
87
+ service.close
88
+ end
75
89
  end
76
90
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '0.2.2'
4
+ VERSION = '0.2.3'
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.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-28 00:00:00.000000000 Z
11
+ date: 2020-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport