pub_sub_model_sync 0.4.0 → 0.5.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +30 -8
  3. data/.rubocop.yml +6 -1
  4. data/CHANGELOG.md +27 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +25 -23
  7. data/README.md +55 -29
  8. data/gemfiles/Gemfile_4 +16 -0
  9. data/gemfiles/Gemfile_5 +14 -0
  10. data/gemfiles/Gemfile_6 +14 -0
  11. data/lib/pub_sub_model_sync.rb +1 -0
  12. data/lib/pub_sub_model_sync/base.rb +17 -0
  13. data/lib/pub_sub_model_sync/config.rb +18 -3
  14. data/lib/pub_sub_model_sync/connector.rb +1 -0
  15. data/lib/pub_sub_model_sync/message_processor.rb +27 -21
  16. data/lib/pub_sub_model_sync/message_publisher.rb +25 -9
  17. data/lib/pub_sub_model_sync/mock_rabbit_service.rb +5 -0
  18. data/lib/pub_sub_model_sync/payload.rb +45 -0
  19. data/lib/pub_sub_model_sync/publisher.rb +1 -0
  20. data/lib/pub_sub_model_sync/publisher_concern.rb +3 -1
  21. data/lib/pub_sub_model_sync/service_base.rb +25 -13
  22. data/lib/pub_sub_model_sync/service_google.rb +4 -18
  23. data/lib/pub_sub_model_sync/service_kafka.rb +4 -17
  24. data/lib/pub_sub_model_sync/service_rabbit.rb +20 -27
  25. data/lib/pub_sub_model_sync/subscriber.rb +3 -3
  26. data/lib/pub_sub_model_sync/subscriber_concern.rb +6 -0
  27. data/lib/pub_sub_model_sync/version.rb +1 -1
  28. data/pub_sub_model_sync.gemspec +1 -1
  29. metadata +11 -29
  30. data/.idea/.gitignore +0 -8
  31. data/.idea/.rakeTasks +0 -7
  32. data/.idea/codeStyles/codeStyleConfig.xml +0 -5
  33. data/.idea/encodings.xml +0 -4
  34. data/.idea/inspectionProfiles/Project_Default.xml +0 -16
  35. data/.idea/misc.xml +0 -7
  36. data/.idea/modules.xml +0 -8
  37. data/.idea/pub_sub_model_sync.iml +0 -96
  38. data/.idea/vcs.xml +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 733ca5e3413a098031be8a2ca5d0c3df9bf972ed8cdbf8954a1968c517ac81f4
4
- data.tar.gz: a4c419efb5446e8a8632942936589b1b0a4bad2ab913a9ddd34cffb2bfdf8f2a
3
+ metadata.gz: f08249ef28730afdc2a0f3b025ef66d5117694fdc4ed995e9eb907e37e60defb
4
+ data.tar.gz: cc8d337de6952db6e9747912178ce7fd09f93c3a600d1d4756671fb6bcefadad
5
5
  SHA512:
6
- metadata.gz: 3054010e2bad46d8d2377a01757b0f52abc31f27832ddc150b12f3fb5d05f50a9aa87aa1bcafff7237918ecdc3d4cc637033dec76d32d7991d69c36d5e949ea8
7
- data.tar.gz: 734be5a88e56fa6f508f1c37acdada68cf3b53db5b2d5b3d612616c27a63f4d46c305d0d2148d656c970d0a6cc9e25ec6840ead4c4a74b1c4f39b777a791e2cc
6
+ metadata.gz: cdca1e7397fa5fae46198fce8ae24036318d619d53cff5c06e8e8c266423a5681e48588eddcced3271747242a35fc77b1c75bf567d5461c4fa7f573ae29c4c15
7
+ data.tar.gz: 0c48d83f3904c5872cdc3b48f2d341be84cbe84899a3fdc0738c9ad290e5e6c1c49ca2ab226e12df4f097d6f927e603b4691f64760a8d4dd9c703431711e71ec
@@ -6,28 +6,50 @@ on:
6
6
  - master
7
7
  pull_request:
8
8
 
9
-
10
9
  jobs:
11
10
  build:
12
11
  name: Tests and Code Style
13
12
  runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ ruby: [2.4, 2.5, 2.6]
16
+ rails: [4, 5, 6]
17
+ include:
18
+ - ruby: 2.7
19
+ rails: 6
20
+ exclude: # rails 6 requires ruby >= 2.5
21
+ - ruby: 2.4
22
+ rails: 6
14
23
 
15
24
  steps:
16
25
  - uses: actions/checkout@v2
17
- - name: Set up Ruby 2.6
26
+ - name: Set up Ruby
18
27
  uses: actions/setup-ruby@v1
19
28
  with:
20
- ruby-version: 2.6.x
21
-
29
+ ruby-version: ${{ matrix.ruby }}
22
30
  - name: Install sqlite3
23
31
  run: sudo apt-get install libsqlite3-dev
24
32
 
25
- - name: Bundle install
33
+ - name: Install bundler
34
+ env:
35
+ GEMFILE_PATH: gemfiles/Gemfile_${{ matrix.rails }}
36
+ RAILS_V: ${{ matrix.rails }}
26
37
  run: |
27
- gem install bundler
28
- bundle install --jobs 4 --retry 3
38
+ rm -f Gemfile.lock && rm -f Gemfile
39
+ cp $GEMFILE_PATH ./Gemfile
40
+ bundler_v='2.1.4'
41
+ if [ $RAILS_V = "4" ]; then bundler_v="1.16.6"; fi
42
+ gem install bundler -v "~> $bundler_v"
43
+ bundle _${bundler_v}_ install --jobs 4 --retry 3
44
+
45
+ # remote ssh debugger
46
+ # - name: Setup tmate session (remote session debugger)
47
+ # uses: mxschmitt/action-tmate@v3
48
+
29
49
  - name: Tests (rspec)
30
50
  run: |
31
51
  bundle exec rspec
52
+
32
53
  - name: Code style (Rubocop)
33
- run: bundle exec rubocop
54
+ run: bundle exec rubocop
55
+ if: matrix.ruby == '2.6' && matrix.rails == '6'
@@ -1,22 +1,27 @@
1
1
  # This is the configuration used to check the rubocop source code.
2
2
 
3
3
  AllCops:
4
+ TargetRubyVersion: 2.6
4
5
  Exclude:
5
6
  - 'spec/spec_helper.rb'
6
7
  - 'Gemfile'
7
8
  - 'Rakefile'
8
9
  - 'bin/*'
9
- TargetRubyVersion: 2.3
10
10
 
11
11
  Metrics/BlockLength:
12
12
  Exclude:
13
13
  - 'spec/**/*.rb'
14
14
 
15
+ Layout/LineLength:
16
+ Max: 120
15
17
 
16
18
  Style/SymbolArray:
17
19
  Exclude:
18
20
  - 'Gemfile'
19
21
 
22
+ Lint/MissingSuper:
23
+ Enabled: false
24
+
20
25
  Style/Documentation:
21
26
  Enabled: false
22
27
 
@@ -1,5 +1,32 @@
1
1
  # Change Log
2
2
 
3
+ # 0.5.0.1 (December 22, 2020)
4
+ - fix: add missing rabbit mock method
5
+
6
+ # 0.5.0 (December 22, 2020)
7
+ - feat: add :publish! and :process! methods to payloads
8
+ - feat: add ability to disable publisher globally
9
+ - fix: skip notifications from the same application
10
+ - fix: rabbitmq use fanout instead of queue to deliver messages to multiple apps
11
+ - refactor: include payload object to carry message info
12
+ - feat: include notification events (when publishing and when processing messages)
13
+
14
+ # 0.4.2.2 (November 29, 2020, deleted cause of typo)
15
+ - feat: rabbitMQ skip receiving messages from the same app
16
+ - feat: rabbitmq use fanout instead of queue to deliver messages to multiple apps
17
+
18
+ # 0.4.2.1 (August 20, 2020)
19
+ - Improve ```ps_subscriber_changed?``` to run validations and check for changes
20
+
21
+ # 0.4.2 (May 12, 2020)
22
+ - chore: remove typo
23
+
24
+ # 0.4.1 (May 12, 2020)
25
+ - chore: improve log messages
26
+ - feat: do not update model if no changes
27
+ - feat: skip publisher after updating if no changes
28
+
29
+
3
30
  # 0.4.0 (May 06, 2020)
4
31
  - rename as_klass to from_klass and as_action to from_action for subscribers
5
32
  - refactor subscribers to be independent
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'rubocop'
3
+ gem 'rubocop', '~> 1.6.0', require: false
4
4
  gem 'bunny' # rabbit-mq
5
5
  gem 'google-cloud-pubsub' # google pub/sub
6
6
  gem 'ruby-kafka' # kafka pub/sub
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (0.4.0)
5
- activesupport
4
+ pub_sub_model_sync (0.5.0.1)
6
5
  rails
7
6
 
8
7
  GEM
@@ -66,7 +65,7 @@ GEM
66
65
  addressable (2.7.0)
67
66
  public_suffix (>= 2.0.2, < 5.0)
68
67
  amq-protocol (2.3.0)
69
- ast (2.4.0)
68
+ ast (2.4.1)
70
69
  builder (3.2.4)
71
70
  bunny (2.14.3)
72
71
  amq-protocol (~> 2.3, >= 2.3.0)
@@ -78,7 +77,7 @@ GEM
78
77
  database_cleaner (~> 1.8.0)
79
78
  diff-lcs (1.3)
80
79
  digest-crc (0.5.1)
81
- erubi (1.9.0)
80
+ erubi (1.10.0)
82
81
  faraday (0.17.3)
83
82
  multipart-post (>= 1.2, < 3)
84
83
  globalid (0.4.2)
@@ -121,9 +120,8 @@ GEM
121
120
  grpc (~> 1.0)
122
121
  i18n (1.8.2)
123
122
  concurrent-ruby (~> 1.0)
124
- jaro_winkler (1.5.4)
125
123
  jwt (2.2.1)
126
- loofah (2.5.0)
124
+ loofah (2.8.0)
127
125
  crass (~> 1.0.2)
128
126
  nokogiri (>= 1.5.9)
129
127
  mail (2.7.1)
@@ -132,21 +130,21 @@ GEM
132
130
  mimemagic (~> 0.3.2)
133
131
  memoist (0.16.2)
134
132
  method_source (1.0.0)
135
- mimemagic (0.3.4)
133
+ mimemagic (0.3.5)
136
134
  mini_mime (1.0.2)
137
135
  mini_portile2 (2.4.0)
138
136
  minitest (5.14.0)
139
137
  multi_json (1.14.1)
140
138
  multipart-post (2.1.1)
141
- nio4r (2.5.2)
142
- nokogiri (1.10.9)
139
+ nio4r (2.5.4)
140
+ nokogiri (1.10.10)
143
141
  mini_portile2 (~> 2.4.0)
144
142
  os (1.0.1)
145
- parallel (1.19.1)
146
- parser (2.7.0.4)
147
- ast (~> 2.4.0)
143
+ parallel (1.20.1)
144
+ parser (2.7.2.0)
145
+ ast (~> 2.4.1)
148
146
  public_suffix (4.0.3)
149
- rack (2.2.2)
147
+ rack (2.2.3)
150
148
  rack-test (1.1.0)
151
149
  rack (>= 1.0, < 3)
152
150
  rails (6.0.2.2)
@@ -177,6 +175,7 @@ GEM
177
175
  thor (>= 0.20.3, < 2.0)
178
176
  rainbow (3.0.0)
179
177
  rake (13.0.1)
178
+ regexp_parser (2.0.1)
180
179
  rexml (3.2.4)
181
180
  rly (0.2.3)
182
181
  rspec (3.9.0)
@@ -192,14 +191,17 @@ GEM
192
191
  diff-lcs (>= 1.2.0, < 2.0)
193
192
  rspec-support (~> 3.9.0)
194
193
  rspec-support (3.9.2)
195
- rubocop (0.80.1)
196
- jaro_winkler (~> 1.5.1)
194
+ rubocop (1.6.1)
197
195
  parallel (~> 1.10)
198
- parser (>= 2.7.0.1)
196
+ parser (>= 2.7.1.5)
199
197
  rainbow (>= 2.2.2, < 4.0)
198
+ regexp_parser (>= 1.8, < 3.0)
200
199
  rexml
200
+ rubocop-ast (>= 1.2.0, < 2.0)
201
201
  ruby-progressbar (~> 1.7)
202
- unicode-display_width (>= 1.4.0, < 1.7)
202
+ unicode-display_width (>= 1.4.0, < 2.0)
203
+ rubocop-ast (1.3.0)
204
+ parser (>= 2.7.1.5)
203
205
  ruby-kafka (1.0.0)
204
206
  digest-crc
205
207
  ruby-progressbar (1.10.1)
@@ -208,10 +210,10 @@ GEM
208
210
  faraday (~> 0.9)
209
211
  jwt (>= 1.5, < 3.0)
210
212
  multi_json (~> 1.10)
211
- sprockets (4.0.0)
213
+ sprockets (4.0.2)
212
214
  concurrent-ruby (~> 1.0)
213
215
  rack (> 1, < 3)
214
- sprockets-rails (3.2.1)
216
+ sprockets-rails (3.2.2)
215
217
  actionpack (>= 4.0)
216
218
  activesupport (>= 4.0)
217
219
  sprockets (>= 3.0.0)
@@ -220,10 +222,10 @@ GEM
220
222
  thread_safe (0.3.6)
221
223
  tzinfo (1.2.7)
222
224
  thread_safe (~> 0.1)
223
- unicode-display_width (1.6.1)
224
- websocket-driver (0.7.1)
225
+ unicode-display_width (1.7.0)
226
+ websocket-driver (0.7.3)
225
227
  websocket-extensions (>= 0.1.0)
226
- websocket-extensions (0.1.4)
228
+ websocket-extensions (0.1.5)
227
229
  zeitwerk (2.3.0)
228
230
 
229
231
  PLATFORMS
@@ -237,7 +239,7 @@ DEPENDENCIES
237
239
  pub_sub_model_sync!
238
240
  rake
239
241
  rspec
240
- rubocop
242
+ rubocop (~> 1.6.0)
241
243
  ruby-kafka
242
244
  sqlite3
243
245
 
data/README.md CHANGED
@@ -61,6 +61,9 @@ And then execute: $ bundle install
61
61
  ```
62
62
  Note: Publishers do not need todo this
63
63
 
64
+ - Check the service status with:
65
+ ```PubSubModelSync::MessagePublisher.publish_data('Test message', {sample_value: 10}, :create)```
66
+
64
67
  ## Examples
65
68
  ```ruby
66
69
  # App 1 (Publisher)
@@ -118,7 +121,7 @@ class User < ActiveRecord::Base
118
121
  puts 'Class message called through custom_greeting'
119
122
  end
120
123
 
121
- # def self.ps_find_model(data, settings)
124
+ # def self.ps_find_model(data)
122
125
  # where(email: data[:email], ...).first_or_initialize
123
126
  # end
124
127
  end
@@ -126,24 +129,18 @@ end
126
129
 
127
130
  Note: Be careful with collision of names
128
131
  ```
129
- class User
130
- # ps_publish %i[name_data:name name:key] # key will be replaced with name_data
131
- ps_publish %i[name_data:name key_data:key] # use alias to avoid collision
132
-
133
- def key_data
134
- name
135
- end
136
- end
132
+ # ps_publish %i[name_data:name name:key] # key will be replaced with name_data
133
+ ps_publish %i[name_data:name key_data:key] # use alias to avoid collision
137
134
  ```
138
135
 
139
136
  ## API
140
137
  ### Subscribers
141
- - Permit to configure class level listeners
138
+ - Permit to configure class level subscriptions
142
139
  ```ps_class_subscribe(action_name, from_action: nil, from_klass: nil)```
143
140
  * from_action: (Optional) Source method name
144
141
  * from_klass: (Optional) Source class name
145
142
 
146
- - Permit to configure instance level listeners (CRUD)
143
+ - Permit to configure instance level subscriptions (CRUD)
147
144
  ```ps_subscribe(attrs, from_klass: nil, actions: nil, id: nil)```
148
145
  * attrs: (Array/Required) Array of all attributes to be synced
149
146
  * from_klass: (String/Optional) Source class name (Instead of the model class name, will use this value)
@@ -151,17 +148,20 @@ end
151
148
  * id: (Sym|Array/Optional, default: id) Attr identifier(s) to find the corresponding model
152
149
 
153
150
  - Permit to configure a custom model finder
154
- ```ps_find_model(data, settings)```
151
+ ```ps_find_model(data)```
155
152
  * data: (Hash) Data received from sync
156
- * settings: (Hash(:klass, :action)) Class and action name from sync
157
153
  Must return an existent or a new model object
158
154
 
159
155
  - Get crud subscription configured for the class
160
156
  ```User.ps_subscriber(action_name)```
161
157
  * action_name (default :create, :sym): can be :create, :update, :destroy
162
158
 
163
- - Inspect all configured listeners
164
- ```PubSubModelSync::Config.listeners```
159
+ - Inspect all configured subscribers
160
+ ```PubSubModelSync::Config.subscribers```
161
+
162
+ - Permit to customize the way to detect if the subscribed model was changed (Only for update action).
163
+ ```.ps_subscriber_changed?(data)```
164
+ By default: ```model.changed?```
165
165
 
166
166
  ### Publishers
167
167
  - Permit to configure crud publishers
@@ -172,6 +172,7 @@ end
172
172
 
173
173
  - Permit to cancel sync called after create/update/destroy (Before initializing sync service)
174
174
  ```model.ps_skip_callback?(action)```
175
+ Default: False
175
176
  Note: Return true to cancel sync
176
177
 
177
178
  - Callback called before preparing data for sync (Permit to stop sync)
@@ -197,11 +198,11 @@ end
197
198
  * action_name: (required, :sim) Action name
198
199
  * as_klass: (optional, :string) Custom class name (Default current model name)
199
200
 
200
- - Publish a class level notification (Same as above: on demand call)
201
- ```PubSubModelSync::MessagePublisher.publish_data(Klass_name, data, action_name)```
202
- * klass_name: (required, Class) same class name as defined in ps_class_subscribe(...)
203
- * data: (required, :hash) message value to deliver
204
- * action_name: (required, :sim) same action name as defined in ps_class_subscribe(...)
201
+ - Publish a class level notification (Same as above: manual call)
202
+ ```ruby
203
+ payload = PubSubModelSync::Payload.new({ title: 'hello' }, { action: :greeting, klass: 'User' })
204
+ payload.publish!
205
+ ```
205
206
 
206
207
  - Get crud publisher configured for the class
207
208
  ```User.ps_publisher(action_name)```
@@ -237,27 +238,23 @@ end
237
238
  ```ruby
238
239
  # Subscriber
239
240
  it 'receive model message' do
240
- action = :create
241
241
  data = { name: 'name', id: 999 }
242
- publisher = PubSubModelSync::MessageProcessor.new(data, 'User', action)
243
- publisher.process
242
+ payload = PubSubModelSync::Payload.new(data, { klass: 'User', action: :create })
243
+ payload.process!
244
244
  expect(User.where(id: data[:id]).any?).to be_truth
245
245
  end
246
246
 
247
247
  it 'receive class message' do
248
- action = :greeting
249
248
  data = { msg: 'hello' }
250
- publisher = PubSubModelSync::MessageProcessor.new(data, 'User', action)
251
- publisher.process
249
+ action = :greeting
250
+ payload = PubSubModelSync::Payload.new(data, { klass: 'User', action: action })
251
+ payload.process!
252
252
  expect(User).to receive(action)
253
253
  end
254
254
 
255
255
  # Publisher
256
256
  it 'publish model action' do
257
257
  publisher = PubSubModelSync::MessagePublisher
258
- data = { name: 'hello'}
259
- action = :create
260
- User.ps_class_publish(data, action: action)
261
258
  user = User.create(name: 'name', email: 'email')
262
259
  expect(publisher).to receive(:publish_model).with(user, :create, anything)
263
260
  end
@@ -271,6 +268,35 @@ end
271
268
  end
272
269
  ```
273
270
 
271
+ ## Extra configurations
272
+ ```ruby
273
+ config = PubSubModelSync::Config
274
+ config.debug = true
275
+ ```
276
+
277
+ - ```debug = true```
278
+ (true/false*) => show advanced log messages
279
+ - ```logger = Rails.logger```
280
+ (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 }```
284
+ (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 }```
290
+ (Proc) => called after publishing a message
291
+ - ```on_publish_error = ->(exception, payload) { sleep 1; payload.publish! }```
292
+ (Proc) => called when failed publishing a message
293
+
294
+ ## TODO
295
+ - Add alias attributes when subscribing (similar to publisher)
296
+ - Add flag ```model.ps_processing``` to indicate that the current transaction is being processed by pub/sub
297
+ - Auto publish update only if payload has changed
298
+ - On delete, payload must only be composed by ids
299
+
274
300
  ## Contributing
275
301
 
276
302
  Bug reports and pull requests are welcome on GitHub at https://github.com/owen2345/pub_sub_model_sync. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -0,0 +1,16 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'rubocop'
4
+ gem 'bunny' # rabbit-mq
5
+ gem 'google-cloud-pubsub' # google pub/sub
6
+ gem 'ruby-kafka' # kafka pub/sub
7
+ gem 'rails', '~> 4'
8
+ gem 'bundler'
9
+ gem 'sqlite3', '1.3.13'
10
+
11
+ group :test do
12
+ gem 'database_cleaner-active_record'
13
+ end
14
+
15
+ # Specify your gem's dependencies in pub_sub_model_sync.gemspec
16
+ gemspec