ribbon-intercom 0.3.4 → 0.4.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/lib/ribbon/intercom.rb +1 -0
- data/lib/ribbon/intercom/client/sdk.rb +28 -24
- data/lib/ribbon/intercom/client/sdk/adapters/adapter.rb +17 -6
- data/lib/ribbon/intercom/client/sdk/adapters/adapter/response.rb +2 -27
- data/lib/ribbon/intercom/client/sdk/adapters/http_adapter.rb +6 -4
- data/lib/ribbon/intercom/client/sdk/adapters/local_adapter.rb +5 -4
- data/lib/ribbon/intercom/errors.rb +5 -0
- data/lib/ribbon/intercom/package.rb +22 -22
- data/lib/ribbon/intercom/packageable/mixin.rb +0 -4
- data/lib/ribbon/intercom/packet.rb +52 -0
- data/lib/ribbon/intercom/packet/method_queue.rb +28 -0
- data/lib/ribbon/intercom/service.rb +66 -47
- data/lib/ribbon/intercom/service/channel/stores/redis_store.rb +17 -7
- data/lib/ribbon/intercom/utils.rb +4 -1
- data/lib/ribbon/intercom/utils/method_chain.rb +38 -0
- data/lib/ribbon/intercom/version.rb +1 -1
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: dbc66058ef3f67f202ae929f759b20a1ca45a0f7
         | 
| 4 | 
            +
              data.tar.gz: d5eee408147e0cf447120c6f2ee8150cba30cec9
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ed1f4b07d38b1fca0657cc08ec4fac171ae9fc65a7132f9afe4f46a9043d6104d4fec26ade902ab93bed155102365fdf56518dfbf93b0a2695805173b8d834b3
         | 
| 7 | 
            +
              data.tar.gz: 723e68476ea49afd0cf055e2ce6adcfb8636ea6c7d12625abfa9ff2486a1502bc4507af01a7fbfc5f4d8935ee1b0bc82de2a9e223547e99ec9513a6cd9e8fe81
         | 
    
        data/lib/ribbon/intercom.rb
    CHANGED
    
    | @@ -9,6 +9,7 @@ module Ribbon | |
| 9 9 | 
             
                autoload(:Client,      'ribbon/intercom/client')
         | 
| 10 10 | 
             
                autoload(:Package,     'ribbon/intercom/package')
         | 
| 11 11 | 
             
                autoload(:Packageable, 'ribbon/intercom/packageable')
         | 
| 12 | 
            +
                autoload(:Packet,      'ribbon/intercom/packet')
         | 
| 12 13 | 
             
                autoload(:Utils,       'ribbon/intercom/utils')
         | 
| 13 14 |  | 
| 14 15 | 
             
                module_function
         | 
| @@ -18,6 +18,14 @@ module Ribbon::Intercom | |
| 18 18 | 
             
                    end
         | 
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 | 
            +
                  def begin
         | 
| 22 | 
            +
                    Utils::MethodChain.begin { |methods|
         | 
| 23 | 
            +
                      queue = Packet::MethodQueue.new
         | 
| 24 | 
            +
                      methods.each { |meth, *args| queue.enqueue(meth, *args) }
         | 
| 25 | 
            +
                      send_packet(method_queue: queue).retval
         | 
| 26 | 
            +
                    }
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 21 29 | 
             
                  ##
         | 
| 22 30 | 
             
                  # Returns the headers defined for this SDK. Optionally, you may also define
         | 
| 23 31 | 
             
                  # additional headers you'd like to add/override.
         | 
| @@ -34,11 +42,19 @@ module Ribbon::Intercom | |
| 34 42 | 
             
                  end
         | 
| 35 43 |  | 
| 36 44 | 
             
                  ##
         | 
| 37 | 
            -
                  # Calls the method on the adapter returning the  | 
| 45 | 
            +
                  # Calls the method on the adapter returning the response Packet
         | 
| 46 | 
            +
                  def call(method_name, *args)
         | 
| 47 | 
            +
                    send_packet(
         | 
| 48 | 
            +
                      method_queue: Packet::MethodQueue.new.enqueue(method_name, *args)
         | 
| 49 | 
            +
                    )
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  ##
         | 
| 53 | 
            +
                  # Sends the packet with the adapter, returning the response Packet.
         | 
| 38 54 | 
             
                  #
         | 
| 39 55 | 
             
                  # Intended to be called by Package.
         | 
| 40 | 
            -
                  def  | 
| 41 | 
            -
                    _process_response(adapter. | 
| 56 | 
            +
                  def send_packet(packet)
         | 
| 57 | 
            +
                    _process_response(adapter.send_packet(packet))
         | 
| 42 58 | 
             
                  end
         | 
| 43 59 |  | 
| 44 60 | 
             
                  def dup
         | 
| @@ -64,32 +80,20 @@ module Ribbon::Intercom | |
| 64 80 | 
             
                  end
         | 
| 65 81 |  | 
| 66 82 | 
             
                  ##
         | 
| 67 | 
            -
                  # Process  | 
| 68 | 
            -
                  def _process_response( | 
| 69 | 
            -
                    _handle_response_error( | 
| 83 | 
            +
                  # Process a Packet object returned by Adapter#send_packet.
         | 
| 84 | 
            +
                  def _process_response(packet)
         | 
| 85 | 
            +
                    _handle_response_error(packet) if packet.error?
         | 
| 70 86 |  | 
| 71 | 
            -
                    _init_packages( | 
| 72 | 
            -
                     | 
| 87 | 
            +
                    _init_packages(packet.retval)
         | 
| 88 | 
            +
                    packet
         | 
| 73 89 | 
             
                  end
         | 
| 74 90 |  | 
| 75 91 | 
             
                  ##
         | 
| 76 92 | 
             
                  # Raises an error depending on what went wrong.
         | 
| 77 | 
            -
                  def _handle_response_error( | 
| 78 | 
            -
                    raise ' | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
                    if encoded_error && !encoded_error.empty?
         | 
| 83 | 
            -
                      begin
         | 
| 84 | 
            -
                        decoded_error = Base64.strict_decode64(encoded_error)
         | 
| 85 | 
            -
                        error = Marshal.load(decoded_error)
         | 
| 86 | 
            -
                      rescue
         | 
| 87 | 
            -
                        error = Errors::ServerError.new('unknown server error')
         | 
| 88 | 
            -
                      end
         | 
| 89 | 
            -
                    end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                    raise error || Errors::ServerError.new('unexpected server error')
         | 
| 92 | 
            -
                  end # _handle_response
         | 
| 93 | 
            +
                  def _handle_response_error(packet)
         | 
| 94 | 
            +
                    raise 'packet contains no error' unless packet.error?
         | 
| 95 | 
            +
                    raise packet.error
         | 
| 96 | 
            +
                  end
         | 
| 93 97 |  | 
| 94 98 | 
             
                  ##
         | 
| 95 99 | 
             
                  # Walks the object and initializes all packages (i.e., sets them up to be
         | 
| @@ -9,11 +9,22 @@ module Ribbon::Intercom | |
| 9 9 |  | 
| 10 10 | 
             
                  ##
         | 
| 11 11 | 
             
                  # Call the method on the service.
         | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # @deprecated This method is only called by tests. Don't call it elsewhere.
         | 
| 12 14 | 
             
                  def call(method_name, *args)
         | 
| 13 | 
            -
                     | 
| 14 | 
            -
                       | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 15 | 
            +
                    send_packet(
         | 
| 16 | 
            +
                      method_queue: Packet::MethodQueue.new.enqueue(method_name, *args)
         | 
| 17 | 
            +
                    )
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  ##
         | 
| 21 | 
            +
                  # Send a packet to the service and returns the Packet returned by the service.
         | 
| 22 | 
            +
                  def send_packet(packet)
         | 
| 23 | 
            +
                    packet = Packet.new(packet) if packet.is_a?(Hash)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    send_packet!(packet.encode).tap { |response|
         | 
| 26 | 
            +
                      raise TypeError, "send_packet! should return an Adapter::Response" unless response.is_a?(Response)
         | 
| 27 | 
            +
                    }.packet
         | 
| 17 28 | 
             
                  end
         | 
| 18 29 |  | 
| 19 30 | 
             
                  ##
         | 
| @@ -43,8 +54,8 @@ module Ribbon::Intercom | |
| 43 54 | 
             
                  end
         | 
| 44 55 |  | 
| 45 56 | 
             
                  ##
         | 
| 46 | 
            -
                  # Actually  | 
| 47 | 
            -
                  def  | 
| 57 | 
            +
                  # Actually send the packet to the service. Should return an Adapter::Response object.
         | 
| 58 | 
            +
                  def send_packet!(packet)
         | 
| 48 59 | 
             
                    raise NotImplementedError
         | 
| 49 60 | 
             
                  end
         | 
| 50 61 |  | 
| @@ -1,37 +1,12 @@ | |
| 1 1 | 
             
            module Ribbon::Intercom
         | 
| 2 2 | 
             
              class Client::SDK::Adapters::Adapter
         | 
| 3 3 | 
             
                class Response < Rack::Response
         | 
| 4 | 
            -
                  attr_reader :method_name
         | 
| 5 | 
            -
             | 
| 6 | 
            -
                  def initialize(body, status, headers, method_name)
         | 
| 7 | 
            -
                    super(body, status, headers)
         | 
| 8 | 
            -
                    @method_name = method_name.to_sym
         | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  def missing_permissions
         | 
| 12 | 
            -
                    @__missing_permissions ||= _intercom_permissions_missing
         | 
| 13 | 
            -
                  end
         | 
| 14 | 
            -
             | 
| 15 4 | 
             
                  def body
         | 
| 16 5 | 
             
                    super.join
         | 
| 17 6 | 
             
                  end
         | 
| 18 7 |  | 
| 19 | 
            -
                   | 
| 20 | 
            -
             | 
| 21 | 
            -
                  def retval
         | 
| 22 | 
            -
                    @__retval ||= Marshal.load(Base64.strict_decode64(body)) unless body.empty?
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                  private
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  def _intercom_permissions_missing
         | 
| 28 | 
            -
                    key = _intercom_permissions_missing_header
         | 
| 29 | 
            -
                    headers[key] && headers[key].split(',').to_set
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  def _intercom_permissions_missing_header
         | 
| 33 | 
            -
                    missing_keys = ['X-Intercom-Permissions-Missing', :x_intercom_permissions_missing]
         | 
| 34 | 
            -
                    missing_keys.detect { |key| headers.key?(key) }
         | 
| 8 | 
            +
                  def packet
         | 
| 9 | 
            +
                    @__packet ||= Packet.decode(body) unless body.empty?
         | 
| 35 10 | 
             
                  end
         | 
| 36 11 | 
             
                end # Response
         | 
| 37 12 | 
             
              end # Client::SDK::Adapters::Adapter
         | 
| @@ -17,13 +17,15 @@ module Ribbon::Intercom | |
| 17 17 | 
             
                    @__connection ||= Connection.new(*@_connection_args)
         | 
| 18 18 | 
             
                  end
         | 
| 19 19 |  | 
| 20 | 
            -
                   | 
| 20 | 
            +
                  ##
         | 
| 21 | 
            +
                  # Send the encoded packet up to the service via an HTTP PUT.
         | 
| 22 | 
            +
                  def send_packet!(encoded_packet)
         | 
| 21 23 | 
             
                    response = connection.put(
         | 
| 22 | 
            -
                      headers: headers | 
| 23 | 
            -
                      body:     | 
| 24 | 
            +
                      headers: headers,
         | 
| 25 | 
            +
                      body:    encoded_packet
         | 
| 24 26 | 
             
                    )
         | 
| 25 27 |  | 
| 26 | 
            -
                    Adapter::Response.new(response.body, response.code.to_i, response | 
| 28 | 
            +
                    Adapter::Response.new(response.body, response.code.to_i, response)
         | 
| 27 29 | 
             
                  end
         | 
| 28 30 | 
             
                end # HttpAdapter
         | 
| 29 31 | 
             
              end # Client::SDK::Adapters
         | 
| @@ -23,17 +23,18 @@ module Ribbon::Intercom | |
| 23 23 | 
             
                    !!service
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 | 
            -
                   | 
| 26 | 
            +
                  ##
         | 
| 27 | 
            +
                  # Mimics an HTTP PUT
         | 
| 28 | 
            +
                  def send_packet!(encoded_packet)
         | 
| 27 29 | 
             
                    response = service.call(
         | 
| 28 30 | 
             
                      _http_headers.merge(
         | 
| 29 31 | 
             
                        'HTTP_AUTHORIZATION'     => _http_auth,
         | 
| 30 | 
            -
                        'HTTP_X_INTERCOM_METHOD' => method_name.to_s,
         | 
| 31 32 | 
             
                        'REQUEST_METHOD'         => 'PUT',
         | 
| 32 | 
            -
                        'rack.input'             => StringIO.new( | 
| 33 | 
            +
                        'rack.input'             => StringIO.new(encoded_packet)
         | 
| 33 34 | 
             
                      )
         | 
| 34 35 | 
             
                    )
         | 
| 35 36 |  | 
| 36 | 
            -
                    Response.new(response[2], response[0], response[1] | 
| 37 | 
            +
                    Adapter::Response.new(response[2], response[0], response[1])
         | 
| 37 38 | 
             
                  end
         | 
| 38 39 |  | 
| 39 40 | 
             
                  private
         | 
| @@ -7,20 +7,20 @@ module Ribbon::Intercom | |
| 7 7 | 
             
                attr_reader :sdk
         | 
| 8 8 |  | 
| 9 9 | 
             
                def initialize(subject_data=nil, data=nil)
         | 
| 10 | 
            -
                   | 
| 10 | 
            +
                  @_subject_data = subject_data
         | 
| 11 11 | 
             
                  @_data = data
         | 
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 14 | 
             
                def sdk=(sdk)
         | 
| 15 | 
            -
                  _set_sdk_headers(sdk)
         | 
| 16 15 | 
             
                  @sdk = sdk
         | 
| 17 16 | 
             
                end
         | 
| 18 17 |  | 
| 19 | 
            -
                 | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 18 | 
            +
                def begin
         | 
| 19 | 
            +
                  Utils::MethodChain.begin { |methods|
         | 
| 20 | 
            +
                    queue = Packet::MethodQueue.new
         | 
| 21 | 
            +
                    methods.each { |meth, *args| queue.enqueue(meth, *args) }
         | 
| 22 | 
            +
                    _send_method_queue(queue)
         | 
| 23 | 
            +
                  }
         | 
| 24 24 | 
             
                end
         | 
| 25 25 |  | 
| 26 26 | 
             
                private
         | 
| @@ -29,35 +29,35 @@ module Ribbon::Intercom | |
| 29 29 | 
             
                  if @_data && @_data.key?(meth)
         | 
| 30 30 | 
             
                    @_data[meth]
         | 
| 31 31 | 
             
                  elsif sdk
         | 
| 32 | 
            -
                     | 
| 32 | 
            +
                    _send_method_queue(Packet::MethodQueue.new.enqueue(meth, *args))
         | 
| 33 33 | 
             
                  else
         | 
| 34 34 | 
             
                    super
         | 
| 35 35 | 
             
                  end
         | 
| 36 36 | 
             
                end
         | 
| 37 37 |  | 
| 38 | 
            +
                def _send_method_queue(method_queue)
         | 
| 39 | 
            +
                  _process_response(
         | 
| 40 | 
            +
                    sdk.send_packet(
         | 
| 41 | 
            +
                      subject:      @_subject_data,
         | 
| 42 | 
            +
                      method_queue: method_queue
         | 
| 43 | 
            +
                    )
         | 
| 44 | 
            +
                  )
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 38 47 | 
             
                def marshal_dump
         | 
| 39 48 | 
             
                  [@_subject_data, @_data]
         | 
| 40 49 | 
             
                end
         | 
| 41 50 |  | 
| 42 51 | 
             
                def marshal_load(array)
         | 
| 43 | 
            -
                   | 
| 52 | 
            +
                  @_subject_data = array[0]
         | 
| 44 53 | 
             
                  @_data = array[1]
         | 
| 45 54 | 
             
                end
         | 
| 46 55 |  | 
| 47 | 
            -
                def  | 
| 48 | 
            -
                   | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
                def _process_response(response)
         | 
| 52 | 
            -
                  self._subject_data = response.headers['X-Intercom-Subject']
         | 
| 53 | 
            -
                  @_data = _retrieve_data_from_response(response)
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                  response.retval
         | 
| 56 | 
            -
                end
         | 
| 56 | 
            +
                def _process_response(packet)
         | 
| 57 | 
            +
                  @_subject_data = packet.subject
         | 
| 58 | 
            +
                  @_data = packet.package_data
         | 
| 57 59 |  | 
| 58 | 
            -
             | 
| 59 | 
            -
                  encoded_data = response.headers['X-Intercom-Package-Data']
         | 
| 60 | 
            -
                  encoded_data && Marshal.load(Base64.strict_decode64(encoded_data))
         | 
| 60 | 
            +
                  packet.retval
         | 
| 61 61 | 
             
                end
         | 
| 62 62 | 
             
              end # Package
         | 
| 63 63 | 
             
            end # Ribbon::Intercom
         | 
| @@ -26,10 +26,6 @@ module Ribbon::Intercom | |
| 26 26 | 
             
                      self.class._package_with_methods.map { |meth| [meth, public_send(meth)] }.to_h
         | 
| 27 27 | 
             
                    )
         | 
| 28 28 | 
             
                  end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                  def encoded_package_data
         | 
| 31 | 
            -
                    Base64.strict_encode64(Marshal.dump(package_data))
         | 
| 32 | 
            -
                  end
         | 
| 33 29 | 
             
                end # Mixin
         | 
| 34 30 | 
             
              end # Service::Subject
         | 
| 35 31 | 
             
            end # Ribbon::Intercom
         | 
| @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            require 'ostruct'
         | 
| 2 | 
            +
            require 'base64'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Ribbon::Intercom
         | 
| 5 | 
            +
              ##
         | 
| 6 | 
            +
              # Represents a collection of data to be passed between the service and client.
         | 
| 7 | 
            +
              class Packet < OpenStruct
         | 
| 8 | 
            +
                autoload(:MethodQueue, 'ribbon/intercom/packet/method_queue')
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                class << self
         | 
| 11 | 
            +
                  def decode(encoded_packet)
         | 
| 12 | 
            +
                    hash = Marshal.load(Base64.strict_decode64(encoded_packet))
         | 
| 13 | 
            +
                    raise Errors::InvalidEncodingError, hash.inspect unless hash.is_a?(Hash)
         | 
| 14 | 
            +
                    new(hash)
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end # Class Methods
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def initialize(params={})
         | 
| 19 | 
            +
                  error = params.delete(:error)
         | 
| 20 | 
            +
                  super(params)
         | 
| 21 | 
            +
                  self.error = error if error
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                ##
         | 
| 25 | 
            +
                # Encode (marshal) the error before saving it. This allows the error to be
         | 
| 26 | 
            +
                # decoded on the client when requested, rather than decoded at the same time
         | 
| 27 | 
            +
                # the packet is decoded, which could cause problems if the error class doesn't
         | 
| 28 | 
            +
                # exist on the client.
         | 
| 29 | 
            +
                def error=(err)
         | 
| 30 | 
            +
                  self._encoded_error = Marshal.dump(err)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                ##
         | 
| 34 | 
            +
                # Decode the error, which may not exist on the client.
         | 
| 35 | 
            +
                # If the error class doesn't exist on the client, raise a ServerError.
         | 
| 36 | 
            +
                def error
         | 
| 37 | 
            +
                  error? && Marshal.load(_encoded_error)
         | 
| 38 | 
            +
                rescue
         | 
| 39 | 
            +
                  Errors::ServerError.new('unknown server error')
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                ##
         | 
| 43 | 
            +
                # Whether the packet contains an error
         | 
| 44 | 
            +
                def error?
         | 
| 45 | 
            +
                  !!_encoded_error
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def encode
         | 
| 49 | 
            +
                  Base64.strict_encode64(Marshal.dump(to_h))
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end # Packet
         | 
| 52 | 
            +
            end # Ribbon::Intercom
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module Ribbon::Intercom
         | 
| 2 | 
            +
              class Packet
         | 
| 3 | 
            +
                class MethodQueue
         | 
| 4 | 
            +
                  ##
         | 
| 5 | 
            +
                  # Enqueue a method with it's arguments.
         | 
| 6 | 
            +
                  # Supports method chaining.
         | 
| 7 | 
            +
                  def enqueue(name, *args)
         | 
| 8 | 
            +
                    self.tap { _queue << [name, *Utils.sanitize(args)] }
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  ##
         | 
| 12 | 
            +
                  # Iterate through the methods and arguments.
         | 
| 13 | 
            +
                  def each(&block)
         | 
| 14 | 
            +
                    _queue.each(&block)
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def empty?
         | 
| 18 | 
            +
                    _queue.empty?
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  private
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def _queue
         | 
| 24 | 
            +
                    @__queue ||= []
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end # MethodQueue
         | 
| 27 | 
            +
              end # Packet
         | 
| 28 | 
            +
            end # Ribbon::Intercom
         | 
| @@ -44,6 +44,7 @@ module Ribbon::Intercom | |
| 44 44 | 
             
                attr_reader :request
         | 
| 45 45 | 
             
                attr_reader :channel
         | 
| 46 46 | 
             
                attr_reader :subject
         | 
| 47 | 
            +
                attr_reader :request_packet
         | 
| 47 48 | 
             
                attr_reader :env
         | 
| 48 49 |  | 
| 49 50 | 
             
                def initialize(opts={})
         | 
| @@ -65,8 +66,8 @@ module Ribbon::Intercom | |
| 65 66 | 
             
                  store.lookup_channel(token)
         | 
| 66 67 | 
             
                end
         | 
| 67 68 |  | 
| 68 | 
            -
                def sufficient_permissions?(intercom_method)
         | 
| 69 | 
            -
                  channel.may?(Utils.method_identifier( | 
| 69 | 
            +
                def sufficient_permissions?(base, intercom_method)
         | 
| 70 | 
            +
                  Utils.basic_type?(base) || channel.may?(Utils.method_identifier(base, intercom_method))
         | 
| 70 71 | 
             
                end
         | 
| 71 72 |  | 
| 72 73 | 
             
                def call(env)
         | 
| @@ -78,8 +79,7 @@ module Ribbon::Intercom | |
| 78 79 |  | 
| 79 80 | 
             
                  response = catch(:response) {
         | 
| 80 81 | 
             
                    begin
         | 
| 81 | 
            -
                       | 
| 82 | 
            -
                      _call_method(intercom_method, *args)
         | 
| 82 | 
            +
                      _process_request
         | 
| 83 83 | 
             
                    rescue Exception => error
         | 
| 84 84 | 
             
                      _respond_with_error!(error)
         | 
| 85 85 | 
             
                    end
         | 
| @@ -97,8 +97,10 @@ module Ribbon::Intercom | |
| 97 97 | 
             
                def _process_request
         | 
| 98 98 | 
             
                  _init_request
         | 
| 99 99 | 
             
                  _authenticate_request!
         | 
| 100 | 
            +
                  _load_request_packet
         | 
| 100 101 | 
             
                  _load_subject
         | 
| 101 | 
            -
                   | 
| 102 | 
            +
                  response_packet = _process_methods
         | 
| 103 | 
            +
                  _respond_with_packet(response_packet)
         | 
| 102 104 | 
             
                end
         | 
| 103 105 |  | 
| 104 106 | 
             
                def _init_request
         | 
| @@ -115,8 +117,12 @@ module Ribbon::Intercom | |
| 115 117 | 
             
                  end
         | 
| 116 118 | 
             
                end
         | 
| 117 119 |  | 
| 120 | 
            +
                def _load_request_packet
         | 
| 121 | 
            +
                  @request_packet = Packet.decode(request.body.read)
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 118 124 | 
             
                def _load_subject
         | 
| 119 | 
            -
                  if (encoded_subject= | 
| 125 | 
            +
                  if (encoded_subject=request_packet.subject) && !encoded_subject.empty?
         | 
| 120 126 | 
             
                    @subject = _decode_subject(encoded_subject)
         | 
| 121 127 | 
             
                    _error!(Errors::InvalidSubjectSignatureError) unless @subject
         | 
| 122 128 | 
             
                  else
         | 
| @@ -124,35 +130,27 @@ module Ribbon::Intercom | |
| 124 130 | 
             
                  end
         | 
| 125 131 | 
             
                end
         | 
| 126 132 |  | 
| 127 | 
            -
                 | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
                   | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
                def _load_args
         | 
| 134 | 
            -
                  _decode_args(request.body.read)
         | 
| 135 | 
            -
                rescue Errors::UnsafeValueError
         | 
| 136 | 
            -
                  _error!(Errors::UnsafeArgumentError, "One or more argument type is invalid.")
         | 
| 133 | 
            +
                ##
         | 
| 134 | 
            +
                # Calls requested methods and returns a response packet for the client.
         | 
| 135 | 
            +
                def _process_methods
         | 
| 136 | 
            +
                  retval = _call_methods
         | 
| 137 | 
            +
                  _prepare_response_packet(retval)
         | 
| 137 138 | 
             
                end
         | 
| 138 139 |  | 
| 139 140 | 
             
                ##
         | 
| 140 | 
            -
                # Call the  | 
| 141 | 
            -
                def  | 
| 142 | 
            -
                   | 
| 143 | 
            -
             | 
| 144 | 
            -
                   | 
| 145 | 
            -
                   | 
| 146 | 
            -
             | 
| 147 | 
            -
                     | 
| 148 | 
            -
             | 
| 149 | 
            -
                     | 
| 150 | 
            -
             | 
| 151 | 
            -
                      headers['X-Intercom-Package-Data'] = subject.encoded_package_data
         | 
| 152 | 
            -
                    end
         | 
| 153 | 
            -
                  end
         | 
| 141 | 
            +
                # Call all the methods in the method queue.
         | 
| 142 | 
            +
                def _call_methods
         | 
| 143 | 
            +
                  method_queue = _load_method_queue
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  intercom_method = nil
         | 
| 146 | 
            +
                  base = subject
         | 
| 147 | 
            +
                  method_queue.each { |meth, *args|
         | 
| 148 | 
            +
                    intercom_method = meth
         | 
| 149 | 
            +
                    _sufficient_permissions!(base, meth)
         | 
| 150 | 
            +
                    base = base.public_send(meth, *args)
         | 
| 151 | 
            +
                  }
         | 
| 154 152 |  | 
| 155 | 
            -
                   | 
| 153 | 
            +
                  _package(base)
         | 
| 156 154 | 
             
                rescue NoMethodError => error
         | 
| 157 155 | 
             
                  if error.name == intercom_method
         | 
| 158 156 | 
             
                    _error!(Errors::InvalidMethodError, intercom_method)
         | 
| @@ -161,6 +159,36 @@ module Ribbon::Intercom | |
| 161 159 | 
             
                  end
         | 
| 162 160 | 
             
                end
         | 
| 163 161 |  | 
| 162 | 
            +
                def _load_method_queue
         | 
| 163 | 
            +
                  request_packet.method_queue.tap { |mq|
         | 
| 164 | 
            +
                    raise "No method queue given" unless mq
         | 
| 165 | 
            +
                    raise "Expected MethodQueue, got: #{mq.inspect}" unless mq.is_a?(Packet::MethodQueue)
         | 
| 166 | 
            +
                    raise "Empty MethodQueue" if mq.empty?
         | 
| 167 | 
            +
                  }
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                ##
         | 
| 171 | 
            +
                # Creates a successful response Packet to be returned to the client.
         | 
| 172 | 
            +
                def _prepare_response_packet(retval)
         | 
| 173 | 
            +
                  Packet.new.tap { |packet|
         | 
| 174 | 
            +
                    unless self == subject # Order matters here! See: issue#52
         | 
| 175 | 
            +
                      # Need to send subject back in case it was modified by the methods.
         | 
| 176 | 
            +
                      packet.subject = _encode_subject(subject)
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                      if subject.is_a?(Packageable::Mixin)
         | 
| 179 | 
            +
                        # Need to send the package data back in case it changed, too.
         | 
| 180 | 
            +
                        packet.package_data = subject.package_data
         | 
| 181 | 
            +
                      end
         | 
| 182 | 
            +
                    end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                    packet.retval = retval
         | 
| 185 | 
            +
                  }
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                def _respond_with_packet(packet, status=200)
         | 
| 189 | 
            +
                  _respond!(status, {}, packet.encode)
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 164 192 | 
             
                ##
         | 
| 165 193 | 
             
                # Package up any non-basic objects that include Packageable::Mixin.
         | 
| 166 194 | 
             
                def _package(object)
         | 
| @@ -187,14 +215,13 @@ module Ribbon::Intercom | |
| 187 215 | 
             
                ##
         | 
| 188 216 | 
             
                # Marshal dumps the subject and signs the resulting bytes with the channel.
         | 
| 189 217 | 
             
                def _encode_subject(subject)
         | 
| 190 | 
            -
                   | 
| 218 | 
            +
                  channel.sign(Marshal.dump(subject))
         | 
| 191 219 | 
             
                end
         | 
| 192 220 |  | 
| 193 221 | 
             
                ##
         | 
| 194 222 | 
             
                # Decodes the subject sent from the client.
         | 
| 195 223 | 
             
                def _decode_subject(encoded_subject)
         | 
| 196 | 
            -
                   | 
| 197 | 
            -
                  marshalled_subject = channel.verify(signed_subject)
         | 
| 224 | 
            +
                  marshalled_subject = channel.verify(encoded_subject)
         | 
| 198 225 | 
             
                  marshalled_subject && Marshal.load(marshalled_subject)
         | 
| 199 226 | 
             
                end
         | 
| 200 227 |  | 
| @@ -211,15 +238,15 @@ module Ribbon::Intercom | |
| 211 238 | 
             
                  end
         | 
| 212 239 | 
             
                end
         | 
| 213 240 |  | 
| 214 | 
            -
                def _sufficient_permissions!(intercom_method)
         | 
| 215 | 
            -
                  unless sufficient_permissions?(intercom_method)
         | 
| 216 | 
            -
                    required = Utils.method_identifier( | 
| 241 | 
            +
                def _sufficient_permissions!(base, intercom_method)
         | 
| 242 | 
            +
                  unless sufficient_permissions?(base, intercom_method)
         | 
| 243 | 
            +
                    required = Utils.method_identifier(base, intercom_method)
         | 
| 217 244 | 
             
                    _error!(Errors::InsufficientPermissionsError, required)
         | 
| 218 245 | 
             
                  end
         | 
| 219 246 | 
             
                end
         | 
| 220 247 |  | 
| 221 248 | 
             
                def _response(status, headers={}, body=EmptyResponse)
         | 
| 222 | 
            -
                  body = body == EmptyResponse ? [] : [ | 
| 249 | 
            +
                  body = body == EmptyResponse ? [] : [body]
         | 
| 223 250 | 
             
                  headers = headers.merge("Content-Type" => "text/plain", "Transfer-Encoding" => "gzip")
         | 
| 224 251 | 
             
                  Rack::Response.new(body, status, headers)
         | 
| 225 252 | 
             
                end
         | 
| @@ -229,7 +256,7 @@ module Ribbon::Intercom | |
| 229 256 | 
             
                end
         | 
| 230 257 |  | 
| 231 258 | 
             
                def _respond_with_error!(error, status=500)
         | 
| 232 | 
            -
                   | 
| 259 | 
            +
                  _respond_with_packet(Packet.new(error: error), status)
         | 
| 233 260 | 
             
                end
         | 
| 234 261 |  | 
| 235 262 | 
             
                def _error!(klass, message=nil)
         | 
| @@ -256,14 +283,6 @@ module Ribbon::Intercom | |
| 256 283 | 
             
                  end
         | 
| 257 284 | 
             
                end
         | 
| 258 285 |  | 
| 259 | 
            -
                def _encode_body(body)
         | 
| 260 | 
            -
                  Base64.strict_encode64(Marshal.dump(body))
         | 
| 261 | 
            -
                end
         | 
| 262 | 
            -
             | 
| 263 | 
            -
                def _encode_error(error)
         | 
| 264 | 
            -
                  Base64.strict_encode64(Marshal.dump(error))
         | 
| 265 | 
            -
                end
         | 
| 266 | 
            -
             | 
| 267 286 | 
             
                ##
         | 
| 268 287 | 
             
                # Decodes the arguments.
         | 
| 269 288 | 
             
                #
         | 
| @@ -74,14 +74,24 @@ module Ribbon::Intercom | |
| 74 74 | 
             
                    private
         | 
| 75 75 |  | 
| 76 76 | 
             
                    def _load_data(token)
         | 
| 77 | 
            -
                       | 
| 78 | 
            -
             | 
| 77 | 
            +
                      channel_data_future = nil
         | 
| 78 | 
            +
                      permissions_future  = nil
         | 
| 79 | 
            +
                      signing_keys_future = nil
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                      @_redis.pipelined {
         | 
| 82 | 
            +
                        channel_data_future = @_redis.hgetall(_key_name(token))
         | 
| 83 | 
            +
                        permissions_future  = @_redis.smembers(_key_name(token, 'permissions'))
         | 
| 84 | 
            +
                        signing_keys_future = @_redis.hgetall(_key_name(token, 'signing_keys'))
         | 
| 85 | 
            +
                      }
         | 
| 79 86 |  | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 87 | 
            +
                      channel_data = channel_data_future.value
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                      if channel_data && !channel_data.empty?
         | 
| 90 | 
            +
                        permissions = permissions_future.value
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                        signing_keys = signing_keys_future.value.map { |key_id, data|
         | 
| 93 | 
            +
                          [key_id.to_i, Marshal.load(Base64.strict_decode64(data))]
         | 
| 94 | 
            +
                        }.to_h
         | 
| 85 95 |  | 
| 86 96 | 
             
                        Utils.symbolize_keys(
         | 
| 87 97 | 
             
                          channel_data.merge(
         | 
| @@ -3,10 +3,13 @@ require 'date' | |
| 3 3 | 
             
            module Ribbon::Intercom
         | 
| 4 4 | 
             
              module Utils
         | 
| 5 5 | 
             
                autoload(:Signer, 'ribbon/intercom/utils/signer')
         | 
| 6 | 
            +
                autoload(:MethodChain, 'ribbon/intercom/utils/method_chain')
         | 
| 6 7 | 
             
                autoload(:Mixins, 'ribbon/intercom/utils/mixins')
         | 
| 7 8 |  | 
| 8 9 | 
             
                BASIC_TYPES = [
         | 
| 9 | 
            -
                  String, Symbol, TrueClass, FalseClass, Integer, Float, NilClass, | 
| 10 | 
            +
                  String, Symbol, TrueClass, FalseClass, Integer, Float, NilClass,
         | 
| 11 | 
            +
                  Date, Time, DateTime,
         | 
| 12 | 
            +
                  Hash, Array, Range
         | 
| 10 13 | 
             
                ].freeze
         | 
| 11 14 |  | 
| 12 15 | 
             
                class << self
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            module Ribbon::Intercom
         | 
| 2 | 
            +
              module Utils
         | 
| 3 | 
            +
                class MethodChain
         | 
| 4 | 
            +
                  class << self
         | 
| 5 | 
            +
                    def begin(&block)
         | 
| 6 | 
            +
                      new(&block)
         | 
| 7 | 
            +
                    end
         | 
| 8 | 
            +
                  end # Class Methods
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  ##
         | 
| 11 | 
            +
                  # Need to override all public instance methods so they can be captured correctly.
         | 
| 12 | 
            +
                  public_instance_methods.each { |meth|
         | 
| 13 | 
            +
                    define_method(meth) { |*args|
         | 
| 14 | 
            +
                      _add_method(meth, *args)
         | 
| 15 | 
            +
                    }
         | 
| 16 | 
            +
                  }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def initialize(&block)
         | 
| 19 | 
            +
                    @_end_block = block
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def end
         | 
| 23 | 
            +
                    @_end_block.call(@_methods)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  private
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def method_missing(meth, *args, &block)
         | 
| 29 | 
            +
                    _add_method(meth, *args)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def _add_method(meth, *args)
         | 
| 33 | 
            +
                    (@_methods ||= []) << [meth, *args]
         | 
| 34 | 
            +
                    self
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end # MethodChain
         | 
| 37 | 
            +
              end # Utils
         | 
| 38 | 
            +
            end # Ribbon::Intercom
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ribbon-intercom
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.4.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Robert Honer
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2015-05- | 
| 12 | 
            +
            date: 2015-05-21 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: rack
         | 
| @@ -172,6 +172,8 @@ files: | |
| 172 172 | 
             
            - lib/ribbon/intercom/package.rb
         | 
| 173 173 | 
             
            - lib/ribbon/intercom/packageable.rb
         | 
| 174 174 | 
             
            - lib/ribbon/intercom/packageable/mixin.rb
         | 
| 175 | 
            +
            - lib/ribbon/intercom/packet.rb
         | 
| 176 | 
            +
            - lib/ribbon/intercom/packet/method_queue.rb
         | 
| 175 177 | 
             
            - lib/ribbon/intercom/railtie.rb
         | 
| 176 178 | 
             
            - lib/ribbon/intercom/service.rb
         | 
| 177 179 | 
             
            - lib/ribbon/intercom/service/channel.rb
         | 
| @@ -180,6 +182,7 @@ files: | |
| 180 182 | 
             
            - lib/ribbon/intercom/service/channel/stores/redis_store.rb
         | 
| 181 183 | 
             
            - lib/ribbon/intercom/service/channel/stores/store.rb
         | 
| 182 184 | 
             
            - lib/ribbon/intercom/utils.rb
         | 
| 185 | 
            +
            - lib/ribbon/intercom/utils/method_chain.rb
         | 
| 183 186 | 
             
            - lib/ribbon/intercom/utils/mixins.rb
         | 
| 184 187 | 
             
            - lib/ribbon/intercom/utils/mixins/mock_safe.rb
         | 
| 185 188 | 
             
            - lib/ribbon/intercom/utils/signer.rb
         |