freddy 0.4.6 → 0.4.7

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
  SHA1:
3
- metadata.gz: 1c9e0020e6933bdc25c5845a3bad630dc56b4ae9
4
- data.tar.gz: 55da31d069e4dd316260d37a0db00965a737039e
3
+ metadata.gz: ec35bbe29d804ef1930904b88277dc9bcd6722e7
4
+ data.tar.gz: 045d21384a3ed02bbe66c41a01f25a5cde723c1e
5
5
  SHA512:
6
- metadata.gz: 78b24b50ec3a7779572c1a45ce1b59d6ce345a058dda2c6781048b65a5312f210cb4595fc678450ea9a73dbf95951a902598def1e1bad3493071b4f903db1e5f
7
- data.tar.gz: 7ee8fd7a118ef27a164becdf4bab34b977ba31dada15040233aff1807c56648d6b0dd1d58ce691a0bf3f563304d0f94a7201ac1b14c625a86b318b2d56a978e5
6
+ metadata.gz: cc2ec82ebe76f48b5e78ac3a695d71d16d49ca42bc08762c4b8c2b35c9c66a86c48fb8e743601bfb31c3c580e4906404f60122fa44e0269de51c4c9485af43d9
7
+ data.tar.gz: fdb43cf87cfdd043f785b827957b9309562a79637af82e9dccbacf62cc62f6f622e1b5b28019c55dda586fdcd2cc3803cbe101b7b81780267f3a6ae7addeddde
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Messaging API supporting acknowledgements and request-response
2
2
 
3
- [![Build Status](https://travis-ci.org/salemove/node-freddy.svg?branch=master)](https://travis-ci.org/salemove/node-freddy)
3
+ [![Build Status](https://travis-ci.org/salemove/freddy.svg?branch=master)](https://travis-ci.org/salemove/freddy)
4
4
  [![Code Climate](https://codeclimate.com/github/salemove/freddy/badges/gpa.svg)](https://codeclimate.com/github/salemove/freddy)
5
5
 
6
6
  ## Setup
data/freddy.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  else
9
9
  spec.name = "freddy"
10
10
  end
11
- spec.version = '0.4.6'
11
+ spec.version = '0.4.7'
12
12
  spec.authors = ["Urmas Talimaa"]
13
13
  spec.email = ["urmas.talimaa@gmail.com"]
14
14
  spec.description = %q{Messaging API}
@@ -48,7 +48,8 @@ class Freddy
48
48
 
49
49
  def subscribe(&block)
50
50
  @queue.subscribe do |info, properties, payload|
51
- block.call(payload, Delivery.new(properties, info.routing_key))
51
+ parsed_payload = Payload.parse(payload)
52
+ block.call(Delivery.new(parsed_payload, properties, info.routing_key))
52
53
  end
53
54
  end
54
55
 
@@ -49,7 +49,8 @@ class Freddy
49
49
 
50
50
  def subscribe(&block)
51
51
  @queue.subscribe do |meta, payload|
52
- block.call(payload, Delivery.new(meta, meta.routing_key))
52
+ parsed_payload = Payload.parse(payload)
53
+ block.call(Delivery.new(parsed_payload, meta, meta.routing_key))
53
54
  end
54
55
  end
55
56
 
@@ -1,69 +1,32 @@
1
1
  require_relative 'responder_handler'
2
2
  require_relative 'message_handler'
3
- require_relative 'request'
4
3
  require_relative 'delivery'
4
+ require_relative 'consumers/tap_into_consumer'
5
+ require_relative 'consumers/respond_to_consumer'
6
+ require_relative 'consumers/response_consumer'
5
7
 
6
8
  class Freddy
7
9
  class Consumer
8
-
9
- class EmptyConsumer < Exception
10
- end
11
-
12
- def initialize(channel, logger, consume_thread_pool)
10
+ def initialize(channel, logger, consume_thread_pool, producer)
13
11
  @channel, @logger = channel, logger
14
- @topic_exchange = @channel.topic Freddy::FREDDY_TOPIC_EXCHANGE_NAME
15
- @consume_thread_pool = consume_thread_pool
16
- @dedicated_thread_pool = Thread.pool(1) # used only internally
17
- end
18
-
19
- def consume(destination, options = {}, &block)
20
- raise EmptyConsumer unless block
21
- consume_from_queue create_queue(destination), options, &block
22
- end
23
-
24
- def consume_from_queue(queue, options = {}, &block)
25
- consume_using_pool(queue, options, @consume_thread_pool, &block)
12
+ @tap_into_consumer = Consumers::TapIntoConsumer.new(consume_thread_pool, channel)
13
+ @respond_to_consumer = Consumers::RespondToConsumer.new(consume_thread_pool, channel, producer, @logger)
14
+ @response_consumer = Consumers::ResponseConsumer.new(@logger)
26
15
  end
27
16
 
28
- def dedicated_consume(queue, &block)
29
- consume_using_pool(queue, {}, @dedicated_thread_pool, &block)
17
+ def response_consume(queue, &block)
18
+ @logger.debug "Consuming messages on #{queue.name}"
19
+ @response_consumer.consume(queue, &block)
30
20
  end
31
21
 
32
22
  def tap_into(pattern, &block)
33
- queue = create_queue('', exclusive: true).bind(@topic_exchange, routing_key: pattern)
34
- consumer = queue.subscribe do |payload, delivery|
35
- @consume_thread_pool.process do
36
- block.call Payload.parse(payload), delivery.routing_key
37
- end
38
- end
39
23
  @logger.debug "Tapping into messages that match #{pattern}"
40
- ResponderHandler.new consumer, @consume_thread_pool
41
- end
42
-
43
- private
44
-
45
- def consume_using_pool(queue, options, pool, &block)
46
- consumer = queue.subscribe do |payload, delivery|
47
- pool.process do
48
- parsed_payload = Payload.parse(payload)
49
- log_receive_event(queue.name, parsed_payload, delivery.correlation_id)
50
- block.call parsed_payload, delivery
51
- end
52
- end
53
- @logger.debug "Consuming messages on #{queue.name}"
54
- ResponderHandler.new consumer, pool
55
- end
56
-
57
- def create_queue(destination, options={})
58
- @channel.queue(destination, options)
24
+ @tap_into_consumer.consume(pattern, &block)
59
25
  end
60
26
 
61
- def log_receive_event(queue_name, payload, correlation_id)
62
- if defined?(Logasm) && @logger.is_a?(Logasm)
63
- @logger.debug "Received message", queue: queue_name, payload: payload, correlation_id: correlation_id
64
- else
65
- @logger.debug "Received message on #{queue_name} with payload #{payload} with correlation_id #{correlation_id}"
66
- end
27
+ def respond_to(destination, &block)
28
+ @logger.info "Listening for requests on #{destination}"
29
+ @respond_to_consumer.consume(destination, &block)
67
30
  end
68
31
  end
69
32
  end
@@ -0,0 +1,59 @@
1
+ class Freddy
2
+ module Consumers
3
+ class RespondToConsumer
4
+ def initialize(consume_thread_pool, channel, producer, logger)
5
+ @consume_thread_pool = consume_thread_pool
6
+ @channel = channel
7
+ @producer = producer
8
+ @logger = logger
9
+ @response_queue_lock = Mutex.new
10
+ end
11
+
12
+ def consume(destination, &block)
13
+ ensure_response_queue_exists
14
+
15
+ consumer = consume_from_destination(destination) do |delivery|
16
+ log_receive_event(destination, delivery)
17
+
18
+ handler_class = MessageHandlers.for_type(delivery.type)
19
+ handler = handler_class.new(@producer, destination, @logger)
20
+
21
+ msg_handler = MessageHandler.new(handler, delivery)
22
+ handler.handle_message delivery.payload, msg_handler, &block
23
+ end
24
+
25
+ ResponderHandler.new(consumer, @consume_thread_pool)
26
+ end
27
+
28
+ private
29
+
30
+ def consume_from_destination(destination, &block)
31
+ @channel.queue(destination).subscribe do |delivery|
32
+ process_message(delivery, &block)
33
+ end
34
+ end
35
+
36
+ def process_message(delivery, &block)
37
+ @consume_thread_pool.process do
38
+ block.call(delivery)
39
+ end
40
+ end
41
+
42
+ def log_receive_event(destination, delivery)
43
+ if defined?(Logasm) && @logger.is_a?(Logasm)
44
+ @logger.debug "Received message", queue: destination, payload: delivery.payload, correlation_id: delivery.correlation_id
45
+ else
46
+ @logger.debug "Received message on #{destination} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
47
+ end
48
+ end
49
+
50
+ def ensure_response_queue_exists
51
+ return @response_queue if defined?(@response_queue)
52
+
53
+ @response_queue_lock.synchronize do
54
+ @response_queue = @channel.queue('', exclusive: true)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ class Freddy
2
+ module Consumers
3
+ class ResponseConsumer
4
+ def initialize(logger)
5
+ @logger = logger
6
+ @dedicated_thread_pool = Thread.pool(1)
7
+ end
8
+
9
+ def consume(queue, &block)
10
+ consumer = queue.subscribe do |delivery|
11
+ process_message(queue, delivery, &block)
12
+ end
13
+ ResponderHandler.new(consumer, @dedicated_thread_pool)
14
+ end
15
+
16
+ private
17
+
18
+ def process_message(queue, delivery, &block)
19
+ @dedicated_thread_pool.process do
20
+ log_receive_event(queue.name, delivery)
21
+ block.call(delivery)
22
+ end
23
+ end
24
+
25
+ def log_receive_event(queue_name, delivery)
26
+ if defined?(Logasm) && @logger.is_a?(Logasm)
27
+ @logger.debug "Received message", queue: queue_name, payload: delivery.payload, correlation_id: delivery.correlation_id
28
+ else
29
+ @logger.debug "Received message on #{queue_name} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ class Freddy
2
+ module Consumers
3
+ class TapIntoConsumer
4
+ def initialize(consume_thread_pool, channel)
5
+ @consume_thread_pool = consume_thread_pool
6
+ @channel = channel
7
+ @topic_exchange = @channel.topic(Freddy::FREDDY_TOPIC_EXCHANGE_NAME)
8
+ end
9
+
10
+ def consume(pattern, &block)
11
+ consumer = create_queue(pattern).subscribe do |delivery|
12
+ process_message(delivery, &block)
13
+ end
14
+
15
+ ResponderHandler.new(consumer, @consume_thread_pool)
16
+ end
17
+
18
+ private
19
+
20
+ def create_queue(pattern)
21
+ @channel
22
+ .queue('', exclusive: true)
23
+ .bind(@topic_exchange, routing_key: pattern)
24
+ end
25
+
26
+ def process_message(delivery, &block)
27
+ @consume_thread_pool.process do
28
+ block.call delivery.payload, delivery.routing_key
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,8 +1,9 @@
1
1
  class Freddy
2
2
  class Delivery
3
- attr_reader :metadata, :routing_key
3
+ attr_reader :routing_key, :payload
4
4
 
5
- def initialize(metadata, routing_key)
5
+ def initialize(payload, metadata, routing_key)
6
+ @payload = payload
6
7
  @metadata = metadata
7
8
  @routing_key = routing_key
8
9
  end
@@ -10,5 +11,13 @@ class Freddy
10
11
  def correlation_id
11
12
  @metadata.correlation_id
12
13
  end
14
+
15
+ def type
16
+ @metadata.type
17
+ end
18
+
19
+ def reply_to
20
+ @metadata.reply_to
21
+ end
13
22
  end
14
23
  end
@@ -1,19 +1,19 @@
1
1
  class Freddy
2
2
  class MessageHandler
3
- attr_reader :destination, :correlation_id
3
+ attr_reader :correlation_id
4
4
 
5
5
  def initialize(adapter, delivery)
6
6
  @adapter = adapter
7
- @metadata = delivery.metadata
8
- @correlation_id = @metadata.correlation_id
7
+ @delivery = delivery
8
+ @correlation_id = @delivery.correlation_id
9
9
  end
10
10
 
11
11
  def success(response = nil)
12
- @adapter.success(@metadata.reply_to, response)
12
+ @adapter.success(@delivery.reply_to, response)
13
13
  end
14
14
 
15
15
  def error(error = {error: "Couldn't process message"})
16
- @adapter.error(@metadata.reply_to, error)
16
+ @adapter.error(@delivery.reply_to, error)
17
17
  end
18
18
  end
19
19
  end
@@ -23,7 +23,7 @@ class Freddy
23
23
 
24
24
  class OjAdapter
25
25
  def self.parse(payload)
26
- Oj.load(payload, symbol_keys: true)
26
+ Oj.strict_load(payload, symbol_keys: true)
27
27
  end
28
28
 
29
29
  def self.dump(payload)
@@ -11,12 +11,6 @@ class Freddy
11
11
  class Request
12
12
  NO_ROUTE = 312
13
13
 
14
- class EmptyRequest < Exception
15
- end
16
-
17
- class EmptyResponder < Exception
18
- end
19
-
20
14
  def initialize(channel, logger, producer, consumer)
21
15
  @channel, @logger = channel, logger
22
16
  @producer, @consumer = producer, consumer
@@ -30,7 +24,6 @@ class Freddy
30
24
  end
31
25
 
32
26
  @listening_for_responses_lock = Mutex.new
33
- @response_queue_lock = Mutex.new
34
27
  end
35
28
 
36
29
  def sync_request(destination, payload, opts)
@@ -63,33 +56,14 @@ class Freddy
63
56
  )
64
57
  end
65
58
 
66
- def respond_to(destination, &block)
67
- raise EmptyResponder unless block
68
-
69
- ensure_response_queue_exists
70
- @logger.info "Listening for requests on #{destination}"
71
- responder_handler = @consumer.consume destination do |payload, delivery|
72
- handler = MessageHandlers.for_type(delivery.metadata.type).new(@producer, destination, @logger)
73
-
74
- msg_handler = MessageHandler.new(handler, delivery)
75
- handler.handle_message payload, msg_handler, &block
76
- end
77
- responder_handler
78
- end
79
-
80
59
  private
81
60
 
82
- def create_response_queue
83
- @channel.queue("", exclusive: true)
84
- end
61
+ def handle_response(delivery)
62
+ correlation_id = delivery.correlation_id
85
63
 
86
- def handle_response(payload, delivery)
87
- correlation_id = delivery.metadata.correlation_id
88
- request = @request_map[correlation_id]
89
- if request
64
+ if request = @request_map.delete(correlation_id)
90
65
  @logger.debug "Got response for request to #{request[:destination]} with correlation_id #{correlation_id}"
91
- @request_map.delete correlation_id
92
- request[:callback].call payload, delivery
66
+ request[:callback].call delivery.payload, delivery
93
67
  else
94
68
  @logger.warn "Got rpc response for correlation_id #{correlation_id} but there is no requester"
95
69
  Utils.notify 'NoRequesterForResponse', "Got rpc response but there is no requester", correlation_id: correlation_id
@@ -100,24 +74,14 @@ class Freddy
100
74
  Utils.notify_exception(e, destination: request[:destination], correlation_id: correlation_id)
101
75
  end
102
76
 
103
- def ensure_response_queue_exists
104
- @response_queue_lock.synchronize do
105
- @response_queue ||= create_response_queue
106
- end
107
- end
108
-
109
77
  def ensure_listening_to_responses
78
+ return @listening_for_responses if defined?(@listening_for_responses)
79
+
110
80
  @listening_for_responses_lock.synchronize do
111
- if @listening_for_responses
112
- true
113
- else
114
- ensure_response_queue_exists
115
- @request_manager.start
116
- @consumer.dedicated_consume @response_queue do |payload, delivery|
117
- handle_response payload, delivery
118
- end
119
- @listening_for_responses = true
120
- end
81
+ @response_queue ||= @channel.queue("", exclusive: true)
82
+ @request_manager.start
83
+ @consumer.response_consume(@response_queue, &method(:handle_response))
84
+ @listening_for_responses = true
121
85
  end
122
86
  end
123
87
  end
@@ -23,7 +23,7 @@ class Freddy
23
23
  raise Timeout::Error, 'execution expired'
24
24
  elsif @response[:error] == 'RequestTimeout'
25
25
  raise TimeoutError.new(@response)
26
- elsif !@delivery || @delivery.metadata.type == 'error'
26
+ elsif !@delivery || @delivery.type == 'error'
27
27
  raise InvalidRequestError.new(@response)
28
28
  else
29
29
  @response
data/lib/freddy.rb CHANGED
@@ -41,14 +41,14 @@ class Freddy
41
41
  @connection = connection
42
42
  @channel = connection.create_channel
43
43
  @consume_thread_pool = Thread.pool(max_concurrency)
44
- @consumer = Consumer.new channel, logger, @consume_thread_pool
45
44
  @producer = Producer.new channel, logger
45
+ @consumer = Consumer.new channel, logger, @consume_thread_pool, @producer
46
46
  @request = Request.new channel, logger, @producer, @consumer
47
47
  end
48
48
  private :initialize
49
49
 
50
50
  def respond_to(destination, &callback)
51
- @request.respond_to destination, &callback
51
+ @consumer.respond_to destination, &callback
52
52
  end
53
53
 
54
54
  def tap_into(pattern, &callback)
@@ -10,12 +10,8 @@ describe Freddy::Consumer do
10
10
 
11
11
  after { freddy.close }
12
12
 
13
- it 'raises exception when no consumer is provided' do
14
- expect { consumer.consume destination }.to raise_error described_class::EmptyConsumer
15
- end
16
-
17
13
  it "doesn't call passed block without any messages" do
18
- consumer.consume destination do
14
+ consumer.respond_to destination do
19
15
  @message_received = true
20
16
  end
21
17
  default_sleep
@@ -4,9 +4,7 @@ describe Freddy::MessageHandler do
4
4
  subject(:handler) { described_class.new(adapter, delivery) }
5
5
 
6
6
  let(:adapter) { double }
7
- let(:delivery) { double(metadata: metadata) }
8
- let(:metadata) { double(reply_to: reply_to, correlation_id: 'abc') }
9
-
7
+ let(:delivery) { double(reply_to: reply_to, correlation_id: 'abc') }
10
8
  let(:reply_to) { double }
11
9
 
12
10
  describe '#success' do
@@ -10,10 +10,6 @@ describe Freddy::Request do
10
10
 
11
11
  after { freddy.close }
12
12
 
13
- it 'raises empty responder exception when responding without callback' do
14
- expect {@responder = request.respond_to destination }.to raise_error described_class::EmptyResponder
15
- end
16
-
17
13
  context 'requesting from multiple threads' do
18
14
  let(:nr_of_threads) { 50 }
19
15
 
@@ -23,7 +23,6 @@ describe 'Logging' do
23
23
 
24
24
  it 'logs all consumed messages' do
25
25
  expect(logger1).to have_received(:info).with(/Listening for requests on \S+/)
26
- expect(logger1).to have_received(:debug).with(/Consuming messages on \S+/)
27
26
  expect(logger1).to have_received(:debug).with(/Received message on \S+ with payload {:pay=>"load"}/)
28
27
  end
29
28
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freddy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Urmas Talimaa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-26 00:00:00.000000000 Z
11
+ date: 2015-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -117,6 +117,9 @@ files:
117
117
  - lib/freddy/adapters/bunny_adapter.rb
118
118
  - lib/freddy/adapters/march_hare_adapter.rb
119
119
  - lib/freddy/consumer.rb
120
+ - lib/freddy/consumers/respond_to_consumer.rb
121
+ - lib/freddy/consumers/response_consumer.rb
122
+ - lib/freddy/consumers/tap_into_consumer.rb
120
123
  - lib/freddy/delivery.rb
121
124
  - lib/freddy/error_response.rb
122
125
  - lib/freddy/invalid_request_error.rb