ably 1.1.8 → 1.2.2

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +14 -5
  3. data/CHANGELOG.md +48 -0
  4. data/README.md +2 -2
  5. data/UPDATING.md +30 -0
  6. data/ably.gemspec +12 -24
  7. data/lib/ably/auth.rb +8 -8
  8. data/lib/ably/logger.rb +4 -4
  9. data/lib/ably/models/channel_details.rb +59 -0
  10. data/lib/ably/models/channel_metrics.rb +84 -0
  11. data/lib/ably/models/channel_occupancy.rb +43 -0
  12. data/lib/ably/models/channel_options.rb +97 -0
  13. data/lib/ably/models/channel_status.rb +53 -0
  14. data/lib/ably/models/device_details.rb +1 -1
  15. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  16. data/lib/ably/models/message.rb +4 -4
  17. data/lib/ably/models/protocol_message.rb +19 -7
  18. data/lib/ably/models/token_details.rb +7 -2
  19. data/lib/ably/models/token_request.rb +1 -1
  20. data/lib/ably/modules/ably.rb +1 -1
  21. data/lib/ably/modules/channels_collection.rb +22 -2
  22. data/lib/ably/modules/conversions.rb +34 -0
  23. data/lib/ably/realtime/auth.rb +2 -2
  24. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  25. data/lib/ably/realtime/channel/channel_state_machine.rb +5 -0
  26. data/lib/ably/realtime/channel.rb +54 -24
  27. data/lib/ably/realtime/channels.rb +1 -1
  28. data/lib/ably/rest/channel.rb +33 -34
  29. data/lib/ably/rest/client.rb +8 -5
  30. data/lib/ably/rest/middleware/encoder.rb +1 -1
  31. data/lib/ably/rest/middleware/exceptions.rb +1 -1
  32. data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
  33. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +1 -1
  34. data/lib/ably/rest/middleware/logger.rb +1 -1
  35. data/lib/ably/rest/middleware/parse_json.rb +1 -1
  36. data/lib/ably/rest/middleware/parse_message_pack.rb +1 -1
  37. data/lib/ably/util/crypto.rb +1 -1
  38. data/lib/ably/version.rb +2 -2
  39. data/spec/acceptance/realtime/channel_spec.rb +247 -21
  40. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  41. data/spec/acceptance/realtime/connection_spec.rb +21 -1
  42. data/spec/acceptance/realtime/message_spec.rb +77 -0
  43. data/spec/acceptance/rest/auth_spec.rb +18 -0
  44. data/spec/acceptance/rest/channel_spec.rb +19 -1
  45. data/spec/acceptance/rest/channels_spec.rb +22 -5
  46. data/spec/acceptance/rest/client_spec.rb +3 -3
  47. data/spec/acceptance/rest/message_spec.rb +61 -3
  48. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  49. data/spec/run_parallel_tests +2 -7
  50. data/spec/unit/logger_spec.rb +6 -14
  51. data/spec/unit/models/channel_details_spec.rb +30 -0
  52. data/spec/unit/models/channel_metrics_spec.rb +42 -0
  53. data/spec/unit/models/channel_occupancy_spec.rb +17 -0
  54. data/spec/unit/models/channel_status_spec.rb +36 -0
  55. data/spec/unit/models/message_spec.rb +14 -0
  56. data/spec/unit/models/protocol_message_spec.rb +53 -7
  57. data/spec/unit/models/token_details_spec.rb +14 -0
  58. data/spec/unit/realtime/channels_spec.rb +52 -14
  59. data/spec/unit/rest/channels_spec.rb +81 -14
  60. metadata +69 -11
@@ -162,15 +162,15 @@ module Ably::Models
162
162
  @delta_extras ||= DeltaExtras.new(attributes[:extras][:delta]).freeze
163
163
  end
164
164
 
165
+ def protocol_message_index
166
+ protocol_message.messages.map(&:object_id).index(self.object_id)
167
+ end
168
+
165
169
  private
166
170
  def raw_hash_object
167
171
  @raw_hash_object
168
172
  end
169
173
 
170
- def protocol_message_index
171
- protocol_message.messages.map(&:object_id).index(self.object_id)
172
- end
173
-
174
174
  def set_attributes_object(new_attributes)
175
175
  @attributes = IdiomaticRubyWrapper(new_attributes.clone, stop_at: [:data, :extras])
176
176
  end
@@ -3,7 +3,7 @@ module Ably::Models
3
3
  # A ProtocolMessage always relates to a single channel only, but
4
4
  # can contain multiple individual Messages or PresenceMessages.
5
5
  # ProtocolMessages are serially numbered on a connection.
6
- # See the {http://docs.ably.io/client-lib-development-guide/protocol/ Ably client library developer documentation}
6
+ # See the {http://ably.com/docs/client-lib-development-guide/protocol/ Ably client library developer documentation}
7
7
  # for further details on the members of a ProtocolMessage
8
8
  #
9
9
  # @!attribute [r] action
@@ -11,7 +11,7 @@ module Ably::Models
11
11
  # @!attribute [r] auth
12
12
  # @return [Ably::Models::AuthDetails] Authentication details used to perform authentication upgrades over an existing transport
13
13
  # @!attribute [r] count
14
- # @return [Integer] The count field is used for ACK and NACK actions. See {http://docs.ably.io/client-lib-development-guide/protocol/#message-acknowledgement message acknowledgement protocol}
14
+ # @return [Integer] The count field is used for ACK and NACK actions. See {http://ably.com/docs/client-lib-development-guide/protocol/#message-acknowledgement message acknowledgement protocol}
15
15
  # @!attribute [r] error
16
16
  # @return [ErrorInfo] Contains error information
17
17
  # @!attribute [r] channel
@@ -66,6 +66,14 @@ module Ably::Models
66
66
  auth: 17
67
67
  )
68
68
 
69
+ ATTACH_FLAGS_MAPPING = {
70
+ resume: 32, # 2^5
71
+ presence: 65536, # 2^16
72
+ publish: 131072, # 2^17
73
+ subscribe: 262144, # 2^18
74
+ presence_subscribe: 524288, # 2^19
75
+ }
76
+
69
77
  # Indicates this protocol message action will generate an ACK response such as :message or :presence
70
78
  # @api private
71
79
  def self.ack_required?(for_action)
@@ -185,6 +193,10 @@ module Ably::Models
185
193
  message_size <= connection_details.max_message_size
186
194
  end
187
195
 
196
+ def params
197
+ @params ||= attributes[:params].to_h
198
+ end
199
+
188
200
  def flags
189
201
  Integer(attributes[:flags])
190
202
  rescue TypeError
@@ -218,27 +230,27 @@ module Ably::Models
218
230
 
219
231
  # @api private
220
232
  def has_attach_resume_flag?
221
- flags & 32 == 32 # 2^5
233
+ flags & ATTACH_FLAGS_MAPPING[:resume] == ATTACH_FLAGS_MAPPING[:resume] # 2^5
222
234
  end
223
235
 
224
236
  # @api private
225
237
  def has_attach_presence_flag?
226
- flags & 65536 == 65536 # 2^16
238
+ flags & ATTACH_FLAGS_MAPPING[:presence] == ATTACH_FLAGS_MAPPING[:presence] # 2^16
227
239
  end
228
240
 
229
241
  # @api private
230
242
  def has_attach_publish_flag?
231
- flags & 131072 == 131072 # 2^17
243
+ flags & ATTACH_FLAGS_MAPPING[:publish] == ATTACH_FLAGS_MAPPING[:publish] # 2^17
232
244
  end
233
245
 
234
246
  # @api private
235
247
  def has_attach_subscribe_flag?
236
- flags & 262144 == 262144 # 2^18
248
+ flags & ATTACH_FLAGS_MAPPING[:subscribe] == ATTACH_FLAGS_MAPPING[:subscribe] # 2^18
237
249
  end
238
250
 
239
251
  # @api private
240
252
  def has_attach_presence_subscribe_flag?
241
- flags & 524288 == 524288 # 2^19
253
+ flags & ATTACH_FLAGS_MAPPING[:presence_subscribe] == ATTACH_FLAGS_MAPPING[:presence_subscribe] # 2^19
242
254
  end
243
255
 
244
256
  def connection_details
@@ -95,10 +95,15 @@ module Ably::Models
95
95
  # Returns true if token is expired or about to expire
96
96
  # For tokens that have not got an explicit expires attribute expired? will always return true
97
97
  #
98
+ # @param attributes [Hash]
99
+ # @option attributes [Time] :from Sets a current time from which token expires
100
+ #
98
101
  # @return [Boolean]
99
- def expired?
102
+ def expired?(attributes = {})
100
103
  return false if !expires
101
- expires < Time.now + TOKEN_EXPIRY_BUFFER
104
+
105
+ from = attributes[:from] || Time.now
106
+ expires < from + TOKEN_EXPIRY_BUFFER
102
107
  end
103
108
 
104
109
  # True if the TokenDetails was created from an opaque string i.e. no metadata exists for this token
@@ -98,7 +98,7 @@ module Ably::Models
98
98
 
99
99
  # @!attribute [r] mac
100
100
  # @return [String] the Message Authentication Code for this request. See the
101
- # {https://www.ably.io/documentation Ably Authentication documentation} for more details.
101
+ # {https://www.ably.com/docs Ably Authentication documentation} for more details.
102
102
  def mac
103
103
  attributes.fetch(:mac) { raise Ably::Exceptions::InvalidTokenRequest, 'MAC is missing' }
104
104
  end
@@ -6,7 +6,7 @@
6
6
  module Ably
7
7
  # Fallback hosts to use when a connection to rest/realtime.ably.io is not possible due to
8
8
  # network failures either at the client, between the client and Ably, within an Ably data center, or at the IO domain registrar
9
- # see https://docs.ably.io/client-lib-development-guide/features/#RSC15a
9
+ # see https://ably.com/docs/client-lib-development-guide/features/#RSC15a
10
10
  #
11
11
  FALLBACK_DOMAIN = 'ably-realtime.com'.freeze
12
12
  FALLBACK_IDS = %w(a b c d e).freeze
@@ -13,14 +13,21 @@ module Ably::Modules
13
13
  # Return a Channel for the given name
14
14
  #
15
15
  # @param name [String] The name of the channel
16
- # @param channel_options [Hash] Channel options including the encryption options
16
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
17
17
  #
18
18
  # @return [Channel]
19
19
  #
20
20
  def get(name, channel_options = {})
21
21
  if channels.has_key?(name)
22
22
  channels[name].tap do |channel|
23
- channel.update_options channel_options if channel_options && !channel_options.empty?
23
+ if channel_options && !channel_options.empty?
24
+ if channel.respond_to?(:need_reattach?) && channel.need_reattach?
25
+ raise_implicit_options_update
26
+ else
27
+ warn_implicit_options_update
28
+ channel.options = channel_options
29
+ end
30
+ end
24
31
  end
25
32
  else
26
33
  channels[name] ||= channel_klass.new(client, name, channel_options)
@@ -70,6 +77,19 @@ module Ably::Modules
70
77
  end
71
78
 
72
79
  private
80
+
81
+ def raise_implicit_options_update
82
+ raise ArgumentError, "You are trying to indirectly update channel options which will trigger reattachment of the channel. Please use Channel#set_options directly if you wish to continue"
83
+ end
84
+
85
+ def warn_implicit_options_update
86
+ logger.warn { "Channels#get: Using this method to update channel options is deprecated and may be removed in a future version of ably-ruby. Please use Channel#setOptions instead" }
87
+ end
88
+
89
+ def logger
90
+ client.logger
91
+ end
92
+
73
93
  def client
74
94
  @client
75
95
  end
@@ -115,5 +115,39 @@ module Ably::Modules
115
115
 
116
116
  raise Ably::Exceptions::UnsupportedDataType.new('Invalid data payload', 400, Ably::Exceptions::Codes::INVALID_MESSAGE_DATA_OR_ENCODING)
117
117
  end
118
+
119
+ # Converts the name, data, attributes into the array of Message objects
120
+ #
121
+ # @return [Array<Ably::Models::Message>]
122
+ #
123
+ def build_messages(name, data = nil, attributes = {})
124
+ return [Ably::Models::Message(ensure_supported_name_and_payload(nil, data, attributes))] if name.nil?
125
+
126
+ Array(name).map do |item|
127
+ Ably::Models::Message(ensure_supported_name_and_payload(item, data, attributes))
128
+ end
129
+ end
130
+
131
+ # Ensures if the first argument (name) is a String, Hash or Ably::Models::Message object,
132
+ # second argument (data) should be a String, Hash, Array or nil (see ensure_supported_payload() method).
133
+ #
134
+ # @return [Hash] Contains :name, :data and other attributes
135
+ #
136
+ # (RSL1a, RSL1b)
137
+ #
138
+ def ensure_supported_name_and_payload(name, data = nil, attributes = {})
139
+ return name.attributes.dup if name.kind_of?(Ably::Models::Message)
140
+
141
+ payload = data
142
+ if (hash = name).kind_of?(Hash)
143
+ name, payload = hash[:name], (hash[:data] || payload)
144
+ attributes.merge!(hash)
145
+ end
146
+
147
+ name = ensure_utf_8(:name, name, allow_nil: true)
148
+ ensure_supported_payload payload
149
+
150
+ attributes.merge({ name: name, data: payload })
151
+ end
118
152
  end
119
153
  end
@@ -2,10 +2,10 @@ require 'ably/auth'
2
2
 
3
3
  module Ably
4
4
  module Realtime
5
- # Auth is responsible for authentication with {https://www.ably.io Ably} using basic or token authentication
5
+ # Auth is responsible for authentication with {https://www.ably.com Ably} using basic or token authentication
6
6
  # This {Ably::Realtime::Auth Realtime::Auth} class wraps the {Ably::Auth Synchronous Ably::Auth} class in an EventMachine friendly way using Deferrables for all IO. See {Ably::Auth Ably::Auth} for more information
7
7
  #
8
- # Find out more about Ably authentication at: https://www.ably.io/documentation/general/authentication/
8
+ # Find out more about Ably authentication at: https://www.ably.com/docs/general/authentication/
9
9
  #
10
10
  # @!attribute [r] client_id
11
11
  # (see Ably::Auth#client_id)
@@ -38,6 +38,8 @@ module Ably::Realtime
38
38
  if attached_protocol_message
39
39
  update_presence_sync_state_following_attached attached_protocol_message
40
40
  channel.properties.set_attach_serial(attached_protocol_message.channel_serial)
41
+ channel.options.set_modes_from_flags(attached_protocol_message.flags)
42
+ channel.options.set_params(attached_protocol_message.params)
41
43
  end
42
44
  end
43
45
 
@@ -64,6 +66,7 @@ module Ably::Realtime
64
66
  end
65
67
 
66
68
  channel.properties.set_attach_serial(protocol_message.channel_serial)
69
+ channel.options.set_modes_from_flags(protocol_message.flags)
67
70
 
68
71
  if protocol_message.has_channel_resumed_flag?
69
72
  logger.debug { "ChannelManager: Additional resumed ATTACHED message received for #{channel.state} channel '#{channel.name}'" }
@@ -199,14 +202,21 @@ module Ably::Realtime
199
202
  end
200
203
 
201
204
  def send_attach_protocol_message
202
- send_state_change_protocol_message Ably::Models::ProtocolMessage::ACTION.Attach, :suspended # move to suspended
205
+ message_options = {}
206
+ message_options[:params] = channel.options.params if channel.options.params.any?
207
+ message_options[:flags] = channel.options.modes_to_flags if channel.options.modes
208
+ if channel.attach_resume
209
+ message_options[:flags] = message_options[:flags].to_i | Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[:resume]
210
+ end
211
+
212
+ send_state_change_protocol_message Ably::Models::ProtocolMessage::ACTION.Attach, :suspended, message_options
203
213
  end
204
214
 
205
215
  def send_detach_protocol_message(previous_state)
206
216
  send_state_change_protocol_message Ably::Models::ProtocolMessage::ACTION.Detach, previous_state # return to previous state if failed
207
217
  end
208
218
 
209
- def send_state_change_protocol_message(new_state, state_if_failed)
219
+ def send_state_change_protocol_message(new_state, state_if_failed, message_options = {})
210
220
  state_at_time_of_request = channel.state
211
221
  @pending_state_change_timer = EventMachine::Timer.new(realtime_request_timeout) do
212
222
  if channel.state == state_at_time_of_request
@@ -227,7 +237,8 @@ module Ably::Realtime
227
237
  next unless pending_state_change_timer
228
238
  connection.send_protocol_message(
229
239
  action: new_state.to_i,
230
- channel: channel.name
240
+ channel: channel.name,
241
+ **message_options.to_h
231
242
  )
232
243
  resend_if_disconnected_and_connected.call
233
244
  end
@@ -237,7 +248,8 @@ module Ably::Realtime
237
248
 
238
249
  connection.send_protocol_message(
239
250
  action: new_state.to_i,
240
- channel: channel.name
251
+ channel: channel.name,
252
+ **message_options.to_h
241
253
  )
242
254
  end
243
255
 
@@ -42,6 +42,11 @@ module Ably::Realtime
42
42
 
43
43
  before_transition(to: [:attached]) do |channel, current_transition|
44
44
  channel.manager.attached current_transition.metadata.protocol_message
45
+ channel.attach_resume!
46
+ end
47
+
48
+ before_transition(to: [:detaching, :failed]) do |channel, _current_transition|
49
+ channel.reset_attach_resume!
45
50
  end
46
51
 
47
52
  after_transition(to: [:detaching]) do |channel, current_transition|
@@ -36,6 +36,7 @@ module Ably
36
36
  include Ably::Modules::MessageEmitter
37
37
  include Ably::Realtime::Channel::Publisher
38
38
  extend Ably::Modules::Enum
39
+ extend Forwardable
39
40
 
40
41
  # ChannelState
41
42
  # The permited states for this channel
@@ -92,17 +93,25 @@ module Ably
92
93
  # @api private
93
94
  attr_reader :manager
94
95
 
96
+ # Flag that specifies whether channel is resuming attachment(reattach) or is doing a 'clean attach' RTL4j1
97
+ # @return [Bolean]
98
+ # @api private
99
+ attr_reader :attach_resume
100
+
101
+ # ChannelOptions params attrribute (#RTL4k)
102
+ # return [Hash]
103
+ def_delegators :options, :params
104
+
95
105
  # Initialize a new Channel object
96
106
  #
97
107
  # @param client [Ably::Rest::Client]
98
108
  # @param name [String] The name of the channel
99
- # @param channel_options [Hash] Channel options, currently reserved for Encryption options
100
- # @option channel_options [Hash,Ably::Models::CipherParams] :cipher A hash of options or a {Ably::Models::CipherParams} to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +:cipher+ options
109
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
101
110
  #
102
111
  def initialize(client, name, channel_options = {})
103
112
  name = ensure_utf_8(:name, name)
104
113
 
105
- update_options channel_options
114
+ @options = Ably::Models::ChannelOptions(channel_options)
106
115
  @client = client
107
116
  @name = name
108
117
  @queue = []
@@ -112,6 +121,7 @@ module Ably
112
121
  @manager = ChannelManager.new(self, client.connection)
113
122
  @push = PushChannel.new(self)
114
123
  @properties = ChannelProperties.new(self)
124
+ @attach_resume = false
115
125
 
116
126
  setup_event_handlers
117
127
  setup_presence
@@ -129,23 +139,31 @@ module Ably
129
139
  # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
130
140
  #
131
141
  # @example
132
- # # Publish a single message
142
+ # # Publish a single message form
133
143
  # channel.publish 'click', { x: 1, y: 2 }
134
144
  #
135
- # # Publish an array of message Hashes
145
+ # # Publish a single message with single Hash form
146
+ # message = { name: 'click', data: { x: 1, y: 2 } }
147
+ # channel.publish message
148
+ #
149
+ # # Publish an array of message Hashes form
136
150
  # messages = [
137
- # { name: 'click', { x: 1, y: 2 } },
138
- # { name: 'click', { x: 2, y: 3 } }
151
+ # { name: 'click', data: { x: 1, y: 2 } },
152
+ # { name: 'click', data: { x: 2, y: 3 } }
139
153
  # ]
140
154
  # channel.publish messages
141
155
  #
142
- # # Publish an array of Ably::Models::Message objects
156
+ # # Publish an array of Ably::Models::Message objects form
143
157
  # messages = [
144
- # Ably::Models::Message(name: 'click', { x: 1, y: 2 })
145
- # Ably::Models::Message(name: 'click', { x: 2, y: 3 })
158
+ # Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
159
+ # Ably::Models::Message(name: 'click', data: { x: 2, y: 3 })
146
160
  # ]
147
161
  # channel.publish messages
148
162
  #
163
+ # # Publish an array of Ably::Models::Message objects form
164
+ # message = Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
165
+ # channel.publish message
166
+ #
149
167
  # channel.publish('click', 'body') do |message|
150
168
  # puts "#{message.name} event received with #{message.data}"
151
169
  # end
@@ -165,13 +183,7 @@ module Ably
165
183
  return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
166
184
  end
167
185
 
168
- messages = if name.kind_of?(Enumerable)
169
- name
170
- else
171
- name = ensure_utf_8(:name, name, allow_nil: true)
172
- ensure_supported_payload data
173
- [{ name: name, data: data }.merge(attributes)]
174
- end
186
+ messages = build_messages(name, data, attributes) # (RSL1a, RSL1b)
175
187
 
176
188
  if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
177
189
  error = Ably::Exceptions::InvalidRequest.new("It is not possible to publish more than #{Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE} messages with a single publish request.")
@@ -309,6 +321,16 @@ module Ably
309
321
  )
310
322
  end
311
323
 
324
+ # Sets or updates the stored channel options. (#RTL16)
325
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
326
+ # @return [Ably::Models::ChannelOptions]
327
+ def set_options(channel_options)
328
+ @options = Ably::Models::ChannelOptions(channel_options)
329
+
330
+ manager.request_reattach if need_reattach?
331
+ end
332
+ alias options= set_options
333
+
312
334
  # @api private
313
335
  def set_channel_error_reason(error)
314
336
  @error_reason = error
@@ -319,24 +341,32 @@ module Ably
319
341
  @error_reason = nil
320
342
  end
321
343
 
322
- # @api private
323
- def update_options(channel_options)
324
- @options = channel_options.clone.freeze
325
- end
326
- alias set_options update_options # (RSL7)
327
- alias options= update_options
328
-
329
344
  # Used by {Ably::Modules::StateEmitter} to debug state changes
330
345
  # @api private
331
346
  def logger
332
347
  client.logger
333
348
  end
334
349
 
350
+ # @api private
351
+ def attach_resume!
352
+ @attach_resume = true
353
+ end
354
+
355
+ # @api private
356
+ def reset_attach_resume!
357
+ @attach_resume = false
358
+ end
359
+
335
360
  # As we are using a state machine, do not allow change_state to be used
336
361
  # #transition_state_machine must be used instead
337
362
  private :change_state
338
363
 
364
+ def need_reattach?
365
+ !!(attaching? || attached?) && !!(options.modes || options.params)
366
+ end
367
+
339
368
  private
369
+
340
370
  def setup_event_handlers
341
371
  __incoming_msgbus__.subscribe(:message) do |message|
342
372
  message.decode(client.encoders, options) do |encode_error, error_message|
@@ -13,7 +13,7 @@ module Ably
13
13
  # Return a {Ably::Realtime::Channel} for the given name
14
14
  #
15
15
  # @param name [String] The name of the channel
16
- # @param channel_options [Hash] Channel options, currently reserved for Encryption options
16
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
17
17
  # @return [Ably::Realtime::Channel}
18
18
  #
19
19
  def get(*args)
@@ -28,21 +28,20 @@ module Ably
28
28
  #
29
29
  # @param client [Ably::Rest::Client]
30
30
  # @param name [String] The name of the channel
31
- # @param channel_options [Hash] Channel options, currently reserved for Encryption options
32
- # @option channel_options [Hash,Ably::Models::CipherParams] :cipher A hash of options or a {Ably::Models::CipherParams} to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +:cipher+ options
31
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
33
32
  #
34
33
  def initialize(client, name, channel_options = {})
35
34
  name = (ensure_utf_8 :name, name)
36
35
 
37
- update_options channel_options
36
+ @options = Ably::Models::ChannelOptions(channel_options)
38
37
  @client = client
39
38
  @name = name
40
39
  @push = PushChannel.new(self)
41
40
  end
42
41
 
43
- # Publish one or more messages to the channel. Three overloaded forms
42
+ # Publish one or more messages to the channel. Five overloaded forms
44
43
  # @param name [String, Array<Ably::Models::Message|Hash>, Ably::Models::Message, nil] The event name of the message to publish, or an Array of [Ably::Model::Message] objects or [Hash] objects with +:name+ and +:data+ pairs, or a single Ably::Model::Message object
45
- # @param data [String, ByteArray, Hash, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument, in which case an optional hash of query parameters
44
+ # @param data [String, Array, Hash, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument, in which case an optional hash of query parameters
46
45
  # @param attributes [Hash, nil] Optional additional message attributes such as :extras, :id, :client_id or :connection_id, applied when name attribute is nil or a string (Deprecated, will be removed in 2.0 in favour of constructing a Message object)
47
46
  # @return [Boolean] true if the message was published, otherwise false
48
47
  #
@@ -50,42 +49,33 @@ module Ably
50
49
  # # Publish a single message with (name, data) form
51
50
  # channel.publish 'click', { x: 1, y: 2 }
52
51
  #
53
- # # Publish an array of message Hashes
52
+ # # Publish a single message with single Hash form
53
+ # message = { name: 'click', data: { x: 1, y: 2 } }
54
+ # channel.publish message
55
+ #
56
+ # # Publish an array of message Hashes form
54
57
  # messages = [
55
58
  # { name: 'click', data: { x: 1, y: 2 } },
56
59
  # { name: 'click', data: { x: 2, y: 3 } }
57
60
  # ]
58
61
  # channel.publish messages
59
62
  #
60
- # # Publish an array of Ably::Models::Message objects
63
+ # # Publish an array of Ably::Models::Message objects form
61
64
  # messages = [
62
- # Ably::Models::Message(name: 'click', { x: 1, y: 2 })
63
- # Ably::Models::Message(name: 'click', { x: 2, y: 3 })
65
+ # Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
66
+ # Ably::Models::Message(name: 'click', data: { x: 2, y: 3 })
64
67
  # ]
65
68
  # channel.publish messages
66
69
  #
67
- # # Publish a single Ably::Models::Message object, with a query params
68
- # # specifying quickAck: true
69
- # message = Ably::Models::Message(name: 'click', { x: 1, y: 2 })
70
- # channel.publish message, quickAck: 'true'
70
+ # # Publish a single Ably::Models::Message object form
71
+ # message = Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
72
+ # channel.publish message
71
73
  #
72
- def publish(first, second = nil, third = {})
73
- messages, qs_params = if first.kind_of?(Enumerable)
74
- # ([Message], qs_params) form
75
- [first, second]
76
- elsif first.kind_of?(Ably::Models::Message)
77
- # (Message, qs_params) form
78
- [[first], second]
79
- else
80
- # (name, data, attributes) form
81
- first = ensure_utf_8(:name, first, allow_nil: true)
82
- ensure_supported_payload second
83
- # RSL1h - attributes as an extra method parameter is extra-spec but need to
84
- # keep it for backcompat until version 2
85
- [[{ name: first, data: second }.merge(third)], nil]
86
- end
74
+ def publish(name, data = nil, attributes = {})
75
+ qs_params = nil
76
+ qs_params = data if name.kind_of?(Enumerable) || name.kind_of?(Ably::Models::Message)
87
77
 
88
- messages.map! { |message| Ably::Models::Message(message.dup) }
78
+ messages = build_messages(name, data, attributes) # (RSL1a, RSL1b)
89
79
 
90
80
  if messages.sum(&:size) > (max_message_size = client.max_message_size || Ably::Rest::Client::MAX_MESSAGE_SIZE)
91
81
  raise Ably::Exceptions::MaxMessageSizeExceeded.new("Maximum message size exceeded #{max_message_size} bytes.")
@@ -163,14 +153,23 @@ module Ably
163
153
  @presence ||= Presence.new(client, self)
164
154
  end
165
155
 
166
- # @api private
167
- def update_options(channel_options)
168
- @options = channel_options.clone.freeze
156
+ # Sets or updates the stored channel options. (#RSL7)
157
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
158
+ # @return [Ably::Models::ChannelOptions]
159
+ def set_options(channel_options)
160
+ @options = Ably::Models::ChannelOptions(channel_options)
161
+ end
162
+ alias options= set_options
163
+
164
+ # Makes GET request for channel details (#RSL8, #RSL8a)
165
+ #
166
+ # @return [Ably::Models::ChannelDetails]
167
+ def status
168
+ Ably::Models::ChannelDetails.new(client.get(base_path).body)
169
169
  end
170
- alias set_options update_options # (RSL7)
171
- alias options= update_options
172
170
 
173
171
  private
172
+
174
173
  def base_path
175
174
  "/channels/#{URI.encode_www_form_component(name)}"
176
175
  end
@@ -4,7 +4,7 @@ require 'logger'
4
4
  require 'uri'
5
5
 
6
6
  require 'typhoeus'
7
- require 'typhoeus/adapters/faraday'
7
+ require 'faraday/typhoeus'
8
8
 
9
9
  require 'ably/rest/middleware/exceptions'
10
10
 
@@ -55,7 +55,7 @@ module Ably
55
55
  # @return [Symbol]
56
56
  attr_reader :protocol
57
57
 
58
- # Client agent i.e. `example-gem/1.2.0 ably-ruby/1.1.5 ruby/1.9.3`
58
+ # Client agent i.e. `example-gem/1.2.0 ably-ruby/1.1.5 ruby/3.1.1`
59
59
  # @return [String]
60
60
  attr_reader :agent
61
61
 
@@ -139,7 +139,7 @@ module Ably
139
139
  # @option options [Symbol] :protocol (:msgpack) Protocol used to communicate with Ably, :json and :msgpack currently supported
140
140
  # @option options [Boolean] :use_binary_protocol (true) When true will use the MessagePack binary protocol, when false it will use JSON encoding. This option will overide :protocol option
141
141
  # @option options [Logger::Severity,Symbol] :log_level (Logger::WARN) Log level for the standard Logger that outputs to STDOUT. Can be set to :fatal (Logger::FATAL), :error (Logger::ERROR), :warn (Logger::WARN), :info (Logger::INFO), :debug (Logger::DEBUG) or :none
142
- # @option options [Logger] :logger A custom logger can be used however it must adhere to the Ruby Logger interface, see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html
142
+ # @option options [Logger] :logger A custom logger can be used however it must adhere to the Ruby Logger interface, see http://www.ruby-doc.org/stdlib-3.1.1/libdoc/logger/rdoc/Logger.html
143
143
  # @option options [String] :client_id client ID identifying this connection to other clients
144
144
  # @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
145
145
  # @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the +auth_url+
@@ -147,7 +147,7 @@ module Ably
147
147
  # @option options [Symbol] :auth_method (:get) HTTP method to use with +auth_url+, must be either +:get+ or +:post+
148
148
  # @option 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.
149
149
  # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
150
- # @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
150
+ # @option options [Boolean] :query_time when true will query the {https://www.ably.com Ably} system for the current time instead of using the local time
151
151
  # @option options [Hash] :default_token_params convenience to pass in +token_params+ that will be used as a default for all token requests. See {Auth#create_token_request}
152
152
  #
153
153
  # @option options [Integer] :http_open_timeout (4 seconds) timeout in seconds for opening an HTTP connection for all HTTP requests
@@ -199,10 +199,13 @@ module Ably
199
199
  @custom_tls_port = options.delete(:tls_port)
200
200
  @add_request_ids = options.delete(:add_request_ids)
201
201
  @log_retries_as_info = options.delete(:log_retries_as_info)
202
- @idempotent_rest_publishing = options.delete(:idempotent_rest_publishing) || Ably.major_minor_version_numeric > 1.1
203
202
  @max_message_size = options.delete(:max_message_size) || MAX_MESSAGE_SIZE
204
203
  @max_frame_size = options.delete(:max_frame_size) || MAX_FRAME_SIZE
205
204
 
205
+ if (@idempotent_rest_publishing = options.delete(:idempotent_rest_publishing)).nil?
206
+ @idempotent_rest_publishing = Ably::PROTOCOL_VERSION.to_f > 1.1
207
+ end
208
+
206
209
  if options[:fallback_hosts_use_default] && options[:fallback_hosts]
207
210
  raise ArgumentError, "fallback_hosts_use_default cannot be set to try when fallback_hosts is also provided"
208
211
  end
@@ -5,7 +5,7 @@ module Ably
5
5
  module Rest
6
6
  module Middleware
7
7
  # Encode the body of the message according to the mime type
8
- class Encoder < ::Faraday::Response::Middleware
8
+ class Encoder < Faraday::Middleware
9
9
  CONTENT_TYPE = 'Content-Type'.freeze unless defined? CONTENT_TYPE
10
10
 
11
11
  def call(env)
@@ -6,7 +6,7 @@ module Ably
6
6
  module Middleware
7
7
  # HTTP exceptions raised by Ably due to an error status code
8
8
  # Ably returns JSON/Msgpack error codes and messages so include this if possible in the exception messages
9
- class Exceptions < Faraday::Response::Middleware
9
+ class Exceptions < Faraday::Middleware
10
10
  def on_complete(env)
11
11
  if env.status >= 400
12
12
  error_status_code = env.status