ably-rest 0.8.3 → 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/lib/submodules/ably-ruby/CHANGELOG.md +10 -0
  4. data/lib/submodules/ably-ruby/README.md +1 -1
  5. data/lib/submodules/ably-ruby/Rakefile +1 -1
  6. data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -20
  7. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +3 -0
  8. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +41 -0
  9. data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +43 -0
  10. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
  11. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +2 -1
  13. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +8 -6
  14. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +4 -0
  15. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +4 -1
  16. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +35 -4
  17. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +13 -13
  18. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +13 -5
  19. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +27 -7
  20. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +22 -12
  21. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +16 -10
  22. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -0
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -4
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +42 -24
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +25 -17
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
  27. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -2
  28. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +2 -2
  29. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +15 -0
  30. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  31. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +9 -9
  32. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +168 -21
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +6 -2
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +6 -5
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +29 -19
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +150 -35
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +146 -23
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +2 -2
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +44 -24
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +77 -46
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +31 -3
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +15 -5
  46. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +9 -7
  47. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +30 -4
  48. data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +9 -6
  49. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +1 -1
  50. data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +44 -0
  51. data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +54 -0
  52. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +8 -0
  53. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
  54. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +18 -0
  55. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6dc33fd0a8246405f3b3db4aa1adfa03711a6ccc
4
- data.tar.gz: 0de772597367cd55d99437a0bbb34302086c1ee8
3
+ metadata.gz: 1b1045e268247db9dd96742a0c11404b03fbb533
4
+ data.tar.gz: 180a9c3dfee6b05b4fc8c8f947e1081bba97b635
5
5
  SHA512:
6
- metadata.gz: 72696b8f25b34f36552978221b44f4f75bdc2772743b43d13686aabe4ed41932e88bc5e93b4b095d3abdc743d72b7d6f1cfba199f14ff7f618d9407bd9cdabd3
7
- data.tar.gz: 4252aa2885f5e75ba6ad219aa4af7edef9989f4ba122972ab6e8b9d9d1b5e08794286819d6f26fa54746b0988a6725307c566b191be6a207e15a96f03a7eefb6
6
+ metadata.gz: 163805382715a09c67f446297b49c5c4588ff2c141c3cc6173eb9917423305b3f532d076f46af854d6b869439e9e465a02eb23735e40be64fe8d017773d782a0
7
+ data.tar.gz: b024865d3a6f266681e0ed4224b6a14c1e4248e21f93958d357d3009010a662d02d779615a21d0c4616e6397fc6c1053c0afc887ef7c52dcde9c286e24960d3d
@@ -1,3 +1,4 @@
1
+ sudo: false
1
2
  env: RSPEC_RETRY=true
2
3
  language: ruby
3
4
  rvm:
@@ -6,6 +6,12 @@
6
6
 
7
7
  **Implemented enhancements:**
8
8
 
9
+ - Add compatibility support for default Crypto params [\#53](https://github.com/ably/ably-ruby/issues/53)
10
+
11
+ - EventEmitter on connection [\#52](https://github.com/ably/ably-ruby/issues/52)
12
+
13
+ - Add test for connectionId attribute for a message sent over REST [\#50](https://github.com/ably/ably-ruby/issues/50)
14
+
9
15
  - Implement :queue\_messages option [\#36](https://github.com/ably/ably-ruby/issues/36)
10
16
 
11
17
  - Check that a non 200-299 status code for REST requests uses fallback hosts [\#35](https://github.com/ably/ably-ruby/issues/35)
@@ -32,6 +38,10 @@
32
38
 
33
39
  **Merged pull requests:**
34
40
 
41
+ - Spec update to fix a number of issues [\#60](https://github.com/ably/ably-ruby/pull/60) ([mattheworiordan](https://github.com/mattheworiordan))
42
+
43
+ - Allow clientId to be provided on init if using externally created token [\#58](https://github.com/ably/ably-ruby/pull/58) ([SimonWoolf](https://github.com/SimonWoolf))
44
+
35
45
  - Separate token params for auth [\#57](https://github.com/ably/ably-ruby/pull/57) ([mattheworiordan](https://github.com/mattheworiordan))
36
46
 
37
47
  - Ensure files are required in a consistent order [\#51](https://github.com/ably/ably-ruby/pull/51) ([SimonWoolf](https://github.com/SimonWoolf))
@@ -187,7 +187,7 @@ token_details = client.auth.request_token
187
187
  token_details.token # => "xVLyHw.CLchevH3hF....MDh9ZC_Q"
188
188
  client = Ably::Rest.new(token: token_details.token)
189
189
 
190
- token = client.auth.create_token_request(token_params: { ttl: 3600 })
190
+ token = client.auth.create_token_request(ttl: 3600)
191
191
  # => {"id"=>...,
192
192
  # "clientId"=>nil,
193
193
  # "ttl"=>3600,
@@ -16,7 +16,7 @@ begin
16
16
  namespace :doc do
17
17
  desc 'Generate Markdown Specification from the RSpec public API tests'
18
18
  task :spec do
19
- ENV['TEST_LIMIT_PROTOCOLS'] = JSON.dump({ msgpack: 'JSON and MsgPack' })
19
+ ENV['PROTOCOL'] = 'json'
20
20
 
21
21
  rspec_task.rspec_opts = %w(
22
22
  --require ./spec/support/markdown_spec_formatter
@@ -43,11 +43,11 @@ module Ably
43
43
  # Creates an Auth object
44
44
  #
45
45
  # @param [Ably::Rest::Client] client {Ably::Rest::Client} this Auth object uses
46
- # @param [Hash] auth_options the authentication options used as a default future token requests
47
46
  # @param [Hash] token_params the token params used as a default for future token requests
47
+ # @param [Hash] auth_options the authentication options used as a default future token requests
48
48
  # @option (see #request_token)
49
49
  #
50
- def initialize(client, auth_options, token_params)
50
+ def initialize(client, token_params, auth_options)
51
51
  unless auth_options.kind_of?(Hash)
52
52
  raise ArgumentError, 'Expected auth_options to be a Hash'
53
53
  end
@@ -74,7 +74,7 @@ module Ably
74
74
  raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided'
75
75
  end
76
76
 
77
- if has_client_id?
77
+ if has_client_id? && !token_creatable_externally?
78
78
  raise ArgumentError, 'client_id cannot be provided without a complete API key. Key name & Secret is needed to authenticate with Ably and obtain a token' unless api_key_present?
79
79
  ensure_utf_8 :client_id, client_id
80
80
  end
@@ -87,8 +87,8 @@ module Ably
87
87
  #
88
88
  # In the event that a new token request is made, the provided options are used.
89
89
  #
90
- # @param [Hash] auth_options the authentication options used for future token requests
91
90
  # @param [Hash] token_params the token params used for future token requests
91
+ # @param [Hash] auth_options the authentication options used for future token requests
92
92
  # @option auth_options [Boolean] :force obtains a new token even if the current token is valid
93
93
  # @option (see #request_token)
94
94
  #
@@ -100,12 +100,12 @@ module Ably
100
100
  # token_details = client.auth.authorise
101
101
  #
102
102
  # # will use token request from block to authorise if not already authorised
103
- # token_details = client.auth.authorise auth_callback: Proc.new do
103
+ # token_details = client.auth.authorise {}, auth_callback: Proc.new do
104
104
  # # create token_request object
105
105
  # token_request
106
106
  # end
107
107
  #
108
- def authorise(auth_options = {}, token_params = {})
108
+ def authorise(token_params = {}, auth_options = {})
109
109
  ensure_valid_auth_attributes auth_options
110
110
 
111
111
  auth_options = auth_options.clone
@@ -120,7 +120,7 @@ module Ably
120
120
  token_params = (auth_options.delete(:token_params) || {}).merge(token_params)
121
121
  @token_params = @token_params.merge(token_params) # update defaults
122
122
 
123
- @current_token_details = request_token(auth_options, token_params)
123
+ @current_token_details = request_token(token_params, auth_options)
124
124
  end
125
125
 
126
126
  # Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests
@@ -143,15 +143,15 @@ module Ably
143
143
  # token_details = client.auth.request_token
144
144
  #
145
145
  # # token request with token params
146
- # client.auth.request_token token_params: { ttl: 1.hour }
146
+ # client.auth.request_token ttl: 1.hour
147
147
  #
148
148
  # # token request using auth block
149
- # token_details = client.auth.request_token auth_callback: Proc.new do
149
+ # token_details = client.auth.request_token {}, auth_callback: Proc.new do
150
150
  # # create token_request object
151
151
  # token_request
152
152
  # end
153
153
  #
154
- def request_token(auth_options = {}, token_params = {})
154
+ def request_token(token_params = {}, auth_options = {})
155
155
  ensure_valid_auth_attributes auth_options
156
156
 
157
157
  token_params = (auth_options[:token_params] || {}).merge(token_params)
@@ -163,7 +163,7 @@ module Ably
163
163
  elsif auth_url = auth_options.delete(:auth_url)
164
164
  token_request_from_auth_url(auth_url, auth_options)
165
165
  else
166
- create_token_request(auth_options, token_params)
166
+ create_token_request(token_params, auth_options)
167
167
  end
168
168
 
169
169
  case token_request
@@ -186,12 +186,6 @@ module Ably
186
186
 
187
187
  # Creates and signs a token request that can then subsequently be used by any client to request a token
188
188
  #
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
189
  # @param [Hash] token_params the token params used in the token request
196
190
  # @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
191
  # @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}
@@ -199,10 +193,16 @@ module Ably
199
193
  # @option token_params [Time] :timestamp the time of the request
200
194
  # @option token_params [String] :nonce an unquoted, unescaped random string of at least 16 characters
201
195
  #
196
+ # @param [Hash] auth_options the authentication options for the token request
197
+ # @option auth_options [String] :key API key comprising the key name and key secret in a single string
198
+ # @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)
199
+ # @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
200
+ # @option auth_options [Hash] :token_params convenience to pass in +token_params+ within the +auth_options+ argument, especially useful when setting default token_params in the client constructor
201
+ #
202
202
  # @return [Models::TokenRequest]
203
203
  #
204
204
  # @example
205
- # client.auth.create_token_request(id: 'asd.asd', token_params: { ttl: 3600 })
205
+ # client.auth.create_token_request({ ttl: 3600 }, { id: 'asd.asd' })
206
206
  # #<Ably::Models::TokenRequest:0x007fd5d919df78
207
207
  # # @hash={
208
208
  # # :id=>"asds.adsa",
@@ -214,7 +214,7 @@ module Ably
214
214
  # # :mac=>"881oZHeFo6oMim7....uE56a8gUxHw="
215
215
  # # }
216
216
  # #>>
217
- def create_token_request(auth_options = {}, token_params = {})
217
+ def create_token_request(token_params = {}, auth_options = {})
218
218
  ensure_valid_auth_attributes auth_options
219
219
 
220
220
  auth_options = auth_options.clone
@@ -401,7 +401,11 @@ module Ably
401
401
 
402
402
  # Returns the current token if it exists or authorises and retrieves a token
403
403
  def token_auth_string
404
- if token
404
+ # If a TokenDetails object has been issued by this library
405
+ # then that Token will take precedence
406
+ if @current_token_details
407
+ authorise.token
408
+ elsif token # token string was configured in the options
405
409
  token
406
410
  else
407
411
  authorise.token
@@ -58,6 +58,9 @@ module Ably
58
58
  # Connection Timeout accessing Realtime or REST service
59
59
  class ConnectionTimeout < ConnectionError; end
60
60
 
61
+ # Transport closed unexpectedly
62
+ class TransportClosed < ConnectionError; end
63
+
61
64
  # Connection closed unexpectedly
62
65
  class ConnectionClosed < ConnectionError; end
63
66
 
@@ -0,0 +1,41 @@
1
+ module Ably::Models
2
+ # ChannelStateChange is a class that is emitted by the {Ably::Realtime::Channel} object
3
+ # when a state change occurs
4
+ #
5
+ # @!attribute [r] current
6
+ # @return [Connection::STATE] Current connection state
7
+ # @!attribute [r] previous
8
+ # @return [Connection::STATE] Previous connection state
9
+ # @!attribute [r] reason
10
+ # @return [Ably::Models::ErrorInfo] Object describing the reason for a state change when not initiated by the consumer of the client library
11
+ #
12
+ class ChannelStateChange
13
+ include Ably::Modules::ModelCommon
14
+
15
+ def initialize(hash_object)
16
+ unless (hash_object.keys - [:current, :previous, :reason, :protocol_message]).empty?
17
+ raise ArgumentError, 'Invalid attributes, expecting :current, :previous, :reason'
18
+ end
19
+
20
+ @hash_object = {
21
+ current: hash_object.fetch(:current),
22
+ previous: hash_object.fetch(:previous),
23
+ retry_in: hash_object[:retry_in],
24
+ reason: hash_object[:reason],
25
+ protocol_message: hash_object[:protocol_message]
26
+ }
27
+ rescue KeyError => e
28
+ raise ArgumentError, e
29
+ end
30
+
31
+ %w(current previous reason protocol_message).each do |attribute|
32
+ define_method attribute do
33
+ @hash_object[attribute.to_sym]
34
+ end
35
+ end
36
+
37
+ def to_s
38
+ "ChannelStateChange: current state #{current}, previous state #{previous}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ module Ably::Models
2
+ # ConnectionStateChange is a class that is emitted by the {Ably::Realtime::Connection} object
3
+ # when a state change occurs
4
+ #
5
+ # @!attribute [r] current
6
+ # @return [Connection::STATE] Current connection state
7
+ # @!attribute [r] previous
8
+ # @return [Connection::STATE] Previous connection state
9
+ # @!attribute [r] retry_in
10
+ # @return [Integer] Time in seconds until the connection will reattempt to connect when in the +:disconnected+ or +:suspended+ state
11
+ # @!attribute [r] reason
12
+ # @return [Ably::Models::ErrorInfo] Object describing the reason for a state change when not initiated by the consumer of the client library
13
+ #
14
+ class ConnectionStateChange
15
+ include Ably::Modules::ModelCommon
16
+
17
+ def initialize(hash_object)
18
+ unless (hash_object.keys - [:current, :previous, :retry_in, :reason, :protocol_message]).empty?
19
+ raise ArgumentError, 'Invalid attributes, expecting :current, :previous, :retry_in, :reason'
20
+ end
21
+
22
+ @hash_object = {
23
+ current: hash_object.fetch(:current),
24
+ previous: hash_object.fetch(:previous),
25
+ retry_in: hash_object[:retry_in],
26
+ reason: hash_object[:reason],
27
+ protocol_message: hash_object[:protocol_message]
28
+ }
29
+ rescue KeyError => e
30
+ raise ArgumentError, e
31
+ end
32
+
33
+ %w(current previous retry_in reason protocol_message).each do |attribute|
34
+ define_method attribute do
35
+ @hash_object[attribute.to_sym]
36
+ end
37
+ end
38
+
39
+ def to_s
40
+ "ConnectionStateChange: current state #{current}, previous state #{previous}"
41
+ end
42
+ end
43
+ end
@@ -131,7 +131,7 @@ module Ably::Models
131
131
  end
132
132
 
133
133
  def logger
134
- return logger if logger
134
+ return @logger if @logger
135
135
  protocol_message.logger if protocol_message
136
136
  end
137
137
  end
@@ -154,7 +154,7 @@ module Ably::Models
154
154
  end
155
155
 
156
156
  def logger
157
- return logger if logger
157
+ return @logger if @logger
158
158
  protocol_message.logger if protocol_message
159
159
  end
160
160
  end
@@ -226,7 +226,8 @@ module Ably::Models
226
226
  !action_enum || (ack_required? && !has_serial?)
227
227
  end
228
228
 
229
- private
229
+ # @!attribute [r] logger
230
+ # @api private
230
231
  attr_reader :logger
231
232
  end
232
233
  end
@@ -34,7 +34,7 @@ module Ably::Models
34
34
  # @param attributes
35
35
  # @option attributes [String] :token token used to authenticate requests
36
36
  # @option attributes [String] :key_name API key name used to create this token
37
- # @option attributes [Time,Integer] :issued Time the token was issued as Time or Integer in milliseconds
37
+ # @option attributes [Time,Integer] :issued Time the token was issued as Time or Integer in milliseconds
38
38
  # @option attributes [Time,Integer] :expires Time the token expires as Time or Integer in milliseconds
39
39
  # @option attributes [String] :capability JSON stringified capabilities assigned to this token
40
40
  # @option attributes [String] :client_id client ID assigned to this token
@@ -52,31 +52,31 @@ module Ably::Models
52
52
  # @!attribute [r] token
53
53
  # @return [String] Token used to authenticate requests
54
54
  def token
55
- hash.fetch(:token)
55
+ hash[:token]
56
56
  end
57
57
 
58
58
  # @!attribute [r] key_name
59
59
  # @return [String] API key name used to create this token. An API key is made up of an API key name and secret delimited by a +:+
60
60
  def key_name
61
- hash.fetch(:key_name)
61
+ hash[:key_name]
62
62
  end
63
63
 
64
64
  # @!attribute [r] issued
65
65
  # @return [Time] Time the token was issued
66
66
  def issued
67
- as_time_from_epoch(hash.fetch(:issued), granularity: :ms)
67
+ as_time_from_epoch(hash[:issued], granularity: :ms, allow_nil: :true)
68
68
  end
69
69
 
70
70
  # @!attribute [r] expires
71
71
  # @return [Time] Time the token expires
72
72
  def expires
73
- as_time_from_epoch(hash.fetch(:expires), granularity: :ms)
73
+ as_time_from_epoch(hash[:expires], granularity: :ms, allow_nil: :true)
74
74
  end
75
75
 
76
76
  # @!attribute [r] capability
77
77
  # @return [Hash] Capabilities assigned to this token
78
78
  def capability
79
- JSON.parse(hash.fetch(:capability))
79
+ JSON.parse(hash.fetch(:capability)) if hash.fetch(:capability)
80
80
  end
81
81
 
82
82
  # @!attribute [r] client_id
@@ -86,9 +86,11 @@ module Ably::Models
86
86
  end
87
87
 
88
88
  # Returns true if token is expired or about to expire
89
+ # For tokens that have not got an explicit expires attribute expired? will always return true
89
90
  #
90
91
  # @return [Boolean]
91
92
  def expired?
93
+ return false if !expires
92
94
  expires < Time.now + TOKEN_EXPIRY_BUFFER
93
95
  end
94
96
 
@@ -6,6 +6,8 @@ module Ably::Modules
6
6
 
7
7
  private
8
8
  def as_since_epoch(time, options = {})
9
+ return nil if options[:allow_nil] && !time
10
+
9
11
  granularity = options.fetch(:granularity, :ms)
10
12
 
11
13
  case time
@@ -19,6 +21,8 @@ module Ably::Modules
19
21
  end
20
22
 
21
23
  def as_time_from_epoch(time, options = {})
24
+ return nil if options[:allow_nil] && !time
25
+
22
26
  granularity = options.fetch(:granularity, :ms)
23
27
 
24
28
  case time
@@ -141,7 +141,10 @@ module Ably::Modules
141
141
  #
142
142
  def deferrable_for_state_change_to(target_state)
143
143
  Ably::Util::SafeDeferrable.new(logger).tap do |deferrable|
144
- once_or_if(target_state, else: proc { |*args| deferrable.fail self, *args }) do
144
+ fail_proc = Proc.new do |state_change|
145
+ deferrable.fail state_change.reason
146
+ end
147
+ once_or_if(target_state, else: fail_proc) do
145
148
  yield self if block_given?
146
149
  deferrable.succeed self
147
150
  end
@@ -13,8 +13,8 @@ module Ably::Modules
13
13
  #
14
14
  # @return [Boolean] true if new_state can be transitioned to by state machine
15
15
  # @api private
16
- def transition_state_machine(new_state, emit_object = nil)
17
- state_machine.transition_state(new_state, emit_object)
16
+ def transition_state_machine(new_state, emit_params = {})
17
+ state_machine.transition_state(new_state, emit_object(new_state, emit_params))
18
18
  end
19
19
 
20
20
  # Call #transition_to! on the StateMachine
@@ -22,8 +22,8 @@ module Ably::Modules
22
22
  #
23
23
  # @return [void]
24
24
  # @api private
25
- def transition_state_machine!(new_state, emit_object = nil)
26
- state_machine.transition_to!(new_state, emit_object)
25
+ def transition_state_machine!(new_state, emit_params = {})
26
+ state_machine.transition_to!(new_state, emit_object(new_state, emit_params))
27
27
  end
28
28
 
29
29
  # Provides an internal method for this object's state to match the StateMachine's current state.
@@ -70,5 +70,36 @@ module Ably::Modules
70
70
  logger.debug "#{self.class.name}: Transitioned to #{state_machine.current_state}"
71
71
  end
72
72
  end
73
+
74
+ def emit_object(new_state, emit_params)
75
+ if self.class.emits_klass
76
+ self.class.emits_klass.new((emit_params || {}).merge(current: STATE(new_state), previous: STATE(state_machine.current_state)))
77
+ else
78
+ emit_params
79
+ end
80
+ end
81
+
82
+ def self.included(base)
83
+ base.extend(ClassMethods)
84
+ end
85
+
86
+ module ClassMethods
87
+ def emits_klass
88
+ @emits_klass ||= if @emits_klass_name
89
+ get_const(@emits_klass_name)
90
+ end
91
+ end
92
+
93
+ def ensure_state_machine_emits(klass)
94
+ @emits_klass_name = klass
95
+ end
96
+
97
+ def get_const(klass_name)
98
+ klass_names = klass_name.split('::')
99
+ klass_names.inject(Kernel) do |klass, name|
100
+ klass.const_get(name)
101
+ end
102
+ end
103
+ end
73
104
  end
74
105
  end