ribbon-intercom 0.2.3 → 0.3.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ribbon/intercom/client/mock_sdk.rb +13 -0
  3. data/lib/ribbon/intercom/client/sdk/adapters/adapter/response.rb +38 -0
  4. data/lib/ribbon/intercom/client/sdk/adapters/adapter.rb +51 -0
  5. data/lib/ribbon/intercom/client/sdk/adapters/http_adapter/connection.rb +34 -0
  6. data/lib/ribbon/intercom/client/sdk/adapters/http_adapter.rb +26 -0
  7. data/lib/ribbon/intercom/client/sdk/adapters/local_adapter.rb +54 -0
  8. data/lib/ribbon/intercom/client/sdk/adapters/mock_adapter.rb +40 -0
  9. data/lib/ribbon/intercom/client/sdk/adapters.rb +10 -0
  10. data/lib/ribbon/intercom/client/sdk.rb +71 -26
  11. data/lib/ribbon/intercom/client.rb +35 -7
  12. data/lib/ribbon/intercom/errors.rb +34 -6
  13. data/lib/ribbon/intercom/package.rb +64 -0
  14. data/lib/ribbon/intercom/packageable/mixin.rb +35 -0
  15. data/lib/ribbon/intercom/packageable.rb +6 -0
  16. data/lib/ribbon/intercom/service/channel/stores/mock_store.rb +40 -0
  17. data/lib/ribbon/intercom/service/channel/stores/redis_store.rb +186 -0
  18. data/lib/ribbon/intercom/service/{channel_stores/base.rb → channel/stores/store.rb} +13 -5
  19. data/lib/ribbon/intercom/service/channel/stores.rb +9 -0
  20. data/lib/ribbon/intercom/service/channel.rb +106 -4
  21. data/lib/ribbon/intercom/service.rb +156 -95
  22. data/lib/ribbon/intercom/utils/mixins/mock_safe.rb +26 -0
  23. data/lib/ribbon/intercom/utils/mixins.rb +5 -0
  24. data/lib/ribbon/intercom/utils/signer.rb +71 -0
  25. data/lib/ribbon/intercom/utils.rb +40 -13
  26. data/lib/ribbon/intercom/version.rb +1 -1
  27. data/lib/ribbon/intercom.rb +10 -7
  28. data/lib/tasks/intercom.rake +3 -3
  29. metadata +20 -20
  30. data/lib/ribbon/intercom/client/sdk/connection.rb +0 -35
  31. data/lib/ribbon/intercom/client/sdk/response.rb +0 -59
  32. data/lib/ribbon/intercom/service/channel_stores/redis_store.rb +0 -60
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc502869702b01539172c77ab6f12fea946251f1
4
- data.tar.gz: 15672af11bd53026f9613035366b5d3b9fe683a5
3
+ metadata.gz: 977c89158ba0646225543c4b65b86fc9db5663b4
4
+ data.tar.gz: 9482ff868c0d2e7974cbf08e329c8e18eb85b21a
5
5
  SHA512:
6
- metadata.gz: 110c7d7b8f7b5f19ac51eaad0d85f7f8ff19f05bbcf8a10aa12b53ec6d5d6ca7887d1034a573de8179bb466f9025b5bc7c0ff5a7dcfbbc5384759f20b72afb9b
7
- data.tar.gz: 44f4a6fbb476e2f80ce65250488ea50405908e6f1ccb3410923b4c4f35e6bab5e74b3545cd00cdd61830d5ee09c23e5aca08737b2a5c83ec260331278037d15e
6
+ metadata.gz: 6ac6cdc49ae1e738c21ad1cdc91a1d02340c7d04d35fe50a3eab1d9ca08e767366b3725b828b21f2e29b132a5bec70668683b337e80096e5b02e1c8f4063650f
7
+ data.tar.gz: ed7da55ea2c9dca75e712cf83f691977ed3aec4bb01f6ed0f5dcdf6999320d59b6dd091b954107f19cdaa7bc20985882c1293177e74f5f2e281720ca0080e5f0
@@ -0,0 +1,13 @@
1
+ module Ribbon::Intercom
2
+ class Client
3
+ class MockSDK < SDK
4
+ def initialize(service)
5
+ super(Adapters::MockAdapter.new(service))
6
+ end
7
+
8
+ def with_permissions(*perms, &block)
9
+ adapter.with_permissions(*perms, &block)
10
+ end
11
+ end # MockSDK
12
+ end # Client
13
+ end # Ribbon::Intercom
@@ -0,0 +1,38 @@
1
+ module Ribbon::Intercom
2
+ class Client::SDK::Adapters::Adapter
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
+ def body
16
+ super.join
17
+ end
18
+
19
+ ##
20
+ # Decode the response body received from the service.
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) }
35
+ end
36
+ end # Response
37
+ end # Client::SDK::Adapters::Adapter
38
+ end # Ribbon::Intercom
@@ -0,0 +1,51 @@
1
+ module Ribbon::Intercom
2
+ module Client::SDK::Adapters
3
+ class Adapter
4
+ autoload(:Response, 'ribbon/intercom/client/sdk/adapters/adapter/response')
5
+
6
+ def initialize(*args)
7
+ connect(*args)
8
+ end
9
+
10
+ ##
11
+ # Call the method on the service.
12
+ def call(method_name, *args)
13
+ call!(method_name, _encode_args(*args)).tap { |response|
14
+ raise TypeError, "call! should return an Adapter::Response" unless response.is_a?(Response)
15
+ raise "response should have correct method name" unless response.method_name == method_name.to_sym
16
+ }
17
+ end
18
+
19
+ def headers(h={})
20
+ (@__headers ||= {}).merge!(h)
21
+ end
22
+
23
+ ##
24
+ # Connect to a service. The specific arguments depend on the Adapter
25
+ # subclass.
26
+ def connect(*args)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ ##
31
+ # Returns whether or not the Adapter is connected to a service.
32
+ def connected?
33
+ raise NotImplementedError
34
+ end
35
+
36
+ ##
37
+ # Actually call the method on the service. Should return an Adapter::Response object.
38
+ def call!(method_name, *args)
39
+ raise NotImplementedError
40
+ end
41
+
42
+ private
43
+
44
+ ##
45
+ # Encode the arguments for transmission to the service.
46
+ def _encode_args(*args)
47
+ Base64.strict_encode64(Marshal.dump(Utils.sanitize(args)))
48
+ end
49
+ end # Adapter
50
+ end # Client::SDK::Adapters
51
+ end # Ribbon::Intercom
@@ -0,0 +1,34 @@
1
+ require 'net/http'
2
+ require 'openssl'
3
+ require 'uri'
4
+
5
+ module Ribbon::Intercom
6
+ module Client::SDK::Adapters
7
+ class HttpAdapter::Connection
8
+ attr_reader :url
9
+
10
+ def initialize(url, token, secret)
11
+ @url = url.is_a?(String) ? URI(url) : url
12
+
13
+ @_client = Net::HTTP.new(url.hostname, url.port)
14
+ @_client.use_ssl = url.is_a?(URI::HTTPS)
15
+ @_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
16
+ @_client.cert_store = OpenSSL::X509::Store.new.tap { |s| s.set_default_paths }
17
+
18
+ @_headers = {
19
+ 'Authorization' => "Basic #{Base64.strict_encode64("#{token}:#{secret}")}"
20
+ }
21
+ end
22
+
23
+ def put(params={})
24
+ @_client.put(url.request_uri, params[:body], _prepare_headers(params[:headers]).merge(@_headers))
25
+ end
26
+
27
+ private
28
+
29
+ def _prepare_headers(headers)
30
+ Hash[(headers || {}).map { |k, v| [k.to_s, v.to_s] }]
31
+ end
32
+ end # HttpAdapter::Connection
33
+ end # Client::SDK::Adapters
34
+ end # Ribbon::Intercom
@@ -0,0 +1,26 @@
1
+ module Ribbon::Intercom
2
+ module Client::SDK::Adapters
3
+ class HttpAdapter < Adapter
4
+ autoload(:Connection, 'ribbon/intercom/client/sdk/adapters/http_adapter/connection')
5
+
6
+ attr_reader :connection
7
+
8
+ def connect(*args)
9
+ @connection = Connection.new(*args)
10
+ end
11
+
12
+ def connected?
13
+ !!connection
14
+ end
15
+
16
+ def call!(method_name, encoded_args)
17
+ response = connection.put(
18
+ headers: headers.merge("X-Intercom-Method" => method_name),
19
+ body: encoded_args
20
+ )
21
+
22
+ Adapter::Response.new(response.body, response.code.to_i, response, method_name)
23
+ end
24
+ end # HttpAdapter
25
+ end # Client::SDK::Adapters
26
+ end # Ribbon::Intercom
@@ -0,0 +1,54 @@
1
+ require 'base64'
2
+
3
+ module Ribbon::Intercom
4
+ module Client::SDK::Adapters
5
+ class LocalAdapter < Adapter
6
+ attr_reader :service
7
+ attr_accessor :channel_token
8
+ attr_accessor :channel_secret
9
+
10
+ def connect(service, params={})
11
+ case service
12
+ when Service
13
+ @service = service
14
+ else
15
+ raise ArgumentError, "Expected a service, got: #{service.inspect}"
16
+ end
17
+
18
+ @channel_token = params[:channel_token]
19
+ @channel_secret = params[:channel_secret]
20
+ end
21
+
22
+ def connected?(*args)
23
+ !!service
24
+ end
25
+
26
+ def call!(method_name, encoded_args)
27
+ response = service.call(
28
+ _http_headers.merge(
29
+ 'HTTP_AUTHORIZATION' => _http_auth,
30
+ 'HTTP_X_INTERCOM_METHOD' => method_name.to_s,
31
+ 'REQUEST_METHOD' => 'PUT',
32
+ 'rack.input' => StringIO.new(encoded_args)
33
+ )
34
+ )
35
+
36
+ Response.new(response[2], response[0], response[1], method_name)
37
+ end
38
+
39
+ private
40
+
41
+ def _http_auth
42
+ "Basic #{Base64.strict_encode64("#{channel_token}:#{channel_secret}")}"
43
+ end
44
+
45
+ def _http_headers
46
+ Hash[headers.map { |name, value| [_header_to_http(name), value] }]
47
+ end
48
+
49
+ def _header_to_http(name)
50
+ name.to_s.upcase.gsub('-', '_')
51
+ end
52
+ end # MockAdapter
53
+ end # Client::SDK::Adapters
54
+ end # Ribbon::Intercom
@@ -0,0 +1,40 @@
1
+ module Ribbon::Intercom
2
+ module Client::SDK::Adapters
3
+ class MockAdapter < LocalAdapter
4
+ attr_reader :store
5
+
6
+ def connect(service)
7
+ if service.is_a?(Class) && service < Service
8
+ service = service.new(store: Service::Channel::Stores::MockStore.new)
9
+ end
10
+
11
+ unless service.is_a?(Service)
12
+ raise ArgumentError, "Expected a service, got: #{service.inspect}"
13
+ end
14
+
15
+ unless service.store.is_a?(Service::Channel::Stores::MockStore)
16
+ raise ArgumentError, "Expected service to have a MockStore, got: #{service.store.inspect}"
17
+ end
18
+
19
+ super(service)
20
+ @store = service.store
21
+ end
22
+
23
+ def with_permissions(*perms, &block)
24
+ channel = store.open_channel(name: 'mock channel', may: perms)
25
+ secret = channel.rotate_secret!
26
+ with_channel(channel.token, secret, &block)
27
+ ensure
28
+ channel.close
29
+ end
30
+
31
+ def with_channel(token, secret)
32
+ token_prv, secret_prv = self.channel_token, self.channel_secret
33
+ self.channel_token, self.channel_secret = token, secret
34
+ yield
35
+ ensure
36
+ self.channel_token, self.channel_secret = token_prv, secret_prv
37
+ end
38
+ end # MockAdapter
39
+ end # Client::SDK::Adapters
40
+ end # Ribbon::Intercom
@@ -0,0 +1,10 @@
1
+ module Ribbon::Intercom
2
+ class Client::SDK
3
+ module Adapters
4
+ autoload(:Adapter, 'ribbon/intercom/client/sdk/adapters/adapter')
5
+ autoload(:HttpAdapter, 'ribbon/intercom/client/sdk/adapters/http_adapter')
6
+ autoload(:LocalAdapter, 'ribbon/intercom/client/sdk/adapters/local_adapter')
7
+ autoload(:MockAdapter, 'ribbon/intercom/client/sdk/adapters/mock_adapter')
8
+ end
9
+ end
10
+ end
@@ -1,49 +1,94 @@
1
+ require 'base64'
2
+
1
3
  module Ribbon::Intercom
2
4
  class Client
3
5
  class SDK
4
- autoload(:Connection, 'ribbon/intercom/client/sdk/connection')
5
- autoload(:Response, 'ribbon/intercom/client/sdk/response')
6
+ autoload(:Adapters, 'ribbon/intercom/client/sdk/adapters')
7
+
8
+ include Utils::Mixins::MockSafe
6
9
 
7
- attr_reader :connection
10
+ attr_reader :adapter
8
11
 
9
12
  def initialize(*args)
10
- connect(*args)
13
+ case args.first
14
+ when Adapters::Adapter
15
+ @adapter = args.first
16
+ else
17
+ @adapter = Adapters::HttpAdapter.new(*args)
18
+ end
19
+ end
20
+
21
+ def headers(h={})
22
+ adapter.headers(h)
11
23
  end
12
24
 
13
25
  def connect(*args)
14
- @connection = Connection.new(*args)
26
+ adapter.connect(*args)
15
27
  end
16
28
 
17
29
  def connected?
18
- !!connection
30
+ adapter.connected?
19
31
  end
20
32
 
21
- def method_missing(meth, *args, &block)
22
- _send_request(meth, *args)
33
+ ##
34
+ # Calls the method on the adapter returning the Adapter::Response.
35
+ #
36
+ # Intended to be called by Package.
37
+ def call(method_name, *args)
38
+ _process_response(adapter.call(method_name, *args))
23
39
  end
24
40
 
25
41
  private
26
42
 
27
- def _send_request(method_name, *args)
28
- response = connection.put("", method_name, args)
43
+ ##
44
+ # Simulates calling the remote method as if it were a local method.
45
+ #
46
+ # Intended to be called by end-users.
47
+ def method_missing(meth, *args, &block)
48
+ call(meth, *args).retval
49
+ end
29
50
 
30
- if response.success?
31
- response.body
32
- else
33
- case response.code
34
- when 401
35
- raise Errors::AuthenticationError, request.body
36
- when 403
37
- raise Errors::MissingPermissionsError, response.missing_permissions.to_a.join(', ')
38
- when 404
39
- raise Errors::InvalidMethodError, "#{method_name} is not a valid method"
40
- when 500
41
- raise Errors::ServerError, response.body
42
- else
43
- raise Errors::RequestFailureError, response.body
51
+ ##
52
+ # Process an Adapter::Response object returned by Adapter#call.
53
+ def _process_response(response)
54
+ _handle_response_error(response) unless response.successful?
55
+
56
+ _init_packages(response.retval)
57
+ response
58
+ end
59
+
60
+ ##
61
+ # Raises an error depending on what went wrong.
62
+ def _handle_response_error(response)
63
+ raise 'called for successful response' if response.successful?
64
+
65
+ encoded_error = response.headers['X-Intercom-Error']
66
+
67
+ if encoded_error && !encoded_error.empty?
68
+ begin
69
+ decoded_error = Base64.strict_decode64(encoded_error)
70
+ error = Marshal.load(decoded_error)
71
+ rescue
72
+ error = Errors::ServerError.new('unknown server error')
44
73
  end
45
74
  end
75
+
76
+ raise error || Errors::ServerError.new('unexpected server error')
77
+ end # _handle_response
78
+
79
+ ##
80
+ # Walks the object and initializes all packages (i.e., sets them up to be
81
+ # accessed by the end-user on the client).
82
+ def _init_packages(object)
83
+ Utils.walk(object) { |object|
84
+ if object.is_a?(Package)
85
+ object.sdk = dup
86
+ object.mock_safe! if mock_safe?
87
+ end
88
+
89
+ object
90
+ }
46
91
  end
47
- end # Client
48
- end # SDK
92
+ end # SDK
93
+ end # Client
49
94
  end # Ribbon::Intercom
@@ -3,6 +3,7 @@ require 'ribbon/config'
3
3
  module Ribbon::Intercom
4
4
  class Client
5
5
  autoload(:SDK, 'ribbon/intercom/client/sdk')
6
+ autoload(:MockSDK, 'ribbon/intercom/client/mock_sdk')
6
7
 
7
8
  def config(&block)
8
9
  (@__config ||= Ribbon::Config.new).tap { |config|
@@ -16,16 +17,43 @@ module Ribbon::Intercom
16
17
  _load_sdk(handle)
17
18
  end
18
19
 
19
- def _load_sdk(handle, url=nil, token=nil, secret=nil)
20
- raise Errors::NoRemotesCreatedError unless config.remote?
20
+ ##
21
+ # Return a mock safe version of this client.
22
+ def mock_safe
23
+ dup.tap { |client|
24
+ client.preload_sdks
21
25
 
22
- (@_sdk_instances ||= Hash.new { |hash, key|
23
- if (remote = config.remote.select { |r| r.first == key }.last)
24
- # Get the last remote if there are duplicates
25
- config = remote.last
26
+ # Make SDK instances mock safe
27
+ client.sdk_instances = Hash[
28
+ client.sdk_instances.map { |service, sdk| [service, sdk.mock_safe] }
29
+ ]
30
+ }
31
+ end
32
+
33
+ def preload_sdks
34
+ if config.service?
35
+ config.service.each { |name, *args|
36
+ _load_sdk(name)
37
+ }
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ attr_accessor :sdk_instances
44
+
45
+ private
46
+
47
+ def _load_sdk(handle)
48
+ raise Errors::ServiceNotDefinedError unless config.service?
49
+
50
+ (self.sdk_instances ||= Hash.new { |hash, key|
51
+ # Get the last service if there are duplicates
52
+ if (service_def = config.service.select { |r| r.first == key }.last)
53
+ name, config = service_def
26
54
  hash[key] = SDK.new(config[:url], config[:token], config[:secret])
27
55
  else
28
- raise Errors::RemoteNotFoundError
56
+ raise Errors::ServiceNotDefinedError
29
57
  end
30
58
  })[handle.to_sym]
31
59
  end
@@ -2,14 +2,44 @@ module Ribbon::Intercom
2
2
  module Errors
3
3
  class Error < StandardError; end
4
4
 
5
+ #############
6
+ # Http Errors
7
+ class HttpError < Error; end
8
+
9
+ ##
10
+ # Request Errors
11
+
12
+ # 400 Error
13
+ class RequestError < HttpError; end
14
+ class UnsafeArgumentError < RequestError; end
15
+ class InvalidSubjectSignatureError < RequestError; end
16
+
17
+ # 401 Error
18
+ class AuthenticationError < RequestError; end
19
+
20
+ # 403 Error
21
+ class ForbiddenError < RequestError; end
22
+ class InsufficientPermissionsError < ForbiddenError; end
23
+
24
+ # 404 Not Found
25
+ class NotFoundError < RequestError; end
26
+ class InvalidMethodError < NotFoundError; end
27
+
28
+ # 405 Method Not Allowed
29
+ class MethodNotAllowedError < RequestError; end
30
+
31
+ ##
32
+ # Server Errors
33
+
34
+ # 500 Error
35
+ class ServerError < HttpError; end
36
+ class UnsafeResponseError < ServerError; end
37
+
5
38
  # General Errors
6
- class MissingPermissionsError < Error; end
7
- class ServerError < Error; end
8
39
  class UnsafeValueError < Error; end
9
40
 
10
41
  # Intercom Errors
11
- class RemoteNotFoundError < Error; end
12
- class NoRemotesCreatedError < Error; end
42
+ class ServiceNotDefinedError < Error; end
13
43
 
14
44
  # Service Errors
15
45
  class NoPermissionsError < Error; end
@@ -22,8 +52,6 @@ module Ribbon::Intercom
22
52
 
23
53
  # SDK Errors
24
54
  class RequestFailureError < Error; end
25
- class InvalidMethodError < Error; end
26
- class AuthenticationError < Error; end
27
55
 
28
56
  # Channel Errors
29
57
  class ChannelNameMissingError < Error; end
@@ -0,0 +1,64 @@
1
+ require 'base64'
2
+
3
+ module Ribbon::Intercom
4
+ class Package
5
+ include Utils::Mixins::MockSafe
6
+
7
+ attr_reader :sdk
8
+
9
+ def initialize(subject_data=nil, data=nil)
10
+ self._subject_data = subject_data
11
+ @_data = data
12
+ end
13
+
14
+ def sdk=(sdk)
15
+ _set_sdk_headers(sdk)
16
+ @sdk = sdk
17
+ end
18
+
19
+ protected
20
+
21
+ def _subject_data=(data)
22
+ @_subject_data = data
23
+ _set_sdk_headers
24
+ end
25
+
26
+ private
27
+
28
+ def method_missing(meth, *args, &block)
29
+ if @_data && @_data.key?(meth)
30
+ @_data[meth]
31
+ elsif sdk
32
+ _process_response(sdk.call(meth, *args))
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def marshal_dump
39
+ [@_subject_data, @_data]
40
+ end
41
+
42
+ def marshal_load(array)
43
+ self._subject_data = array[0]
44
+ @_data = array[1]
45
+
46
+ end
47
+
48
+ def _set_sdk_headers(sdk=sdk)
49
+ sdk.headers('X-Intercom-Subject' => @_subject_data) if sdk
50
+ end
51
+
52
+ def _process_response(response)
53
+ self._subject_data = response.headers['X-Intercom-Subject']
54
+ @_data = _retrieve_data_from_response(response)
55
+
56
+ response.retval
57
+ end
58
+
59
+ def _retrieve_data_from_response(response)
60
+ encoded_data = response.headers['X-Intercom-Package-Data']
61
+ encoded_data && Marshal.load(Base64.strict_decode64(encoded_data))
62
+ end
63
+ end # Package
64
+ end # Ribbon::Intercom
@@ -0,0 +1,35 @@
1
+ require 'set'
2
+
3
+ module Ribbon::Intercom
4
+ class Packageable
5
+ module Mixin
6
+ class << self
7
+ def included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def package_with(*args)
14
+ args.map { |m| _package_with_methods << m.to_sym }
15
+ end
16
+
17
+ def _package_with_methods
18
+ @__package_with_methods ||= [].to_set
19
+ end
20
+ end # ClassMethods
21
+
22
+ ##
23
+ # Returns the package data for the instance as a hash.
24
+ def package_data
25
+ Utils.sanitize(
26
+ self.class._package_with_methods.map { |meth| [meth, public_send(meth)] }.to_h
27
+ )
28
+ end
29
+
30
+ def encoded_package_data
31
+ Base64.strict_encode64(Marshal.dump(package_data))
32
+ end
33
+ end # Mixin
34
+ end # Service::Subject
35
+ end # Ribbon::Intercom
@@ -0,0 +1,6 @@
1
+ module Ribbon::Intercom
2
+ class Packageable
3
+ autoload(:Mixin, 'ribbon/intercom/packageable/mixin')
4
+ include Mixin
5
+ end # Packageable
6
+ end # Ribbon::Intercom
@@ -0,0 +1,40 @@
1
+ module Ribbon::Intercom
2
+ class Service
3
+ module Channel::Stores
4
+ class MockStore < Store
5
+ def channels
6
+ @__channels ||= {}
7
+ end
8
+
9
+ def lock
10
+ @__lock ||= Mutex.new
11
+ end
12
+
13
+ def token_exists?(token)
14
+ channels.key?(token)
15
+ end
16
+
17
+ def lookup_channel(token)
18
+ channels[token]
19
+ end
20
+
21
+ def persist(channel)
22
+ raise Errors::InvalidChannelError, channel.inspect unless channel.is_a?(Channel)
23
+ channels[channel.token] = channel
24
+ end
25
+
26
+ def delete(channel)
27
+ raise Errors::InvalidChannelError, channel.inspect unless channel.is_a?(Channel)
28
+ channels.delete(channel.token)
29
+ nil
30
+ end
31
+
32
+ def with_lock(channel, &block)
33
+ # This is a global lock, not a per-channel lock, but this store is only
34
+ # for testing so let's KISS.
35
+ lock.synchronize(&block)
36
+ end
37
+ end # RedisStore
38
+ end # Channel::Stores
39
+ end # Service
40
+ end # Ribbon::Intercom