ribbon-intercom 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3dd1e6bc419cb94651ef42356db8b7b8c6c48a6
4
+ data.tar.gz: 8c97e000d932acf6012513ab7339e506b0c6852a
5
+ SHA512:
6
+ metadata.gz: 7642b346c15bf955093543ebee843814cace7994bb3565b95db3e833ac13816d7b1f945abea4f8c4adcbe9e284349b7dab6f171271911e5ccf3990ccf8a2856e
7
+ data.tar.gz: 1bde67801438c049bf1877b2c78e8cfb44f25ca82f372370afd48ab0fbc67f7c6e87876e18d9bf1c7c810cacef2b7bb6220b021931d60392b0d74e77f56ef589
@@ -0,0 +1,25 @@
1
+ require 'ribbon/intercom/version'
2
+ require 'rack'
3
+
4
+ module Ribbon
5
+ module Intercom
6
+ require 'ribbon/intercom/railtie' if defined?(Rails)
7
+ autoload(:Service, 'ribbon/intercom/service')
8
+ autoload(:Errors, 'ribbon/intercom/errors')
9
+ autoload(:Client, 'ribbon/intercom/client')
10
+ autoload(:Utils, 'ribbon/intercom/utils')
11
+
12
+ module_function
13
+
14
+ def method_missing(meth, *args, &block)
15
+ client.send(meth, *args, &block)
16
+ end
17
+
18
+ def client
19
+ @__client ||= Client.new
20
+ end
21
+ end # Intercom
22
+ end # Ribbon
23
+
24
+ # Create a shortcut to the module
25
+ Intercom = Ribbon::Intercom
@@ -0,0 +1,31 @@
1
+ require 'ribbon/config'
2
+
3
+ module Ribbon::Intercom
4
+ class Client
5
+ autoload(:SDK, 'ribbon/intercom/client/sdk')
6
+
7
+ def config(&block)
8
+ (@__config ||= Ribbon::Config.new).tap { |config|
9
+ if block_given?
10
+ config.define(&block)
11
+ end
12
+ }
13
+ end
14
+
15
+ def [](handle)
16
+ _load_sdk(handle)
17
+ end
18
+
19
+ def _load_sdk(handle, url=nil, token=nil, secret=nil)
20
+ (@_sdk_instances ||= Hash.new { |hash, key|
21
+ if (remote = config.remote.select { |r| r.first == key }.last)
22
+ # Get the last remote if there are duplicates
23
+ config = remote.last
24
+ SDK.new(config[:url], config[:token], config[:secret])
25
+ else
26
+ raise Errors::RemoteNotFoundError
27
+ end
28
+ })[handle.to_sym]
29
+ end
30
+ end # Client
31
+ end # Ribbon::Intercom
@@ -0,0 +1,49 @@
1
+ module Ribbon::Intercom
2
+ class Client
3
+ class SDK
4
+ autoload(:Connection, 'ribbon/intercom/client/sdk/connection')
5
+ autoload(:Response, 'ribbon/intercom/client/sdk/response')
6
+
7
+ attr_reader :connection
8
+
9
+ def initialize(*args)
10
+ connect(*args)
11
+ end
12
+
13
+ def connect(*args)
14
+ @connection = Connection.new(*args)
15
+ end
16
+
17
+ def connected?
18
+ !!connection
19
+ end
20
+
21
+ def method_missing(meth, *args, &block)
22
+ _send_request(meth, *args)
23
+ end
24
+
25
+ private
26
+
27
+ def _send_request(method_name, *args)
28
+ response = connection.put("", method_name, args)
29
+
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
44
+ end
45
+ end
46
+ end
47
+ end # Client
48
+ end # SDK
49
+ end # Ribbon::Intercom
@@ -0,0 +1,35 @@
1
+ require 'rest-client'
2
+
3
+ module Ribbon::Intercom
4
+ class Client
5
+ class SDK
6
+ class Connection
7
+ def initialize(url, token, secret)
8
+ @client = RestClient::Resource.new(
9
+ url,
10
+ user: token,
11
+ password: secret,
12
+ ) { |response, request, result| response }
13
+ end
14
+
15
+ def put(path, method_name, args)
16
+ path = _build_path(path)
17
+ headers = { "X-Intercom-Method" => method_name }
18
+ payload = _encode_args(args)
19
+
20
+ Response.new(@client[path].put(payload, headers))
21
+ end
22
+
23
+ private
24
+
25
+ def _build_path(path)
26
+ path[0] == '/' ? path : '/' + path
27
+ end
28
+
29
+ def _encode_args(args)
30
+ Base64.strict_encode64(Marshal.dump(Utils.sanitize(args)))
31
+ end
32
+ end # Connection
33
+ end # SDK
34
+ end # Client
35
+ end # Ribbon::Intercom
@@ -0,0 +1,59 @@
1
+ require 'base64'
2
+ require 'set'
3
+
4
+ module Ribbon::Intercom
5
+ class Client
6
+ class SDK
7
+ class Response
8
+ attr_reader :headers
9
+ attr_reader :body
10
+ attr_reader :code
11
+
12
+ def initialize(rest_client_response)
13
+ response = rest_client_response
14
+ @code = response.code
15
+ @headers = rest_client_response.headers
16
+ @body = _process_body(response.body)
17
+ _deep_freeze(@body)
18
+ end
19
+
20
+ def success?
21
+ code >= 200 && code < 300
22
+ end
23
+
24
+ def error?
25
+ !success?
26
+ end
27
+
28
+ def missing_permissions
29
+ @__missing_permissions ||= headers[:x_intercom_permissions_missing] &&
30
+ headers[:x_intercom_permissions_missing].split(',').to_set
31
+ end
32
+
33
+ def method_missing(meth, *args, &block)
34
+ if body.key?(meth.to_s)
35
+ body[meth.to_s]
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def _process_body(body)
44
+ Marshal.load(Base64.strict_decode64(body)) unless body.empty?
45
+ end
46
+
47
+ def _deep_freeze(obj)
48
+ if obj.respond_to?(:each)
49
+ obj.each do |elem|
50
+ _deep_freeze(elem)
51
+ end
52
+ end
53
+
54
+ obj.freeze
55
+ end
56
+ end # Response
57
+ end # SDK
58
+ end # Client
59
+ end # Ribbon::Intercom
@@ -0,0 +1,32 @@
1
+ module Ribbon::Intercom
2
+ module Errors
3
+ class Error < StandardError; end
4
+
5
+ # General Errors
6
+ class MissingPermissionsError < Error; end
7
+ class ServerError < Error; end
8
+ class UnsafeValueError < Error; end
9
+
10
+ # Intercom Errors
11
+ class RemoteNotFoundError < Error; end
12
+
13
+ # Service Errors
14
+ class NoPermissionsError < Error; end
15
+ class MissingStoreError < Error; end
16
+
17
+ # Channel Store Errors
18
+ class ChannelStoreError < Error; end
19
+ class InvalidStoreParamsError < ChannelStoreError; end
20
+ class InvalidChannelError < ChannelStoreError; end
21
+
22
+ # SDK Errors
23
+ class RequestFailureError < Error; end
24
+ class InvalidMethodError < Error; end
25
+ class AuthenticationError < Error; end
26
+
27
+ # Channel Errors
28
+ class ChannelNameMissingError < Error; end
29
+ class ChannelTokenMissingError < Error; end
30
+ class ChannelSecretMissingError < Error; end
31
+ end # Errors
32
+ end # Ribbon::Intercom
@@ -0,0 +1,16 @@
1
+ require 'ribbon/intercom'
2
+ require 'rails'
3
+
4
+ module Ribbon
5
+ module Intercom
6
+ class Railtie < Rails::Railtie
7
+ railtie_name :intercom
8
+
9
+ rake_tasks do
10
+ Dir[
11
+ File.expand_path("../../../tasks", __FILE__) + '/**/*.rake'
12
+ ].each { |rake_file| load rake_file }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,185 @@
1
+ require 'base64'
2
+ require 'yaml'
3
+ require 'set'
4
+
5
+ module Ribbon::Intercom
6
+ class Service
7
+ autoload(:Channel, 'ribbon/intercom/service/channel')
8
+
9
+ module ChannelStores
10
+ autoload(:Base, 'ribbon/intercom/service/channel_stores/base')
11
+ autoload(:RedisStore, 'ribbon/intercom/service/channel_stores/redis_store')
12
+ end
13
+
14
+ class << self
15
+ def instance
16
+ @instance ||= new(store: _load_store)
17
+ end
18
+
19
+ def default_store(store_name, params={})
20
+ @_store_name = store_name
21
+ @_store_params = params
22
+ end
23
+
24
+ def permissions(*perms)
25
+ @_next_permissions = perms.map(&:to_sym).to_set
26
+ end
27
+
28
+ def method_permissions
29
+ if (ancestor = ancestors.find { |a| a <= Service && a != self })
30
+ ancestor.method_permissions
31
+ else
32
+ {}
33
+ end.merge(@_method_permissions).dup
34
+ end
35
+
36
+ def method_missing(meth, *args, &block)
37
+ instance.send(meth, *args, &block)
38
+ end
39
+
40
+ def _load_store
41
+ raise "Store name missing" unless (store_name = @_store_name.to_s)
42
+
43
+ store = store_name.split("_").map(&:capitalize).join + "Store"
44
+ Intercom::Service::ChannelStores.const_get(store).new(@_store_params)
45
+ end
46
+ end # Class methods
47
+
48
+ attr_reader :request
49
+ attr_reader :channel
50
+ attr_reader :env
51
+
52
+ def initialize(opts={})
53
+ @_opts = opts.dup
54
+ end
55
+
56
+ def store
57
+ @store ||= @_opts[:store] or raise Errors::MissingStoreError
58
+ end
59
+
60
+ def open_channel(params={})
61
+ # Accept either an array of permissions or a string
62
+ store.open_channel(params)
63
+ end
64
+
65
+ def lookup_channel(token)
66
+ store.lookup_channel(token)
67
+ end
68
+
69
+ def method_permissions(method_name)
70
+ self.class.method_permissions[method_name.to_sym]
71
+ end
72
+
73
+ def method_permissions!(method_name)
74
+ method_permissions(method_name) or raise Errors::InvalidMethodError
75
+ end
76
+
77
+ def sufficient_permissions?(intercom_method)
78
+ permissions = method_permissions!(intercom_method)
79
+ channel.may?(*permissions)
80
+ end
81
+
82
+ def sufficient_permissions!(intercom_method)
83
+ sufficient_permissions?(intercom_method) or raise Errors::MissingPermissionsError
84
+ end
85
+
86
+ def process_request
87
+ @request = Rack::Request.new(env)
88
+ return _respond!(405) unless request.put?
89
+ return _respond!(401) unless _perform_basic_auth(request.env)
90
+
91
+ # Check permissions
92
+ intercom_method = env['HTTP_X_INTERCOM_METHOD'].to_sym
93
+ sufficient_permissions!(intercom_method)
94
+
95
+ # Execute the requested method
96
+ args = _decode_args(request.body.read)
97
+
98
+ [intercom_method, args]
99
+ rescue Errors::UnsafeValueError
100
+ _respond!(400, {}, "Arg types are invalid")
101
+ rescue Errors::MissingPermissionsError
102
+ _respond!(403, {"X-Intercom-Permissions-Missing" => _missing_permissions(intercom_method).to_a.join(',')}, "Permissions missing.")
103
+ rescue Errors::InvalidMethodError
104
+ _respond!(404, {}, "Method requested not found.")
105
+ end
106
+
107
+ def call_method(intercom_method, *args)
108
+ body = Utils.sanitize(send(intercom_method, *args))
109
+ _respond!(200, {}, body)
110
+ rescue Errors::UnsafeValueError
111
+ _respond!(500, {}, "Return value type is invalid")
112
+ end
113
+
114
+ def call(env)
115
+ dup.call!(env)
116
+ end
117
+
118
+ def call!(env)
119
+ @env = env
120
+
121
+ response = catch(:response) {
122
+ intercom_method, args = process_request
123
+ call_method(intercom_method, *args)
124
+ }
125
+
126
+ response.finish
127
+ rescue Exception => e
128
+ _response(500, {}, e.message).finish
129
+ end
130
+
131
+ private
132
+
133
+ def _perform_basic_auth(env)
134
+ auth = Rack::Auth::Basic::Request.new(env)
135
+
136
+ if auth.provided? && auth.basic?
137
+ token = auth.credentials[0]
138
+ secret = auth.credentials[1]
139
+
140
+ # Check if the request is authenticated
141
+ @channel = lookup_channel(token)
142
+ channel && channel.valid_secret?(secret)
143
+ end
144
+ end
145
+
146
+ def _missing_permissions(intercom_method)
147
+ method_permissions(intercom_method) - channel.permissions
148
+ end
149
+
150
+ def _response(status, headers={}, body="")
151
+ body = [_encode_body(body)]
152
+ headers = headers.merge("Content-Type" => "text/plain", "Transfer-Encoding" => "gzip")
153
+ Rack::Response.new(body, status, headers)
154
+ end
155
+
156
+ def _respond!(status, headers={}, body="")
157
+ throw :response, _response(status, headers, body)
158
+ end
159
+
160
+ def _encode_body(body)
161
+ Base64.strict_encode64(Marshal.dump(body))
162
+ end
163
+
164
+ def _decode_args(args)
165
+ Utils.sanitize(Marshal.load(Base64.strict_decode64(args))).tap { |args|
166
+ raise Errors::UnsafeValueError unless args.is_a?(Array)
167
+ }
168
+ end
169
+ end # Service
170
+
171
+ # Re-opening the class so the above instance methods don't get added to the
172
+ # permissions hash
173
+ class Service
174
+ class << self
175
+ def method_added(method_name)
176
+ (@_method_permissions ||= {})[method_name] = (@_next_permissions || [method_name].to_set).dup.freeze
177
+ @_next_permissions = nil
178
+ end
179
+ end # Class Methods
180
+
181
+ def rotate_secret
182
+ channel.rotate_secret!
183
+ end
184
+ end # Service
185
+ end # Ribbon::Intercom
@@ -0,0 +1,101 @@
1
+ require 'securerandom'
2
+ require 'bcrypt'
3
+
4
+ module Ribbon::Intercom
5
+ class Service
6
+ class Channel
7
+ include BCrypt
8
+
9
+ attr_reader :name
10
+ attr_reader :store
11
+ attr_reader :token
12
+ attr_reader :secret_hash_crt
13
+ attr_reader :secret_hash_prv
14
+
15
+ def initialize(store, params={})
16
+ @store = store
17
+ @name = params[:name] or raise Errors::ChannelNameMissingError
18
+ @token = params[:token]
19
+ @secret_hash_crt = params[:secret_hash_crt]
20
+ @secret_hash_prv = params[:secret_hash_prv]
21
+
22
+ may(*((params[:may] || []) | [:rotate_secret]))
23
+ end
24
+
25
+ def rotate_secret
26
+ SecureRandom.hex(16).tap { |secret|
27
+ @secret_hash_prv = secret_hash_crt
28
+ @secret_hash_crt = Password.create(secret)
29
+ }
30
+ end
31
+
32
+ def rotate_secret!
33
+ rotate_secret.tap { save }
34
+ end
35
+
36
+ def may(*args)
37
+ (@_allowed_to ||= Hash.new(false)).merge!(
38
+ Hash[
39
+ args.map { |perm| [perm.to_sym, true] }
40
+ ]
41
+ ).keys.to_set
42
+ end
43
+
44
+ def may!(*args)
45
+ may(*args).tap { save }
46
+ end
47
+
48
+ def may?(*args)
49
+ args.all? { |perm| @_allowed_to[perm.to_sym] }
50
+ end
51
+
52
+ def permissions
53
+ @_allowed_to.keys.to_set
54
+ end
55
+
56
+ def valid_secret?(secret)
57
+ !!secret && (secret_crt == secret || secret_prv == secret)
58
+ end
59
+
60
+ def secret_crt
61
+ @__secret_crt ||= _to_bcrypt_pw(secret_hash_crt)
62
+ end
63
+
64
+ def secret_prv
65
+ @__secret_prv ||= _to_bcrypt_pw(secret_hash_prv)
66
+ end
67
+
68
+ def save
69
+ # Loop until unique
70
+ unless token
71
+ loop { break unless store.token_exists?(@token = SecureRandom.hex(4)) }
72
+ end
73
+
74
+ _run_validations
75
+ store.persist(self)
76
+ end
77
+
78
+ def ==(other)
79
+ other.is_a?(Channel) &&
80
+ name == other.name &&
81
+ store == other.store &&
82
+ token == other.token &&
83
+ secret_crt.to_s == other.secret_crt.to_s &&
84
+ secret_prv.to_s == other.secret_prv.to_s &&
85
+ permissions == other.permissions
86
+ end
87
+
88
+ private
89
+
90
+ def _to_bcrypt_pw(pw)
91
+ Password.new(pw) if pw && !pw.empty?
92
+ end
93
+
94
+ def _run_validations
95
+ raise Errors::ChannelNameMissingError unless name
96
+ raise Errors::ChannelTokenMissingError unless token
97
+ raise Errors::ChannelSecretMissingError unless secret_hash_crt
98
+ end
99
+ end # Service
100
+ end # Channel
101
+ end # Ribbon::Intercom
@@ -0,0 +1,23 @@
1
+ module Ribbon::Intercom
2
+ class Service
3
+ module ChannelStores
4
+ class Base
5
+ def open_channel(params={})
6
+ raise NotImplementedError
7
+ end
8
+
9
+ def token_exists?(token)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def lookup_channel(token)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def persist(channel)
18
+ raise NotImplementedError
19
+ end
20
+ end # Base
21
+ end # ChannelStores
22
+ end # Service
23
+ end # Ribbon::Intercom
@@ -0,0 +1,60 @@
1
+ require 'redis'
2
+
3
+ module Ribbon::Intercom
4
+ class Service
5
+ module ChannelStores
6
+ class RedisStore < Base
7
+ def initialize(params={})
8
+ if params[:url]
9
+ @_redis = Redis.new(url: params[:url])
10
+ elsif params[:redis]
11
+ @_redis = params[:redis]
12
+ else
13
+ raise Errors::InvalidStoreParamsError
14
+ end
15
+ end
16
+
17
+ def open_channel(params={})
18
+ Channel.new(self, params)
19
+ end
20
+
21
+ def token_exists?(token)
22
+ @_redis.exists("channel:#{token}")
23
+ end
24
+
25
+ def lookup_channel(token)
26
+ if token_exists?(token) && (channel_data = @_redis.hgetall("channel:#{token}"))
27
+ permissions = @_redis.smembers("channel:#{token}:permissions")
28
+
29
+ Channel.new(self,
30
+ Utils.symbolize_keys(
31
+ channel_data.merge(may: permissions)
32
+ )
33
+ )
34
+ end
35
+ end
36
+
37
+ def persist(channel)
38
+ raise Errors::InvalidChannelError, channel.inspect unless channel.is_a?(Channel)
39
+
40
+ data_hash = {}
41
+ [:name, :token, :secret_hash_crt, :secret_hash_prv].each { |key|
42
+ value = channel.send(key)
43
+ data_hash[key] = value if value
44
+ }
45
+
46
+ # Save channel data as hash
47
+ @_redis.mapped_hmset("channel:#{channel.token}", data_hash)
48
+
49
+ # Associate permissions set to its channel
50
+ channel_key = "channel:#{channel.token}:permissions"
51
+ channel.tap { |c|
52
+ c.permissions.each { |p|
53
+ @_redis.sadd(channel_key, p)
54
+ }
55
+ }
56
+ end
57
+ end # RedisStore
58
+ end # ChannelStores
59
+ end # Service
60
+ end # Ribbon::Intercom
@@ -0,0 +1,30 @@
1
+ module Ribbon::Intercom
2
+ class Utils
3
+ class << self
4
+ def sanitize(object)
5
+ case object
6
+ when String, Symbol, TrueClass, FalseClass, Integer, Float, NilClass
7
+ object
8
+ when Hash
9
+ sanitize_hash(object)
10
+ when Array
11
+ sanitize_array(object)
12
+ else
13
+ raise Errors::UnsafeValueError, object.inspect
14
+ end
15
+ end
16
+
17
+ def sanitize_array(array)
18
+ array.map { |obj| sanitize(obj) }
19
+ end
20
+
21
+ def sanitize_hash(hash)
22
+ Hash[hash.map { |key, val| [sanitize(key), sanitize(val)] }]
23
+ end
24
+
25
+ def symbolize_keys(hash)
26
+ hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
27
+ end
28
+ end # Class methods
29
+ end # Utils
30
+ end # Ribbon::Intercom
@@ -0,0 +1,5 @@
1
+ module Ribbon
2
+ module Intercom
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ require 'optparse'
2
+ require 'ribbon/intercom'
3
+ require 'json'
4
+
5
+ namespace :intercom do
6
+ namespace :client do
7
+ task :rotate_secret, [:remote] => :environment do |t, args|
8
+ remote = args[:remote]
9
+ puts "New #{remote} secret: #{Intercom[remote].rotate_secret}"
10
+ end
11
+ end
12
+
13
+ namespace :service do
14
+ task :open_channel, [:service_name, :channel_name] => :environment do |t, args|
15
+ service = args[:service_name].split("_").map(&:capitalize).join
16
+ channel_name = args[:channel_name]
17
+
18
+ Intercom::const_get(service).open_channel(name: channel_name)
19
+ puts "New channel: #{channel_name}"
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,212 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ribbon-intercom
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Honer
8
+ - Kayvon Ghaffari
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-04-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.5'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.5.2
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '1.5'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.2
34
+ - !ruby/object:Gem::Dependency
35
+ name: bcrypt
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.1.3
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 3.1.3
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: 3.1.3
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 3.1.3
54
+ - !ruby/object:Gem::Dependency
55
+ name: redis
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rest-client
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 1.7.2
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 1.7.2
82
+ - !ruby/object:Gem::Dependency
83
+ name: ribbon-config
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 0.1.0
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.1.0
96
+ - !ruby/object:Gem::Dependency
97
+ name: rspec
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec-rails
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: sqlite3
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: rails
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: 4.0.0
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: 4.0.0
152
+ - !ruby/object:Gem::Dependency
153
+ name: mock_redis
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ description: A standard for authenticating requests among services
167
+ email:
168
+ - robert@ribbonpayments.com
169
+ - kayvon@ribbon.co
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - lib/ribbon/intercom.rb
175
+ - lib/ribbon/intercom/client.rb
176
+ - lib/ribbon/intercom/client/sdk.rb
177
+ - lib/ribbon/intercom/client/sdk/connection.rb
178
+ - lib/ribbon/intercom/client/sdk/response.rb
179
+ - lib/ribbon/intercom/errors.rb
180
+ - lib/ribbon/intercom/railtie.rb
181
+ - lib/ribbon/intercom/service.rb
182
+ - lib/ribbon/intercom/service/channel.rb
183
+ - lib/ribbon/intercom/service/channel_stores/base.rb
184
+ - lib/ribbon/intercom/service/channel_stores/redis_store.rb
185
+ - lib/ribbon/intercom/utils.rb
186
+ - lib/ribbon/intercom/version.rb
187
+ - lib/tasks/intercom.rake
188
+ homepage: http://github.com/ribbon/intercom
189
+ licenses:
190
+ - BSD
191
+ metadata: {}
192
+ post_install_message:
193
+ rdoc_options: []
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ requirements: []
207
+ rubyforge_project:
208
+ rubygems_version: 2.4.3
209
+ signing_key:
210
+ specification_version: 4
211
+ summary: A standard for authenticating requests among services
212
+ test_files: []