ably 1.1.8 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
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