ably 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +185 -0
  4. data/LICENSE +15 -0
  5. data/README.md +8 -4
  6. data/SPEC.md +999 -531
  7. data/ably.gemspec +1 -1
  8. data/lib/ably.rb +1 -1
  9. data/lib/ably/auth.rb +114 -87
  10. data/lib/ably/exceptions.rb +40 -14
  11. data/lib/ably/models/message.rb +3 -5
  12. data/lib/ably/models/paginated_result.rb +3 -12
  13. data/lib/ably/models/presence_message.rb +8 -2
  14. data/lib/ably/models/protocol_message.rb +15 -3
  15. data/lib/ably/models/stat.rb +1 -1
  16. data/lib/ably/models/token_details.rb +1 -1
  17. data/lib/ably/modules/channels_collection.rb +7 -1
  18. data/lib/ably/modules/conversions.rb +1 -1
  19. data/lib/ably/modules/encodeable.rb +6 -3
  20. data/lib/ably/modules/message_pack.rb +2 -2
  21. data/lib/ably/modules/model_common.rb +1 -1
  22. data/lib/ably/modules/state_machine.rb +2 -2
  23. data/lib/ably/realtime.rb +1 -0
  24. data/lib/ably/realtime/auth.rb +191 -0
  25. data/lib/ably/realtime/channel.rb +97 -25
  26. data/lib/ably/realtime/channel/channel_manager.rb +11 -3
  27. data/lib/ably/realtime/client.rb +22 -6
  28. data/lib/ably/realtime/connection.rb +74 -41
  29. data/lib/ably/realtime/connection/connection_manager.rb +48 -33
  30. data/lib/ably/realtime/presence.rb +17 -3
  31. data/lib/ably/rest/channel.rb +43 -16
  32. data/lib/ably/rest/client.rb +57 -26
  33. data/lib/ably/rest/middleware/exceptions.rb +3 -1
  34. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -2
  35. data/lib/ably/rest/presence.rb +1 -0
  36. data/lib/ably/version.rb +1 -1
  37. data/spec/acceptance/realtime/auth_spec.rb +242 -0
  38. data/spec/acceptance/realtime/channel_spec.rb +277 -5
  39. data/spec/acceptance/realtime/channels_spec.rb +64 -0
  40. data/spec/acceptance/realtime/client_spec.rb +26 -5
  41. data/spec/acceptance/realtime/connection_failures_spec.rb +23 -6
  42. data/spec/acceptance/realtime/connection_spec.rb +167 -16
  43. data/spec/acceptance/realtime/message_spec.rb +9 -8
  44. data/spec/acceptance/realtime/presence_history_spec.rb +1 -0
  45. data/spec/acceptance/realtime/presence_spec.rb +121 -10
  46. data/spec/acceptance/realtime/stats_spec.rb +13 -1
  47. data/spec/acceptance/rest/auth_spec.rb +161 -79
  48. data/spec/acceptance/rest/base_spec.rb +3 -3
  49. data/spec/acceptance/rest/channel_spec.rb +142 -15
  50. data/spec/acceptance/rest/channels_spec.rb +23 -0
  51. data/spec/acceptance/rest/client_spec.rb +180 -18
  52. data/spec/acceptance/rest/message_spec.rb +8 -8
  53. data/spec/acceptance/rest/presence_spec.rb +136 -25
  54. data/spec/acceptance/rest/stats_spec.rb +60 -4
  55. data/spec/shared/client_initializer_behaviour.rb +54 -3
  56. data/spec/unit/auth_spec.rb +7 -6
  57. data/spec/unit/models/message_spec.rb +1 -9
  58. data/spec/unit/models/paginated_result_spec.rb +1 -18
  59. data/spec/unit/models/presence_message_spec.rb +1 -1
  60. data/spec/unit/models/protocol_message_spec.rb +21 -1
  61. data/spec/unit/realtime/channel_spec.rb +10 -3
  62. data/spec/unit/realtime/channels_spec.rb +27 -8
  63. data/spec/unit/rest/channel_spec.rb +0 -8
  64. data/spec/unit/rest/client_spec.rb +7 -7
  65. metadata +13 -7
  66. data/LICENSE.txt +0 -22
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'faraday', '~> 0.9'
25
25
  spec.add_runtime_dependency 'json'
26
26
  spec.add_runtime_dependency 'websocket-driver', '~> 0.3'
27
- spec.add_runtime_dependency 'msgpack', '0.6.0pre1'
27
+ spec.add_runtime_dependency 'msgpack', '>= 0.6.2'
28
28
 
29
29
  spec.add_development_dependency 'bundler', '~> 1.3'
30
30
  spec.add_development_dependency 'rake'
@@ -1,5 +1,5 @@
1
1
  %w(modules util).each do |namespace|
2
- Dir.glob(File.expand_path("ably/#{namespace}/*.rb", File.dirname(__FILE__))).each do |file|
2
+ Dir.glob(File.expand_path("ably/#{namespace}/*.rb", File.dirname(__FILE__))).sort.each do |file|
3
3
  require file
4
4
  end
5
5
  end
@@ -22,44 +22,53 @@ module Ably
22
22
  # @!attribute [r] key_secret
23
23
  # @return [String] Key secret (private secure part of the API key), if present
24
24
  # @!attribute [r] options
25
- # @return [Hash] {Ably::Auth} options configured for this client
26
-
25
+ # @return [Hash] Default {Ably::Auth} options configured for this client
26
+ # @!attribute [r] token_params
27
+ # @return [Hash] Default token params used for token requests, see {#request_token}
28
+ #
27
29
  class Auth
28
30
  include Ably::Modules::Conversions
29
31
  include Ably::Modules::HttpHelpers
30
32
 
31
33
  # Default capability Hash object and TTL in seconds for issued tokens
32
34
  TOKEN_DEFAULTS = {
33
- capability: { '*' => ['*'] },
34
- ttl: 60 * 60 # 1 hour in seconds
35
- }
35
+ capability: { '*' => ['*'] },
36
+ ttl: 60 * 60, # 1 hour in seconds
37
+ renew_token_buffer: 10 # buffer to allow a token to be reissued before the token is considered expired (Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER)
38
+ }.freeze
36
39
 
37
- attr_reader :options, :current_token_details
40
+ attr_reader :options, :token_params, :current_token_details
38
41
  alias_method :auth_options, :options
39
42
 
40
43
  # Creates an Auth object
41
44
  #
42
45
  # @param [Ably::Rest::Client] client {Ably::Rest::Client} this Auth object uses
43
- # @param [Hash] options (see Ably::Rest::Client#initialize)
44
- # @option (see Ably::Rest::Client#initialize)
46
+ # @param [Hash] auth_options the authentication options used as a default future token requests
47
+ # @param [Hash] token_params the token params used as a default for future token requests
48
+ # @option (see #request_token)
45
49
  #
46
- def initialize(client, options)
47
- ensure_valid_auth_attributes options
50
+ def initialize(client, auth_options, token_params)
51
+ unless auth_options.kind_of?(Hash)
52
+ raise ArgumentError, 'Expected auth_options to be a Hash'
53
+ end
48
54
 
49
- auth_options = options.dup
55
+ unless token_params.kind_of?(Hash)
56
+ raise ArgumentError, 'Expected token_params to be a Hash'
57
+ end
58
+
59
+ ensure_valid_auth_attributes auth_options
50
60
 
51
61
  @client = client
52
- @options = auth_options
62
+ @options = auth_options.dup
63
+ @token_params = token_params.dup
53
64
 
54
- unless auth_options.kind_of?(Hash)
55
- raise ArgumentError, 'Expected auth_options to be a Hash'
56
- end
65
+ @options.delete :force # Forcing token auth for every request is not a valid default
57
66
 
58
- if auth_options[:key] && (auth_options[:key_secret] || auth_options[:key_name])
67
+ if options[:key] && (options[:key_secret] || options[:key_name])
59
68
  raise ArgumentError, 'key and key_name or key_secret are mutually exclusive. Provider either a key or key_name & key_secret'
60
69
  end
61
70
 
62
- split_api_key_into_key_and_secret! auth_options if auth_options[:key]
71
+ split_api_key_into_key_and_secret! options if options[:key]
63
72
 
64
73
  if using_basic_auth? && !api_key_present?
65
74
  raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided'
@@ -71,85 +80,90 @@ module Ably
71
80
  end
72
81
 
73
82
  @options.freeze
83
+ @token_params.freeze
74
84
  end
75
85
 
76
86
  # Ensures valid auth credentials are present for the library instance. This may rely on an already-known and valid token, and will obtain a new token if necessary.
77
87
  #
78
- # In the event that a new token request is made, the specified options are used.
88
+ # In the event that a new token request is made, the provided options are used.
79
89
  #
80
- # @param [Hash] options the options for the token request
81
- # @option options (see #request_token)
82
- # @option options [String] :key API key comprising the key name and key secret in a single string
83
- # @option options [Boolean] :force obtains a new token even if the current token is valid
90
+ # @param [Hash] auth_options the authentication options used for future token requests
91
+ # @param [Hash] token_params the token params used for future token requests
92
+ # @option auth_options [Boolean] :force obtains a new token even if the current token is valid
93
+ # @option (see #request_token)
84
94
  #
85
- # @return (see #request_token)
95
+ # @return (see #create_token_request)
86
96
  #
87
97
  # @example
88
98
  # # will issue a simple token request using basic auth
89
99
  # client = Ably::Rest::Client.new(key: 'key.id:secret')
90
- # token = client.auth.authorise
100
+ # token_details = client.auth.authorise
91
101
  #
92
102
  # # will use token request from block to authorise if not already authorised
93
- # token = client.auth.authorise do |options|
103
+ # token_details = client.auth.authorise auth_callback: Proc.new do
94
104
  # # create token_request object
95
105
  # token_request
96
106
  # end
97
107
  #
98
- def authorise(options = {})
99
- ensure_valid_auth_attributes options
108
+ def authorise(auth_options = {}, token_params = {})
109
+ ensure_valid_auth_attributes auth_options
110
+
111
+ auth_options = auth_options.clone
100
112
 
101
- if current_token_details && !options[:force]
113
+ if current_token_details && !auth_options.delete(:force)
102
114
  return current_token_details unless current_token_details.expired?
103
115
  end
104
116
 
105
- options = options.clone
106
- split_api_key_into_key_and_secret! options if options[:key]
107
- @options = @options.merge(options) # update default options
117
+ split_api_key_into_key_and_secret! auth_options if auth_options[:key]
118
+ @options = @options.merge(auth_options) # update defaults
119
+
120
+ token_params = (auth_options.delete(:token_params) || {}).merge(token_params)
121
+ @token_params = @token_params.merge(token_params) # update defaults
108
122
 
109
- @current_token_details = request_token(options)
123
+ @current_token_details = request_token(auth_options, token_params)
110
124
  end
111
125
 
112
126
  # Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests
113
127
  #
114
- # @param [Hash] options the options for the token request
115
- # @option options [String] :key complete API key for the designated application
116
- # @option options [String] :client_id client ID identifying this connection to other clients (defaults to client client_id if configured)
117
- # @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.
118
- # @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
119
- # @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
120
- # @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
121
- # @option options [Proc] :auth_callback this Proc / block will be called with the {#auth_options Auth#auth_options Hash} as the first argument whenever a new token is required.
122
- # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
123
- # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
124
- # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
125
- # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
126
- # @option options [Time] :timestamp the time of the of the request
127
- # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
128
+ # @param [Hash] auth_options (see #create_token_request)
129
+ # @option auth_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
130
+ # @option auth_options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the +auth_url+
131
+ # @option auth_options [Hash] :auth_params a set of application-specific query params to be added to any request made to the +auth_url+
132
+ # @option auth_options [Symbol] :auth_method (:get) HTTP method to use with +auth_url+, must be either +:get+ or +:post+
133
+ # @option auth_options [Proc] :auth_callback when provided, the Proc will be called with the token params hash as the first argument, whenever a new token is required.
134
+ # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
135
+ # @param [Hash] token_params (see #create_token_request)
136
+ # @option (see #create_token_request)
128
137
  #
129
138
  # @return [Ably::Models::TokenDetails]
130
139
  #
131
140
  # @example
132
141
  # # simple token request using basic auth
133
142
  # client = Ably::Rest::Client.new(key: 'key.id:secret')
134
- # token = client.auth.request_token
143
+ # token_details = client.auth.request_token
144
+ #
145
+ # # token request with token params
146
+ # client.auth.request_token token_params: { ttl: 1.hour }
135
147
  #
136
148
  # # token request using auth block
137
- # token = client.auth.request_token do |options|
149
+ # token_details = client.auth.request_token auth_callback: Proc.new do
138
150
  # # create token_request object
139
151
  # token_request
140
152
  # end
141
153
  #
142
- def request_token(options = {})
143
- ensure_valid_auth_attributes options
154
+ def request_token(auth_options = {}, token_params = {})
155
+ ensure_valid_auth_attributes auth_options
144
156
 
145
- token_options = auth_options.merge(options)
157
+ token_params = (auth_options[:token_params] || {}).merge(token_params)
158
+ token_params = self.token_params.merge(token_params)
159
+ auth_options = self.options.merge(auth_options)
146
160
 
147
- token_request = if auth_callback = token_options.delete(:auth_callback)
148
- auth_callback.call(token_options)
149
- elsif auth_url = token_options.delete(:auth_url)
150
- token_request_from_auth_url(auth_url, token_options)
161
+ token_request = if auth_callback = auth_options.delete(:auth_callback)
162
+ auth_callback.call(token_params)
163
+ elsif auth_url = auth_options.delete(:auth_url)
164
+ token_request_from_auth_url(auth_url, auth_options)
151
165
  else
152
- create_token_request(token_options)
166
+ create_token_request(auth_options, token_params)
153
167
  end
154
168
 
155
169
  case token_request
@@ -172,19 +186,23 @@ module Ably
172
186
 
173
187
  # Creates and signs a token request that can then subsequently be used by any client to request a token
174
188
  #
175
- # @param [Hash] options the options for the token request
176
- # @option options [String] :key complete API key for the designated application
177
- # @option options [String] :client_id client ID identifying this connection to other clients
178
- # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
179
- # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
180
- # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
181
- # @option options [Time] :timestamp the time of the of the request
182
- # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
189
+ # @param [Hash] auth_options the authentication options for the token request
190
+ # @option auth_options [String] :key API key comprising the key name and key secret in a single string
191
+ # @option auth_options [String] :client_id client ID identifying this connection to other clients (will use +client_id+ specified when library was instanced if provided)
192
+ # @option auth_options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
193
+ # @option auth_options [Hash] :token_params convenience to pass in +token_params+ within the +auth_options+ argument, this helps avoid the following +authorise({key: key}, {ttl: 23})+ by allowing +authorise(key:key,token_params:{ttl:23})+
194
+ #
195
+ # @param [Hash] token_params the token params used in the token request
196
+ # @option token_params [String] :client_id A client ID to associate with this token. The generated token may be used to authenticate as this +client_id+
197
+ # @option token_params [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
198
+ # @option token_params [Hash] :capability canonicalised representation of the resource paths and associated operations
199
+ # @option token_params [Time] :timestamp the time of the request
200
+ # @option token_params [String] :nonce an unquoted, unescaped random string of at least 16 characters
183
201
  #
184
202
  # @return [Models::TokenRequest]
185
203
  #
186
204
  # @example
187
- # client.auth.create_token_request(id: 'asd.asd', ttl: 3600)
205
+ # client.auth.create_token_request(id: 'asd.asd', token_params: { ttl: 3600 })
188
206
  # #<Ably::Models::TokenRequest:0x007fd5d919df78
189
207
  # # @hash={
190
208
  # # :id=>"asds.adsa",
@@ -196,30 +214,37 @@ module Ably
196
214
  # # :mac=>"881oZHeFo6oMim7....uE56a8gUxHw="
197
215
  # # }
198
216
  # #>>
199
- def create_token_request(options = {})
200
- ensure_valid_auth_attributes options
217
+ def create_token_request(auth_options = {}, token_params = {})
218
+ ensure_valid_auth_attributes auth_options
201
219
 
202
- token_options = options.clone
220
+ auth_options = auth_options.clone
221
+ token_params = (auth_options[:token_params] || {}).merge(token_params)
203
222
 
204
- split_api_key_into_key_and_secret! token_options if token_options[:key]
205
- request_key_name = token_options.delete(:key_name) || key_name
206
- request_key_secret = token_options.delete(:key_secret) || key_secret
207
- raise Ably::Exceptions::TokenRequestError, 'Key Name and Key Secret are required to generate a new token request' unless request_key_name && request_key_secret
223
+ split_api_key_into_key_and_secret! auth_options if auth_options[:key]
224
+ request_key_name = auth_options.delete(:key_name) || key_name
225
+ request_key_secret = auth_options.delete(:key_secret) || key_secret
208
226
 
209
- timestamp = if token_options[:query_time]
227
+ raise Ably::Exceptions::TokenRequestFailed, 'Key Name and Key Secret are required to generate a new token request' unless request_key_name && request_key_secret
228
+
229
+ timestamp = if auth_options[:query_time]
210
230
  client.time
211
231
  else
212
- token_options.delete(:timestamp) || Time.now
232
+ token_params.delete(:timestamp) || Time.now
213
233
  end
214
234
  timestamp = Time.at(timestamp) if timestamp.kind_of?(Integer)
215
235
 
236
+ ttl = [
237
+ (token_params[:ttl] || TOKEN_DEFAULTS.fetch(:ttl)),
238
+ Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER + TOKEN_DEFAULTS.fetch(:renew_token_buffer) # never issue a token that will be immediately considered expired due to the buffer
239
+ ].max
240
+
216
241
  token_request = {
217
- keyName: token_options[:key_name] || request_key_name,
218
- clientId: token_options[:client_id] || client_id,
219
- ttl: ((token_options[:ttl] || TOKEN_DEFAULTS.fetch(:ttl)) * 1000).to_i,
242
+ keyName: request_key_name,
243
+ clientId: token_params[:client_id] || auth_options[:client_id] || client_id,
244
+ ttl: (ttl * 1000).to_i,
220
245
  timestamp: (timestamp.to_f * 1000).round,
221
- capability: token_options[:capability] || TOKEN_DEFAULTS.fetch(:capability),
222
- nonce: token_options[:nonce] || SecureRandom.hex.force_encoding('UTF-8')
246
+ capability: token_params[:capability] || TOKEN_DEFAULTS.fetch(:capability),
247
+ nonce: token_params[:nonce] || SecureRandom.hex.force_encoding('UTF-8')
223
248
  }
224
249
 
225
250
  token_request[:capability] = JSON.dump(token_request[:capability]) if token_request[:capability].is_a?(Hash)
@@ -227,7 +252,7 @@ module Ably
227
252
  token_request[:mac] = sign_params(token_request, request_key_secret)
228
253
 
229
254
  # Undocumented feature to request a persisted token
230
- token_request[:persisted] = options[:persisted] if options[:persisted]
255
+ token_request[:persisted] = token_params[:persisted] if token_params[:persisted]
231
256
 
232
257
  Models::TokenRequest.new(token_request)
233
258
  end
@@ -252,7 +277,7 @@ module Ably
252
277
  # True when Token Auth is being used to authenticate with Ably
253
278
  def using_token_auth?
254
279
  return options[:use_token_auth] if options.has_key?(:use_token_auth)
255
- token || current_token_details || has_client_id? || token_creatable_externally?
280
+ !!(token || current_token_details || has_client_id? || token_creatable_externally?)
256
281
  end
257
282
 
258
283
  def client_id
@@ -270,6 +295,7 @@ module Ably
270
295
  end
271
296
 
272
297
  # Auth header string used in HTTP requests to Ably
298
+ # Will reauthorise implicitly if required and capable
273
299
  #
274
300
  # @return [String] HTTP authentication value used in HTTP_AUTHORIZATION header
275
301
  def auth_header
@@ -281,6 +307,7 @@ module Ably
281
307
  end
282
308
 
283
309
  # Auth params used in URI endpoint for Realtime connections
310
+ # Will reauthorise implicitly if required and capable
284
311
  #
285
312
  # @return [Hash] Auth params for a new Realtime connection
286
313
  def auth_params
@@ -353,7 +380,7 @@ module Ably
353
380
  end
354
381
 
355
382
  def ensure_api_key_sent_over_secure_connection
356
- raise Ably::Exceptions::InsecureRequestError, 'Cannot use Basic Auth over non-TLS connections' unless authentication_security_requirements_met?
383
+ raise Ably::Exceptions::InsecureRequest, 'Cannot use Basic Auth over non-TLS connections' unless authentication_security_requirements_met?
357
384
  end
358
385
 
359
386
  # Basic Auth HTTP Authorization header value
@@ -424,18 +451,18 @@ module Ably
424
451
  # Retrieve a token request from a specified URL, expects a JSON response
425
452
  #
426
453
  # @return [Hash]
427
- def token_request_from_auth_url(auth_url, options = {})
454
+ def token_request_from_auth_url(auth_url, auth_options)
428
455
  uri = URI.parse(auth_url)
429
456
  connection = Faraday.new("#{uri.scheme}://#{uri.host}", connection_options)
430
- method = options[:auth_method] || :get
457
+ method = auth_options[:auth_method] || :get
431
458
 
432
459
  response = connection.send(method) do |request|
433
460
  request.url uri.path
434
- request.params = options[:auth_params] || {}
435
- request.headers = options[:auth_headers] || {}
461
+ request.params = CGI.parse(uri.query || '').merge(auth_options[:auth_params] || {})
462
+ request.headers = auth_options[:auth_headers] || {}
436
463
  end
437
464
 
438
- if !response.body.kind_of?(Hash) && response.headers['Content-Type'] != 'text/plain'
465
+ if !response.body.kind_of?(Hash) && !response.headers['Content-Type'].to_s.match(%r{text/plain}i)
439
466
  raise Ably::Exceptions::InvalidResponseBody,
440
467
  "Content Type #{response.headers['Content-Type']} is not supported by this client library"
441
468
  end
@@ -16,14 +16,22 @@ module Ably
16
16
  @status = status
17
17
  @code = code
18
18
  end
19
+
20
+ def to_s
21
+ message = [super]
22
+ if status || code
23
+ additional_info = []
24
+ additional_info << "code: #{code}" if code
25
+ additional_info << "http status: #{status}" if status
26
+ message << "(#{additional_info.join(', ')})"
27
+ end
28
+ message.join(' ')
29
+ end
19
30
  end
20
31
 
21
32
  # An invalid request was received by Ably
22
33
  class InvalidRequest < BaseAblyException; end
23
34
 
24
- # The token is invalid
25
- class InvalidToken < BaseAblyException; end
26
-
27
35
  # Ably Protocol message received that is invalid
28
36
  class ProtocolError < BaseAblyException; end
29
37
 
@@ -39,19 +47,28 @@ module Ably
39
47
  super message, status, code
40
48
  @base_error = base_error
41
49
  end
50
+
51
+ def to_s
52
+ message = [super]
53
+ message << "#{@base_error}" if @base_error
54
+ message.join(' < ')
55
+ end
42
56
  end
43
57
 
44
58
  # Connection Timeout accessing Realtime or REST service
45
- class ConnectionTimeoutError < ConnectionError; end
59
+ class ConnectionTimeout < ConnectionError; end
46
60
 
47
61
  # Connection closed unexpectedly
48
- class ConnectionClosedError < ConnectionError; end
62
+ class ConnectionClosed < ConnectionError; end
49
63
 
50
- # Invalid State Change error on a {https://github.com/gocardless/statesman Statesman State Machine}
51
- class StateChangeError < BaseAblyException; end
64
+ # Connection suspended
65
+ class ConnectionSuspended < ConnectionError; end
52
66
 
53
- # The state of the object is not suitable for this operation
54
- class IncompatibleStateForOperation < BaseAblyException; end
67
+ # Connection failed
68
+ class ConnectionFailed < ConnectionError; end
69
+
70
+ # Invalid State Change error on a {https://github.com/gocardless/statesman Statesman State Machine}
71
+ class InvalidStateChange < BaseAblyException; end
55
72
 
56
73
  # A generic Ably exception taht supports a status & code.
57
74
  # See https://github.com/ably/ably-common/blob/master/protocol/errors.json for a list of Ably errors
@@ -61,21 +78,30 @@ module Ably
61
78
  class ServerError < BaseAblyException; end
62
79
 
63
80
  # PaginatedResult cannot retrieve the page
64
- class InvalidPageError < BaseAblyException; end
81
+ class PageMissing < BaseAblyException; end
65
82
 
66
83
  # The expected response from the server was invalid
67
84
  class InvalidResponseBody < BaseAblyException; end
68
85
 
69
86
  # The request cannot be performed because it is insecure
70
- class InsecureRequestError < BaseAblyException; end
87
+ class InsecureRequest < BaseAblyException; end
71
88
 
72
89
  # The token request could not be created
73
- class TokenRequestError < BaseAblyException; end
90
+ class TokenRequestFailed < BaseAblyException; end
91
+
92
+ # The token has expired
93
+ class TokenExpired < BaseAblyException; end
74
94
 
75
95
  # The message could not be delivered to the server
76
- class MessageDeliveryError < BaseAblyException; end
96
+ class MessageDeliveryFailed < BaseAblyException; end
97
+
98
+ # The client has been configured to not queue messages i.e. only publish down an active connection
99
+ class MessageQueueingDisabled < BaseAblyException; end
77
100
 
78
101
  # The data payload type is not supported
79
- class UnsupportedDataTypeError < BaseAblyException; end
102
+ class UnsupportedDataType < BaseAblyException; end
103
+
104
+ # When a channel is detached / failed, certain operations are not permitted such as publishing messages
105
+ class ChannelInactive < BaseAblyException; end
80
106
  end
81
107
  end