pub_sub_model_sync 0.1.4 → 0.1.5

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: 4eda619ab984f99ffe46cfef36fa380aaa6766c1564081dcc4e5e53e6f7976e0
4
- data.tar.gz: dd056d897c96bac38a20c115fee501cbb1383038483f74849bbb0e7217568163
3
+ metadata.gz: fd18a29b1e99da881b9c362dbc5541f6aeee0211988bcf301336691ac071e96d
4
+ data.tar.gz: cbc025ff0301e1bf4fd35d78fa2b70f06ff8a3c7c1dbfa12448f1cd3a53e592f
5
5
  SHA512:
6
- metadata.gz: 75796a135c2f820d89303c29c9bb6dc78eaa1e207391a07ba86a45f547a7007b4b00f767bb949d354c5a90c504b5d2d287619fd3106bca1dce71e75588ac6173
7
- data.tar.gz: fe4829f970142bfea0089beb4e86a27c9d1e08049213372ae77f94b9d02d16752218d58e88b25ac1f34848931da3651e4405caa3a8932401c1410d7f04e3d0da
6
+ metadata.gz: 3036baf17c9f78c6b4b445eb5a3a2f7be63c2a78cb36dcf9aad1abcf1eff8b691979cd1ff79e1bac916b44b1996e0f80bedf841be102ad1ec29684c1437910a2
7
+ data.tar.gz: aead45d302ed53d84457376dc9fa64f05d267b7441125722d4621d4d2fc9d7097fa24f3aa693c50862b616f5a25b32e77a202a0503143d347f80290728701680
@@ -1,5 +1,10 @@
1
1
  # Change Log
2
2
 
3
+ # 0.1.5
4
+ - Add apache kafka support
5
+ - Add Service interface for future references
6
+ - Improve Services to use a single/common message performer
7
+
3
8
  # 0.1.4
4
9
  - Add attribute aliases when publishing, ```ps_publish(['name:full_name', 'email'])```
5
10
  - Ability to retrieve publisher/subscriber crud settings
data/Gemfile CHANGED
@@ -3,6 +3,7 @@ source "https://rubygems.org"
3
3
  gem 'rubocop'
4
4
  gem 'bunny' # rabbit-mq
5
5
  gem 'google-cloud-pubsub' # google pub/sub
6
+ gem 'ruby-kafka' # kafka pub/sub
6
7
 
7
8
  # Specify your gem's dependencies in pub_sub_model_sync.gemspec
8
9
  gemspec
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (0.1.4)
4
+ pub_sub_model_sync (0.1.5)
5
5
  activesupport
6
6
  rails
7
7
 
@@ -60,6 +60,7 @@ GEM
60
60
  concurrent-ruby (1.1.6)
61
61
  crass (1.0.6)
62
62
  diff-lcs (1.3)
63
+ digest-crc (0.5.1)
63
64
  erubi (1.9.0)
64
65
  faraday (0.17.3)
65
66
  multipart-post (>= 1.2, < 3)
@@ -81,7 +82,7 @@ GEM
81
82
  googleauth (>= 0.6.2, < 0.10.0)
82
83
  grpc (>= 1.7.2, < 2.0)
83
84
  rly (~> 0.2.3)
84
- google-protobuf (3.11.4-universal-darwin)
85
+ google-protobuf (3.11.4)
85
86
  googleapis-common-protos (1.3.9)
86
87
  google-protobuf (~> 3.0)
87
88
  googleapis-common-protos-types (~> 1.0)
@@ -95,7 +96,7 @@ GEM
95
96
  multi_json (~> 1.11)
96
97
  os (>= 0.9, < 2.0)
97
98
  signet (~> 0.7)
98
- grpc (1.27.0-universal-darwin)
99
+ grpc (1.27.0)
99
100
  google-protobuf (~> 3.11)
100
101
  googleapis-common-protos-types (~> 1.0)
101
102
  grpc-google-iam-v1 (0.6.9)
@@ -180,6 +181,8 @@ GEM
180
181
  rexml
181
182
  ruby-progressbar (~> 1.7)
182
183
  unicode-display_width (>= 1.4.0, < 1.7)
184
+ ruby-kafka (1.0.0)
185
+ digest-crc
183
186
  ruby-progressbar (1.10.1)
184
187
  signet (0.11.0)
185
188
  addressable (~> 2.3)
@@ -214,6 +217,7 @@ DEPENDENCIES
214
217
  rake
215
218
  rspec
216
219
  rubocop
220
+ ruby-kafka
217
221
  sqlite3
218
222
 
219
223
  BUNDLED WITH
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # PubSubModelSync
2
- Permit to sync models data and make calls between rails apps using google or rabbitmq pub/sub service.
2
+ Permit to sync models data and make calls between rails apps using google or rabbitmq or apache kafka pub/sub service.
3
3
 
4
4
  Note: This gem is based on [MultipleMan](https://github.com/influitive/multiple_man) which for now looks unmaintained.
5
5
 
@@ -14,8 +14,10 @@ Note: This gem is based on [MultipleMan](https://github.com/influitive/multiple_
14
14
  Add this line to your application's Gemfile:
15
15
  ```ruby
16
16
  gem 'pub_sub_model_sync'
17
+
17
18
  gem 'google-cloud-pubsub' # to use google pub/sub service
18
19
  gem 'bunny' # to use rabbit-mq pub/sub service
20
+ gem 'ruby-kafka' # to use apache kafka pub/sub service
19
21
  ```
20
22
  And then execute: $ bundle install
21
23
 
@@ -43,6 +45,14 @@ And then execute: $ bundle install
43
45
  ```
44
46
  See details here: https://github.com/ruby-amqp/bunny
45
47
 
48
+ - configuration for Apache Kafka (You need kafka installed)
49
+ ```ruby
50
+ PubSubModelSync::Config.service_name = :kafka
51
+ PubSubModelSync::Config.kafka_connection = [["kafka1:9092", "kafka2:9092"], logger: Rails.logger]
52
+ PubSubModelSync::Config.topic_name = 'sample-topic'
53
+ ```
54
+ See details here: https://github.com/zendesk/ruby-kafka
55
+
46
56
  - Add publishers/subscribers to your models (See examples below)
47
57
 
48
58
  - Start subscribers to listen for publishers (Only in the app that has subscribers)
@@ -74,6 +84,7 @@ end
74
84
  # Samples
75
85
  User.create(name: 'test user', email: 'sample@gmail.com') # Review your App 2 to see the created user (only name will be saved)
76
86
  User.ps_class_publish({ msg: 'Hello' }, action: :greeting) # User.greeting method (Class method) will be called in App2
87
+ PubSubModelSync::Publisher.new.publish_data(User, { msg: 'Hello' }, :greeting) # similar to above when not included publisher concern
77
88
  ```
78
89
 
79
90
  ## Advanced Example
@@ -109,15 +120,22 @@ end
109
120
  # when using google service
110
121
  require 'pub_sub_model_sync/mock_google_service'
111
122
  config.before(:each) do
112
- pub_sub_mock = PubSubModelSync::MockGoogleService.new
113
- allow(Google::Cloud::Pubsub).to receive(:new).and_return(pub_sub_mock)
123
+ google_mock = PubSubModelSync::MockGoogleService.new
124
+ allow(Google::Cloud::Pubsub).to receive(:new).and_return(google_mock)
114
125
  end
115
126
 
116
127
  # when using rabbitmq service
117
128
  require 'pub_sub_model_sync/mock_rabbit_service'
118
129
  config.before(:each) do
119
- bunny_mock = PubSubModelSync::MockRabbitService.new
120
- allow(Bunny).to receive(:new).and_return(bunny_mock)
130
+ rabbit_mock = PubSubModelSync::MockRabbitService.new
131
+ allow(Bunny).to receive(:new).and_return(rabbit_mock)
132
+ end
133
+
134
+ # when using apache kafka service
135
+ require 'pub_sub_model_sync/mock_kafka_service'
136
+ config.before(:each) do
137
+ kafka_mock = PubSubModelSync::MockKafkaService.new
138
+ allow(Kafka).to receive(:new).and_return(kafka_mock)
121
139
  end
122
140
 
123
141
  ```
@@ -11,8 +11,11 @@ require 'pub_sub_model_sync/publisher_concern'
11
11
  require 'pub_sub_model_sync/runner'
12
12
  require 'pub_sub_model_sync/connector'
13
13
  require 'pub_sub_model_sync/message_processor'
14
+
15
+ require 'pub_sub_model_sync/service_base'
14
16
  require 'pub_sub_model_sync/service_google'
15
17
  require 'pub_sub_model_sync/service_rabbit'
18
+ require 'pub_sub_model_sync/service_kafka'
16
19
 
17
20
  module PubSubModelSync
18
21
  class Error < StandardError; end
@@ -13,6 +13,9 @@ module PubSubModelSync
13
13
  # rabbitmq service
14
14
  cattr_accessor :bunny_connection, :queue_name, :topic_name
15
15
 
16
+ # kafka service
17
+ cattr_accessor :kafka_connection, :topic_name
18
+
16
19
  def self.log(msg, kind = :info)
17
20
  msg = "PS_MSYNC ==> #{msg}"
18
21
  if logger == :raise_error
@@ -16,6 +16,8 @@ module PubSubModelSync
16
16
  case Config.service_name
17
17
  when :google
18
18
  PubSubModelSync::ServiceGoogle.new
19
+ when :kafka
20
+ PubSubModelSync::ServiceKafka.new
19
21
  else # :rabbit_mq
20
22
  PubSubModelSync::ServiceRabbit.new
21
23
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PubSubModelSync
4
+ class MockKafkaService
5
+ class MockProducer
6
+ def produce(*_args)
7
+ true
8
+ end
9
+
10
+ def deliver_messages(*_args)
11
+ true
12
+ end
13
+
14
+ def shutdown
15
+ true
16
+ end
17
+ end
18
+
19
+ class MockConsumer
20
+ def each_message(*_args)
21
+ true
22
+ end
23
+
24
+ def stop(*_args)
25
+ true
26
+ end
27
+
28
+ def subscribe(*_args)
29
+ true
30
+ end
31
+ end
32
+
33
+ def producer(*_args)
34
+ MockProducer.new
35
+ end
36
+
37
+ def consumer(*_args)
38
+ MockConsumer.new
39
+ end
40
+
41
+ def close
42
+ true
43
+ end
44
+ end
45
+ end
@@ -25,8 +25,7 @@ module PubSubModelSync
25
25
  {
26
26
  klass: klass.to_s,
27
27
  action: action.to_sym,
28
- id: id,
29
- service_model_sync: true
28
+ id: id
30
29
  }
31
30
  end
32
31
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PubSubModelSync
4
+ class ServiceBase
5
+ SERVICE_KEY = 'service_model_sync'
6
+
7
+ def listen_messages
8
+ raise 'method :listen_messages must be defined in service'
9
+ end
10
+
11
+ def publish(_data, _attributes)
12
+ raise 'method :publish must be defined in service'
13
+ end
14
+
15
+ def stop
16
+ raise 'method :stop must be defined in service'
17
+ end
18
+
19
+ private
20
+
21
+ # @param payload (String JSON): '{"data":{},"attributes":{..}}'
22
+ # refer: PubSubModelSync::Publisher (.publish_model | .publish_data)
23
+ def perform_message(payload)
24
+ data, attrs = parse_message_payload(payload)
25
+ args = [data, attrs[:klass], attrs[:action], attrs]
26
+ PubSubModelSync::MessageProcessor.new(*args).process
27
+ end
28
+
29
+ def parse_message_payload(payload)
30
+ message_payload = JSON.parse(payload).symbolize_keys
31
+ data = message_payload[:data].symbolize_keys
32
+ attrs = message_payload[:attributes].symbolize_keys
33
+ [data, attrs]
34
+ end
35
+ end
36
+ end
@@ -6,7 +6,7 @@ rescue LoadError # rubocop:disable Lint/SuppressedException
6
6
  end
7
7
 
8
8
  module PubSubModelSync
9
- class ServiceGoogle
9
+ class ServiceGoogle < ServiceBase
10
10
  attr_accessor :service, :topic, :subscription, :config, :subscriber
11
11
 
12
12
  def initialize
@@ -30,7 +30,9 @@ module PubSubModelSync
30
30
 
31
31
  def publish(data, attributes)
32
32
  log("Publishing message: #{[data, attributes]}")
33
- topic.publish(data.to_json, attributes)
33
+
34
+ payload = { data: data, attributes: attributes }.to_json
35
+ topic.publish(payload, { SERVICE_KEY => true })
34
36
  end
35
37
 
36
38
  def stop
@@ -47,12 +49,9 @@ module PubSubModelSync
47
49
 
48
50
  def process_message(received_message)
49
51
  message = received_message.message
50
- attrs = message.attributes.symbolize_keys
51
- return unless attrs[:service_model_sync]
52
+ return unless message.attributes[SERVICE_KEY]
52
53
 
53
- data = JSON.parse(message.data).symbolize_keys
54
- args = [data, attrs[:klass], attrs[:action], attrs]
55
- PubSubModelSync::MessageProcessor.new(*args).process
54
+ perform_message(message.data)
56
55
  rescue => e
57
56
  log("Error processing message: #{[received_message, e.message]}", :error)
58
57
  ensure
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'kafka'
5
+ rescue LoadError # rubocop:disable Lint/SuppressedException
6
+ end
7
+
8
+ module PubSubModelSync
9
+ class ServiceKafka < ServiceBase
10
+ cattr_accessor :producer
11
+
12
+ attr_accessor :service, :consumer
13
+ attr_accessor :config
14
+ CONSUMER_GROUP = 'service_model_sync'
15
+
16
+ def initialize
17
+ @config = PubSubModelSync::Config
18
+ @service = Kafka.new(*config.kafka_connection)
19
+ end
20
+
21
+ def listen_messages
22
+ log('Listener starting...')
23
+ start_consumer
24
+ consumer.each_message(&method(:process_message))
25
+ rescue PubSubModelSync::Runner::ShutDown
26
+ raise
27
+ rescue => e
28
+ log("Error listening message: #{[e.message, e.backtrace]}", :error)
29
+ end
30
+
31
+ def publish(data, attributes)
32
+ log("Publishing: #{[data, attributes]}")
33
+ payload = { data: data, attributes: attributes }
34
+ producer.produce(payload.to_json, message_settings)
35
+ producer.deliver_messages
36
+ rescue => e
37
+ info = [data, attributes, e.message, e.backtrace]
38
+ log("Error publishing: #{info}", :error)
39
+ end
40
+
41
+ def stop
42
+ log('Listener stopping...')
43
+ consumer.stop
44
+ end
45
+
46
+ private
47
+
48
+ def message_settings
49
+ { topic: config.topic_name, headers: { SERVICE_KEY => true } }
50
+ end
51
+
52
+ def start_consumer
53
+ @consumer = service.consumer(group_id: CONSUMER_GROUP)
54
+ consumer.subscribe(config.topic_name)
55
+ end
56
+
57
+ def producer
58
+ return self.class.producer if self.class.producer
59
+
60
+ at_exit { self.class.producer.shutdown }
61
+ self.class.producer = service.producer
62
+ end
63
+
64
+ def process_message(message)
65
+ return unless message.headers[SERVICE_KEY]
66
+
67
+ perform_message(message.value)
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)
75
+ end
76
+ end
77
+ end
@@ -6,10 +6,9 @@ rescue LoadError # rubocop:disable Lint/SuppressedException
6
6
  end
7
7
 
8
8
  module PubSubModelSync
9
- class ServiceRabbit
9
+ class ServiceRabbit < ServiceBase
10
10
  attr_accessor :service, :channel, :queue, :topic
11
11
  attr_accessor :config
12
- SERVICE_KEY = 'service_model_sync'
13
12
 
14
13
  def initialize
15
14
  @config = PubSubModelSync::Config
@@ -31,7 +30,7 @@ module PubSubModelSync
31
30
  log("Publishing: #{[data, attributes]}")
32
31
  subscribe_to_queue
33
32
  payload = { data: data, attributes: attributes }
34
- topic.publish(payload.to_json, routing_key: queue.name, type: SERVICE_KEY)
33
+ topic.publish(payload.to_json, message_settings)
35
34
  rescue => e
36
35
  info = [data, attributes, e.message, e.backtrace]
37
36
  log("Error publishing: #{info}", :error)
@@ -44,24 +43,19 @@ module PubSubModelSync
44
43
 
45
44
  private
46
45
 
46
+ def message_settings
47
+ { routing_key: queue.name, type: SERVICE_KEY }
48
+ end
49
+
47
50
  def process_message(_delivery_info, meta_info, payload)
48
51
  return unless meta_info[:type] == SERVICE_KEY
49
52
 
50
- data, attrs = parse_message_payload(payload)
51
- args = [data, attrs[:klass], attrs[:action], attrs]
52
- PubSubModelSync::MessageProcessor.new(*args).process
53
+ perform_message(payload)
53
54
  rescue => e
54
55
  error = [payload, e.message, e.backtrace]
55
56
  log("Error processing message: #{error}", :error)
56
57
  end
57
58
 
58
- def parse_message_payload(payload)
59
- message_payload = JSON.parse(payload).symbolize_keys
60
- data = message_payload[:data].symbolize_keys
61
- attrs = message_payload[:attributes].symbolize_keys
62
- [data, attrs]
63
- end
64
-
65
59
  def subscribe_to_queue
66
60
  service.start
67
61
  @channel = service.create_channel
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
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.1.4
4
+ version: 0.1.5
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-25 00:00:00.000000000 Z
11
+ date: 2020-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -119,12 +119,15 @@ files:
119
119
  - lib/pub_sub_model_sync/connector.rb
120
120
  - lib/pub_sub_model_sync/message_processor.rb
121
121
  - lib/pub_sub_model_sync/mock_google_service.rb
122
+ - lib/pub_sub_model_sync/mock_kafka_service.rb
122
123
  - lib/pub_sub_model_sync/mock_rabbit_service.rb
123
124
  - lib/pub_sub_model_sync/publisher.rb
124
125
  - lib/pub_sub_model_sync/publisher_concern.rb
125
126
  - lib/pub_sub_model_sync/railtie.rb
126
127
  - lib/pub_sub_model_sync/runner.rb
128
+ - lib/pub_sub_model_sync/service_base.rb
127
129
  - lib/pub_sub_model_sync/service_google.rb
130
+ - lib/pub_sub_model_sync/service_kafka.rb
128
131
  - lib/pub_sub_model_sync/service_rabbit.rb
129
132
  - lib/pub_sub_model_sync/subscriber_concern.rb
130
133
  - lib/pub_sub_model_sync/tasks/worker.rake