honest_pubsub 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -4
- data/lib/honest_pubsub.rb +2 -1
- data/lib/honest_pubsub/cli.rb +1 -1
- data/lib/honest_pubsub/db_logger.rb +1 -1
- data/lib/honest_pubsub/server.rb +0 -1
- data/lib/honest_pubsub/server/client_queue_listener.rb +3 -3
- data/lib/honest_pubsub/server/rabbit_mq_subscriber.rb +72 -0
- data/lib/honest_pubsub/subscriber.rb +85 -55
- data/lib/honest_pubsub/version.rb +1 -1
- data/spec/honest_pubsub/cli_spec.rb +1 -1
- data/spec/honest_pubsub/server/client_queue_listener_spec.rb +4 -4
- data/spec/honest_pubsub/server/client_worker_spec.rb +6 -6
- data/spec/honest_pubsub/server/rabbitmq_subscriber_spec.rb +5 -0
- data/spec/spec_helper.rb +2 -2
- metadata +4 -6
- data/lib/honest_pubsub/server/client_worker.rb +0 -99
- data/spec/honest_pubsub/#subscriber_spec.rb# +0 -9
- data/spec/honest_pubsub/subscriber_spec.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf7921af321ad1e8d84d9baea70ee538439a7e5f
|
4
|
+
data.tar.gz: 6a503f60a553dd038a09baab0918464b551878a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cf1256690f3dd50217c72a7cfa9a00af39fa830cad8ae644c3c079e0a376a2a90fcf1c9354f7480649082ed8591da66fd4e9fb236e58253374161c8adc3a4b1
|
7
|
+
data.tar.gz: 9ce9f05eedec207e79e8e0a2354a95374d133ad1c89cd6dca0fdbf32eef455cc3996c390a0e4563e7a6803b4b0dd6624ab1f4a199612681d89e6a7eb25fb8894
|
data/README.md
CHANGED
@@ -56,13 +56,11 @@ Context is automatically cleared up by the gem for a Rails Application. Otherwis
|
|
56
56
|
You can declare a subscriber class as follows
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
class UserWelcomeSubscriber < HonestPubsub::
|
59
|
+
class UserWelcomeSubscriber < HonestPubsub::Subscriber
|
60
60
|
subscribe_to "user_created" # The message prefix that you're subscribing to
|
61
61
|
# subscribe_to "user_created", on: 'welcome_emails_queue' # If you want to specify the queue name
|
62
62
|
|
63
|
-
def perform(
|
64
|
-
# context is a hash that was put into HonestPubsub::Context
|
65
|
-
# Payload contains the data that was published
|
63
|
+
def perform(payload)
|
66
64
|
# My awesome logic goes here...
|
67
65
|
end
|
68
66
|
end
|
@@ -82,6 +80,27 @@ Usage: bundle exec start_subscribers [options]
|
|
82
80
|
|
83
81
|
```
|
84
82
|
|
83
|
+
#### Validations
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class UserWelcomeSubscriber < HonestPubsub::Subscriber
|
87
|
+
subscribe_to "user_created" # The message prefix that you're subscribing to
|
88
|
+
validates_payload_with :id_present
|
89
|
+
validates_payload_with do |payload|
|
90
|
+
payload[:email].present?
|
91
|
+
end
|
92
|
+
|
93
|
+
def perform(payload)
|
94
|
+
# My awesome logic goes here...
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def id_present(payload)
|
100
|
+
payload[:id].present?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
85
104
|
|
86
105
|
## Contributing
|
87
106
|
|
data/lib/honest_pubsub.rb
CHANGED
@@ -10,10 +10,11 @@ require "honest_pubsub/logger"
|
|
10
10
|
require "honest_pubsub/logging"
|
11
11
|
require "honest_pubsub/message"
|
12
12
|
require "honest_pubsub/publisher"
|
13
|
-
require "honest_pubsub/
|
13
|
+
require "honest_pubsub/server/rabbit_mq_subscriber"
|
14
14
|
require "honest_pubsub/version"
|
15
15
|
require "honest_pubsub/exceptions/payload_validation_error"
|
16
16
|
require 'honest_pubsub/server'
|
17
|
+
require 'honest_pubsub/subscriber'
|
17
18
|
|
18
19
|
if defined?(Rails::Railtie)
|
19
20
|
require "honest_pubsub/middleware"
|
data/lib/honest_pubsub/cli.rb
CHANGED
@@ -53,7 +53,7 @@ module HonestPubsub
|
|
53
53
|
if subscribers.present?
|
54
54
|
subscribers.map(&:constantize)
|
55
55
|
else
|
56
|
-
HonestPubsub::
|
56
|
+
HonestPubsub::Subscriber.class_variable_get(:@@registered_subscribers)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -17,7 +17,7 @@ module HonestPubsub
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def start_subscribe
|
20
|
-
subscriber = ::HonestPubsub::
|
20
|
+
subscriber = ::HonestPubsub::Server::RabbitMQSubscriber.new("")
|
21
21
|
subscriber.start("logger") do |info, properties, contents|
|
22
22
|
# Write to mongo the contents and the routing_key, with the routing_key being indexed
|
23
23
|
routing_key = info[:routing_key]
|
data/lib/honest_pubsub/server.rb
CHANGED
@@ -16,7 +16,7 @@ module HonestPubsub
|
|
16
16
|
@worker_class = worker_class
|
17
17
|
@queue_name = queue
|
18
18
|
@durable = durable
|
19
|
-
@subscriber = ::HonestPubsub::
|
19
|
+
@subscriber = ::HonestPubsub::Server::RabbitMQSubscriber.new(@request_key, @durable, @topic)
|
20
20
|
end
|
21
21
|
|
22
22
|
def start
|
@@ -35,8 +35,8 @@ module HonestPubsub
|
|
35
35
|
|
36
36
|
def message_received(delivery_info, properties, envelope)
|
37
37
|
HonestPubsub.logger.debug("Message received by listener on #{worker_class.name} with payload: #{envelope[:payload]}")
|
38
|
-
worker = worker_class.new(delivery_info, properties)
|
39
|
-
worker.perform!(envelope[:
|
38
|
+
worker = worker_class.new(delivery_info, properties, envelope[:context])
|
39
|
+
worker.perform!(envelope[:payload])
|
40
40
|
rescue => e
|
41
41
|
HonestPubsub.logger.error("Failed message for #{worker_class.name}")
|
42
42
|
Airbrake.notify("Failed perform for #{worker_class.name}",
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module HonestPubsub
|
2
|
+
module Server
|
3
|
+
|
4
|
+
class RabbitMQSubscriber
|
5
|
+
attr_reader :listener
|
6
|
+
attr_reader :exchange
|
7
|
+
attr_reader :channel
|
8
|
+
|
9
|
+
def initialize(routing_key, durable = true, topic="honest")
|
10
|
+
@initial_key = routing_key
|
11
|
+
@durable = durable
|
12
|
+
@topic = topic
|
13
|
+
|
14
|
+
if @initial_key.present?
|
15
|
+
@routing_key = "#{@topic}.#{@initial_key}.#"
|
16
|
+
else
|
17
|
+
@routing_key = "#{@topic}.#"
|
18
|
+
end
|
19
|
+
@logger = ::HonestPubsub::Logger.new
|
20
|
+
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# name - used to ensure that certain consumers are actually listening to an exchange
|
25
|
+
# pass in a lambda for this method to work. We might only want to expose the content instead of
|
26
|
+
# all 3 chunks.
|
27
|
+
def start(name, blocking=false)
|
28
|
+
@connection = Bunny.new(Configuration.configuration[:connection])
|
29
|
+
begin
|
30
|
+
@connection.start
|
31
|
+
rescue => e
|
32
|
+
Airbrake.notify("RabbitMQ unreachable!", params: { message: e.message}, environment_name: ENV['RAILS_ENV'] )
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
|
36
|
+
@channel = @connection.create_channel
|
37
|
+
@exchange = @channel.topic(@topic, :durable=>@durable, :auto_delete=>false)
|
38
|
+
|
39
|
+
# FIX!!! -thl
|
40
|
+
# Need to ensure that the ids for a server will be reproducible in case a server
|
41
|
+
# goes down and has to get restarted.
|
42
|
+
if @initial_key.present?
|
43
|
+
@queue = "#{@initial_key}.#{name}"
|
44
|
+
else
|
45
|
+
@queue = "#{name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
queue_arguments = {}
|
49
|
+
queue_arguments["x-dead-letter-exchange"] = Configuration.configuration[:dead_letter] if Configuration.configuration[:dead_letter].present?
|
50
|
+
@listener = @channel.queue(@queue, :arguments=>queue_arguments ).bind(@exchange, :routing_key => @routing_key, :exclusive=>false)
|
51
|
+
# Parameters for subscribe that might be useful:
|
52
|
+
# :block=>true - Used for long running consumer applications. (backend servers?)
|
53
|
+
@consumer = @listener.subscribe(:consumer_tag=>name, :block=>blocking)
|
54
|
+
@consumer.on_delivery do |delivery_info, properties, contents|
|
55
|
+
HonestPubsub.logger.debug( "Message delivery with contents: #{contents}")
|
56
|
+
if delivery_info[:redelivered]
|
57
|
+
Airbrake.notify("PubSub Message redelivery", params: {info: delivery_info, props: properties, contents: contents}, environment_name: ENV['RAILS_ENV'] )
|
58
|
+
end
|
59
|
+
message = ::HonestPubsub::Message.new.parse(contents)
|
60
|
+
@logger.log_receive(delivery_info[:routing_key], message)
|
61
|
+
yield delivery_info, properties, message
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def teardown
|
67
|
+
@consumer.cancel if @consumer.present?
|
68
|
+
@connection.close if @connection.present?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,69 +1,99 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
# This class provders the worker for executing the subscriber. It receives a message from rabbitmq and
|
5
|
+
# creates an instance of the subscriber to execute with the message and context
|
6
|
+
|
1
7
|
module HonestPubsub
|
2
8
|
class Subscriber
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(routing_key, durable = true, topic="honest")
|
8
|
-
@initial_key = routing_key
|
9
|
-
@durable = durable
|
10
|
-
@topic = topic
|
11
|
-
|
12
|
-
if @initial_key.present?
|
13
|
-
@routing_key = "#{@topic}.#{@initial_key}.#"
|
14
|
-
else
|
15
|
-
@routing_key = "#{@topic}.#"
|
16
|
-
end
|
17
|
-
@logger = ::HonestPubsub::Logger.new
|
9
|
+
@@registered_subscribers = []
|
10
|
+
class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue
|
11
|
+
attr_accessor :delivery_routing_data, :delivery_properties, :context
|
18
12
|
|
19
|
-
|
13
|
+
def self.inherited(klass)
|
14
|
+
@@registered_subscribers << klass
|
20
15
|
end
|
21
16
|
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
rescue => e
|
30
|
-
Airbrake.notify("RabbitMQ unreachable!", params: { message: e.message}, environment_name: ENV['RAILS_ENV'] )
|
31
|
-
raise e
|
17
|
+
# Specify the routing key that the subscriber class should listen to.
|
18
|
+
# @param [String] routing_key_name The routing key to subscribe to. Must be characters only separated by periods (.)
|
19
|
+
# @param [Hash] options Allowed option is :on to optionally specify a queue. If not provided, queue name is generated from the routing key
|
20
|
+
def self.subscribe_to(routing_key_name, options = {})
|
21
|
+
options.assert_valid_keys(:on)
|
22
|
+
unless validate_routing_key_name(routing_key_name)
|
23
|
+
raise ArgumentError.new("#{routing_key_name} is not supported. Only lower case characters separated by periods are allowed.")
|
32
24
|
end
|
25
|
+
self.subscribed_key = routing_key_name
|
26
|
+
self.subscribed_queue = generated_queue_name(routing_key_name, options[:on])
|
27
|
+
end
|
33
28
|
|
34
|
-
|
35
|
-
|
29
|
+
# Sets the validator for payload
|
30
|
+
#
|
31
|
+
# @param validator The validator to use for validating the payload.
|
32
|
+
# Returns false if the payload is not valid.
|
33
|
+
# Proc must accept a payload as an argument.
|
34
|
+
def self.validates_payload_with(*validators)
|
35
|
+
self.payload_validators ||= []
|
36
|
+
self.payload_validators += validators
|
37
|
+
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
# Sets an error handler for the class
|
40
|
+
def self.handle_errors_with(handler)
|
41
|
+
error_handler = handler
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Hash] context Context from invocation
|
45
|
+
# @param [Object] delivery_routing_data Contains routing information like originator and routing key
|
46
|
+
# @param [Object] delivery_properties
|
47
|
+
def initialize(delivery_routing_data, delivery_properties, context)
|
48
|
+
@delivery_routing_data = delivery_routing_data
|
49
|
+
@delivery_properties = delivery_properties
|
50
|
+
@context = context
|
51
|
+
end
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@consumer.on_delivery do |delivery_info, properties, contents|
|
53
|
-
HonestPubsub.logger.debug( "Message delivery with contents: #{contents}")
|
54
|
-
if delivery_info[:redelivered]
|
55
|
-
Airbrake.notify("PubSub Message redelivery", params: {info: delivery_info, props: properties, contents: contents}, environment_name: ENV['RAILS_ENV'] )
|
56
|
-
end
|
57
|
-
message = ::HonestPubsub::Message.new.parse(contents)
|
58
|
-
@logger.log_receive(delivery_info[:routing_key], message)
|
59
|
-
yield delivery_info, properties, message
|
60
|
-
true
|
53
|
+
# Performs validation if validates_payload_with is defined and then calls the perform method
|
54
|
+
# @param [Object] payload Payload of the message
|
55
|
+
def perform!(payload)
|
56
|
+
if !valid_payload?(payload)
|
57
|
+
HonestPubsub.logger.error("Payload validation failed for #{self.class.name}")
|
58
|
+
raise ::HonestPubsub::PayloadValidationError.new("Invalid Payload for #{self.class.name}")
|
61
59
|
end
|
60
|
+
|
61
|
+
perform(context, payload)
|
62
62
|
end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
# Actual subscribers need to implement perform method. This is the method where the message is actually processed.
|
65
|
+
# @param [Object] payload Payload of the message
|
66
|
+
def perform(payload)
|
67
|
+
raise "Need implementation for your worker."
|
67
68
|
end
|
69
|
+
|
70
|
+
# @return [String] The original routing key with which the current message was published
|
71
|
+
def routing_key
|
72
|
+
delivery_routing_data[:routing_key]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Iterates over all the payload validators and returns false if any of them are false
|
76
|
+
# @param [Object] payload The payload/arguments of the message
|
77
|
+
# @return [Boolen] Should return true or false value - If no validators are specified, then returns true
|
78
|
+
def valid_payload?(payload)
|
79
|
+
return true unless payload_validators.present?
|
80
|
+
|
81
|
+
payload_validators.inject(true) { |is_valid, validator|
|
82
|
+
is_valid && (validator.respond_to?(:call) ? validator.call(payload) : send(validator, payload))
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def self.validate_routing_key_name(key)
|
89
|
+
return true if key.blank?
|
90
|
+
key.match(/\A([a-z]+\.?)*([a-z]+)\Z/).present?
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.generated_queue_name(routing_key, queue_name)
|
94
|
+
return queue_name if queue_name.present?
|
95
|
+
[HonestPubsub::Configuration.application_name.to_s.gsub(/[^\w\_]/, ''), routing_key.gsub(".", '_')].reject(&:blank?).join('_')
|
96
|
+
end
|
97
|
+
|
68
98
|
end
|
69
|
-
end
|
99
|
+
end
|
@@ -123,7 +123,7 @@ describe HonestPubsub::CLI do
|
|
123
123
|
}
|
124
124
|
|
125
125
|
context "No subscribers passed through CLI" do
|
126
|
-
let(:all_subscribers) { HonestPubsub::
|
126
|
+
let(:all_subscribers) { HonestPubsub::Subscriber.class_variable_get(:@@registered_subscribers) }
|
127
127
|
it "loads all subscribers" do
|
128
128
|
expect(all_subscribers).to include(MyTestSubscriber1)
|
129
129
|
expect(all_subscribers).to include(MyTestSubscriber2)
|
@@ -13,7 +13,7 @@ describe HonestPubsub::Server::ClientQueueListener do
|
|
13
13
|
|
14
14
|
before do
|
15
15
|
context_before
|
16
|
-
Object.const_set :Klass, Class.new(HonestPubsub::
|
16
|
+
Object.const_set :Klass, Class.new(HonestPubsub::Subscriber)
|
17
17
|
validators.each do |validator|
|
18
18
|
Klass.validates_payload_with validator
|
19
19
|
end
|
@@ -30,7 +30,7 @@ describe HonestPubsub::Server::ClientQueueListener do
|
|
30
30
|
|
31
31
|
context "Normal execution" do
|
32
32
|
let!(:klass_instance){
|
33
|
-
instance = Klass.new({},{})
|
33
|
+
instance = Klass.new({},{}, {})
|
34
34
|
allow(instance).to receive(:perform)
|
35
35
|
instance
|
36
36
|
}
|
@@ -48,7 +48,7 @@ describe HonestPubsub::Server::ClientQueueListener do
|
|
48
48
|
|
49
49
|
context "Exception during execution" do
|
50
50
|
let!(:klass_instance) {
|
51
|
-
instance = Klass.new({},{})
|
51
|
+
instance = Klass.new({},{}, {})
|
52
52
|
allow(instance).to receive(:perform){
|
53
53
|
raise StandardError.new("test")
|
54
54
|
}
|
@@ -61,7 +61,7 @@ describe HonestPubsub::Server::ClientQueueListener do
|
|
61
61
|
|
62
62
|
context "Payload error" do
|
63
63
|
let!(:klass_instance) {
|
64
|
-
instance = Klass.new({},{})
|
64
|
+
instance = Klass.new({},{}, {})
|
65
65
|
allow(instance).to receive(:perform)
|
66
66
|
instance
|
67
67
|
}
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe HonestPubsub::
|
4
|
-
before(:each) { Object.const_set :Klass, Class.new(HonestPubsub::
|
3
|
+
describe HonestPubsub::Subscriber do
|
4
|
+
before(:each) { Object.const_set :Klass, Class.new(HonestPubsub::Subscriber) }
|
5
5
|
after(:each) { Object.send :remove_const, :Klass }
|
6
6
|
|
7
7
|
describe "#routing_key" do
|
8
|
-
subject { HonestPubsub::
|
8
|
+
subject { HonestPubsub::Subscriber.new(routing_data, properties, {}).routing_key }
|
9
9
|
|
10
10
|
let(:properties) { {} }
|
11
11
|
|
@@ -26,18 +26,18 @@ describe HonestPubsub::Server::ClientWorker do
|
|
26
26
|
context "One validator" do
|
27
27
|
let(:validators){ [lambda{|payload| true}] }
|
28
28
|
it {
|
29
|
-
expect(Klass.new({},{}).payload_validators.length).to eq 1 }
|
29
|
+
expect(Klass.new({},{}, {}).payload_validators.length).to eq 1 }
|
30
30
|
end
|
31
31
|
|
32
32
|
context "Two validators" do
|
33
33
|
let(:validators){ [lambda{|payload| true}, lambda{|payload| true}] }
|
34
|
-
it { expect(Klass.new({},{}).payload_validators.length).to eq 2 }
|
34
|
+
it { expect(Klass.new({},{}, {}).payload_validators.length).to eq 2 }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
describe '#valid_payload?' do
|
39
39
|
context
|
40
|
-
subject { Klass.new({},{}).valid_payload?({}) }
|
40
|
+
subject { Klass.new({},{}, {}).valid_payload?({}) }
|
41
41
|
|
42
42
|
before do
|
43
43
|
validators.each do |validator|
|
data/spec/spec_helper.rb
CHANGED
@@ -29,14 +29,14 @@ end
|
|
29
29
|
HonestPubsub::Configuration.configure_with("test", File.join(HonestPubsub.root,"spec/config.yml") )
|
30
30
|
|
31
31
|
# Some test subscriber classes to make testing easier
|
32
|
-
class MyTestSubscriber1 < HonestPubsub::
|
32
|
+
class MyTestSubscriber1 < HonestPubsub::Subscriber
|
33
33
|
|
34
34
|
def perform(payload)
|
35
35
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
class MyTestSubscriber2 < HonestPubsub::
|
39
|
+
class MyTestSubscriber2 < HonestPubsub::Subscriber
|
40
40
|
def perform(payload)
|
41
41
|
|
42
42
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honest_pubsub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tech@honest.com
|
@@ -213,16 +213,15 @@ files:
|
|
213
213
|
- lib/honest_pubsub/railtie.rb
|
214
214
|
- lib/honest_pubsub/server.rb
|
215
215
|
- lib/honest_pubsub/server/client_queue_listener.rb
|
216
|
-
- lib/honest_pubsub/server/
|
216
|
+
- lib/honest_pubsub/server/rabbit_mq_subscriber.rb
|
217
217
|
- lib/honest_pubsub/server/subscriber_server.rb
|
218
218
|
- lib/honest_pubsub/subscriber.rb
|
219
219
|
- lib/honest_pubsub/version.rb
|
220
220
|
- spec/config.yml
|
221
|
-
- spec/honest_pubsub/#subscriber_spec.rb#
|
222
221
|
- spec/honest_pubsub/cli_spec.rb
|
223
222
|
- spec/honest_pubsub/server/client_queue_listener_spec.rb
|
224
223
|
- spec/honest_pubsub/server/client_worker_spec.rb
|
225
|
-
- spec/honest_pubsub/
|
224
|
+
- spec/honest_pubsub/server/rabbitmq_subscriber_spec.rb
|
226
225
|
- spec/logger_spec.rb
|
227
226
|
- spec/message_spec.rb
|
228
227
|
- spec/spec_helper.rb
|
@@ -252,11 +251,10 @@ specification_version: 4
|
|
252
251
|
summary: Pub sub gem for Honest Company
|
253
252
|
test_files:
|
254
253
|
- spec/config.yml
|
255
|
-
- spec/honest_pubsub/#subscriber_spec.rb#
|
256
254
|
- spec/honest_pubsub/cli_spec.rb
|
257
255
|
- spec/honest_pubsub/server/client_queue_listener_spec.rb
|
258
256
|
- spec/honest_pubsub/server/client_worker_spec.rb
|
259
|
-
- spec/honest_pubsub/
|
257
|
+
- spec/honest_pubsub/server/rabbitmq_subscriber_spec.rb
|
260
258
|
- spec/logger_spec.rb
|
261
259
|
- spec/message_spec.rb
|
262
260
|
- spec/spec_helper.rb
|
@@ -1,99 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'active_support/core_ext'
|
3
|
-
|
4
|
-
# This class provders the worker for executing the subscriber. It receives a message from rabbitmq and
|
5
|
-
# creates an instance of the subscriber to execute with the message and context
|
6
|
-
|
7
|
-
module HonestPubsub
|
8
|
-
module Server
|
9
|
-
class ClientWorker
|
10
|
-
@@registered_subscribers = []
|
11
|
-
class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue
|
12
|
-
attr_accessor :delivery_routing_data, :delivery_properties
|
13
|
-
|
14
|
-
def self.inherited(klass)
|
15
|
-
@@registered_subscribers << klass
|
16
|
-
end
|
17
|
-
|
18
|
-
# Specify the routing key that the subscriber class should listen to.
|
19
|
-
# @param [String] routing_key_name The routing key to subscribe to. Must be characters only separated by periods (.)
|
20
|
-
# @param [Hash] options Allowed option is :on to optionally specify a queue. If not provided, queue name is generated from the routing key
|
21
|
-
def self.subscribe_to(routing_key_name, options = {})
|
22
|
-
options.assert_valid_keys(:on)
|
23
|
-
unless validate_routing_key_name(routing_key_name)
|
24
|
-
raise ArgumentError.new("#{routing_key_name} is not supported. Only lower case characters separated by periods are allowed.")
|
25
|
-
end
|
26
|
-
self.subscribed_key = routing_key_name
|
27
|
-
self.subscribed_queue = generated_queue_name(routing_key_name, options[:on])
|
28
|
-
end
|
29
|
-
|
30
|
-
# Sets the validator for payload
|
31
|
-
#
|
32
|
-
# @param validator The validator to use for validating the payload.
|
33
|
-
# Returns false if the payload is not valid.
|
34
|
-
# Proc must accept a payload as an argument.
|
35
|
-
def self.validates_payload_with(*validators)
|
36
|
-
self.payload_validators ||= []
|
37
|
-
self.payload_validators += validators
|
38
|
-
end
|
39
|
-
|
40
|
-
# Sets an error handler for the class
|
41
|
-
def self.handle_errors_with(handler)
|
42
|
-
error_handler = handler
|
43
|
-
end
|
44
|
-
|
45
|
-
def initialize(delivery_routing_data, delivery_properties)
|
46
|
-
@delivery_routing_data = delivery_routing_data
|
47
|
-
@delivery_properties = delivery_properties
|
48
|
-
end
|
49
|
-
|
50
|
-
# Performs validation if validates_payload_with is defined and then calls the perform method
|
51
|
-
# @param [Object] payload Payload of the message
|
52
|
-
# @param [Hash] context Context from invocation
|
53
|
-
def perform!(context, payload)
|
54
|
-
if !valid_payload?(payload)
|
55
|
-
HonestPubsub.logger.error("Payload validation failed for #{self.class.name}")
|
56
|
-
raise ::HonestPubsub::PayloadValidationError.new("Invalid Payload for #{self.class.name}")
|
57
|
-
end
|
58
|
-
|
59
|
-
perform(context, payload)
|
60
|
-
end
|
61
|
-
|
62
|
-
# Actual subscribers need to implement perform method. This is the method where the message is actually processed.
|
63
|
-
# @param [Object] payload Payload of the message
|
64
|
-
# @param [Hash] context Context from invocation
|
65
|
-
def perform(context, payload)
|
66
|
-
raise "Need implementation for your worker."
|
67
|
-
end
|
68
|
-
|
69
|
-
# @return [String] The original routing key with which the current message was published
|
70
|
-
def routing_key
|
71
|
-
delivery_routing_data[:routing_key]
|
72
|
-
end
|
73
|
-
|
74
|
-
# Iterates over all the payload validators and returns false if any of them are false
|
75
|
-
# @param [Object] payload The payload/arguments of the message
|
76
|
-
# @return [Boolen] Should return true or false value - If no validators are specified, then returns true
|
77
|
-
def valid_payload?(payload)
|
78
|
-
return true unless payload_validators.present?
|
79
|
-
|
80
|
-
payload_validators.inject(true) { |is_valid, validator|
|
81
|
-
is_valid && (validator.respond_to?(:call) ? validator.call(payload) : send(validator, payload))
|
82
|
-
}
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def self.validate_routing_key_name(key)
|
88
|
-
return true if key.blank?
|
89
|
-
key.match(/\A([a-z]+\.?)*([a-z]+)\Z/).present?
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.generated_queue_name(routing_key, queue_name)
|
93
|
-
return queue_name if queue_name.present?
|
94
|
-
[HonestPubsub::Configuration.application_name.to_s.gsub(/[^\w\_]/, ''), routing_key.gsub(".", '_')].reject(&:blank?).join('_')
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|