ably 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 +7 -0
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +103 -0
- data/Rakefile +4 -0
- data/ably.gemspec +32 -0
- data/lib/ably.rb +11 -0
- data/lib/ably/auth.rb +381 -0
- data/lib/ably/exceptions.rb +16 -0
- data/lib/ably/realtime.rb +38 -0
- data/lib/ably/realtime/callbacks.rb +15 -0
- data/lib/ably/realtime/channel.rb +51 -0
- data/lib/ably/realtime/client.rb +82 -0
- data/lib/ably/realtime/connection.rb +61 -0
- data/lib/ably/rest.rb +15 -0
- data/lib/ably/rest/channel.rb +58 -0
- data/lib/ably/rest/client.rb +194 -0
- data/lib/ably/rest/middleware/exceptions.rb +42 -0
- data/lib/ably/rest/middleware/external_exceptions.rb +26 -0
- data/lib/ably/rest/middleware/parse_json.rb +15 -0
- data/lib/ably/rest/paged_resource.rb +107 -0
- data/lib/ably/rest/presence.rb +44 -0
- data/lib/ably/support.rb +14 -0
- data/lib/ably/token.rb +55 -0
- data/lib/ably/version.rb +3 -0
- data/spec/acceptance/realtime_client_spec.rb +12 -0
- data/spec/acceptance/rest/auth_spec.rb +441 -0
- data/spec/acceptance/rest/base_spec.rb +113 -0
- data/spec/acceptance/rest/channel_spec.rb +68 -0
- data/spec/acceptance/rest/presence_spec.rb +22 -0
- data/spec/acceptance/rest/stats_spec.rb +57 -0
- data/spec/acceptance/rest/time_spec.rb +14 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/api_helper.rb +41 -0
- data/spec/support/test_app.rb +77 -0
- data/spec/unit/auth.rb +9 -0
- data/spec/unit/realtime_spec.rb +9 -0
- data/spec/unit/rest_spec.rb +99 -0
- data/spec/unit/token_spec.rb +90 -0
- metadata +240 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
module Ably
|
2
|
+
class InvalidRequest < StandardError
|
3
|
+
attr_reader :status, :code
|
4
|
+
def initialize(message, status: nil, code: nil)
|
5
|
+
super message
|
6
|
+
@status = status
|
7
|
+
@code = code
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ServerError < StandardError; end
|
12
|
+
class InvalidPageError < StandardError; end
|
13
|
+
class InvalidResponseBody < StandardError; end
|
14
|
+
class InsecureRequestError < StandardError; end
|
15
|
+
class TokenRequestError < StandardError; end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "websocket/driver"
|
3
|
+
|
4
|
+
require "ably/realtime/callbacks"
|
5
|
+
require "ably/realtime/channel"
|
6
|
+
require "ably/realtime/client"
|
7
|
+
require "ably/realtime/connection"
|
8
|
+
|
9
|
+
module Ably
|
10
|
+
module Realtime
|
11
|
+
# Actions which are sent by the Ably Realtime API
|
12
|
+
#
|
13
|
+
# The values correspond to the ints which the API
|
14
|
+
# understands.
|
15
|
+
ACTIONS = {
|
16
|
+
heartbeat: 0,
|
17
|
+
ack: 1,
|
18
|
+
nack: 2,
|
19
|
+
connect: 3,
|
20
|
+
connected: 4,
|
21
|
+
disconnect: 5,
|
22
|
+
disconnected: 6,
|
23
|
+
close: 7,
|
24
|
+
closed: 8,
|
25
|
+
error: 9,
|
26
|
+
attach: 10,
|
27
|
+
attached: 11,
|
28
|
+
detach: 12,
|
29
|
+
detached: 13,
|
30
|
+
presence: 14,
|
31
|
+
message: 15
|
32
|
+
}
|
33
|
+
|
34
|
+
def self.new(*args)
|
35
|
+
Ably::Realtime::Client.new(*args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Ably
|
2
|
+
module Realtime
|
3
|
+
module Callbacks
|
4
|
+
def on(event, &block)
|
5
|
+
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
6
|
+
@callbacks[event] << block
|
7
|
+
end
|
8
|
+
|
9
|
+
def trigger(event, *args)
|
10
|
+
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
11
|
+
@callbacks[event].each { |cb| cb.call(*args) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Ably
|
2
|
+
module Realtime
|
3
|
+
class Channel
|
4
|
+
include Callbacks
|
5
|
+
|
6
|
+
attr_reader :client, :name
|
7
|
+
|
8
|
+
def initialize(client, name)
|
9
|
+
@state = :initialised
|
10
|
+
@client = client
|
11
|
+
@name = name
|
12
|
+
@subscriptions = Hash.new { |hash, key| hash[key] = [] }
|
13
|
+
|
14
|
+
on(:message) do |message|
|
15
|
+
event = message[:name]
|
16
|
+
|
17
|
+
@subscriptions[:all].each { |cb| cb.call(message) }
|
18
|
+
@subscriptions[event].each { |cb| cb.call(message) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def publish(event, data)
|
23
|
+
message = { name: event, data: data }
|
24
|
+
|
25
|
+
if attached?
|
26
|
+
client.send_message(name, message)
|
27
|
+
else
|
28
|
+
on(:attached) { client.send_message(name, message) }
|
29
|
+
attach
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def subscribe(event = :all, &blk)
|
34
|
+
@subscriptions[event] << blk
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def attached?
|
39
|
+
@state == :attached
|
40
|
+
end
|
41
|
+
|
42
|
+
def attach
|
43
|
+
unless @state == :attaching
|
44
|
+
@state = :attaching
|
45
|
+
client.attach_to_channel(name)
|
46
|
+
on(:attached) { @state = :attached }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Ably
|
2
|
+
module Realtime
|
3
|
+
# A client for the Ably Realtime API
|
4
|
+
class Client
|
5
|
+
include Callbacks
|
6
|
+
|
7
|
+
DOMAIN = "staging-realtime.ably.io"
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@rest_client = Ably::Rest::Client.new(options)
|
11
|
+
|
12
|
+
on(:attached) do |data|
|
13
|
+
channel = channel(data[:channel])
|
14
|
+
|
15
|
+
channel.trigger(:attached)
|
16
|
+
end
|
17
|
+
|
18
|
+
on(:message) do |data|
|
19
|
+
channel = channel(data[:channel])
|
20
|
+
|
21
|
+
data[:messages].each do |message|
|
22
|
+
channel.trigger(:message, message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def token
|
28
|
+
@token ||= @rest_client.request_token
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return a Realtime Channel for the given name
|
32
|
+
#
|
33
|
+
# @param name [String] The name of the channel
|
34
|
+
# @return [Ably::Realtime::Channel]
|
35
|
+
def channel(name)
|
36
|
+
@channels ||= {}
|
37
|
+
@channels[name] ||= Ably::Realtime::Channel.new(self, name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_message(channel_name, message)
|
41
|
+
payload = {
|
42
|
+
action: ACTIONS[:message],
|
43
|
+
channel: channel_name,
|
44
|
+
messages: [message]
|
45
|
+
}.to_json
|
46
|
+
|
47
|
+
connection.send(payload)
|
48
|
+
end
|
49
|
+
|
50
|
+
def attach_to_channel(channel_name)
|
51
|
+
payload = {
|
52
|
+
action: ACTIONS[:attach],
|
53
|
+
channel: channel_name
|
54
|
+
}.to_json
|
55
|
+
|
56
|
+
connection.send(payload)
|
57
|
+
end
|
58
|
+
|
59
|
+
def use_tls?
|
60
|
+
@rest_client.use_tls?
|
61
|
+
end
|
62
|
+
|
63
|
+
def endpoint
|
64
|
+
@endpoint ||= URI::Generic.build(
|
65
|
+
scheme: use_tls? ? "wss" : "ws",
|
66
|
+
host: DOMAIN,
|
67
|
+
query: "access_token=#{token.id}&binary=false×tamp=#{Time.now.to_i}"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def connection
|
72
|
+
@connection ||= begin
|
73
|
+
host = endpoint.host
|
74
|
+
port = use_tls? ? 443 : 80
|
75
|
+
|
76
|
+
EventMachine.connect(host, port, Connection, self)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Ably
|
2
|
+
module Realtime
|
3
|
+
class Connection < EventMachine::Connection
|
4
|
+
include Callbacks
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
# Ably::Realtime interface
|
11
|
+
def send(data)
|
12
|
+
@driver.text(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
# EventMachine::Connection interface
|
16
|
+
def post_init
|
17
|
+
trigger :initalised
|
18
|
+
|
19
|
+
setup_driver
|
20
|
+
end
|
21
|
+
|
22
|
+
def connection_completed
|
23
|
+
trigger :connecting
|
24
|
+
|
25
|
+
start_tls if @client.use_tls?
|
26
|
+
@driver.start
|
27
|
+
end
|
28
|
+
|
29
|
+
def receive_data(data)
|
30
|
+
@driver.parse(data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def unbind
|
34
|
+
trigger :disconnected
|
35
|
+
end
|
36
|
+
|
37
|
+
# WebSocket::Driver interface
|
38
|
+
def url
|
39
|
+
@client.endpoint.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def write(data)
|
43
|
+
send_data(data)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def setup_driver
|
48
|
+
@driver = WebSocket::Driver.client(self)
|
49
|
+
|
50
|
+
@driver.on("open") { trigger :connected }
|
51
|
+
|
52
|
+
@driver.on("message") do |event|
|
53
|
+
message = JSON.parse(event.data, symbolize_names: true)
|
54
|
+
action = ACTIONS.detect { |k,v| v == message[:action] }.first
|
55
|
+
|
56
|
+
@client.trigger action, message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/ably/rest.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "ably/rest/channel"
|
2
|
+
require "ably/rest/client"
|
3
|
+
require "ably/rest/paged_resource"
|
4
|
+
require "ably/rest/presence"
|
5
|
+
|
6
|
+
module Ably
|
7
|
+
module Rest
|
8
|
+
# Convenience method providing an alias to {Ably::Rest::Client} constructor.
|
9
|
+
#
|
10
|
+
# @return [Ably::Rest::Client]
|
11
|
+
def self.new(*args)
|
12
|
+
Ably::Rest::Client.new(*args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Ably
|
2
|
+
module Rest
|
3
|
+
class Channel
|
4
|
+
attr_reader :client, :name
|
5
|
+
|
6
|
+
# Initialize a new Channel object
|
7
|
+
#
|
8
|
+
# @param client [Ably::Rest::Client]
|
9
|
+
# @param name [String] The name of the channel
|
10
|
+
def initialize(client, name)
|
11
|
+
@client = client
|
12
|
+
@name = name
|
13
|
+
end
|
14
|
+
|
15
|
+
# Publish a message to the channel
|
16
|
+
#
|
17
|
+
# @param message [Hash] The message to publish (must contain :name and :data keys)
|
18
|
+
# @return [Boolean] true if the message was published, otherwise false
|
19
|
+
def publish(event, message)
|
20
|
+
payload = {
|
21
|
+
name: event,
|
22
|
+
data: message
|
23
|
+
}
|
24
|
+
|
25
|
+
response = client.post("#{base_path}/publish", payload)
|
26
|
+
|
27
|
+
response.status == 201
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the message history of the channel
|
31
|
+
#
|
32
|
+
# Options:
|
33
|
+
# - start: Time or millisecond since epoch
|
34
|
+
# - end: Time or millisecond since epoch
|
35
|
+
# - direction: :forwards or :backwards
|
36
|
+
# - limit: Maximum number of messages to retrieve up to 10,000
|
37
|
+
# - by: :message, :bundle or :hour. Defaults to :message
|
38
|
+
#
|
39
|
+
# @return [PagedResource] An Array of hashes representing the message history that supports paging (next, first)
|
40
|
+
def history(options = {})
|
41
|
+
url = "#{base_path}/messages"
|
42
|
+
# TODO: Remove live param as all history should be live
|
43
|
+
response = client.get(url, options.merge(live: true))
|
44
|
+
|
45
|
+
PagedResource.new(response, url, client)
|
46
|
+
end
|
47
|
+
|
48
|
+
def presence
|
49
|
+
@presence ||= Presence.new(client, self)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def base_path
|
54
|
+
"/channels/#{CGI.escape(name)}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require "json"
|
2
|
+
require "faraday"
|
3
|
+
|
4
|
+
require "ably/rest/middleware/exceptions"
|
5
|
+
require "ably/rest/middleware/parse_json"
|
6
|
+
|
7
|
+
module Ably
|
8
|
+
module Rest
|
9
|
+
# Wrapper for the Ably REST API
|
10
|
+
#
|
11
|
+
# @!attribute [r] auth
|
12
|
+
# @return {Ably::Auth} authentication object configured for this connection
|
13
|
+
# @!attribute [r] client_id
|
14
|
+
# @return [String] A client ID, used for identifying this client for presence purposes
|
15
|
+
# @!attribute [r] auth_options
|
16
|
+
# @return [Hash] {Ably::Auth} options configured for this client
|
17
|
+
# @!attribute [r] tls
|
18
|
+
# @return [Boolean] True if client is configured to use TLS for all Ably communication
|
19
|
+
# @!attribute [r] environment
|
20
|
+
# @return [String] May contain 'sandbox' when testing the client library against an alternate Ably environment
|
21
|
+
class Client
|
22
|
+
include Ably::Support
|
23
|
+
extend Forwardable
|
24
|
+
|
25
|
+
DOMAIN = "rest.ably.io"
|
26
|
+
|
27
|
+
attr_reader :tls, :environment, :auth
|
28
|
+
def_delegator :auth, :client_id, :auth_options
|
29
|
+
|
30
|
+
# Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
|
31
|
+
#
|
32
|
+
# @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
|
33
|
+
# @option options [Boolean] :tls TLS is used by default, providing a value of false disbles TLS. Please note Basic Auth is disallowed without TLS as secrets cannot be transmitted over unsecured connections.
|
34
|
+
# @option options [String] :api_key API key comprising the key ID and key secret in a single string
|
35
|
+
# @option options [String] :key_id key ID for the designated application (defaults to client key_id)
|
36
|
+
# @option options [String] :key_secret key secret for the designated application used to sign token requests (defaults to client key_secret)
|
37
|
+
# @option options [String] :client_id client ID identifying this connection to other clients (defaults to client client_id if configured)
|
38
|
+
# @option options [String] :auth_url a URL to be used to GET or POST a set of token request params, to obtain a signed token request.
|
39
|
+
# @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
|
40
|
+
# @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
|
41
|
+
# @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
|
42
|
+
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
43
|
+
# @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
|
44
|
+
# @option options [Boolean] :query_time when true will query the {https://ably.io Ably} system for the current time instead of using the local time
|
45
|
+
# @option options [Integer] :timestamp the time of the of the request in seconds since the epoch
|
46
|
+
# @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
|
47
|
+
# @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment
|
48
|
+
# @option options [Boolean] :debug_http Send HTTP debugging information from Faraday for all HTTP requests to STDOUT
|
49
|
+
#
|
50
|
+
# @yield [options] (optional) if an auth block is passed to this method, then this block will be called to create a new token request object
|
51
|
+
# @yieldparam [Hash] options options passed to request_token will be in turn sent to the block in this argument
|
52
|
+
# @yieldreturn [Hash] valid token request object, see {#create_token_request}
|
53
|
+
#
|
54
|
+
# @return [Ably::Rest::Client]
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# # create a new client authenticating with basic auth
|
58
|
+
# client = Ably::Rest::Client.new('key.id:secret')
|
59
|
+
#
|
60
|
+
# # create a new client and configure a client ID used for presence
|
61
|
+
# client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
|
62
|
+
#
|
63
|
+
def initialize(options, &auth_block)
|
64
|
+
if options.kind_of?(String)
|
65
|
+
options = { api_key: options }
|
66
|
+
end
|
67
|
+
|
68
|
+
@tls = options.delete(:tls) == false ? false : true
|
69
|
+
@environment = options.delete(:environment) # nil is production
|
70
|
+
@debug_http = options.delete(:debug_http)
|
71
|
+
|
72
|
+
@auth = Auth.new(self, options, &auth_block)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return a REST {Ably::Rest::Channel} for the given name
|
76
|
+
#
|
77
|
+
# @param name [String] The name of the channel
|
78
|
+
# @return [Ably::Rest::Channel]
|
79
|
+
def channel(name)
|
80
|
+
@channels ||= {}
|
81
|
+
@channels[name] ||= Ably::Rest::Channel.new(self, name)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return the stats for the application
|
85
|
+
#
|
86
|
+
# @return [Array] An Array of hashes representing the stats
|
87
|
+
def stats(params = {})
|
88
|
+
default_params = {
|
89
|
+
:direction => :forwards,
|
90
|
+
:by => :minute
|
91
|
+
}
|
92
|
+
|
93
|
+
response = get("/stats", default_params.merge(params))
|
94
|
+
|
95
|
+
response.body
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return the Ably service time
|
99
|
+
#
|
100
|
+
# @return [Time] The time as reported by the Ably service
|
101
|
+
def time
|
102
|
+
response = get('/time', {}, send_auth_header: false)
|
103
|
+
|
104
|
+
Time.at(response.body.first / 1000.0)
|
105
|
+
end
|
106
|
+
|
107
|
+
# True if client is configured to use TLS for all Ably communication
|
108
|
+
#
|
109
|
+
# @return [Boolean]
|
110
|
+
def use_tls?
|
111
|
+
@tls == true
|
112
|
+
end
|
113
|
+
|
114
|
+
# Perform an HTTP GET request to the API using configured authentication
|
115
|
+
#
|
116
|
+
# @return [Faraday::Response]
|
117
|
+
def get(path, params = {}, options = {})
|
118
|
+
request(:get, path, params, options)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Perform an HTTP POST request to the API using configured authentication
|
122
|
+
#
|
123
|
+
# @return [Faraday::Response]
|
124
|
+
def post(path, params, options = {})
|
125
|
+
request(:post, path, params, options)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Default Ably REST endpoint used for all requests
|
129
|
+
#
|
130
|
+
# @return [URI::Generic]
|
131
|
+
def endpoint
|
132
|
+
URI::Generic.build(
|
133
|
+
scheme: use_tls? ? "https" : "http",
|
134
|
+
host: [@environment, DOMAIN].compact.join('-')
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def request(method, path, params = {}, options = {})
|
140
|
+
connection.send(method, path, params) do |request|
|
141
|
+
unless options[:send_auth_header] == false
|
142
|
+
request.headers[:authorization] = auth.auth_header
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Return a Faraday::Connection to use to make HTTP requests
|
148
|
+
#
|
149
|
+
# @return [Faraday::Connection]
|
150
|
+
def connection
|
151
|
+
@connection ||= Faraday.new(endpoint.to_s, connection_options)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Return a Hash of connection options to initiate the Faraday::Connection with
|
155
|
+
#
|
156
|
+
# @return [Hash]
|
157
|
+
def connection_options
|
158
|
+
@connection_options ||= {
|
159
|
+
builder: middleware,
|
160
|
+
headers: {
|
161
|
+
accept: "application/json",
|
162
|
+
user_agent: user_agent
|
163
|
+
},
|
164
|
+
request: {
|
165
|
+
open_timeout: 5,
|
166
|
+
timeout: 10
|
167
|
+
}
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
# Return a Faraday middleware stack to initiate the Faraday::Connection with
|
172
|
+
#
|
173
|
+
# @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
|
174
|
+
def middleware
|
175
|
+
@middleware ||= Faraday::RackBuilder.new do |builder|
|
176
|
+
# Convert request params to "www-form-urlencoded"
|
177
|
+
builder.use Faraday::Request::UrlEncoded
|
178
|
+
|
179
|
+
# Parse JSON response bodies
|
180
|
+
builder.use Ably::Rest::Middleware::ParseJson
|
181
|
+
|
182
|
+
# Log HTTP requests if debug_http option set
|
183
|
+
builder.response :logger if @debug_http
|
184
|
+
|
185
|
+
# Raise exceptions if response code is invalid
|
186
|
+
builder.use Ably::Rest::Middleware::Exceptions
|
187
|
+
|
188
|
+
# Set Faraday's HTTP adapter
|
189
|
+
builder.adapter Faraday.default_adapter
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|