pub_sub_model_sync 0.4.1 → 0.5.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/ruby.yml +6 -1
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +26 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +25 -22
- data/README.md +59 -16
- data/lib/pub_sub_model_sync.rb +1 -0
- data/lib/pub_sub_model_sync/base.rb +17 -0
- data/lib/pub_sub_model_sync/config.rb +19 -3
- data/lib/pub_sub_model_sync/connector.rb +1 -0
- data/lib/pub_sub_model_sync/message_processor.rb +35 -21
- data/lib/pub_sub_model_sync/message_publisher.rb +29 -9
- data/lib/pub_sub_model_sync/mock_rabbit_service.rb +5 -0
- data/lib/pub_sub_model_sync/payload.rb +45 -0
- data/lib/pub_sub_model_sync/publisher.rb +1 -0
- data/lib/pub_sub_model_sync/publisher_concern.rb +3 -4
- data/lib/pub_sub_model_sync/service_base.rb +25 -13
- data/lib/pub_sub_model_sync/service_google.rb +4 -18
- data/lib/pub_sub_model_sync/service_kafka.rb +4 -17
- data/lib/pub_sub_model_sync/service_rabbit.rb +20 -27
- data/lib/pub_sub_model_sync/subscriber.rb +20 -19
- data/lib/pub_sub_model_sync/subscriber_concern.rb +10 -0
- data/lib/pub_sub_model_sync/version.rb +1 -1
- data/pub_sub_model_sync.gemspec +1 -0
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83c10bee147f91d55fa2fd1a8fc81f5864c2bb6af7476caa0de4044f9e6ad3e9
|
4
|
+
data.tar.gz: 53e28b6855a0139df908a79e532f7138cba78e153d6e51e4a8f739063df290e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c269ccd1f3587605ff43342c3948cd03b171f0fc6b124815b98c5e4e2f1ab30d574f90f2c07087185042d20c2c5c1b3f7052b11b1679c002c6e24f7065125893
|
7
|
+
data.tar.gz: b1702e276e58ab5d796856c5a14c975fe70959f2603a27afaad2110811c1053ebc88c0b7fd7f7466910f63e578efdab26fd943d725aa3b21412f3372e4b35ab5
|
data/.github/workflows/ruby.yml
CHANGED
@@ -42,9 +42,14 @@ jobs:
|
|
42
42
|
gem install bundler -v "~> $bundler_v"
|
43
43
|
bundle _${bundler_v}_ install --jobs 4 --retry 3
|
44
44
|
|
45
|
+
# remote ssh debugger
|
46
|
+
# - name: Setup tmate session (remote session debugger)
|
47
|
+
# uses: mxschmitt/action-tmate@v3
|
48
|
+
|
45
49
|
- name: Tests (rspec)
|
46
50
|
run: |
|
47
51
|
bundle exec rspec
|
48
52
|
|
49
53
|
- name: Code style (Rubocop)
|
50
|
-
run: bundle exec rubocop
|
54
|
+
run: bundle exec rubocop
|
55
|
+
if: matrix.ruby == '2.6' && matrix.rails == '6'
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,7 @@
|
|
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'
|
@@ -11,10 +12,16 @@ Metrics/BlockLength:
|
|
11
12
|
Exclude:
|
12
13
|
- 'spec/**/*.rb'
|
13
14
|
|
15
|
+
Layout/LineLength:
|
16
|
+
Max: 120
|
17
|
+
|
14
18
|
Style/SymbolArray:
|
15
19
|
Exclude:
|
16
20
|
- 'Gemfile'
|
17
21
|
|
22
|
+
Lint/MissingSuper:
|
23
|
+
Enabled: false
|
24
|
+
|
18
25
|
Style/Documentation:
|
19
26
|
Enabled: false
|
20
27
|
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,31 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
# 0.
|
3
|
+
# 0.5.1 (December 24, 2020)
|
4
|
+
- feat: rename publisher callbacks to be more understandable
|
5
|
+
- feat: add callbacks to listen when processing a message (before saving sync)
|
6
|
+
|
7
|
+
# 0.5.0.1 (December 22, 2020)
|
8
|
+
- fix: add missing rabbit mock method
|
9
|
+
|
10
|
+
# 0.5.0 (December 22, 2020)
|
11
|
+
- feat: add :publish! and :process! methods to payloads
|
12
|
+
- feat: add ability to disable publisher globally
|
13
|
+
- fix: skip notifications from the same application
|
14
|
+
- fix: rabbitmq use fanout instead of queue to deliver messages to multiple apps
|
15
|
+
- refactor: include payload object to carry message info
|
16
|
+
- feat: include notification events (when publishing and when processing messages)
|
17
|
+
|
18
|
+
# 0.4.2.2 (November 29, 2020, deleted cause of typo)
|
19
|
+
- feat: rabbitMQ skip receiving messages from the same app
|
20
|
+
- feat: rabbitmq use fanout instead of queue to deliver messages to multiple apps
|
21
|
+
|
22
|
+
# 0.4.2.1 (August 20, 2020)
|
23
|
+
- Improve ```ps_subscriber_changed?``` to run validations and check for changes
|
24
|
+
|
25
|
+
# 0.4.2 (May 12, 2020)
|
26
|
+
- chore: remove typo
|
27
|
+
|
28
|
+
# 0.4.1 (May 12, 2020)
|
4
29
|
- chore: improve log messages
|
5
30
|
- feat: do not update model if no changes
|
6
31
|
- feat: skip publisher after updating if no changes
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pub_sub_model_sync (0.
|
4
|
+
pub_sub_model_sync (0.5.1)
|
5
5
|
rails
|
6
6
|
|
7
7
|
GEM
|
@@ -65,7 +65,7 @@ GEM
|
|
65
65
|
addressable (2.7.0)
|
66
66
|
public_suffix (>= 2.0.2, < 5.0)
|
67
67
|
amq-protocol (2.3.0)
|
68
|
-
ast (2.4.
|
68
|
+
ast (2.4.1)
|
69
69
|
builder (3.2.4)
|
70
70
|
bunny (2.14.3)
|
71
71
|
amq-protocol (~> 2.3, >= 2.3.0)
|
@@ -77,7 +77,7 @@ GEM
|
|
77
77
|
database_cleaner (~> 1.8.0)
|
78
78
|
diff-lcs (1.3)
|
79
79
|
digest-crc (0.5.1)
|
80
|
-
erubi (1.
|
80
|
+
erubi (1.10.0)
|
81
81
|
faraday (0.17.3)
|
82
82
|
multipart-post (>= 1.2, < 3)
|
83
83
|
globalid (0.4.2)
|
@@ -120,9 +120,8 @@ GEM
|
|
120
120
|
grpc (~> 1.0)
|
121
121
|
i18n (1.8.2)
|
122
122
|
concurrent-ruby (~> 1.0)
|
123
|
-
jaro_winkler (1.5.4)
|
124
123
|
jwt (2.2.1)
|
125
|
-
loofah (2.
|
124
|
+
loofah (2.8.0)
|
126
125
|
crass (~> 1.0.2)
|
127
126
|
nokogiri (>= 1.5.9)
|
128
127
|
mail (2.7.1)
|
@@ -131,21 +130,21 @@ GEM
|
|
131
130
|
mimemagic (~> 0.3.2)
|
132
131
|
memoist (0.16.2)
|
133
132
|
method_source (1.0.0)
|
134
|
-
mimemagic (0.3.
|
133
|
+
mimemagic (0.3.5)
|
135
134
|
mini_mime (1.0.2)
|
136
135
|
mini_portile2 (2.4.0)
|
137
136
|
minitest (5.14.0)
|
138
137
|
multi_json (1.14.1)
|
139
138
|
multipart-post (2.1.1)
|
140
|
-
nio4r (2.5.
|
141
|
-
nokogiri (1.10.
|
139
|
+
nio4r (2.5.4)
|
140
|
+
nokogiri (1.10.10)
|
142
141
|
mini_portile2 (~> 2.4.0)
|
143
142
|
os (1.0.1)
|
144
|
-
parallel (1.
|
145
|
-
parser (2.7.0
|
146
|
-
ast (~> 2.4.
|
143
|
+
parallel (1.20.1)
|
144
|
+
parser (2.7.2.0)
|
145
|
+
ast (~> 2.4.1)
|
147
146
|
public_suffix (4.0.3)
|
148
|
-
rack (2.2.
|
147
|
+
rack (2.2.3)
|
149
148
|
rack-test (1.1.0)
|
150
149
|
rack (>= 1.0, < 3)
|
151
150
|
rails (6.0.2.2)
|
@@ -176,6 +175,7 @@ GEM
|
|
176
175
|
thor (>= 0.20.3, < 2.0)
|
177
176
|
rainbow (3.0.0)
|
178
177
|
rake (13.0.1)
|
178
|
+
regexp_parser (2.0.1)
|
179
179
|
rexml (3.2.4)
|
180
180
|
rly (0.2.3)
|
181
181
|
rspec (3.9.0)
|
@@ -191,14 +191,17 @@ GEM
|
|
191
191
|
diff-lcs (>= 1.2.0, < 2.0)
|
192
192
|
rspec-support (~> 3.9.0)
|
193
193
|
rspec-support (3.9.2)
|
194
|
-
rubocop (
|
195
|
-
jaro_winkler (~> 1.5.1)
|
194
|
+
rubocop (1.6.1)
|
196
195
|
parallel (~> 1.10)
|
197
|
-
parser (>= 2.7.
|
196
|
+
parser (>= 2.7.1.5)
|
198
197
|
rainbow (>= 2.2.2, < 4.0)
|
198
|
+
regexp_parser (>= 1.8, < 3.0)
|
199
199
|
rexml
|
200
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
200
201
|
ruby-progressbar (~> 1.7)
|
201
|
-
unicode-display_width (>= 1.4.0, <
|
202
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
203
|
+
rubocop-ast (1.3.0)
|
204
|
+
parser (>= 2.7.1.5)
|
202
205
|
ruby-kafka (1.0.0)
|
203
206
|
digest-crc
|
204
207
|
ruby-progressbar (1.10.1)
|
@@ -207,10 +210,10 @@ GEM
|
|
207
210
|
faraday (~> 0.9)
|
208
211
|
jwt (>= 1.5, < 3.0)
|
209
212
|
multi_json (~> 1.10)
|
210
|
-
sprockets (4.0.
|
213
|
+
sprockets (4.0.2)
|
211
214
|
concurrent-ruby (~> 1.0)
|
212
215
|
rack (> 1, < 3)
|
213
|
-
sprockets-rails (3.2.
|
216
|
+
sprockets-rails (3.2.2)
|
214
217
|
actionpack (>= 4.0)
|
215
218
|
activesupport (>= 4.0)
|
216
219
|
sprockets (>= 3.0.0)
|
@@ -219,10 +222,10 @@ GEM
|
|
219
222
|
thread_safe (0.3.6)
|
220
223
|
tzinfo (1.2.7)
|
221
224
|
thread_safe (~> 0.1)
|
222
|
-
unicode-display_width (1.
|
223
|
-
websocket-driver (0.7.
|
225
|
+
unicode-display_width (1.7.0)
|
226
|
+
websocket-driver (0.7.3)
|
224
227
|
websocket-extensions (>= 0.1.0)
|
225
|
-
websocket-extensions (0.1.
|
228
|
+
websocket-extensions (0.1.5)
|
226
229
|
zeitwerk (2.3.0)
|
227
230
|
|
228
231
|
PLATFORMS
|
@@ -236,7 +239,7 @@ DEPENDENCIES
|
|
236
239
|
pub_sub_model_sync!
|
237
240
|
rake
|
238
241
|
rspec
|
239
|
-
rubocop
|
242
|
+
rubocop (~> 1.6.0)
|
240
243
|
ruby-kafka
|
241
244
|
sqlite3
|
242
245
|
|
data/README.md
CHANGED
@@ -59,7 +59,16 @@ And then execute: $ bundle install
|
|
59
59
|
```ruby
|
60
60
|
rake pub_sub_model_sync:start
|
61
61
|
```
|
62
|
-
Note: Publishers do not need todo this
|
62
|
+
Note: Publishers do not need todo this
|
63
|
+
Note2 (Rails 6+): Due to Zeitwerk, you need to load listeners manually when syncing outside ```rake pub_sub_model_sync:start```
|
64
|
+
```ruby
|
65
|
+
# PubSubModelSync::Config.subscribers ==> []
|
66
|
+
Rails.application.try(:eager_load!)
|
67
|
+
# PubSubModelSync::Config.subscribers ==> [#<PubSubModelSync::Subscriber:0x000.. @klass="Article", @action=:create..., ....]
|
68
|
+
```
|
69
|
+
|
70
|
+
- Check the service status with:
|
71
|
+
```PubSubModelSync::MessagePublisher.publish_data('Test message', {sample_value: 10}, :create)```
|
63
72
|
|
64
73
|
## Examples
|
65
74
|
```ruby
|
@@ -153,9 +162,16 @@ Note: Be careful with collision of names
|
|
153
162
|
```User.ps_subscriber(action_name)```
|
154
163
|
* action_name (default :create, :sym): can be :create, :update, :destroy
|
155
164
|
|
156
|
-
- Inspect all configured subscribers
|
165
|
+
- Inspect all configured subscribers
|
157
166
|
```PubSubModelSync::Config.subscribers```
|
158
167
|
|
168
|
+
- Permit to customize the way to detect if the subscribed model was changed (Only for update action).
|
169
|
+
```.ps_subscriber_changed?(data)```
|
170
|
+
By default: ```model.changed?```
|
171
|
+
|
172
|
+
- Permit to perform custom actions before saving sync of the model (:cancel can be returned to skip sync)
|
173
|
+
```.ps_before_save_sync(payload)```
|
174
|
+
|
159
175
|
### Publishers
|
160
176
|
- Permit to configure crud publishers
|
161
177
|
```ps_publish(attrs, actions: nil, as_klass: nil)```
|
@@ -191,11 +207,11 @@ Note: Be careful with collision of names
|
|
191
207
|
* action_name: (required, :sim) Action name
|
192
208
|
* as_klass: (optional, :string) Custom class name (Default current model name)
|
193
209
|
|
194
|
-
- Publish a class level notification (Same as above:
|
195
|
-
```
|
196
|
-
|
197
|
-
|
198
|
-
|
210
|
+
- Publish a class level notification (Same as above: manual call)
|
211
|
+
```ruby
|
212
|
+
payload = PubSubModelSync::Payload.new({ title: 'hello' }, { action: :greeting, klass: 'User' })
|
213
|
+
payload.publish!
|
214
|
+
```
|
199
215
|
|
200
216
|
- Get crud publisher configured for the class
|
201
217
|
```User.ps_publisher(action_name)```
|
@@ -231,27 +247,23 @@ Note: Be careful with collision of names
|
|
231
247
|
```ruby
|
232
248
|
# Subscriber
|
233
249
|
it 'receive model message' do
|
234
|
-
action = :create
|
235
250
|
data = { name: 'name', id: 999 }
|
236
|
-
|
237
|
-
|
251
|
+
payload = PubSubModelSync::Payload.new(data, { klass: 'User', action: :create })
|
252
|
+
payload.process!
|
238
253
|
expect(User.where(id: data[:id]).any?).to be_truth
|
239
254
|
end
|
240
255
|
|
241
256
|
it 'receive class message' do
|
242
|
-
action = :greeting
|
243
257
|
data = { msg: 'hello' }
|
244
|
-
|
245
|
-
|
258
|
+
action = :greeting
|
259
|
+
payload = PubSubModelSync::Payload.new(data, { klass: 'User', action: action })
|
260
|
+
payload.process!
|
246
261
|
expect(User).to receive(action)
|
247
262
|
end
|
248
263
|
|
249
264
|
# Publisher
|
250
265
|
it 'publish model action' do
|
251
266
|
publisher = PubSubModelSync::MessagePublisher
|
252
|
-
data = { name: 'hello'}
|
253
|
-
action = :create
|
254
|
-
User.ps_class_publish(data, action: action)
|
255
267
|
user = User.create(name: 'name', email: 'email')
|
256
268
|
expect(publisher).to receive(:publish_model).with(user, :create, anything)
|
257
269
|
end
|
@@ -265,6 +277,37 @@ Note: Be careful with collision of names
|
|
265
277
|
end
|
266
278
|
```
|
267
279
|
|
280
|
+
## Extra configurations
|
281
|
+
```ruby
|
282
|
+
config = PubSubModelSync::Config
|
283
|
+
config.debug = true
|
284
|
+
```
|
285
|
+
|
286
|
+
- ```debug = true```
|
287
|
+
(true/false*) => show advanced log messages
|
288
|
+
- ```logger = Rails.logger```
|
289
|
+
(Logger) => define custom logger
|
290
|
+
- ```disabled_callback_publisher = ->(_model, _action) { false }```
|
291
|
+
(true/false*) => if true, does not listen model callbacks for auto sync (Create/Update/Destroy)
|
292
|
+
- ```on_before_processing = ->(payload, subscriber) { puts payload }```
|
293
|
+
(Proc) => called before processing received message (:cancel can be returned to skip processing)
|
294
|
+
- ```on_success_processing = ->(payload, subscriber) { puts payload }```
|
295
|
+
(Proc) => called when a message was successfully processed
|
296
|
+
- ```on_error_processing = ->(exception, payload) { sleep 1; payload.process! }```
|
297
|
+
(Proc) => called when a message failed when processing
|
298
|
+
- ```on_before_publish = ->(payload) { puts payload }```
|
299
|
+
(Proc) => called before publishing a message (:cancel can be returned to skip publishing)
|
300
|
+
- ```on_after_publish = ->(payload) { puts payload }```
|
301
|
+
(Proc) => called after publishing a message
|
302
|
+
- ```on_error_publish = ->(exception, payload) { sleep 1; payload.publish! }```
|
303
|
+
(Proc) => called when failed publishing a message
|
304
|
+
|
305
|
+
## TODO
|
306
|
+
- Add alias attributes when subscribing (similar to publisher)
|
307
|
+
- Add flag ```model.ps_processing``` to indicate that the current transaction is being processed by pub/sub
|
308
|
+
- Auto publish update only if payload has changed
|
309
|
+
- On delete, payload must only be composed by ids
|
310
|
+
|
268
311
|
## Contributing
|
269
312
|
|
270
313
|
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.
|
data/lib/pub_sub_model_sync.rb
CHANGED
@@ -5,6 +5,7 @@ require 'active_support'
|
|
5
5
|
|
6
6
|
require 'pub_sub_model_sync/railtie'
|
7
7
|
require 'pub_sub_model_sync/config'
|
8
|
+
require 'pub_sub_model_sync/base'
|
8
9
|
require 'pub_sub_model_sync/subscriber_concern'
|
9
10
|
require 'pub_sub_model_sync/message_publisher'
|
10
11
|
require 'pub_sub_model_sync/publisher_concern'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PubSubModelSync
|
4
|
+
class Base
|
5
|
+
delegate :config, :log, to: self
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def config
|
9
|
+
PubSubModelSync::Config
|
10
|
+
end
|
11
|
+
|
12
|
+
def log(message, kind = :info)
|
13
|
+
config.log message, kind
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -5,16 +5,27 @@ module PubSubModelSync
|
|
5
5
|
cattr_accessor(:subscribers) { [] }
|
6
6
|
cattr_accessor(:publishers) { [] }
|
7
7
|
cattr_accessor(:service_name) { :google }
|
8
|
-
|
8
|
+
|
9
|
+
# customizable callbacks
|
10
|
+
cattr_accessor(:debug) { false }
|
11
|
+
cattr_accessor :logger # LoggerInst
|
12
|
+
|
13
|
+
cattr_accessor(:on_before_processing) { ->(_payload, _subscriber) {} } # return :cancel to skip
|
14
|
+
cattr_accessor(:on_success_processing) { ->(_payload, _subscriber) {} }
|
15
|
+
cattr_accessor(:on_error_processing) { ->(_exception, _payload) {} }
|
16
|
+
cattr_accessor(:on_before_publish) { ->(_payload) {} } # return :cancel to skip
|
17
|
+
cattr_accessor(:on_after_publish) { ->(_payload) {} }
|
18
|
+
cattr_accessor(:on_error_publish) { ->(_exception, _payload) {} }
|
19
|
+
cattr_accessor(:disabled_callback_publisher) { ->(_model, _action) { false } }
|
9
20
|
|
10
21
|
# google service
|
11
22
|
cattr_accessor :project, :credentials, :topic_name, :subscription_name
|
12
23
|
|
13
24
|
# rabbitmq service
|
14
|
-
cattr_accessor :bunny_connection, :queue_name, :topic_name
|
25
|
+
cattr_accessor :bunny_connection, :queue_name, :topic_name, :subscription_name
|
15
26
|
|
16
27
|
# kafka service
|
17
|
-
cattr_accessor :kafka_connection, :topic_name
|
28
|
+
cattr_accessor :kafka_connection, :topic_name, :subscription_name
|
18
29
|
|
19
30
|
def self.log(msg, kind = :info)
|
20
31
|
msg = "PS_MSYNC ==> #{msg}"
|
@@ -24,5 +35,10 @@ module PubSubModelSync
|
|
24
35
|
logger ? logger.send(kind, msg) : puts(msg)
|
25
36
|
end
|
26
37
|
end
|
38
|
+
|
39
|
+
def self.subscription_key
|
40
|
+
subscription_name ||
|
41
|
+
(Rails.application.class.parent_name rescue '') # rubocop:disable Style/RescueModifier
|
42
|
+
end
|
27
43
|
end
|
28
44
|
end
|
@@ -1,40 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PubSubModelSync
|
4
|
-
class MessageProcessor
|
5
|
-
attr_accessor :
|
6
|
-
|
7
|
-
# @param
|
8
|
-
def initialize(data, klass, action)
|
9
|
-
|
10
|
-
@
|
11
|
-
|
4
|
+
class MessageProcessor < PubSubModelSync::Base
|
5
|
+
attr_accessor :payload
|
6
|
+
|
7
|
+
# @param payload (Payload): payload to be delivered
|
8
|
+
# @Deprecated: def initialize(data, klass, action)
|
9
|
+
def initialize(payload, klass = nil, action = nil)
|
10
|
+
@payload = payload
|
11
|
+
return if @payload.is_a?(Payload)
|
12
|
+
|
13
|
+
# support for deprecated
|
14
|
+
log('Deprecated: Use Payload instead of new(data, klass, action)')
|
15
|
+
@payload = PubSubModelSync::Payload.new(payload, { klass: klass, action: action })
|
12
16
|
end
|
13
17
|
|
14
18
|
def process
|
15
|
-
|
16
|
-
subscribers.each { |subscriber| run_subscriber(subscriber) }
|
19
|
+
filter_subscribers.each(&method(:run_subscriber))
|
17
20
|
end
|
18
21
|
|
19
22
|
private
|
20
23
|
|
21
24
|
def run_subscriber(subscriber)
|
22
|
-
subscriber
|
23
|
-
|
25
|
+
return unless processable?(subscriber)
|
26
|
+
|
27
|
+
subscriber.process!(payload)
|
28
|
+
config.on_success_processing.call(payload, subscriber)
|
29
|
+
log "processed message with: #{payload}"
|
24
30
|
rescue => e
|
25
|
-
|
26
|
-
log("error processing message: #{info}", :error)
|
31
|
+
print_subscriber_error(e)
|
27
32
|
end
|
28
33
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
+
def processable?(subscriber)
|
35
|
+
cancel = config.on_before_processing.call(payload, subscriber) == :cancel
|
36
|
+
log("process message cancelled: #{payload}") if cancel && config.debug
|
37
|
+
!cancel
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
|
40
|
+
# @param error (Error)
|
41
|
+
def print_subscriber_error(error)
|
42
|
+
info = [payload, error.message, error.backtrace]
|
43
|
+
res = config.on_error_processing.call(error, payload)
|
44
|
+
log("Error processing message: #{info}", :error) if res != :skip_log
|
45
|
+
end
|
46
|
+
|
47
|
+
def filter_subscribers
|
48
|
+
config.subscribers.select do |subscriber|
|
49
|
+
subscriber.settings[:from_klass].to_s == payload.klass.to_s &&
|
50
|
+
subscriber.settings[:from_action].to_s == payload.action.to_s
|
51
|
+
end
|
38
52
|
end
|
39
53
|
end
|
40
54
|
end
|
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PubSubModelSync
|
4
|
-
class MessagePublisher
|
4
|
+
class MessagePublisher < PubSubModelSync::Base
|
5
5
|
class << self
|
6
|
-
delegate :publish, to: :connector
|
7
|
-
|
8
6
|
def connector
|
9
7
|
@connector ||= PubSubModelSync::Connector.new
|
10
8
|
end
|
11
9
|
|
12
10
|
def publish_data(klass, data, action)
|
13
|
-
|
14
|
-
publish(
|
11
|
+
payload = PubSubModelSync::Payload.new(data, { klass: klass, action: action.to_sym })
|
12
|
+
publish(payload)
|
15
13
|
end
|
16
14
|
|
17
15
|
# @param model: ActiveRecord model
|
@@ -21,12 +19,34 @@ module PubSubModelSync
|
|
21
19
|
return if model.ps_skip_sync?(action)
|
22
20
|
|
23
21
|
publisher ||= model.class.ps_publisher(action)
|
24
|
-
|
25
|
-
|
22
|
+
payload_info = publisher.payload(model, action)
|
23
|
+
payload = PubSubModelSync::Payload.new(payload_info[:data], payload_info[:attrs])
|
24
|
+
res_before = model.ps_before_sync(action, payload.data)
|
26
25
|
return if res_before == :cancel
|
27
26
|
|
28
|
-
publish(payload
|
29
|
-
model.ps_after_sync(action, payload
|
27
|
+
publish(payload)
|
28
|
+
model.ps_after_sync(action, payload.data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def publish(payload)
|
32
|
+
if config.on_before_publish.call(payload) == :cancel
|
33
|
+
log("Publish message cancelled: #{payload}") if config.debug
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
log("Publishing message: #{[payload]}")
|
38
|
+
connector.publish(payload)
|
39
|
+
config.on_after_publish.call(payload)
|
40
|
+
rescue => e
|
41
|
+
notify_error(e, payload)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def notify_error(exception, payload)
|
47
|
+
info = [payload, exception.message, exception.backtrace]
|
48
|
+
res = config.on_error_publish.call(exception, payload)
|
49
|
+
log("Error publishing: #{info}", :error) if res != :skip_log
|
30
50
|
end
|
31
51
|
end
|
32
52
|
end
|
@@ -20,12 +20,17 @@ module PubSubModelSync
|
|
20
20
|
def name
|
21
21
|
'name'
|
22
22
|
end
|
23
|
+
|
24
|
+
def publish(*_args)
|
25
|
+
true
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
class MockChannel
|
26
30
|
def queue(*_args)
|
27
31
|
@queue ||= MockQueue.new
|
28
32
|
end
|
33
|
+
alias fanout queue
|
29
34
|
|
30
35
|
def topic(*_args)
|
31
36
|
@topic ||= MockTopic.new
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PubSubModelSync
|
4
|
+
class Payload
|
5
|
+
attr_reader :data, :attributes, :headers
|
6
|
+
|
7
|
+
# @param data (Hash: { any value }):
|
8
|
+
# @param attributes (Hash: { klass: string, action: :sym }):
|
9
|
+
def initialize(data, attributes, headers = {})
|
10
|
+
@data = data
|
11
|
+
@attributes = attributes
|
12
|
+
@headers = headers
|
13
|
+
build_headers
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
{ data: data, attributes: attributes, headers: headers }
|
18
|
+
end
|
19
|
+
|
20
|
+
def klass
|
21
|
+
attributes[:klass]
|
22
|
+
end
|
23
|
+
|
24
|
+
def action
|
25
|
+
attributes[:action]
|
26
|
+
end
|
27
|
+
|
28
|
+
def process!
|
29
|
+
publisher = PubSubModelSync::MessageProcessor.new(self)
|
30
|
+
publisher.process
|
31
|
+
end
|
32
|
+
|
33
|
+
def publish!
|
34
|
+
klass = PubSubModelSync::MessagePublisher
|
35
|
+
klass.publish(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_headers
|
41
|
+
headers[:uuid] ||= SecureRandom.uuid
|
42
|
+
headers[:app_key] ||= PubSubModelSync::Config.subscription_key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -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,8 +63,8 @@ module PubSubModelSync
|
|
64
63
|
|
65
64
|
def ps_register_callback(action, publisher)
|
66
65
|
after_commit(on: action) do |model|
|
67
|
-
|
68
|
-
|
66
|
+
disabled = PubSubModelSync::Config.disabled_callback_publisher.call(model, action)
|
67
|
+
if !disabled && !model.ps_skip_callback?(action)
|
69
68
|
klass = PubSubModelSync::MessagePublisher
|
70
69
|
klass.publish_model(model, action.to_sym, publisher)
|
71
70
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pub_sub_model_sync/payload'
|
3
4
|
module PubSubModelSync
|
4
|
-
class ServiceBase
|
5
|
+
class ServiceBase < PubSubModelSync::Base
|
5
6
|
SERVICE_KEY = 'service_model_sync'
|
6
7
|
|
7
8
|
def listen_messages
|
8
9
|
raise 'method :listen_messages must be defined in service'
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
+
# @param _payload (Payload)
|
13
|
+
def publish(_payload)
|
12
14
|
raise 'method :publish must be defined in service'
|
13
15
|
end
|
14
16
|
|
@@ -18,19 +20,29 @@ module PubSubModelSync
|
|
18
20
|
|
19
21
|
private
|
20
22
|
|
21
|
-
# @param
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
# @param (String: Payload in json format)
|
24
|
+
def process_message(payload_info)
|
25
|
+
payload = parse_payload(payload_info)
|
26
|
+
log("Received message: #{[payload]}") if config.debug
|
27
|
+
if same_app_message?(payload)
|
28
|
+
log("Skip message from same origin: #{[payload]}") if config.debug
|
29
|
+
else
|
30
|
+
payload.process!
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
error = [payload, e.message, e.backtrace]
|
34
|
+
log("Error parsing received message: #{error}", :error)
|
27
35
|
end
|
28
36
|
|
29
|
-
def
|
30
|
-
|
31
|
-
data
|
32
|
-
|
33
|
-
|
37
|
+
def parse_payload(payload_info)
|
38
|
+
info = JSON.parse(payload_info).deep_symbolize_keys
|
39
|
+
::PubSubModelSync::Payload.new(info[:data], info[:attributes], info[:headers])
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param payload (Payload)
|
43
|
+
def same_app_message?(payload)
|
44
|
+
key = payload.headers[:app_key]
|
45
|
+
key && key == config.subscription_key
|
34
46
|
end
|
35
47
|
end
|
36
48
|
end
|
@@ -7,10 +7,9 @@ end
|
|
7
7
|
|
8
8
|
module PubSubModelSync
|
9
9
|
class ServiceGoogle < ServiceBase
|
10
|
-
attr_accessor :service, :topic, :subscription, :
|
10
|
+
attr_accessor :service, :topic, :subscription, :subscriber
|
11
11
|
|
12
12
|
def initialize
|
13
|
-
@config = PubSubModelSync::Config
|
14
13
|
@service = Google::Cloud::Pubsub.new(project: config.project,
|
15
14
|
credentials: config.credentials)
|
16
15
|
@topic = service.topic(config.topic_name) ||
|
@@ -28,13 +27,8 @@ module PubSubModelSync
|
|
28
27
|
log('Listener stopped')
|
29
28
|
end
|
30
29
|
|
31
|
-
def publish(
|
32
|
-
|
33
|
-
payload = { data: data, attributes: attributes }.to_json
|
34
|
-
topic.publish(payload, { SERVICE_KEY => true })
|
35
|
-
rescue => e
|
36
|
-
info = [attributes, data, e.message, e.backtrace]
|
37
|
-
log("Error publishing: #{info}", :error)
|
30
|
+
def publish(payload)
|
31
|
+
topic.publish(payload.to_json, { SERVICE_KEY => true })
|
38
32
|
end
|
39
33
|
|
40
34
|
def stop
|
@@ -51,17 +45,9 @@ module PubSubModelSync
|
|
51
45
|
|
52
46
|
def process_message(received_message)
|
53
47
|
message = received_message.message
|
54
|
-
|
55
|
-
|
56
|
-
perform_message(message.data)
|
57
|
-
rescue => e
|
58
|
-
log("Error processing message: #{[received_message, e.message]}", :error)
|
48
|
+
super(message.data) if message.attributes[SERVICE_KEY]
|
59
49
|
ensure
|
60
50
|
received_message.acknowledge!
|
61
51
|
end
|
62
|
-
|
63
|
-
def log(msg, kind = :info)
|
64
|
-
config.log("Google Service ==> #{msg}", kind)
|
65
|
-
end
|
66
52
|
end
|
67
53
|
end
|
@@ -8,9 +8,8 @@ end
|
|
8
8
|
module PubSubModelSync
|
9
9
|
class ServiceKafka < ServiceBase
|
10
10
|
cattr_accessor :producer
|
11
|
+
attr_accessor :config, :service, :consumer
|
11
12
|
|
12
|
-
attr_accessor :service, :consumer
|
13
|
-
attr_accessor :config
|
14
13
|
CONSUMER_GROUP = 'service_model_sync'
|
15
14
|
|
16
15
|
def initialize
|
@@ -23,19 +22,14 @@ module PubSubModelSync
|
|
23
22
|
start_consumer
|
24
23
|
consumer.each_message(&method(:process_message))
|
25
24
|
rescue PubSubModelSync::Runner::ShutDown
|
26
|
-
|
25
|
+
log('Listener stopped')
|
27
26
|
rescue => e
|
28
27
|
log("Error listening message: #{[e.message, e.backtrace]}", :error)
|
29
28
|
end
|
30
29
|
|
31
|
-
def publish(
|
32
|
-
log("Publishing: #{[attributes, data]}")
|
33
|
-
payload = { data: data, attributes: attributes }
|
30
|
+
def publish(payload)
|
34
31
|
producer.produce(payload.to_json, message_settings)
|
35
32
|
producer.deliver_messages
|
36
|
-
rescue => e
|
37
|
-
info = [attributes, data, e.message, e.backtrace]
|
38
|
-
log("Error publishing: #{info}", :error)
|
39
33
|
end
|
40
34
|
|
41
35
|
def stop
|
@@ -64,14 +58,7 @@ module PubSubModelSync
|
|
64
58
|
def process_message(message)
|
65
59
|
return unless message.headers[SERVICE_KEY]
|
66
60
|
|
67
|
-
|
68
|
-
rescue => e
|
69
|
-
error = [message, e.message, e.backtrace]
|
70
|
-
log("Error processing message: #{error}", :error)
|
71
|
-
end
|
72
|
-
|
73
|
-
def log(msg, kind = :info)
|
74
|
-
config.log("Kafka Service ==> #{msg}", kind)
|
61
|
+
super(message.value)
|
75
62
|
end
|
76
63
|
end
|
77
64
|
end
|
@@ -7,8 +7,7 @@ end
|
|
7
7
|
|
8
8
|
module PubSubModelSync
|
9
9
|
class ServiceRabbit < ServiceBase
|
10
|
-
attr_accessor :service, :channel, :queue, :topic
|
11
|
-
attr_accessor :config
|
10
|
+
attr_accessor :config, :service, :channel, :queue, :topic
|
12
11
|
|
13
12
|
def initialize
|
14
13
|
@config = PubSubModelSync::Config
|
@@ -22,22 +21,21 @@ module PubSubModelSync
|
|
22
21
|
queue.subscribe(subscribe_settings, &method(:process_message))
|
23
22
|
loop { sleep 5 }
|
24
23
|
rescue PubSubModelSync::Runner::ShutDown
|
25
|
-
|
24
|
+
log('Listener stopped')
|
26
25
|
rescue => e
|
27
26
|
log("Error listening message: #{[e.message, e.backtrace]}", :error)
|
28
27
|
end
|
29
28
|
|
30
|
-
def publish(
|
31
|
-
|
32
|
-
deliver_data(
|
33
|
-
# TODO: max retry
|
34
|
-
rescue Timeout::Error => e
|
35
|
-
log("Error publishing (retrying....): #{e.message}", :error)
|
36
|
-
initialize
|
37
|
-
retry
|
29
|
+
def publish(payload)
|
30
|
+
qty_retry ||= 0
|
31
|
+
deliver_data(payload)
|
38
32
|
rescue => e
|
39
|
-
|
40
|
-
|
33
|
+
if e.is_a?(Timeout::Error) && (qty_retry += 1) <= 2
|
34
|
+
log("Error publishing (retrying....): #{e.message}", :error)
|
35
|
+
initialize
|
36
|
+
retry
|
37
|
+
end
|
38
|
+
raise
|
41
39
|
end
|
42
40
|
|
43
41
|
def stop
|
@@ -48,7 +46,10 @@ module PubSubModelSync
|
|
48
46
|
private
|
49
47
|
|
50
48
|
def message_settings
|
51
|
-
{
|
49
|
+
{
|
50
|
+
routing_key: queue.name,
|
51
|
+
type: SERVICE_KEY
|
52
|
+
}
|
52
53
|
end
|
53
54
|
|
54
55
|
def subscribe_settings
|
@@ -58,10 +59,7 @@ module PubSubModelSync
|
|
58
59
|
def process_message(_delivery_info, meta_info, payload)
|
59
60
|
return unless meta_info[:type] == SERVICE_KEY
|
60
61
|
|
61
|
-
|
62
|
-
rescue => e
|
63
|
-
error = [payload, e.message, e.backtrace]
|
64
|
-
log("Error processing message: #{error}", :error)
|
62
|
+
super(payload)
|
65
63
|
end
|
66
64
|
|
67
65
|
def subscribe_to_queue
|
@@ -69,21 +67,16 @@ module PubSubModelSync
|
|
69
67
|
@channel = service.create_channel
|
70
68
|
queue_settings = { durable: true, auto_delete: false }
|
71
69
|
@queue = channel.queue(config.queue_name, queue_settings)
|
72
|
-
|
70
|
+
subscribe_to_exchange
|
73
71
|
end
|
74
72
|
|
75
|
-
def
|
76
|
-
@topic = channel.
|
73
|
+
def subscribe_to_exchange
|
74
|
+
@topic = channel.fanout(config.topic_name)
|
77
75
|
queue.bind(topic, routing_key: queue.name)
|
78
76
|
end
|
79
77
|
|
80
|
-
def
|
81
|
-
config.log("Rabbit Service ==> #{msg}", kind)
|
82
|
-
end
|
83
|
-
|
84
|
-
def deliver_data(data, attributes)
|
78
|
+
def deliver_data(payload)
|
85
79
|
subscribe_to_queue
|
86
|
-
payload = { data: data, attributes: attributes }
|
87
80
|
topic.publish(payload.to_json, message_settings)
|
88
81
|
|
89
82
|
# Ugly fix: "IO timeout when reading 7 bytes"
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module PubSubModelSync
|
4
4
|
class Subscriber
|
5
5
|
attr_accessor :klass, :action, :attrs, :settings
|
6
|
+
attr_reader :payload
|
6
7
|
|
7
8
|
# @param settings: (Hash) { id: :id, direct_mode: false,
|
8
9
|
# from_klass: klass, from_action: action }
|
@@ -15,51 +16,51 @@ module PubSubModelSync
|
|
15
16
|
@settings = def_settings.merge(settings)
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
+
def process!(payload)
|
20
|
+
@payload = payload
|
19
21
|
if settings[:direct_mode]
|
20
|
-
run_class_message
|
22
|
+
run_class_message
|
21
23
|
else
|
22
|
-
run_model_message
|
24
|
+
run_model_message
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
private
|
27
29
|
|
28
|
-
def run_class_message
|
30
|
+
def run_class_message
|
29
31
|
model_class = klass.constantize
|
30
|
-
model_class.send(action,
|
32
|
+
model_class.send(action, payload.data)
|
31
33
|
end
|
32
34
|
|
33
35
|
# support for: create, update, destroy
|
34
|
-
def run_model_message
|
35
|
-
model = find_model
|
36
|
+
def run_model_message
|
37
|
+
model = find_model
|
38
|
+
return if model.ps_before_save_sync(payload) == :cancel
|
39
|
+
|
36
40
|
if action == :destroy
|
37
41
|
model.destroy!
|
38
42
|
else
|
39
|
-
populate_model(model
|
40
|
-
return if action == :update && !model.
|
43
|
+
populate_model(model)
|
44
|
+
return if action == :update && !model.ps_subscriber_changed?(payload.data)
|
41
45
|
|
42
46
|
model.save!
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
|
-
def find_model
|
50
|
+
def find_model
|
47
51
|
model_class = klass.constantize
|
48
|
-
if model_class.respond_to?(:ps_find_model)
|
49
|
-
return model_class.ps_find_model(message)
|
50
|
-
end
|
52
|
+
return model_class.ps_find_model(payload.data) if model_class.respond_to?(:ps_find_model)
|
51
53
|
|
52
|
-
model_class.where(model_identifiers
|
54
|
+
model_class.where(model_identifiers).first_or_initialize
|
53
55
|
end
|
54
56
|
|
55
|
-
def model_identifiers
|
57
|
+
def model_identifiers
|
56
58
|
identifiers = Array(settings[:id])
|
57
|
-
identifiers.map { |key| [key,
|
59
|
+
identifiers.map { |key| [key, payload.data[key.to_sym]] }.to_h
|
58
60
|
end
|
59
61
|
|
60
|
-
def populate_model(model
|
61
|
-
values =
|
62
|
-
puts "===========values: #{values.inspect}-------#{message.inspect}"
|
62
|
+
def populate_model(model)
|
63
|
+
values = payload.data.slice(*attrs)
|
63
64
|
values.each do |attr, value|
|
64
65
|
model.send("#{attr}=", value)
|
65
66
|
end
|
@@ -6,6 +6,16 @@ module PubSubModelSync
|
|
6
6
|
base.extend(ClassMethods)
|
7
7
|
end
|
8
8
|
|
9
|
+
# check if model was changed to skip nonsense .update!()
|
10
|
+
def ps_subscriber_changed?(_data)
|
11
|
+
validate
|
12
|
+
changed?
|
13
|
+
end
|
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
|
+
|
9
19
|
module ClassMethods
|
10
20
|
def ps_subscribe(attrs, actions: nil, from_klass: name, id: :id)
|
11
21
|
settings = { id: id, from_klass: from_klass }
|
data/pub_sub_model_sync.gemspec
CHANGED
@@ -5,6 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
require 'pub_sub_model_sync/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
+
spec.required_ruby_version = '>= 2.4' # rubocop:disable Gemspec/RequiredRubyVersion
|
8
9
|
spec.name = 'pub_sub_model_sync'
|
9
10
|
spec.version = PubSubModelSync::VERSION
|
10
11
|
spec.authors = ['Owen']
|
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.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Owen
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- gemfiles/Gemfile_5
|
105
105
|
- gemfiles/Gemfile_6
|
106
106
|
- lib/pub_sub_model_sync.rb
|
107
|
+
- lib/pub_sub_model_sync/base.rb
|
107
108
|
- lib/pub_sub_model_sync/config.rb
|
108
109
|
- lib/pub_sub_model_sync/connector.rb
|
109
110
|
- lib/pub_sub_model_sync/message_processor.rb
|
@@ -111,6 +112,7 @@ files:
|
|
111
112
|
- lib/pub_sub_model_sync/mock_google_service.rb
|
112
113
|
- lib/pub_sub_model_sync/mock_kafka_service.rb
|
113
114
|
- lib/pub_sub_model_sync/mock_rabbit_service.rb
|
115
|
+
- lib/pub_sub_model_sync/payload.rb
|
114
116
|
- lib/pub_sub_model_sync/publisher.rb
|
115
117
|
- lib/pub_sub_model_sync/publisher_concern.rb
|
116
118
|
- lib/pub_sub_model_sync/railtie.rb
|
@@ -131,7 +133,7 @@ metadata:
|
|
131
133
|
homepage_uri: https://github.com/owen2345/pub_sub_model_sync
|
132
134
|
source_code_uri: https://github.com/owen2345/pub_sub_model_sync
|
133
135
|
changelog_uri: https://github.com/owen2345/pub_sub_model_sync/blob/master/CHANGELOG.md
|
134
|
-
post_install_message:
|
136
|
+
post_install_message:
|
135
137
|
rdoc_options: []
|
136
138
|
require_paths:
|
137
139
|
- lib
|
@@ -139,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
139
141
|
requirements:
|
140
142
|
- - ">="
|
141
143
|
- !ruby/object:Gem::Version
|
142
|
-
version: '
|
144
|
+
version: '2.4'
|
143
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
146
|
requirements:
|
145
147
|
- - ">="
|
@@ -147,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
149
|
version: '0'
|
148
150
|
requirements: []
|
149
151
|
rubygems_version: 3.0.8
|
150
|
-
signing_key:
|
152
|
+
signing_key:
|
151
153
|
specification_version: 4
|
152
154
|
summary: Permit to sync models between apps through pub/sub
|
153
155
|
test_files: []
|