ably 1.1.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/README.md +1 -1
  4. data/UPDATING.md +30 -0
  5. data/lib/ably/auth.rb +3 -3
  6. data/lib/ably/models/channel_options.rb +97 -0
  7. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  8. data/lib/ably/models/message.rb +4 -4
  9. data/lib/ably/models/protocol_message.rb +17 -5
  10. data/lib/ably/models/token_details.rb +7 -2
  11. data/lib/ably/modules/channels_collection.rb +22 -2
  12. data/lib/ably/modules/conversions.rb +34 -0
  13. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  14. data/lib/ably/realtime/channel/channel_state_machine.rb +5 -0
  15. data/lib/ably/realtime/channel.rb +54 -24
  16. data/lib/ably/realtime/channels.rb +1 -1
  17. data/lib/ably/rest/channel.rb +26 -34
  18. data/lib/ably/rest/client.rb +4 -1
  19. data/lib/ably/util/crypto.rb +1 -1
  20. data/lib/ably/version.rb +2 -2
  21. data/spec/acceptance/realtime/channel_spec.rb +247 -21
  22. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  23. data/spec/acceptance/realtime/connection_spec.rb +1 -1
  24. data/spec/acceptance/realtime/message_spec.rb +77 -0
  25. data/spec/acceptance/rest/auth_spec.rb +18 -0
  26. data/spec/acceptance/rest/channel_spec.rb +1 -1
  27. data/spec/acceptance/rest/channels_spec.rb +22 -5
  28. data/spec/acceptance/rest/client_spec.rb +2 -2
  29. data/spec/acceptance/rest/message_spec.rb +61 -3
  30. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  31. data/spec/unit/models/message_spec.rb +14 -0
  32. data/spec/unit/models/protocol_message_spec.rb +53 -7
  33. data/spec/unit/models/token_details_spec.rb +14 -0
  34. data/spec/unit/realtime/channels_spec.rb +52 -14
  35. data/spec/unit/rest/channels_spec.rb +81 -14
  36. metadata +21 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba8f2b01b35fceead7297645f54cb854017e14c8a004882d5b414becc450feef
4
- data.tar.gz: c116cce13d8ed06a9858e252a81918530d54f15e1a20c4b3ef30e5334408ec1b
3
+ metadata.gz: f42d74184e0de100d535fa3c01595727b78a6d2cd6018d1743e2593c20fe7ed7
4
+ data.tar.gz: db674f894bc006187816c0e51df19b84a7473d178b8360271a27640e7b3e4f6e
5
5
  SHA512:
6
- metadata.gz: e0761591d9f838201f208c3d44d8068ad77c87591e737083cab548e77a104a6d7bd2da2cc360e01ad661c33f3af09220813ad19fe7d93f6759a7a88de82180d9
7
- data.tar.gz: ca14639736fc07b1d00323df0073b37e0e2f51d6a2681bf868173708aafd45a89fbc872d4c16c600a41ebea9488c336a5422bdddbf8b80daa912ce9ef231945c
6
+ metadata.gz: 5950df21ad66e4c59eb6bd772ab71aa91b421679139166ddaa9076ea4fc7cc9a6eb9993a3f4e15e30a2d362a05c008587b153617c967da6a41aa6e0efdc3f091
7
+ data.tar.gz: a9d61d9b6c12397df274f55028a1fcbd6c285235cb489027188c799949466a6a74c4e6ea3829bd44851be48af14e05a2f7d49494ea61d1ea190176773079df23
data/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Change Log
2
2
 
3
+ ## [v1.2.0](https://github.com/ably/ably-ruby/tree/v1.2.0)
4
+
5
+ [Full Changelog](https://github.com/ably/ably-ruby/compare/v1.1.8...v1.2.0)
6
+
7
+ This release updates ably-ruby to be compliant with the 1.2 version of the Ably client library feature specification. There are some minor breaking changes, please see [the migration guide](./UPDATING.md) for more information.
8
+
9
+ **Closed issues:**
10
+
11
+ - create migration guide for the upgrade to ruby 1.2 [\#347](https://github.com/ably/ably-ruby/issues/347)
12
+ - Write spec tests for RTL17 [\#304](https://github.com/ably/ably-ruby/issues/304)
13
+ - Write spec tests for RTL16, RTL16a [\#303](https://github.com/ably/ably-ruby/issues/303)
14
+ - Write spec tests for RTL4m [\#302](https://github.com/ably/ably-ruby/issues/302)
15
+ - Write spec tests for RTL4l [\#301](https://github.com/ably/ably-ruby/issues/301)
16
+ - Write spec tests for RTL4k, RTL4k1 [\#300](https://github.com/ably/ably-ruby/issues/300)
17
+ - Write spec tests for RTL4j, RTL4j1, RTL4j2 [\#299](https://github.com/ably/ably-ruby/issues/299)
18
+ - Write spec tests for RTS3c, RTS3c1 [\#298](https://github.com/ably/ably-ruby/issues/298)
19
+ - Write spec tests for RTS3a [\#297](https://github.com/ably/ably-ruby/issues/297)
20
+ - Add support for RTL21 [\#296](https://github.com/ably/ably-ruby/issues/296)
21
+ - Add support for RTL17 [\#292](https://github.com/ably/ably-ruby/issues/292)
22
+ - Add support for RTL16, RTL16a [\#291](https://github.com/ably/ably-ruby/issues/291)
23
+ - Add support for RTL4m [\#290](https://github.com/ably/ably-ruby/issues/290)
24
+ - Add support for RTL4l [\#289](https://github.com/ably/ably-ruby/issues/289)
25
+ - Add support for RTL4k, RTL4k1 [\#288](https://github.com/ably/ably-ruby/issues/288)
26
+ - Add support for RTL4j, RTL4j1, RTL4j2 [\#287](https://github.com/ably/ably-ruby/issues/287)
27
+ - Add support for RTS3c, RTS3c1 [\#286](https://github.com/ably/ably-ruby/issues/286)
28
+ - Add support for RTS3a [\#285](https://github.com/ably/ably-ruby/issues/285)
29
+ - Write spec tests for RSL1a, b, h, k1, k2, l, l1 \(Channels\) [\#283](https://github.com/ably/ably-ruby/issues/283)
30
+ - Write spec tests for RSN3a, c \(Channels\) [\#282](https://github.com/ably/ably-ruby/issues/282)
31
+ - Write spec tests for RSA4b, b1, c, RSA16 \(Authentication\) [\#281](https://github.com/ably/ably-ruby/issues/281)
32
+ - Add support for RSN3a, c \(Channels\) [\#269](https://github.com/ably/ably-ruby/issues/269)
33
+ - Add support for RSA4b, b1, c, RSA16 \(Authentication\) [\#268](https://github.com/ably/ably-ruby/issues/268)
34
+ - Add support for RSC7a, RSC7c \(RestClient\)
35
+ [\#266](https://github.com/ably/ably-ruby/issues/266)
36
+ - Add support for Test Guidance G4 [\#265](https://github.com/ably/ably-ruby/issues/265)
37
+ - Add support for TO3o, TO3p [\#264](https://github.com/ably/ably-ruby/issues/264)
38
+
39
+ **Merged pull requests:**
40
+
41
+ - Add migration guide from 1.1.8 to 1.2.0 [\#348](https://github.com/ably/ably-ruby/pull/348) ([TheSmartnik](https://github.com/TheSmartnik))
42
+ - RTL21 [\#345](https://github.com/ably/ably-ruby/pull/345) ([lukaszsliwa](https://github.com/lukaszsliwa))
43
+ - RTL4j [\#341](https://github.com/ably/ably-ruby/pull/341) ([TheSmartnik](https://github.com/TheSmartnik))
44
+ - RSL1a, RSL1b [\#340](https://github.com/ably/ably-ruby/pull/340) ([lukaszsliwa](https://github.com/lukaszsliwa))
45
+ - Add support for RSA4b, b1, c, RSA16 \(Authentication\) [\#338](https://github.com/ably/ably-ruby/pull/338) ([lukaszsliwa](https://github.com/lukaszsliwa))
46
+ - ChannelOptions related tasks [\#336](https://github.com/ably/ably-ruby/pull/336) ([TheSmartnik](https://github.com/TheSmartnik))
47
+ - Update RSC7 [\#334](https://github.com/ably/ably-ruby/pull/334) ([TheSmartnik](https://github.com/TheSmartnik))
48
+
3
49
  ## [v1.1.8](https://github.com/ably/ably-ruby/tree/v1.1.8)
4
50
 
5
51
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v1.1.7...v1.1.8)
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  _[Ably](https://ably.com) is the platform that powers synchronized digital experiences in realtime. Whether attending an event in a virtual venue, receiving realtime financial information, or monitoring live car performance data – consumers simply expect realtime digital experiences as standard. Ably provides a suite of APIs to build, extend, and deliver powerful digital experiences in realtime for more than 250 million devices across 80 countries each month. Organizations like Bloomberg, HubSpot, Verizon, and Hopin depend on Ably’s platform to offload the growing complexity of business-critical realtime data synchronization at global scale. For more information, see the [Ably documentation](https://ably.com/documentation)._
7
7
 
8
- This is a Ruby client library for Ably. The library currently targets the [Ably 1.1 client library specification](https://ably.com/documentation/client-lib-development-guide/features/). You can see the complete list of features this client library supports in [our client library SDKs feature support matrix](https://ably.com/download/sdk-feature-support-matrix).
8
+ This is a Ruby client library for Ably. The library currently targets the [Ably 1.2 client library specification](https://ably.com/documentation/client-lib-development-guide/features/). You can see the complete list of features this client library supports in [our client library SDKs feature support matrix](https://ably.com/download/sdk-feature-support-matrix).
9
9
 
10
10
  ## Supported platforms
11
11
 
data/UPDATING.md ADDED
@@ -0,0 +1,30 @@
1
+ # Upgrade / Migration Guide
2
+
3
+ ## Version 1.1.8 to 1.2.0
4
+
5
+ ### Notable Changes
6
+ This release is all about channel options. Here is the full [changelog](https://github.com/ably/ably-ruby/blob/main/CHANGELOG.md)
7
+
8
+ * Channel options were extracted into a seperate model [ChannelOptions](https://github.com/ably/ably-ruby/blob/main/lib/ably/models/channel_options.rb). However it's still backward campatible with `Hash` and you don't need to do make any adjustments to your code
9
+
10
+ * The `ChannelOptions` class now supports `:params`, `:modes` and `:cipher` as options. Previously only `:cipher` was available
11
+
12
+ * The client `:idempotent_rest_publishing` option is `true` by default. Previously `:idempotent_rest_publishing` was `false` by default.
13
+
14
+ ### Breaking Changes
15
+
16
+ * Changing channel options with `Channels#get` is now deprecated in favor of explicit options change
17
+
18
+ 1. If channel state is attached or attaching an exception will be raised
19
+ 2. Otherwise the library will emit a warning
20
+
21
+ For example, the following code
22
+ ```
23
+ client.channels.get(channel_name, new_channel_options)
24
+ ```
25
+
26
+ Should be changed to:
27
+ ```
28
+ channel = client.channels.get(channel_name)
29
+ channel.options = new_channel_options
30
+ ```
data/lib/ably/auth.rb CHANGED
@@ -510,11 +510,11 @@ module Ably
510
510
  end
511
511
 
512
512
  def authorize_when_necessary
513
- if current_token_details && !current_token_details.expired?
513
+ if current_token_details && !current_token_details.expired?(from: current_time)
514
514
  return current_token_details
515
- else
516
- authorize
517
515
  end
516
+
517
+ authorize
518
518
  end
519
519
 
520
520
  # Returns the current device clock time unless the
@@ -0,0 +1,97 @@
1
+ module Ably::Models
2
+ # Convert token details argument to a {ChannelOptions} object
3
+ #
4
+ # @param attributes (see #initialize)
5
+ #
6
+ # @return [ChannelOptions]
7
+ def self.ChannelOptions(attributes)
8
+ case attributes
9
+ when ChannelOptions
10
+ return attributes
11
+ else
12
+ ChannelOptions.new(attributes)
13
+ end
14
+ end
15
+
16
+ # Represents options of a channel
17
+ class ChannelOptions
18
+ extend Ably::Modules::Enum
19
+ extend Forwardable
20
+ include Ably::Modules::ModelCommon
21
+
22
+ MODES = ruby_enum('MODES',
23
+ presence: 0,
24
+ publish: 1,
25
+ subscribe: 2,
26
+ presence_subscribe: 3
27
+ )
28
+
29
+ attr_reader :attributes
30
+
31
+ alias_method :to_h, :attributes
32
+
33
+ def_delegators :attributes, :fetch, :size, :empty?
34
+ # Initialize a new ChannelOptions
35
+ #
36
+ # @option params [Hash] (TB2c) params (for realtime client libraries only) a of key/value pairs
37
+ # @option modes [Hash] modes (for realtime client libraries only) an array of ChannelMode
38
+ # @option cipher [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.
39
+ #
40
+ def initialize(attrs)
41
+ @attributes = IdiomaticRubyWrapper(attrs.clone)
42
+
43
+ attributes[:modes] = modes.to_a.map { |mode| Ably::Models::ChannelOptions::MODES[mode] } if modes
44
+ attributes[:cipher] = Ably::Models::CipherParams(cipher) if cipher
45
+ attributes.clone
46
+ end
47
+
48
+ # @!attribute cipher
49
+ #
50
+ # @return [CipherParams]
51
+ def cipher
52
+ attributes[:cipher]
53
+ end
54
+
55
+ # @!attribute params
56
+ #
57
+ # @return [Hash]
58
+ def params
59
+ attributes[:params].to_h
60
+ end
61
+
62
+ # @!attribute modes
63
+ #
64
+ # @return [Array<ChannelOptions::MODES>]
65
+ def modes
66
+ attributes[:modes]
67
+ end
68
+
69
+ # Converts modes to a bitfield that coresponds to ProtocolMessage#flags
70
+ #
71
+ # @return [Integer]
72
+ def modes_to_flags
73
+ modes.map { |mode| Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[mode.to_sym] }.reduce(:|)
74
+ end
75
+
76
+ # @return [Hash]
77
+ # @api private
78
+ def set_params(hash)
79
+ attributes[:params] = hash
80
+ end
81
+
82
+ # Sets modes from ProtocolMessage#flags
83
+ #
84
+ # @return [Array<ChannelOptions::MODES>]
85
+ # @api private
86
+ def set_modes_from_flags(flags)
87
+ return unless flags
88
+
89
+ message_modes = MODES.select do |mode|
90
+ flag = Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[mode.to_sym]
91
+ flags & flag == flag
92
+ end
93
+
94
+ attributes[:modes] = message_modes.map { |mode| Ably::Models::ChannelOptions::MODES[mode] }
95
+ end
96
+ end
97
+ end
@@ -94,6 +94,10 @@ module Ably::Models
94
94
  attributes.size
95
95
  end
96
96
 
97
+ def empty?
98
+ attributes.empty?
99
+ end
100
+
97
101
  def keys
98
102
  map { |key, value| key }
99
103
  end
@@ -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
@@ -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
@@ -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
@@ -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)