freddy 1.7.0 → 2.0.0
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/ci.yml +31 -0
- data/.rubocop.yml +9 -28
- data/.ruby-gemset +1 -1
- data/.ruby-version +1 -1
- data/Gemfile +3 -4
- data/README.md +5 -10
- data/freddy.gemspec +10 -17
- data/lib/freddy.rb +9 -14
- data/lib/freddy/adapters.rb +3 -28
- data/lib/freddy/adapters/bunny_adapter.rb +19 -2
- data/lib/freddy/consumers.rb +1 -1
- data/lib/freddy/consumers/respond_to_consumer.rb +3 -13
- data/lib/freddy/consumers/response_consumer.rb +2 -4
- data/lib/freddy/consumers/tap_into_consumer.rb +12 -24
- data/lib/freddy/delivery.rb +46 -13
- data/lib/freddy/payload.rb +2 -33
- data/lib/freddy/producers.rb +1 -1
- data/lib/freddy/producers/reply_producer.rb +12 -5
- data/lib/freddy/producers/send_and_forget_producer.rb +5 -11
- data/lib/freddy/producers/send_and_wait_response_producer.rb +19 -33
- data/lib/freddy/request_manager.rb +1 -9
- data/lib/freddy/tracing.rb +37 -0
- data/lib/freddy/version.rb +5 -0
- data/spec/.rubocop.yml +26 -0
- data/spec/freddy/error_response_spec.rb +6 -6
- data/spec/freddy/payload_spec.rb +25 -16
- data/spec/integration/concurrency_spec.rb +8 -12
- data/spec/integration/tracing_spec.rb +15 -32
- data/spec/spec_helper.rb +5 -13
- metadata +28 -15
- data/.npmignore +0 -8
- data/.travis.yml +0 -16
- data/lib/freddy/adapters/march_hare_adapter.rb +0 -64
- data/lib/freddy/trace_carrier.rb +0 -28
- data/spec/freddy/trace_carrier_spec.rb +0 -56
    
        data/lib/freddy/payload.rb
    CHANGED
    
    | @@ -1,11 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
              require 'oj'
         | 
| 5 | 
            -
            rescue LoadError
         | 
| 6 | 
            -
              require 'symbolizer'
         | 
| 7 | 
            -
              require 'json'
         | 
| 8 | 
            -
            end
         | 
| 3 | 
            +
            require 'oj'
         | 
| 9 4 |  | 
| 10 5 | 
             
            class Freddy
         | 
| 11 6 | 
             
              class Payload
         | 
| @@ -20,7 +15,7 @@ class Freddy | |
| 20 15 | 
             
                end
         | 
| 21 16 |  | 
| 22 17 | 
             
                def self.json_handler
         | 
| 23 | 
            -
                  @json_handler ||=  | 
| 18 | 
            +
                  @json_handler ||= OjAdapter
         | 
| 24 19 | 
             
                end
         | 
| 25 20 |  | 
| 26 21 | 
             
                class OjAdapter
         | 
| @@ -35,31 +30,5 @@ class Freddy | |
| 35 30 | 
             
                    Oj.dump(payload, DUMP_OPTIONS)
         | 
| 36 31 | 
             
                  end
         | 
| 37 32 | 
             
                end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                class JsonAdapter
         | 
| 40 | 
            -
                  def self.parse(payload)
         | 
| 41 | 
            -
                    # MRI has :symbolize_keys, but JRuby does not. Not adding it at the
         | 
| 42 | 
            -
                    # moment.
         | 
| 43 | 
            -
                    Symbolizer.symbolize(JSON.parse(payload))
         | 
| 44 | 
            -
                  end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                  def self.dump(payload)
         | 
| 47 | 
            -
                    JSON.dump(serialize_time_objects(payload))
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                  def self.serialize_time_objects(object)
         | 
| 51 | 
            -
                    if object.is_a?(Hash)
         | 
| 52 | 
            -
                      object.reduce({}) do |hash, (key, value)|
         | 
| 53 | 
            -
                        hash.merge(key => serialize_time_objects(value))
         | 
| 54 | 
            -
                      end
         | 
| 55 | 
            -
                    elsif object.is_a?(Array)
         | 
| 56 | 
            -
                      object.map(&method(:serialize_time_objects))
         | 
| 57 | 
            -
                    elsif object.is_a?(Time) || object.is_a?(Date)
         | 
| 58 | 
            -
                      object.iso8601
         | 
| 59 | 
            -
                    else
         | 
| 60 | 
            -
                      object
         | 
| 61 | 
            -
                    end
         | 
| 62 | 
            -
                  end
         | 
| 63 | 
            -
                end
         | 
| 64 33 | 
             
              end
         | 
| 65 34 | 
             
            end
         | 
    
        data/lib/freddy/producers.rb
    CHANGED
    
    
| @@ -10,17 +10,24 @@ class Freddy | |
| 10 10 | 
             
                    @exchange = channel.default_exchange
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 | 
            -
                  def produce( | 
| 14 | 
            -
                     | 
| 15 | 
            -
                       | 
| 16 | 
            -
             | 
| 13 | 
            +
                  def produce(routing_key, payload, properties)
         | 
| 14 | 
            +
                    span = Tracing.span_for_produce(
         | 
| 15 | 
            +
                      @exchange,
         | 
| 16 | 
            +
                      routing_key,
         | 
| 17 | 
            +
                      payload,
         | 
| 18 | 
            +
                      correlation_id: properties[:correlation_id]
         | 
| 19 | 
            +
                    )
         | 
| 17 20 |  | 
| 18 21 | 
             
                    properties = properties.merge(
         | 
| 19 | 
            -
                      routing_key:  | 
| 22 | 
            +
                      routing_key: routing_key,
         | 
| 20 23 | 
             
                      content_type: CONTENT_TYPE
         | 
| 21 24 | 
             
                    )
         | 
| 25 | 
            +
                    Tracing.inject_tracing_information_to_properties!(properties)
         | 
| 22 26 |  | 
| 23 27 | 
             
                    @exchange.publish Payload.dump(payload), properties
         | 
| 28 | 
            +
                  ensure
         | 
| 29 | 
            +
                    # We won't wait for a reply. Just finish the span immediately.
         | 
| 30 | 
            +
                    span.finish
         | 
| 24 31 | 
             
                  end
         | 
| 25 32 | 
             
                end
         | 
| 26 33 | 
             
              end
         | 
| @@ -11,19 +11,15 @@ class Freddy | |
| 11 11 | 
             
                    @topic_exchange = channel.topic Freddy::FREDDY_TOPIC_EXCHANGE_NAME
         | 
| 12 12 | 
             
                  end
         | 
| 13 13 |  | 
| 14 | 
            -
                  def produce( | 
| 15 | 
            -
                    span =  | 
| 16 | 
            -
                                                  tags: {
         | 
| 17 | 
            -
                                                    'message_bus.destination' => destination,
         | 
| 18 | 
            -
                                                    'component' => 'freddy',
         | 
| 19 | 
            -
                                                    'span.kind' => 'producer' # Message Bus
         | 
| 20 | 
            -
                                                  })
         | 
| 14 | 
            +
                  def produce(routing_key, payload, properties)
         | 
| 15 | 
            +
                    span = Tracing.span_for_produce(@topic_exchange, routing_key, payload)
         | 
| 21 16 |  | 
| 22 17 | 
             
                    properties = properties.merge(
         | 
| 23 | 
            -
                      routing_key:  | 
| 18 | 
            +
                      routing_key: routing_key,
         | 
| 24 19 | 
             
                      content_type: CONTENT_TYPE
         | 
| 25 20 | 
             
                    )
         | 
| 26 | 
            -
                     | 
| 21 | 
            +
                    Tracing.inject_tracing_information_to_properties!(properties)
         | 
| 22 | 
            +
             | 
| 27 23 | 
             
                    json_payload = Payload.dump(payload)
         | 
| 28 24 |  | 
| 29 25 | 
             
                    # Connection adapters handle thread safety for #publish themselves. No
         | 
| @@ -33,8 +29,6 @@ class Freddy | |
| 33 29 | 
             
                  ensure
         | 
| 34 30 | 
             
                    # We don't know how many listeners there are and we do not know when
         | 
| 35 31 | 
             
                    # this message gets processed. Instead we close the span immediately.
         | 
| 36 | 
            -
                    # Listeners should use FollowsFrom to add trace information.
         | 
| 37 | 
            -
                    # https://github.com/opentracing/specification/blob/master/specification.md
         | 
| 38 32 | 
             
                    span.finish
         | 
| 39 33 | 
             
                  end
         | 
| 40 34 | 
             
                end
         | 
| @@ -12,7 +12,6 @@ class Freddy | |
| 12 12 | 
             
                    @request_manager = RequestManager.new(@logger)
         | 
| 13 13 |  | 
| 14 14 | 
             
                    @exchange = @channel.default_exchange
         | 
| 15 | 
            -
                    @topic_exchange = @channel.topic Freddy::FREDDY_TOPIC_EXCHANGE_NAME
         | 
| 16 15 |  | 
| 17 16 | 
             
                    @channel.on_no_route do |correlation_id|
         | 
| 18 17 | 
             
                      @request_manager.no_route(correlation_id)
         | 
| @@ -24,43 +23,37 @@ class Freddy | |
| 24 23 | 
             
                    @response_consumer.consume(@channel, @response_queue, &method(:handle_response))
         | 
| 25 24 | 
             
                  end
         | 
| 26 25 |  | 
| 27 | 
            -
                  def produce( | 
| 26 | 
            +
                  def produce(routing_key, payload, timeout_in_seconds:, delete_on_timeout:, **properties)
         | 
| 28 27 | 
             
                    correlation_id = SecureRandom.uuid
         | 
| 29 28 |  | 
| 30 | 
            -
                    span =  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
                                                    'message_bus.response_queue' => @response_queue.name,
         | 
| 37 | 
            -
                                                    'message_bus.correlation_id' => correlation_id,
         | 
| 38 | 
            -
                                                    'freddy.timeout_in_seconds' => timeout_in_seconds
         | 
| 39 | 
            -
                                                  })
         | 
| 29 | 
            +
                    span = Tracing.span_for_produce(
         | 
| 30 | 
            +
                      @exchange,
         | 
| 31 | 
            +
                      routing_key,
         | 
| 32 | 
            +
                      payload,
         | 
| 33 | 
            +
                      correlation_id: correlation_id, timeout_in_seconds: timeout_in_seconds
         | 
| 34 | 
            +
                    )
         | 
| 40 35 |  | 
| 41 36 | 
             
                    container = SyncResponseContainer.new(
         | 
| 42 | 
            -
                      on_timeout(correlation_id,  | 
| 37 | 
            +
                      on_timeout(correlation_id, routing_key, timeout_in_seconds, span)
         | 
| 43 38 | 
             
                    )
         | 
| 44 39 |  | 
| 45 40 | 
             
                    @request_manager.store(correlation_id,
         | 
| 46 41 | 
             
                                           callback: container,
         | 
| 47 42 | 
             
                                           span: span,
         | 
| 48 | 
            -
                                           destination:  | 
| 43 | 
            +
                                           destination: routing_key)
         | 
| 49 44 |  | 
| 50 45 | 
             
                    properties[:expiration] = (timeout_in_seconds * 1000).to_i if delete_on_timeout
         | 
| 51 46 |  | 
| 52 47 | 
             
                    properties = properties.merge(
         | 
| 53 | 
            -
                      routing_key:  | 
| 48 | 
            +
                      routing_key: routing_key, content_type: CONTENT_TYPE,
         | 
| 54 49 | 
             
                      correlation_id: correlation_id, reply_to: @response_queue.name,
         | 
| 55 50 | 
             
                      mandatory: true, type: 'request'
         | 
| 56 51 | 
             
                    )
         | 
| 57 | 
            -
                     | 
| 58 | 
            -
                    json_payload = Payload.dump(payload)
         | 
| 52 | 
            +
                    Tracing.inject_tracing_information_to_properties!(properties)
         | 
| 59 53 |  | 
| 60 54 | 
             
                    # Connection adapters handle thread safety for #publish themselves. No
         | 
| 61 | 
            -
                    # need to lock  | 
| 62 | 
            -
                    @ | 
| 63 | 
            -
                    @exchange.publish json_payload, properties.dup
         | 
| 55 | 
            +
                    # need to lock this.
         | 
| 56 | 
            +
                    @exchange.publish Payload.dump(payload), properties.dup
         | 
| 64 57 |  | 
| 65 58 | 
             
                    container.wait_for_response(timeout_in_seconds)
         | 
| 66 59 | 
             
                  end
         | 
| @@ -82,28 +75,21 @@ class Freddy | |
| 82 75 | 
             
                                  "with correlation_id #{delivery.correlation_id}"
         | 
| 83 76 | 
             
                    request[:callback].call(delivery.payload, delivery)
         | 
| 84 77 | 
             
                  rescue InvalidRequestError => e
         | 
| 85 | 
            -
                    request[:span]. | 
| 86 | 
            -
                    request[:span]. | 
| 87 | 
            -
                      event: 'invalid request',
         | 
| 88 | 
            -
                      message: e.message,
         | 
| 89 | 
            -
                      'error.object': e
         | 
| 90 | 
            -
                    )
         | 
| 78 | 
            +
                    request[:span].record_exception(e)
         | 
| 79 | 
            +
                    request[:span].status = OpenTelemetry::Trace::Status.error
         | 
| 91 80 | 
             
                    raise e
         | 
| 92 81 | 
             
                  ensure
         | 
| 93 82 | 
             
                    request[:span].finish
         | 
| 94 83 | 
             
                  end
         | 
| 95 84 |  | 
| 96 | 
            -
                  def on_timeout(correlation_id,  | 
| 85 | 
            +
                  def on_timeout(correlation_id, routing_key, timeout_in_seconds, span)
         | 
| 97 86 | 
             
                    proc do
         | 
| 98 | 
            -
                      @logger.warn "Request timed out waiting response from #{ | 
| 87 | 
            +
                      @logger.warn "Request timed out waiting response from #{routing_key}"\
         | 
| 99 88 | 
             
                                   ", correlation id #{correlation_id}, timeout #{timeout_in_seconds}s"
         | 
| 100 89 |  | 
| 101 90 | 
             
                      @request_manager.delete(correlation_id)
         | 
| 102 | 
            -
                      span. | 
| 103 | 
            -
                      span. | 
| 104 | 
            -
                        event: 'timed out',
         | 
| 105 | 
            -
                        message: "Timed out waiting response from #{destination}"
         | 
| 106 | 
            -
                      )
         | 
| 91 | 
            +
                      span.add_event('timeout')
         | 
| 92 | 
            +
                      span.status = OpenTelemetry::Trace::Status.error("Timed out waiting response from #{routing_key}")
         | 
| 107 93 | 
             
                      span.finish
         | 
| 108 94 | 
             
                    end
         | 
| 109 95 | 
             
                  end
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            class Freddy
         | 
| 4 4 | 
             
              class RequestManager
         | 
| 5 5 | 
             
                def initialize(logger)
         | 
| 6 | 
            -
                  @requests =  | 
| 6 | 
            +
                  @requests = {}
         | 
| 7 7 | 
             
                  @logger = logger
         | 
| 8 8 | 
             
                end
         | 
| 9 9 |  | 
| @@ -22,13 +22,5 @@ class Freddy | |
| 22 22 | 
             
                def delete(correlation_id)
         | 
| 23 23 | 
             
                  @requests.delete(correlation_id)
         | 
| 24 24 | 
             
                end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                class ConcurrentHash < Hash
         | 
| 27 | 
            -
                  # CRuby hash does not need any locks. Only adding when using JRuby.
         | 
| 28 | 
            -
                  if RUBY_PLATFORM == 'java'
         | 
| 29 | 
            -
                    require 'jruby/synchronized'
         | 
| 30 | 
            -
                    include JRuby::Synchronized
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
                end
         | 
| 33 25 | 
             
              end
         | 
| 34 26 | 
             
            end
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Freddy
         | 
| 4 | 
            +
              module Tracing
         | 
| 5 | 
            +
                # NOTE: Make sure you finish the span youself.
         | 
| 6 | 
            +
                def self.span_for_produce(exchange, routing_key, payload, correlation_id: nil, timeout_in_seconds: nil)
         | 
| 7 | 
            +
                  destination = exchange.name
         | 
| 8 | 
            +
                  destination_kind = exchange.type == :direct ? 'queue' : 'topic'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  attributes = {
         | 
| 11 | 
            +
                    'payload.type' => (payload[:type] || 'unknown').to_s,
         | 
| 12 | 
            +
                    OpenTelemetry::SemanticConventions::Trace::MESSAGING_SYSTEM => 'rabbitmq',
         | 
| 13 | 
            +
                    OpenTelemetry::SemanticConventions::Trace::MESSAGING_RABBITMQ_ROUTING_KEY => routing_key,
         | 
| 14 | 
            +
                    OpenTelemetry::SemanticConventions::Trace::MESSAGING_DESTINATION => destination,
         | 
| 15 | 
            +
                    OpenTelemetry::SemanticConventions::Trace::MESSAGING_DESTINATION_KIND => destination_kind,
         | 
| 16 | 
            +
                    OpenTelemetry::SemanticConventions::Trace::MESSAGING_OPERATION => 'send'
         | 
| 17 | 
            +
                  }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  attributes['freddy.timeout_in_seconds'] = timeout_in_seconds if timeout_in_seconds
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  if correlation_id
         | 
| 22 | 
            +
                    attributes[OpenTelemetry::SemanticConventions::Trace::MESSAGING_CONVERSATION_ID] = correlation_id
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  Freddy.tracer.start_span(
         | 
| 26 | 
            +
                    ".#{routing_key} send",
         | 
| 27 | 
            +
                    kind: OpenTelemetry::Trace::SpanKind::PRODUCER,
         | 
| 28 | 
            +
                    attributes: attributes
         | 
| 29 | 
            +
                  )
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def self.inject_tracing_information_to_properties!(properties)
         | 
| 33 | 
            +
                  properties[:headers] ||= {}
         | 
| 34 | 
            +
                  OpenTelemetry.propagation.inject(properties[:headers])
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
    
        data/spec/.rubocop.yml
    ADDED
    
    | @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            require: rubocop-rspec
         | 
| 2 | 
            +
            inherit_from: ../.rubocop.yml
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            RSpec/ExampleLength:
         | 
| 5 | 
            +
              Enabled: no
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            RSpec/MultipleExpectations:
         | 
| 8 | 
            +
              Enabled: no
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            RSpec/MessageSpies:
         | 
| 11 | 
            +
              Enabled: no
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            RSpec/VerifiedDoubles:
         | 
| 14 | 
            +
              Enabled: no
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            RSpec/InstanceVariable:
         | 
| 17 | 
            +
              Enabled: no
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            RSpec/NestedGroups:
         | 
| 20 | 
            +
              Enabled: no
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            RSpec/DescribeClass:
         | 
| 23 | 
            +
              Enabled: no
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            RSpec/MultipleMemoizedHelpers:
         | 
| 26 | 
            +
              Enabled: no
         | 
| @@ -13,10 +13,10 @@ describe Freddy::ErrorResponse do | |
| 13 13 | 
             
                end
         | 
| 14 14 |  | 
| 15 15 | 
             
                describe '#message' do
         | 
| 16 | 
            -
                  subject { error.message }
         | 
| 16 | 
            +
                  subject(:message) { error.message }
         | 
| 17 17 |  | 
| 18 18 | 
             
                  it 'uses error type as a message' do
         | 
| 19 | 
            -
                     | 
| 19 | 
            +
                    expect(message).to eq('SomeError')
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 | 
             
                end
         | 
| 22 22 | 
             
              end
         | 
| @@ -31,10 +31,10 @@ describe Freddy::ErrorResponse do | |
| 31 31 | 
             
                end
         | 
| 32 32 |  | 
| 33 33 | 
             
                describe '#message' do
         | 
| 34 | 
            -
                  subject { error.message }
         | 
| 34 | 
            +
                  subject(:message) { error.message }
         | 
| 35 35 |  | 
| 36 36 | 
             
                  it 'uses error type as a message' do
         | 
| 37 | 
            -
                     | 
| 37 | 
            +
                    expect(message).to eq('SomeError: extra info')
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 | 
             
                end
         | 
| 40 40 | 
             
              end
         | 
| @@ -49,10 +49,10 @@ describe Freddy::ErrorResponse do | |
| 49 49 | 
             
                end
         | 
| 50 50 |  | 
| 51 51 | 
             
                describe '#message' do
         | 
| 52 | 
            -
                  subject { error.message }
         | 
| 52 | 
            +
                  subject(:message) { error.message }
         | 
| 53 53 |  | 
| 54 54 | 
             
                  it 'uses default error message as a message' do
         | 
| 55 | 
            -
                     | 
| 55 | 
            +
                    expect(message).to eq('Use #response to get the error response')
         | 
| 56 56 | 
             
                  end
         | 
| 57 57 | 
             
                end
         | 
| 58 58 | 
             
              end
         | 
    
        data/spec/freddy/payload_spec.rb
    CHANGED
    
    | @@ -2,19 +2,28 @@ require 'spec_helper' | |
| 2 2 |  | 
| 3 3 | 
             
            describe Freddy::Payload do
         | 
| 4 4 | 
             
              describe '#dump' do
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                   | 
| 7 | 
            -
                     | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
                   | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
                   | 
| 17 | 
            -
             | 
| 5 | 
            +
                context 'with a given Ruby engine' do
         | 
| 6 | 
            +
                  let(:ts) do
         | 
| 7 | 
            +
                    RUBY_ENGINE == 'jruby' ? '{"time":"2016-01-04T20:18:00Z"}' : '{"time":"2016-01-04T20:18:00.000000Z"}'
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                  let(:ts_array) do
         | 
| 10 | 
            +
                    RUBY_ENGINE == 'jruby' ? '{"time":["2016-01-04T20:18:00Z"]}' : '{"time":["2016-01-04T20:18:00.000000Z"]}'
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  it 'serializes time objects as iso8601 format strings' do
         | 
| 14 | 
            +
                    expect(dump(time: Time.utc(2016, 1, 4, 20, 18)))
         | 
| 15 | 
            +
                      .to eq(ts)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  it 'serializes time objects in an array as iso8601 format strings' do
         | 
| 19 | 
            +
                    expect(dump(time: [Time.utc(2016, 1, 4, 20, 18)]))
         | 
| 20 | 
            +
                      .to eq(ts_array)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  it 'serializes time objects in a nested hash as iso8601 format strings' do
         | 
| 24 | 
            +
                    expect(dump(x: { time: Time.utc(2016, 1, 4, 20, 18) }))
         | 
| 25 | 
            +
                      .to eq("{\"x\":#{ts}}")
         | 
| 26 | 
            +
                  end
         | 
| 18 27 | 
             
                end
         | 
| 19 28 |  | 
| 20 29 | 
             
                it 'serializes date objects as iso8601 format strings' do
         | 
| @@ -33,17 +42,17 @@ describe Freddy::Payload do | |
| 33 42 | 
             
                end
         | 
| 34 43 |  | 
| 35 44 | 
             
                it 'serializes datetime objects as iso8601 format strings' do
         | 
| 36 | 
            -
                  expect(dump(datetime: DateTime.new(2016, 1, 4, 20, 18))) | 
| 45 | 
            +
                  expect(dump(datetime: DateTime.new(2016, 1, 4, 20, 18)))
         | 
| 37 46 | 
             
                    .to eq('{"datetime":"2016-01-04T20:18:00+00:00"}')
         | 
| 38 47 | 
             
                end
         | 
| 39 48 |  | 
| 40 49 | 
             
                it 'serializes datetime objects in an array as iso8601 format strings' do
         | 
| 41 | 
            -
                  expect(dump(datetime: [DateTime.new(2016, 1, 4, 20, 18)])) | 
| 50 | 
            +
                  expect(dump(datetime: [DateTime.new(2016, 1, 4, 20, 18)]))
         | 
| 42 51 | 
             
                    .to eq('{"datetime":["2016-01-04T20:18:00+00:00"]}')
         | 
| 43 52 | 
             
                end
         | 
| 44 53 |  | 
| 45 54 | 
             
                it 'serializes datetime objects in a nested hash as iso8601 format strings' do
         | 
| 46 | 
            -
                  expect(dump(x: { datetime: DateTime.new(2016, 1, 4, 20, 18) })) | 
| 55 | 
            +
                  expect(dump(x: { datetime: DateTime.new(2016, 1, 4, 20, 18) }))
         | 
| 47 56 | 
             
                    .to eq('{"x":{"datetime":"2016-01-04T20:18:00+00:00"}}')
         | 
| 48 57 | 
             
                end
         | 
| 49 58 |  | 
| @@ -10,21 +10,17 @@ describe 'Concurrency' do | |
| 10 10 |  | 
| 11 11 | 
             
              it 'supports multiple requests in #respond_to' do
         | 
| 12 12 | 
             
                freddy1.respond_to 'Concurrency1' do |_payload, msg_handler|
         | 
| 13 | 
            -
                   | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                   | 
| 18 | 
            -
                    msg_handler.error(e.response)
         | 
| 19 | 
            -
                  end
         | 
| 13 | 
            +
                  freddy1.deliver_with_response 'Concurrency2', msg: 'noop'
         | 
| 14 | 
            +
                  result2 = freddy1.deliver_with_response 'Concurrency3', msg: 'noop'
         | 
| 15 | 
            +
                  msg_handler.success(result2)
         | 
| 16 | 
            +
                rescue Freddy::InvalidRequestError => e
         | 
| 17 | 
            +
                  msg_handler.error(e.response)
         | 
| 20 18 | 
             
                end
         | 
| 21 19 |  | 
| 22 20 | 
             
                freddy2.respond_to 'Concurrency2' do |_payload, msg_handler|
         | 
| 23 | 
            -
                   | 
| 24 | 
            -
             | 
| 25 | 
            -
                   | 
| 26 | 
            -
                    msg_handler.error(e.response)
         | 
| 27 | 
            -
                  end
         | 
| 21 | 
            +
                  msg_handler.success(from: 'Concurrency2')
         | 
| 22 | 
            +
                rescue Freddy::InvalidRequestError => e
         | 
| 23 | 
            +
                  msg_handler.error(e.response)
         | 
| 28 24 | 
             
                end
         | 
| 29 25 |  | 
| 30 26 | 
             
                freddy3.respond_to 'Concurrency3' do |_payload, msg_handler|
         |