Class: Ably::Rest::Client

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Modules::Conversions, Modules::HttpHelpers
Defined in:
lib/ably/rest/client.rb

Overview

Client for the Ably REST API

Constant Summary collapse

DOMAIN =
"rest.ably.io"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) {|options| ... } ⇒ Ably::Rest::Client

Creates a Rest Client and configures the Auth object for the connection.

Examples:

# create a new client authenticating with basic auth
client = Ably::Rest::Client.new('key.id:secret')

# create a new client and configure a client ID used for presence
client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')

Parameters:

  • options (Hash, String)

    an options Hash used to configure the client and the authentication, or String with an API key

Options Hash (options):

  • :tls (Boolean)

    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.

  • :api_key (String)

    API key comprising the key ID and key secret in a single string

  • :environment (String)

    Specify ‘sandbox’ when testing the client library against an alternate Ably environment

  • :debug_http (Boolean)

    Send HTTP debugging information from Faraday for all HTTP requests to STDOUT

  • :key_id (String)

    key ID for the designated application (defaults to client key_id)

  • :key_secret (String)

    key secret for the designated application used to sign token requests (defaults to client key_secret)

  • :client_id (String)

    client ID identifying this connection to other clients (defaults to client client_id if configured)

  • :auth_url (String)

    a URL to be used to GET or POST a set of token request params, to obtain a signed token request.

  • :auth_headers (Hash)

    a set of application-specific headers to be added to any request made to the authUrl

  • :auth_params (Hash)

    a set of application-specific query params to be added to any request made to the authUrl

  • :auth_method (Symbol)

    HTTP method to use with auth_url, must be either :get or :post (defaults to :get)

  • :ttl (Integer)

    validity time in seconds for the requested Token. Limits may apply, see /

  • :capability (Hash)

    canonicalised representation of the resource paths and associated operations

  • :query_time (Boolean)

    when true will query the Ably system for the current time instead of using the local time

  • :timestamp (Time)

    the time of the of the request

  • :nonce (String)

    an unquoted, unescaped random string of at least 16 characters

  • :force (Boolean)

    obtains a new token even if the current token is valid

Yields:

  • (options)

    (optional) if an auth block is passed to this method, then this block will be called to create a new token request object

Yield Parameters:

  • options (Hash)

    options passed to request_token will be in turn sent to the block in this argument

Yield Returns:

  • (Hash)

    valid token request object, see #create_token_request



53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ably/rest/client.rb', line 53

def initialize(options, &auth_block)
  options = options.clone

  if options.kind_of?(String)
    options = { api_key: options }
  end

  @tls                 = options.delete(:tls) == false ? false : true
  @environment         = options.delete(:environment) # nil is production
  @debug_http          = options.delete(:debug_http)

  @auth     = Auth.new(self, options, &auth_block)
  @channels = Ably::Rest::Channels.new(self)
end

Instance Attribute Details

#authAbly::Auth (readonly)

Returns authentication object configured for this connection.

Returns:

  • (Ably::Auth)

    authentication object configured for this connection



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ably/rest/client.rb', line 21

class Client
  include Ably::Modules::Conversions
  include Ably::Modules::HttpHelpers
  extend Forwardable

  DOMAIN = "rest.ably.io"

  attr_reader :tls, :environment, :auth, :channels
  def_delegators :auth, :client_id, :auth_options

  # Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
  #
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
  # @option options (see Ably::Auth#authorise)
  # @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.
  # @option options [String]  :api_key      API key comprising the key ID and key secret in a single string
  # @option options [String]  :environment  Specify 'sandbox' when testing the client library against an alternate Ably environment
  # @option options [Boolean] :debug_http   Send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @yield (see Ably::Auth#authorise)
  # @yieldparam (see Ably::Auth#authorise)
  # @yieldreturn (see Ably::Auth#authorise)
  #
  # @return [Ably::Rest::Client]
  #
  # @example
  #    # create a new client authenticating with basic auth
  #    client = Ably::Rest::Client.new('key.id:secret')
  #
  #    # create a new client and configure a client ID used for presence
  #    client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
  #
  def initialize(options, &auth_block)
    options = options.clone

    if options.kind_of?(String)
      options = { api_key: options }
    end

    @tls                 = options.delete(:tls) == false ? false : true
    @environment         = options.delete(:environment) # nil is production
    @debug_http          = options.delete(:debug_http)

    @auth     = Auth.new(self, options, &auth_block)
    @channels = Ably::Rest::Channels.new(self)
  end

  # Return a REST {Ably::Rest::Channel} for the given name
  #
  # @param (see Ably::Rest::Channels#get)
  #
  # @return (see Ably::Rest::Channels#get)
  def channel(name, channel_options = {})
    channels.get(name, channel_options)
  end

  # Return the stats for the application
  #
  # @return [Array] An Array of hashes representing the stats
  def stats(params = {})
    default_params = {
      :direction => :forwards,
      :by        => :minute
    }

    response = get("/stats", default_params.merge(params))

    response.body
  end

  # Return the Ably service time
  #
  # @return [Time] The time as reported by the Ably service
  def time
    response = get('/time', {}, send_auth_header: false)

    as_time_from_epoch(response.body.first)
  end

  # True if client is configured to use TLS for all Ably communication
  #
  # @return [Boolean]
  def use_tls?
    @tls == true
  end

  # Perform an HTTP GET request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def get(path, params = {}, options = {})
    request(:get, path, params, options)
  end

  # Perform an HTTP POST request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def post(path, params, options = {})
    request(:post, path, params, options)
  end

  # Default Ably REST endpoint used for all requests
  #
  # @return [URI::Generic]
  def endpoint
    URI::Generic.build(
      scheme: use_tls? ? "https" : "http",
      host:   [@environment, DOMAIN].compact.join('-')
    )
  end

  # When true, will send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @return [Boolean]
  def debug_http?
    !!@debug_http
  end

  private
  def request(method, path, params = {}, options = {})
    reauthorise_on_authorisation_failure do
      connection.send(method, path, params) do |request|
        unless options[:send_auth_header] == false
          request.headers[:authorization] = auth.auth_header
        end
      end
    end
  end

  def reauthorise_on_authorisation_failure
    attempts = 0
    begin
      yield
    rescue Ably::Exceptions::InvalidRequest => e
      attempts += 1
      if attempts == 1 && e.code == 40140 && auth.token_renewable?
        auth.authorise force: true
        retry
      else
        raise Ably::Exceptions::InvalidToken.new(e.message, status: e.status, code: e.code)
      end
    end
  end

  # Return a Faraday::Connection to use to make HTTP requests
  #
  # @return [Faraday::Connection]
  def connection
    @connection ||= Faraday.new(endpoint.to_s, connection_options)
  end

  # Return a Hash of connection options to initiate the Faraday::Connection with
  #
  # @return [Hash]
  def connection_options
    @connection_options ||= {
      builder: middleware,
      headers: {
        accept:     "application/json",
        user_agent: user_agent
      },
      request: {
        open_timeout: 5,
        timeout:      10
      }
    }
  end

  # Return a Faraday middleware stack to initiate the Faraday::Connection with
  #
  # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
  def middleware
    @middleware ||= Faraday::RackBuilder.new do |builder|
      # Convert request params to "www-form-urlencoded"
      builder.use Faraday::Request::UrlEncoded

      # Parse JSON response bodies
      builder.use Ably::Rest::Middleware::ParseJson

      # Log HTTP requests if debug_http option set
      builder.response :logger if @debug_http

      # Raise exceptions if response code is invalid
      builder.use Ably::Rest::Middleware::Exceptions

      # Set Faraday's HTTP adapter
      builder.adapter Faraday.default_adapter
    end
  end
end

#auth_optionsHash (readonly)

Returns Auth options configured for this client.

Returns:

  • (Hash)

    Auth options configured for this client



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ably/rest/client.rb', line 21

class Client
  include Ably::Modules::Conversions
  include Ably::Modules::HttpHelpers
  extend Forwardable

  DOMAIN = "rest.ably.io"

  attr_reader :tls, :environment, :auth, :channels
  def_delegators :auth, :client_id, :auth_options

  # Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
  #
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
  # @option options (see Ably::Auth#authorise)
  # @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.
  # @option options [String]  :api_key      API key comprising the key ID and key secret in a single string
  # @option options [String]  :environment  Specify 'sandbox' when testing the client library against an alternate Ably environment
  # @option options [Boolean] :debug_http   Send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @yield (see Ably::Auth#authorise)
  # @yieldparam (see Ably::Auth#authorise)
  # @yieldreturn (see Ably::Auth#authorise)
  #
  # @return [Ably::Rest::Client]
  #
  # @example
  #    # create a new client authenticating with basic auth
  #    client = Ably::Rest::Client.new('key.id:secret')
  #
  #    # create a new client and configure a client ID used for presence
  #    client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
  #
  def initialize(options, &auth_block)
    options = options.clone

    if options.kind_of?(String)
      options = { api_key: options }
    end

    @tls                 = options.delete(:tls) == false ? false : true
    @environment         = options.delete(:environment) # nil is production
    @debug_http          = options.delete(:debug_http)

    @auth     = Auth.new(self, options, &auth_block)
    @channels = Ably::Rest::Channels.new(self)
  end

  # Return a REST {Ably::Rest::Channel} for the given name
  #
  # @param (see Ably::Rest::Channels#get)
  #
  # @return (see Ably::Rest::Channels#get)
  def channel(name, channel_options = {})
    channels.get(name, channel_options)
  end

  # Return the stats for the application
  #
  # @return [Array] An Array of hashes representing the stats
  def stats(params = {})
    default_params = {
      :direction => :forwards,
      :by        => :minute
    }

    response = get("/stats", default_params.merge(params))

    response.body
  end

  # Return the Ably service time
  #
  # @return [Time] The time as reported by the Ably service
  def time
    response = get('/time', {}, send_auth_header: false)

    as_time_from_epoch(response.body.first)
  end

  # True if client is configured to use TLS for all Ably communication
  #
  # @return [Boolean]
  def use_tls?
    @tls == true
  end

  # Perform an HTTP GET request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def get(path, params = {}, options = {})
    request(:get, path, params, options)
  end

  # Perform an HTTP POST request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def post(path, params, options = {})
    request(:post, path, params, options)
  end

  # Default Ably REST endpoint used for all requests
  #
  # @return [URI::Generic]
  def endpoint
    URI::Generic.build(
      scheme: use_tls? ? "https" : "http",
      host:   [@environment, DOMAIN].compact.join('-')
    )
  end

  # When true, will send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @return [Boolean]
  def debug_http?
    !!@debug_http
  end

  private
  def request(method, path, params = {}, options = {})
    reauthorise_on_authorisation_failure do
      connection.send(method, path, params) do |request|
        unless options[:send_auth_header] == false
          request.headers[:authorization] = auth.auth_header
        end
      end
    end
  end

  def reauthorise_on_authorisation_failure
    attempts = 0
    begin
      yield
    rescue Ably::Exceptions::InvalidRequest => e
      attempts += 1
      if attempts == 1 && e.code == 40140 && auth.token_renewable?
        auth.authorise force: true
        retry
      else
        raise Ably::Exceptions::InvalidToken.new(e.message, status: e.status, code: e.code)
      end
    end
  end

  # Return a Faraday::Connection to use to make HTTP requests
  #
  # @return [Faraday::Connection]
  def connection
    @connection ||= Faraday.new(endpoint.to_s, connection_options)
  end

  # Return a Hash of connection options to initiate the Faraday::Connection with
  #
  # @return [Hash]
  def connection_options
    @connection_options ||= {
      builder: middleware,
      headers: {
        accept:     "application/json",
        user_agent: user_agent
      },
      request: {
        open_timeout: 5,
        timeout:      10
      }
    }
  end

  # Return a Faraday middleware stack to initiate the Faraday::Connection with
  #
  # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
  def middleware
    @middleware ||= Faraday::RackBuilder.new do |builder|
      # Convert request params to "www-form-urlencoded"
      builder.use Faraday::Request::UrlEncoded

      # Parse JSON response bodies
      builder.use Ably::Rest::Middleware::ParseJson

      # Log HTTP requests if debug_http option set
      builder.response :logger if @debug_http

      # Raise exceptions if response code is invalid
      builder.use Ably::Rest::Middleware::Exceptions

      # Set Faraday's HTTP adapter
      builder.adapter Faraday.default_adapter
    end
  end
end

#channelsObject (readonly)

Returns the value of attribute channels.



28
29
30
# File 'lib/ably/rest/client.rb', line 28

def channels
  @channels
end

#client_idString (readonly)

Returns A client ID, used for identifying this client for presence purposes.

Returns:

  • (String)

    A client ID, used for identifying this client for presence purposes



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ably/rest/client.rb', line 21

class Client
  include Ably::Modules::Conversions
  include Ably::Modules::HttpHelpers
  extend Forwardable

  DOMAIN = "rest.ably.io"

  attr_reader :tls, :environment, :auth, :channels
  def_delegators :auth, :client_id, :auth_options

  # Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
  #
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
  # @option options (see Ably::Auth#authorise)
  # @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.
  # @option options [String]  :api_key      API key comprising the key ID and key secret in a single string
  # @option options [String]  :environment  Specify 'sandbox' when testing the client library against an alternate Ably environment
  # @option options [Boolean] :debug_http   Send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @yield (see Ably::Auth#authorise)
  # @yieldparam (see Ably::Auth#authorise)
  # @yieldreturn (see Ably::Auth#authorise)
  #
  # @return [Ably::Rest::Client]
  #
  # @example
  #    # create a new client authenticating with basic auth
  #    client = Ably::Rest::Client.new('key.id:secret')
  #
  #    # create a new client and configure a client ID used for presence
  #    client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
  #
  def initialize(options, &auth_block)
    options = options.clone

    if options.kind_of?(String)
      options = { api_key: options }
    end

    @tls                 = options.delete(:tls) == false ? false : true
    @environment         = options.delete(:environment) # nil is production
    @debug_http          = options.delete(:debug_http)

    @auth     = Auth.new(self, options, &auth_block)
    @channels = Ably::Rest::Channels.new(self)
  end

  # Return a REST {Ably::Rest::Channel} for the given name
  #
  # @param (see Ably::Rest::Channels#get)
  #
  # @return (see Ably::Rest::Channels#get)
  def channel(name, channel_options = {})
    channels.get(name, channel_options)
  end

  # Return the stats for the application
  #
  # @return [Array] An Array of hashes representing the stats
  def stats(params = {})
    default_params = {
      :direction => :forwards,
      :by        => :minute
    }

    response = get("/stats", default_params.merge(params))

    response.body
  end

  # Return the Ably service time
  #
  # @return [Time] The time as reported by the Ably service
  def time
    response = get('/time', {}, send_auth_header: false)

    as_time_from_epoch(response.body.first)
  end

  # True if client is configured to use TLS for all Ably communication
  #
  # @return [Boolean]
  def use_tls?
    @tls == true
  end

  # Perform an HTTP GET request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def get(path, params = {}, options = {})
    request(:get, path, params, options)
  end

  # Perform an HTTP POST request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def post(path, params, options = {})
    request(:post, path, params, options)
  end

  # Default Ably REST endpoint used for all requests
  #
  # @return [URI::Generic]
  def endpoint
    URI::Generic.build(
      scheme: use_tls? ? "https" : "http",
      host:   [@environment, DOMAIN].compact.join('-')
    )
  end

  # When true, will send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @return [Boolean]
  def debug_http?
    !!@debug_http
  end

  private
  def request(method, path, params = {}, options = {})
    reauthorise_on_authorisation_failure do
      connection.send(method, path, params) do |request|
        unless options[:send_auth_header] == false
          request.headers[:authorization] = auth.auth_header
        end
      end
    end
  end

  def reauthorise_on_authorisation_failure
    attempts = 0
    begin
      yield
    rescue Ably::Exceptions::InvalidRequest => e
      attempts += 1
      if attempts == 1 && e.code == 40140 && auth.token_renewable?
        auth.authorise force: true
        retry
      else
        raise Ably::Exceptions::InvalidToken.new(e.message, status: e.status, code: e.code)
      end
    end
  end

  # Return a Faraday::Connection to use to make HTTP requests
  #
  # @return [Faraday::Connection]
  def connection
    @connection ||= Faraday.new(endpoint.to_s, connection_options)
  end

  # Return a Hash of connection options to initiate the Faraday::Connection with
  #
  # @return [Hash]
  def connection_options
    @connection_options ||= {
      builder: middleware,
      headers: {
        accept:     "application/json",
        user_agent: user_agent
      },
      request: {
        open_timeout: 5,
        timeout:      10
      }
    }
  end

  # Return a Faraday middleware stack to initiate the Faraday::Connection with
  #
  # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
  def middleware
    @middleware ||= Faraday::RackBuilder.new do |builder|
      # Convert request params to "www-form-urlencoded"
      builder.use Faraday::Request::UrlEncoded

      # Parse JSON response bodies
      builder.use Ably::Rest::Middleware::ParseJson

      # Log HTTP requests if debug_http option set
      builder.response :logger if @debug_http

      # Raise exceptions if response code is invalid
      builder.use Ably::Rest::Middleware::Exceptions

      # Set Faraday's HTTP adapter
      builder.adapter Faraday.default_adapter
    end
  end
end

#environmentString (readonly)

Returns May contain ‘sandbox’ when testing the client library against an alternate Ably environment.

Returns:

  • (String)

    May contain ‘sandbox’ when testing the client library against an alternate Ably environment



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ably/rest/client.rb', line 21

class Client
  include Ably::Modules::Conversions
  include Ably::Modules::HttpHelpers
  extend Forwardable

  DOMAIN = "rest.ably.io"

  attr_reader :tls, :environment, :auth, :channels
  def_delegators :auth, :client_id, :auth_options

  # Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
  #
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
  # @option options (see Ably::Auth#authorise)
  # @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.
  # @option options [String]  :api_key      API key comprising the key ID and key secret in a single string
  # @option options [String]  :environment  Specify 'sandbox' when testing the client library against an alternate Ably environment
  # @option options [Boolean] :debug_http   Send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @yield (see Ably::Auth#authorise)
  # @yieldparam (see Ably::Auth#authorise)
  # @yieldreturn (see Ably::Auth#authorise)
  #
  # @return [Ably::Rest::Client]
  #
  # @example
  #    # create a new client authenticating with basic auth
  #    client = Ably::Rest::Client.new('key.id:secret')
  #
  #    # create a new client and configure a client ID used for presence
  #    client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
  #
  def initialize(options, &auth_block)
    options = options.clone

    if options.kind_of?(String)
      options = { api_key: options }
    end

    @tls                 = options.delete(:tls) == false ? false : true
    @environment         = options.delete(:environment) # nil is production
    @debug_http          = options.delete(:debug_http)

    @auth     = Auth.new(self, options, &auth_block)
    @channels = Ably::Rest::Channels.new(self)
  end

  # Return a REST {Ably::Rest::Channel} for the given name
  #
  # @param (see Ably::Rest::Channels#get)
  #
  # @return (see Ably::Rest::Channels#get)
  def channel(name, channel_options = {})
    channels.get(name, channel_options)
  end

  # Return the stats for the application
  #
  # @return [Array] An Array of hashes representing the stats
  def stats(params = {})
    default_params = {
      :direction => :forwards,
      :by        => :minute
    }

    response = get("/stats", default_params.merge(params))

    response.body
  end

  # Return the Ably service time
  #
  # @return [Time] The time as reported by the Ably service
  def time
    response = get('/time', {}, send_auth_header: false)

    as_time_from_epoch(response.body.first)
  end

  # True if client is configured to use TLS for all Ably communication
  #
  # @return [Boolean]
  def use_tls?
    @tls == true
  end

  # Perform an HTTP GET request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def get(path, params = {}, options = {})
    request(:get, path, params, options)
  end

  # Perform an HTTP POST request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def post(path, params, options = {})
    request(:post, path, params, options)
  end

  # Default Ably REST endpoint used for all requests
  #
  # @return [URI::Generic]
  def endpoint
    URI::Generic.build(
      scheme: use_tls? ? "https" : "http",
      host:   [@environment, DOMAIN].compact.join('-')
    )
  end

  # When true, will send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @return [Boolean]
  def debug_http?
    !!@debug_http
  end

  private
  def request(method, path, params = {}, options = {})
    reauthorise_on_authorisation_failure do
      connection.send(method, path, params) do |request|
        unless options[:send_auth_header] == false
          request.headers[:authorization] = auth.auth_header
        end
      end
    end
  end

  def reauthorise_on_authorisation_failure
    attempts = 0
    begin
      yield
    rescue Ably::Exceptions::InvalidRequest => e
      attempts += 1
      if attempts == 1 && e.code == 40140 && auth.token_renewable?
        auth.authorise force: true
        retry
      else
        raise Ably::Exceptions::InvalidToken.new(e.message, status: e.status, code: e.code)
      end
    end
  end

  # Return a Faraday::Connection to use to make HTTP requests
  #
  # @return [Faraday::Connection]
  def connection
    @connection ||= Faraday.new(endpoint.to_s, connection_options)
  end

  # Return a Hash of connection options to initiate the Faraday::Connection with
  #
  # @return [Hash]
  def connection_options
    @connection_options ||= {
      builder: middleware,
      headers: {
        accept:     "application/json",
        user_agent: user_agent
      },
      request: {
        open_timeout: 5,
        timeout:      10
      }
    }
  end

  # Return a Faraday middleware stack to initiate the Faraday::Connection with
  #
  # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
  def middleware
    @middleware ||= Faraday::RackBuilder.new do |builder|
      # Convert request params to "www-form-urlencoded"
      builder.use Faraday::Request::UrlEncoded

      # Parse JSON response bodies
      builder.use Ably::Rest::Middleware::ParseJson

      # Log HTTP requests if debug_http option set
      builder.response :logger if @debug_http

      # Raise exceptions if response code is invalid
      builder.use Ably::Rest::Middleware::Exceptions

      # Set Faraday's HTTP adapter
      builder.adapter Faraday.default_adapter
    end
  end
end

#tlsBoolean (readonly)

Returns True if client is configured to use TLS for all Ably communication.

Returns:

  • (Boolean)

    True if client is configured to use TLS for all Ably communication



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ably/rest/client.rb', line 21

class Client
  include Ably::Modules::Conversions
  include Ably::Modules::HttpHelpers
  extend Forwardable

  DOMAIN = "rest.ably.io"

  attr_reader :tls, :environment, :auth, :channels
  def_delegators :auth, :client_id, :auth_options

  # Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
  #
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
  # @option options (see Ably::Auth#authorise)
  # @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.
  # @option options [String]  :api_key      API key comprising the key ID and key secret in a single string
  # @option options [String]  :environment  Specify 'sandbox' when testing the client library against an alternate Ably environment
  # @option options [Boolean] :debug_http   Send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @yield (see Ably::Auth#authorise)
  # @yieldparam (see Ably::Auth#authorise)
  # @yieldreturn (see Ably::Auth#authorise)
  #
  # @return [Ably::Rest::Client]
  #
  # @example
  #    # create a new client authenticating with basic auth
  #    client = Ably::Rest::Client.new('key.id:secret')
  #
  #    # create a new client and configure a client ID used for presence
  #    client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
  #
  def initialize(options, &auth_block)
    options = options.clone

    if options.kind_of?(String)
      options = { api_key: options }
    end

    @tls                 = options.delete(:tls) == false ? false : true
    @environment         = options.delete(:environment) # nil is production
    @debug_http          = options.delete(:debug_http)

    @auth     = Auth.new(self, options, &auth_block)
    @channels = Ably::Rest::Channels.new(self)
  end

  # Return a REST {Ably::Rest::Channel} for the given name
  #
  # @param (see Ably::Rest::Channels#get)
  #
  # @return (see Ably::Rest::Channels#get)
  def channel(name, channel_options = {})
    channels.get(name, channel_options)
  end

  # Return the stats for the application
  #
  # @return [Array] An Array of hashes representing the stats
  def stats(params = {})
    default_params = {
      :direction => :forwards,
      :by        => :minute
    }

    response = get("/stats", default_params.merge(params))

    response.body
  end

  # Return the Ably service time
  #
  # @return [Time] The time as reported by the Ably service
  def time
    response = get('/time', {}, send_auth_header: false)

    as_time_from_epoch(response.body.first)
  end

  # True if client is configured to use TLS for all Ably communication
  #
  # @return [Boolean]
  def use_tls?
    @tls == true
  end

  # Perform an HTTP GET request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def get(path, params = {}, options = {})
    request(:get, path, params, options)
  end

  # Perform an HTTP POST request to the API using configured authentication
  #
  # @return [Faraday::Response]
  def post(path, params, options = {})
    request(:post, path, params, options)
  end

  # Default Ably REST endpoint used for all requests
  #
  # @return [URI::Generic]
  def endpoint
    URI::Generic.build(
      scheme: use_tls? ? "https" : "http",
      host:   [@environment, DOMAIN].compact.join('-')
    )
  end

  # When true, will send HTTP debugging information from Faraday for all HTTP requests to STDOUT
  #
  # @return [Boolean]
  def debug_http?
    !!@debug_http
  end

  private
  def request(method, path, params = {}, options = {})
    reauthorise_on_authorisation_failure do
      connection.send(method, path, params) do |request|
        unless options[:send_auth_header] == false
          request.headers[:authorization] = auth.auth_header
        end
      end
    end
  end

  def reauthorise_on_authorisation_failure
    attempts = 0
    begin
      yield
    rescue Ably::Exceptions::InvalidRequest => e
      attempts += 1
      if attempts == 1 && e.code == 40140 && auth.token_renewable?
        auth.authorise force: true
        retry
      else
        raise Ably::Exceptions::InvalidToken.new(e.message, status: e.status, code: e.code)
      end
    end
  end

  # Return a Faraday::Connection to use to make HTTP requests
  #
  # @return [Faraday::Connection]
  def connection
    @connection ||= Faraday.new(endpoint.to_s, connection_options)
  end

  # Return a Hash of connection options to initiate the Faraday::Connection with
  #
  # @return [Hash]
  def connection_options
    @connection_options ||= {
      builder: middleware,
      headers: {
        accept:     "application/json",
        user_agent: user_agent
      },
      request: {
        open_timeout: 5,
        timeout:      10
      }
    }
  end

  # Return a Faraday middleware stack to initiate the Faraday::Connection with
  #
  # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
  def middleware
    @middleware ||= Faraday::RackBuilder.new do |builder|
      # Convert request params to "www-form-urlencoded"
      builder.use Faraday::Request::UrlEncoded

      # Parse JSON response bodies
      builder.use Ably::Rest::Middleware::ParseJson

      # Log HTTP requests if debug_http option set
      builder.response :logger if @debug_http

      # Raise exceptions if response code is invalid
      builder.use Ably::Rest::Middleware::Exceptions

      # Set Faraday's HTTP adapter
      builder.adapter Faraday.default_adapter
    end
  end
end

Instance Method Details

#channel(name, channel_options = {}) ⇒ Ably::Rest::Channel

Return a REST Ably::Rest::Channel for the given name

Parameters:

  • name (String)

    The name of the channel

  • channel_options (Hash) (defaults to: {})

    Channel options, currently reserved for Encryption options

Returns:



73
74
75
# File 'lib/ably/rest/client.rb', line 73

def channel(name, channel_options = {})
  channels.get(name, channel_options)
end

#debug_http?Boolean

When true, will send HTTP debugging information from Faraday for all HTTP requests to STDOUT

Returns:

  • (Boolean)


134
135
136
# File 'lib/ably/rest/client.rb', line 134

def debug_http?
  !!@debug_http
end

#endpointURI::Generic

Default Ably REST endpoint used for all requests

Returns:

  • (URI::Generic)


124
125
126
127
128
129
# File 'lib/ably/rest/client.rb', line 124

def endpoint
  URI::Generic.build(
    scheme: use_tls? ? "https" : "http",
    host:   [@environment, DOMAIN].compact.join('-')
  )
end

#get(path, params = {}, options = {}) ⇒ Faraday::Response

Perform an HTTP GET request to the API using configured authentication

Returns:

  • (Faraday::Response)


110
111
112
# File 'lib/ably/rest/client.rb', line 110

def get(path, params = {}, options = {})
  request(:get, path, params, options)
end

#post(path, params, options = {}) ⇒ Faraday::Response

Perform an HTTP POST request to the API using configured authentication

Returns:

  • (Faraday::Response)


117
118
119
# File 'lib/ably/rest/client.rb', line 117

def post(path, params, options = {})
  request(:post, path, params, options)
end

#stats(params = {}) ⇒ Array

Return the stats for the application

Returns:

  • (Array)

    An Array of hashes representing the stats



80
81
82
83
84
85
86
87
88
89
# File 'lib/ably/rest/client.rb', line 80

def stats(params = {})
  default_params = {
    :direction => :forwards,
    :by        => :minute
  }

  response = get("/stats", default_params.merge(params))

  response.body
end

#timeTime

Return the Ably service time

Returns:

  • (Time)

    The time as reported by the Ably service



94
95
96
97
98
# File 'lib/ably/rest/client.rb', line 94

def time
  response = get('/time', {}, send_auth_header: false)

  as_time_from_epoch(response.body.first)
end

#use_tls?Boolean

True if client is configured to use TLS for all Ably communication

Returns:

  • (Boolean)


103
104
105
# File 'lib/ably/rest/client.rb', line 103

def use_tls?
  @tls == true
end