freddy-jruby 0.5.1 → 0.5.6

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: 304a469e602a811401b7e58433f7adc482b35798
4
- data.tar.gz: 959e5d55ea51de9e6f42bc4a85abe767125ee5eb
3
+ metadata.gz: 0b6494076ba5683b88c6381cd4fe89140d4cb877
4
+ data.tar.gz: 8dcea8a4f75bba5192ec372998a822f9fbc4ab70
5
5
  SHA512:
6
- metadata.gz: 49d8f58e7ecb635cd784486ecdf78a1316745b3de5d17aa5f60f9f6fb478aacf0be5191511f40429438de0d5a53ea188c617e26f5be0345834d87c43b63a3c82
7
- data.tar.gz: 1f61a010e7a14caf92e7ce3990f888e7d0383786a8bc30a1e4a697c411b1a43b0f53743dec3564ab028a0ff13b5c13f5b7639f5da4c8fc50eb7f6c046a60e1c1
6
+ metadata.gz: 4d05477dbd8e2aec9e0e4044edef0720dfaad0c96f9c2ece269c32388fb412332c0573ff44fc7926885bcafa4fa0e4c9895f7f17abadc9768b81cda3579de4fd
7
+ data.tar.gz: e49f5287155ad988a10474366f13e9b371712abedd9f6c3f46082d5ae57a73f2f1b78cfe04b42131e96e2a30b2a67af933be83322642d7325ebde0ffcf40e4a1
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ Gemfile.lock
3
3
  pkg
4
4
  .tags
5
5
  .tags1
6
+ coverage
data/.travis.yml CHANGED
@@ -1,7 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.1.0
4
- - jruby-9.0.0.0
4
+ - jruby-9.0.4.0
5
+ addons:
6
+ code_climate:
7
+ repo_token: 1f3842b985fdeff6a36168165d491ca5f444667e9381a85c899a61706a9dd285
5
8
  services:
6
9
  - rabbitmq
7
10
  before_script:
data/Gemfile CHANGED
@@ -1,8 +1,7 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- group :test, :development do
4
- gem 'rspec'
5
- gem 'pry'
6
- end
3
+ gem 'rspec'
4
+ gem 'pry'
5
+ gem 'codeclimate-test-reporter'
7
6
 
8
7
  gemspec
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
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
+ [![Test Coverage](https://codeclimate.com/github/salemove/freddy/badges/coverage.svg)](https://codeclimate.com/github/salemove/freddy/coverage)
5
6
 
6
7
  ## Setup
7
8
 
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.5.1'
11
+ spec.version = '0.5.6'
12
12
  spec.authors = ["Urmas Talimaa"]
13
13
  spec.email = ["urmas.talimaa@gmail.com"]
14
14
  spec.description = %q{Messaging API}
data/lib/freddy.rb CHANGED
@@ -34,7 +34,7 @@ class Freddy
34
34
  @connection = connection
35
35
  @logger = logger
36
36
 
37
- @tap_into_consumer = Consumers::TapIntoConsumer.new(consume_thread_pool)
37
+ @tap_into_consumer = Consumers::TapIntoConsumer.new(consume_thread_pool, @logger)
38
38
  @respond_to_consumer = Consumers::RespondToConsumer.new(consume_thread_pool, @logger)
39
39
 
40
40
  @send_and_forget_producer = Producers::SendAndForgetProducer.new(
@@ -75,9 +75,11 @@ class Freddy
75
75
 
76
76
  channel = @connection.create_channel
77
77
  producer = Producers::ReplyProducer.new(channel, @logger)
78
- handler_factory = MessageHandlers::Factory.new(producer, @logger)
78
+ handler_adapter_factory = MessageHandlerAdapters::Factory.new(producer)
79
79
 
80
- @respond_to_consumer.consume(destination, channel, handler_factory, &callback)
80
+ @respond_to_consumer.consume(
81
+ destination, channel, handler_adapter_factory, &callback
82
+ )
81
83
  end
82
84
 
83
85
  # Listens for messages without consuming them
@@ -9,5 +9,22 @@ class Freddy
9
9
  BunnyAdapter
10
10
  end
11
11
  end
12
+
13
+ module Shared
14
+ class Queue
15
+ def initialize(queue)
16
+ @queue = queue
17
+ end
18
+
19
+ def bind(*args)
20
+ @queue.bind(*args)
21
+ self
22
+ end
23
+
24
+ def name
25
+ @queue.name
26
+ end
27
+ end
28
+ end
12
29
  end
13
30
  end
@@ -45,26 +45,13 @@ class Freddy
45
45
  end
46
46
  end
47
47
 
48
- class Queue
49
- def initialize(queue)
50
- @queue = queue
51
- end
52
-
48
+ class Queue < Shared::Queue
53
49
  def subscribe(&block)
54
50
  @queue.subscribe do |info, properties, payload|
55
51
  parsed_payload = Payload.parse(payload)
56
52
  block.call(Delivery.new(parsed_payload, properties, info.routing_key))
57
53
  end
58
54
  end
59
-
60
- def bind(*args)
61
- @queue.bind(*args)
62
- self
63
- end
64
-
65
- def name
66
- @queue.name
67
- end
68
55
  end
69
56
  end
70
57
  end
@@ -44,26 +44,13 @@ class Freddy
44
44
  end
45
45
  end
46
46
 
47
- class Queue
48
- def initialize(queue)
49
- @queue = queue
50
- end
51
-
47
+ class Queue < Shared::Queue
52
48
  def subscribe(&block)
53
49
  @queue.subscribe do |meta, payload|
54
50
  parsed_payload = Payload.parse(payload)
55
51
  block.call(Delivery.new(parsed_payload, meta, meta.routing_key))
56
52
  end
57
53
  end
58
-
59
- def bind(*args)
60
- @queue.bind(*args)
61
- self
62
- end
63
-
64
- def name
65
- @queue.name
66
- end
67
54
  end
68
55
  end
69
56
  end
@@ -1 +1,13 @@
1
+ class Freddy
2
+ module Consumers
3
+ def self.log_receive_event(logger, queue_name, delivery)
4
+ if defined?(Logasm) && logger.is_a?(Logasm)
5
+ logger.debug "Received message", queue: queue_name, payload: delivery.payload, correlation_id: delivery.correlation_id
6
+ else
7
+ logger.debug "Received message on #{queue_name} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
8
+ end
9
+ end
10
+ end
11
+ end
12
+
1
13
  Dir[File.dirname(__FILE__) + '/consumers/*.rb'].each(&method(:require))
@@ -6,14 +6,14 @@ class Freddy
6
6
  @logger = logger
7
7
  end
8
8
 
9
- def consume(destination, channel, handler_factory, &block)
9
+ def consume(destination, channel, handler_adapter_factory, &block)
10
10
  consumer = consume_from_destination(destination, channel) do |delivery|
11
- log_receive_event(destination, delivery)
11
+ Consumers.log_receive_event(@logger, destination, delivery)
12
12
 
13
- handler = handler_factory.build(delivery.type, destination)
13
+ adapter = handler_adapter_factory.for(delivery)
14
14
 
15
- msg_handler = MessageHandler.new(handler, delivery)
16
- handler.handle_message delivery.payload, msg_handler, &block
15
+ msg_handler = MessageHandler.new(adapter, delivery)
16
+ block.call(delivery.payload, msg_handler)
17
17
  end
18
18
 
19
19
  ResponderHandler.new(consumer, @consume_thread_pool)
@@ -32,14 +32,6 @@ class Freddy
32
32
  block.call(delivery)
33
33
  end
34
34
  end
35
-
36
- def log_receive_event(destination, delivery)
37
- if defined?(Logasm) && @logger.is_a?(Logasm)
38
- @logger.debug "Received message", queue: destination, payload: delivery.payload, correlation_id: delivery.correlation_id
39
- else
40
- @logger.debug "Received message on #{destination} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
41
- end
42
- end
43
35
  end
44
36
  end
45
37
  end
@@ -18,18 +18,10 @@ class Freddy
18
18
 
19
19
  def process_message(queue, delivery, &block)
20
20
  @dedicated_thread_pool.process do
21
- log_receive_event(queue.name, delivery)
21
+ Consumers.log_receive_event(@logger, queue.name, delivery)
22
22
  block.call(delivery)
23
23
  end
24
24
  end
25
-
26
- def log_receive_event(queue_name, delivery)
27
- if defined?(Logasm) && @logger.is_a?(Logasm)
28
- @logger.debug "Received message", queue: queue_name, payload: delivery.payload, correlation_id: delivery.correlation_id
29
- else
30
- @logger.debug "Received message on #{queue_name} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
31
- end
32
- end
33
25
  end
34
26
  end
35
27
  end
@@ -1,7 +1,8 @@
1
1
  class Freddy
2
2
  module Consumers
3
3
  class TapIntoConsumer
4
- def initialize(consume_thread_pool)
4
+ def initialize(consume_thread_pool, logger)
5
+ @logger = logger
5
6
  @consume_thread_pool = consume_thread_pool
6
7
  end
7
8
 
@@ -9,7 +10,7 @@ class Freddy
9
10
  queue = create_queue(pattern, channel)
10
11
 
11
12
  consumer = queue.subscribe do |delivery|
12
- process_message(delivery, &block)
13
+ process_message(queue, delivery, &block)
13
14
  end
14
15
 
15
16
  ResponderHandler.new(consumer, @consume_thread_pool)
@@ -25,8 +26,9 @@ class Freddy
25
26
  .bind(topic_exchange, routing_key: pattern)
26
27
  end
27
28
 
28
- def process_message(delivery, &block)
29
+ def process_message(queue, delivery, &block)
29
30
  @consume_thread_pool.process do
31
+ Consumers.log_receive_event(@logger, queue.name, delivery)
30
32
  block.call delivery.payload, delivery.routing_key
31
33
  end
32
34
  end
@@ -1,19 +1,16 @@
1
1
  class Freddy
2
2
  class MessageHandler
3
- attr_reader :correlation_id
4
-
5
3
  def initialize(adapter, delivery)
6
4
  @adapter = adapter
7
5
  @delivery = delivery
8
- @correlation_id = @delivery.correlation_id
9
6
  end
10
7
 
11
8
  def success(response = nil)
12
- @adapter.success(@delivery.reply_to, response)
9
+ @adapter.success(@delivery, response)
13
10
  end
14
11
 
15
- def error(error = {error: "Couldn't process message"})
16
- @adapter.error(@delivery.reply_to, error)
12
+ def error(response = {error: "Couldn't process message"})
13
+ @adapter.error(@delivery, response)
17
14
  end
18
15
  end
19
16
  end
@@ -0,0 +1,50 @@
1
+ class Freddy
2
+ module MessageHandlerAdapters
3
+ class Factory
4
+ def initialize(producer)
5
+ @no_op_handler = NoOpHandler.new
6
+ @request_handler = RequestHandler.new(producer)
7
+ end
8
+
9
+ def for(delivery)
10
+ if delivery.type == 'request'
11
+ @request_handler
12
+ else
13
+ @no_op_handler
14
+ end
15
+ end
16
+ end
17
+
18
+ class NoOpHandler
19
+ def success(*)
20
+ # NOP
21
+ end
22
+
23
+ def error(*)
24
+ # NOP
25
+ end
26
+ end
27
+
28
+ class RequestHandler
29
+ def initialize(producer)
30
+ @producer = producer
31
+ end
32
+
33
+ def success(delivery, response)
34
+ send_response(delivery, response, type: 'success')
35
+ end
36
+
37
+ def error(delivery, response)
38
+ send_response(delivery, response, type: 'error')
39
+ end
40
+
41
+ private
42
+
43
+ def send_response(delivery, response, opts = {})
44
+ @producer.produce delivery.reply_to.force_encoding('utf-8'), response, {
45
+ correlation_id: delivery.correlation_id
46
+ }.merge(opts)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -17,27 +17,21 @@ class Freddy
17
17
  end
18
18
 
19
19
  @response_queue = @channel.queue("", exclusive: true)
20
- @request_manager.start
21
20
 
22
21
  @response_consumer = Consumers::ResponseConsumer.new(@logger)
23
22
  @response_consumer.consume(@response_queue, &method(:handle_response))
24
23
  end
25
24
 
26
- def produce(destination, payload, properties)
27
- timeout_in_seconds = properties.fetch(:timeout_in_seconds)
28
- container = SyncResponseContainer.new
29
- async_request destination, payload, properties, &container
30
- container.wait_for_response(timeout_in_seconds + 0.1)
31
- end
25
+ def produce(destination, payload, timeout_in_seconds:, delete_on_timeout:, **properties)
26
+ correlation_id = SecureRandom.uuid
32
27
 
33
- private
28
+ container = SyncResponseContainer.new(
29
+ on_timeout(correlation_id, destination, timeout_in_seconds)
30
+ )
34
31
 
35
- def async_request(destination, payload, timeout_in_seconds:, delete_on_timeout:, **properties, &block)
36
- correlation_id = SecureRandom.uuid
37
32
  @request_manager.store(correlation_id,
38
- callback: block,
39
- destination: destination,
40
- expires_at: Time.now + timeout_in_seconds
33
+ callback: container,
34
+ destination: destination
41
35
  )
42
36
 
43
37
  if delete_on_timeout
@@ -59,6 +53,8 @@ class Freddy
59
53
  # need to lock these.
60
54
  @topic_exchange.publish json_payload, properties.dup
61
55
  @exchange.publish json_payload, properties.dup
56
+
57
+ container.wait_for_response(timeout_in_seconds)
62
58
  end
63
59
 
64
60
  def handle_response(delivery)
@@ -78,11 +74,23 @@ class Freddy
78
74
  @logger.debug "Got response for request to #{request[:destination]} "\
79
75
  "with correlation_id #{delivery.correlation_id}"
80
76
  request[:callback].call(delivery.payload, delivery)
81
- rescue => e
82
- @logger.error "Exception occured while handling the response of "\
83
- "request made to #{request[:destination]} with "\
84
- "correlation_id #{correlation_id}: #{Utils.format_exception(e)}"
85
- Utils.notify_exception(e, destination: request[:destination], correlation_id: correlation_id)
77
+ end
78
+
79
+ def on_timeout(correlation_id, destination, timeout_in_seconds)
80
+ Proc.new do
81
+ @logger.warn "Request timed out waiting response from #{destination}"\
82
+ ", correlation id #{correlation_id}"
83
+
84
+ Utils.notify 'RequestTimeout',
85
+ "Request timed out waiting for response from #{destination}",
86
+ {
87
+ correlation_id: correlation_id,
88
+ destination: destination,
89
+ timeout_in_seconds: timeout_in_seconds
90
+ }
91
+
92
+ @request_manager.delete(correlation_id)
93
+ end
86
94
  end
87
95
  end
88
96
  end
@@ -1,20 +1,10 @@
1
1
  class Freddy
2
2
  class RequestManager
3
-
4
3
  def initialize(logger)
5
4
  @requests = Hamster.mutable_hash
6
5
  @logger = logger
7
6
  end
8
7
 
9
- def start
10
- @timeout_thread = Thread.new do
11
- while true do
12
- clear_timeouts Time.now
13
- sleep 0.05
14
- end
15
- end
16
- end
17
-
18
8
  def no_route(correlation_id)
19
9
  if request = @requests[correlation_id]
20
10
  @requests.delete correlation_id
@@ -29,26 +19,5 @@ class Freddy
29
19
  def delete(correlation_id)
30
20
  @requests.delete(correlation_id)
31
21
  end
32
-
33
- private
34
-
35
- def clear_timeouts(now)
36
- @requests.each do |key, value|
37
- timeout(key, value) if now > value[:expires_at]
38
- end
39
- end
40
-
41
- def timeout(correlation_id, request)
42
- @requests.delete correlation_id
43
-
44
- @logger.warn "Request timed out waiting response from #{request[:destination]}, correlation id #{correlation_id}"
45
- Utils.notify 'RequestTimeout', "Request timed out waiting for response from #{request[:destination]}", {
46
- correlation_id: correlation_id,
47
- destination: request[:destination],
48
- expires_at: request[:expires_at]
49
- }
50
-
51
- request[:callback].call({error: 'RequestTimeout', message: 'Timed out waiting for response'}, nil)
52
- end
53
22
  end
54
23
  end
@@ -3,41 +3,36 @@ require 'timeout'
3
3
 
4
4
  class Freddy
5
5
  class SyncResponseContainer
6
- def initialize
6
+ def initialize(on_timeout)
7
7
  @mutex = Mutex.new
8
+ @resource = ConditionVariable.new
9
+ @on_timeout = on_timeout
8
10
  end
9
11
 
10
12
  def call(response, delivery)
13
+ if response.nil?
14
+ raise StandardError, 'unexpected nil value for response'
15
+ end
16
+
11
17
  @response = response
12
18
  @delivery = delivery
13
- @mutex.synchronize { @waiting.wakeup }
19
+ @mutex.synchronize { @resource.signal }
14
20
  end
15
21
 
16
22
  def wait_for_response(timeout)
17
- @mutex.synchronize do
18
- @waiting = Thread.current
19
- @mutex.sleep(timeout)
20
- end
23
+ @mutex.synchronize { @response || @resource.wait(@mutex, timeout) }
21
24
 
22
- if @response.nil?
23
- raise Timeout::Error, 'execution expired'
24
- elsif @response[:error] == 'RequestTimeout'
25
- raise TimeoutError.new(@response)
25
+ if !@response
26
+ @on_timeout.call
27
+ raise TimeoutError.new(
28
+ error: 'RequestTimeout',
29
+ message: 'Timed out waiting for response'
30
+ )
26
31
  elsif !@delivery || @delivery.type == 'error'
27
32
  raise InvalidRequestError.new(@response)
28
33
  else
29
34
  @response
30
35
  end
31
36
  end
32
-
33
- private
34
-
35
- def to_proc
36
- Proc.new {|*args| self.call(*args)}
37
- end
38
-
39
- def filled?
40
- !@response.nil?
41
- end
42
37
  end
43
38
  end
data/lib/freddy/utils.rb CHANGED
@@ -1,12 +1,10 @@
1
1
  class Freddy
2
2
  class Utils
3
3
  def self.format_exception(exception)
4
- backtrace = exception.backtrace.map do |x|
5
- x.match(/^(.+?):(\d+)(|:in `(.+)')$/);
6
- [$1, $2, $4]
7
- end.join("\n")
4
+ message = exception.message
5
+ backtrace = exception.backtrace.join("\n")
8
6
 
9
- "#{exception.exception}\n#{backtrace}"
7
+ "#{message}\n#{backtrace}"
10
8
  end
11
9
 
12
10
  def self.notify(name, message, parameters={})
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::Consumers do
4
+ describe '.log_receive_event' do
5
+ subject { described_class.log_receive_event(logger, queue_name, delivery) }
6
+
7
+ let(:queue_name) { 'salemove' }
8
+ let(:delivery) do
9
+ instance_double(Freddy::Delivery,
10
+ payload: {key: 'value'},
11
+ correlation_id: 'a1b2'
12
+ )
13
+ end
14
+
15
+ context 'when configured with logasm logger' do
16
+ let(:logger) { logasm_class.new }
17
+ let(:logasm_class) { Class.new }
18
+
19
+ before do
20
+ stub_const('::Logasm', logasm_class)
21
+ end
22
+
23
+ it 'logs the received event' do
24
+ expect(logger).to receive(:debug).with('Received message',
25
+ queue: 'salemove', payload: {key: 'value'}, correlation_id: 'a1b2'
26
+ )
27
+
28
+ subject
29
+ end
30
+ end
31
+
32
+ context 'when configured with regular logger' do
33
+ let(:logger) { Logger.new('/dev/null') }
34
+
35
+ it 'logs the received event' do
36
+ expect(logger).to receive(:debug)
37
+ .with('Received message on salemove with payload {:key=>"value"} with correlation_id a1b2')
38
+
39
+ subject
40
+ end
41
+ end
42
+ end
43
+ end
@@ -4,12 +4,11 @@ describe Freddy::MessageHandler do
4
4
  subject(:handler) { described_class.new(adapter, delivery) }
5
5
 
6
6
  let(:adapter) { double }
7
- let(:delivery) { double(reply_to: reply_to, correlation_id: 'abc') }
8
- let(:reply_to) { double }
7
+ let(:delivery) { double }
9
8
 
10
9
  describe '#success' do
11
10
  it 'delegates to the adapter' do
12
- expect(adapter).to receive(:success).with(reply_to, x: 'y')
11
+ expect(adapter).to receive(:success).with(delivery, x: 'y')
13
12
 
14
13
  subject.success(x: 'y')
15
14
  end
@@ -17,7 +16,7 @@ describe Freddy::MessageHandler do
17
16
 
18
17
  describe '#error' do
19
18
  it 'delegates to the adapter' do
20
- expect(adapter).to receive(:error).with(reply_to, error: 'text')
19
+ expect(adapter).to receive(:error).with(delivery, error: 'text')
21
20
 
22
21
  subject.error(error: 'text')
23
22
  end
@@ -1,13 +1,54 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Freddy::SyncResponseContainer do
4
- let(:container) { described_class.new }
4
+ let(:container) { described_class.new(on_timeout) }
5
+ let(:on_timeout) { Proc.new {} }
5
6
 
6
7
  context 'when timeout' do
7
8
  subject { container.wait_for_response(0.01) }
8
9
 
9
10
  it 'raises timeout error' do
10
- expect { subject }.to raise_error(Timeout::Error, 'execution expired')
11
+ expect { subject }.to raise_error do |error|
12
+ expect(error).to be_a(Freddy::TimeoutError)
13
+ expect(error.response).to eq(
14
+ error: 'RequestTimeout',
15
+ message: 'Timed out waiting for response'
16
+ )
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'when nil response' do
22
+ let(:delivery) { {} }
23
+
24
+ it 'raises timeout error' do
25
+ expect {
26
+ container.call(nil, delivery)
27
+ }.to raise_error(StandardError, 'unexpected nil value for response')
28
+ end
29
+ end
30
+
31
+ describe '#wait_for_response' do
32
+ let(:timeout) { 2 }
33
+ let(:response) { {msg: 'response'} }
34
+ let(:delivery) { OpenStruct.new(type: 'success') }
35
+
36
+ context 'when called after #call' do
37
+ let(:max_wait_time_in_seconds) { 0.5 }
38
+
39
+ before do
40
+ container.call(response, delivery)
41
+ end
42
+
43
+ it 'returns response' do
44
+ expect(container.wait_for_response(timeout)).to eq(response)
45
+ end
46
+
47
+ it 'does not wait for timeout' do
48
+ expect {
49
+ container.wait_for_response(timeout)
50
+ }.to change(Time, :now).by_at_most(max_wait_time_in_seconds)
51
+ end
11
52
  end
12
53
  end
13
54
  end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::Utils do
4
+ describe '.format_exception' do
5
+ subject { described_class.format_exception(exception) }
6
+
7
+ let(:exception) { double(backtrace: backtrace, message: message) }
8
+ let(:message) { 'format exception test' }
9
+ let(:backtrace) { ['line1', 'line2', 'line3'] }
10
+
11
+ it 'format the exception' do
12
+ should eq "format exception test\n" \
13
+ "line1\n" \
14
+ "line2\n" \
15
+ 'line3'
16
+ end
17
+ end
18
+
19
+ describe '.notify' do
20
+ subject { described_class.notify(error_class, error_message, parameters) }
21
+
22
+ let(:env_attributes) { double }
23
+ let(:error_class) { double }
24
+ let(:error_message) { double }
25
+ let(:parameters) { double }
26
+
27
+ context 'when Airbrake is defined' do
28
+ let(:airbrake) { double }
29
+
30
+ before do
31
+ allow(ENV).to receive(:to_hash) { env_attributes }
32
+ stub_const('::Airbrake', airbrake)
33
+ end
34
+
35
+ it 'notifies airbrake' do
36
+ expect(airbrake).to receive(:notify_or_ignore).with(
37
+ error_class: error_class,
38
+ error_message: error_message,
39
+ cgi_data: env_attributes,
40
+ parameters: parameters
41
+ )
42
+
43
+ subject
44
+ end
45
+ end
46
+
47
+ context 'when Airbrake is not defined' do
48
+ it 'does nothing' do
49
+ should eq(nil)
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '.notify_exception' do
55
+ subject { described_class.notify_exception(exception, {a: 'b'}) }
56
+
57
+ let(:exception) { double }
58
+
59
+ context 'when Airbrake is defined' do
60
+ let(:airbrake) { double }
61
+
62
+ before do
63
+ stub_const('::Airbrake', airbrake)
64
+ end
65
+
66
+ it 'notifies airbrake' do
67
+ expect(airbrake).to receive(:notify_or_ignore) do |ex, content|
68
+ expect(ex).to eq(exception)
69
+ expect(content[:cgi_data]).to be_instance_of(Hash)
70
+ expect(content[:parameters]).to eq(a: 'b')
71
+ end
72
+
73
+ subject
74
+ end
75
+ end
76
+
77
+ context 'when Airbrake is not defined' do
78
+ it 'does nothing' do
79
+ should eq(nil)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Reply' do
4
+ let(:freddy) { Freddy.build(logger, config) }
5
+
6
+ let(:destination) { random_destination }
7
+ let(:request_payload) { {req: 'load'} }
8
+ let(:response_payload) { {res: 'load'} }
9
+
10
+ after { freddy.close }
11
+
12
+ context 'when a synchronized request' do
13
+ before do
14
+ freddy.respond_to(destination) do |payload, msg_handler|
15
+ msg_handler.success(response_payload)
16
+ end
17
+ end
18
+
19
+ it 'sends reply' do
20
+ response = freddy.deliver_with_response(destination, request_payload)
21
+ expect(response).to eq(response_payload)
22
+ end
23
+
24
+ it 'does not send the reply to the topic queue' do
25
+ freddy.tap_into 'amq.*' do |payload|
26
+ @message_received = true
27
+ end
28
+
29
+ freddy.deliver_with_response(destination, request_payload)
30
+ default_sleep
31
+
32
+ expect(@message_received).to be_falsy
33
+ end
34
+ end
35
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,16 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'codeclimate-test-reporter'
6
+ SimpleCov.start do
7
+ formatter SimpleCov::Formatter::MultiFormatter.new([
8
+ SimpleCov::Formatter::HTMLFormatter,
9
+ CodeClimate::TestReporter::Formatter
10
+ ])
11
+ add_filter '/spec/'
12
+ end
13
+
1
14
  require 'pry'
2
15
  require 'securerandom'
3
16
  require 'freddy'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freddy-jruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Urmas Talimaa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-08 00:00:00.000000000 Z
11
+ date: 2016-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -124,7 +124,7 @@ files:
124
124
  - lib/freddy/error_response.rb
125
125
  - lib/freddy/invalid_request_error.rb
126
126
  - lib/freddy/message_handler.rb
127
- - lib/freddy/message_handlers.rb
127
+ - lib/freddy/message_handler_adapaters.rb
128
128
  - lib/freddy/payload.rb
129
129
  - lib/freddy/producers.rb
130
130
  - lib/freddy/producers/reply_producer.rb
@@ -136,13 +136,16 @@ files:
136
136
  - lib/freddy/timeout_error.rb
137
137
  - lib/freddy/utils.rb
138
138
  - spec/freddy/consumers/respond_to_consumer_spec.rb
139
+ - spec/freddy/consumers_spec.rb
139
140
  - spec/freddy/error_response_spec.rb
140
141
  - spec/freddy/freddy_spec.rb
141
142
  - spec/freddy/message_handler_spec.rb
142
143
  - spec/freddy/responder_handler_spec.rb
143
144
  - spec/freddy/sync_response_container_spec.rb
145
+ - spec/freddy/utils_spec.rb
144
146
  - spec/integration/concurrency_spec.rb
145
147
  - spec/integration/logging_spec.rb
148
+ - spec/integration/reply_spec.rb
146
149
  - spec/spec_helper.rb
147
150
  homepage:
148
151
  licenses:
@@ -170,11 +173,14 @@ specification_version: 4
170
173
  summary: API for inter-application messaging supporting acknowledgements and request-response
171
174
  test_files:
172
175
  - spec/freddy/consumers/respond_to_consumer_spec.rb
176
+ - spec/freddy/consumers_spec.rb
173
177
  - spec/freddy/error_response_spec.rb
174
178
  - spec/freddy/freddy_spec.rb
175
179
  - spec/freddy/message_handler_spec.rb
176
180
  - spec/freddy/responder_handler_spec.rb
177
181
  - spec/freddy/sync_response_container_spec.rb
182
+ - spec/freddy/utils_spec.rb
178
183
  - spec/integration/concurrency_spec.rb
179
184
  - spec/integration/logging_spec.rb
185
+ - spec/integration/reply_spec.rb
180
186
  - spec/spec_helper.rb
@@ -1,77 +0,0 @@
1
- class Freddy
2
- module MessageHandlers
3
- class Factory
4
- def initialize(producer, logger)
5
- @producer = producer
6
- @logger = logger
7
- end
8
-
9
- def build(type, destination)
10
- if type == 'request'
11
- RequestHandler.new(@producer, destination, @logger)
12
- else
13
- StandardMessageHandler.new(destination, @logger)
14
- end
15
- end
16
- end
17
-
18
- class StandardMessageHandler
19
- def initialize(destination, logger)
20
- @destination = destination
21
- @logger = logger
22
- end
23
-
24
- def handle_message(payload, msg_handler, &block)
25
- block.call payload, msg_handler
26
- rescue Exception => e
27
- @logger.error "Exception occured while processing message from #{Utils.format_exception(e)}"
28
- Utils.notify_exception(e, destination: @destination)
29
- end
30
-
31
- def success(*)
32
- # NOP
33
- end
34
-
35
- def error(*)
36
- # NOP
37
- end
38
- end
39
-
40
- class RequestHandler
41
- def initialize(producer, destination, logger)
42
- @producer = producer
43
- @logger = logger
44
- @destination = destination
45
- end
46
-
47
- def handle_message(payload, msg_handler, &block)
48
- @correlation_id = msg_handler.correlation_id
49
-
50
- if !@correlation_id
51
- @logger.error "Received request without correlation_id"
52
- else
53
- block.call payload, msg_handler
54
- end
55
- rescue Exception => e
56
- @logger.error "Exception occured while handling the request with correlation_id #{@correlation_id}: #{Utils.format_exception(e)}"
57
- Utils.notify_exception(e, correlation_id: @correlation_id, destination: @destination)
58
- end
59
-
60
- def success(reply_to, response)
61
- send_response(reply_to, response, type: 'success')
62
- end
63
-
64
- def error(reply_to, response)
65
- send_response(reply_to, response, type: 'error')
66
- end
67
-
68
- private
69
-
70
- def send_response(reply_to, response, opts = {})
71
- @producer.produce reply_to.force_encoding('utf-8'), response, {
72
- correlation_id: @correlation_id
73
- }.merge(opts)
74
- end
75
- end
76
- end
77
- end