ably-rest 1.1.2 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +1 -1
  4. data/MAINTAINERS.md +1 -0
  5. data/README.md +4 -2
  6. data/ably-rest.gemspec +15 -18
  7. data/lib/ably-rest.rb +2 -0
  8. data/lib/submodules/ably-ruby/.github/workflows/check.yml +50 -0
  9. data/lib/submodules/ably-ruby/CHANGELOG.md +200 -0
  10. data/lib/submodules/ably-ruby/COPYRIGHT +1 -0
  11. data/lib/submodules/ably-ruby/LICENSE +172 -11
  12. data/lib/submodules/ably-ruby/MAINTAINERS.md +1 -0
  13. data/lib/submodules/ably-ruby/README.md +24 -22
  14. data/lib/submodules/ably-ruby/SPEC.md +1020 -929
  15. data/lib/submodules/ably-ruby/UPDATING.md +30 -0
  16. data/lib/submodules/ably-ruby/ably.gemspec +16 -23
  17. data/lib/submodules/ably-ruby/lib/ably/agent.rb +3 -0
  18. data/lib/submodules/ably-ruby/lib/ably/auth.rb +20 -10
  19. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +8 -2
  20. data/lib/submodules/ably-ruby/lib/ably/logger.rb +4 -4
  21. data/lib/submodules/ably-ruby/lib/ably/models/channel_details.rb +59 -0
  22. data/lib/submodules/ably-ruby/lib/ably/models/channel_metrics.rb +84 -0
  23. data/lib/submodules/ably-ruby/lib/ably/models/channel_occupancy.rb +43 -0
  24. data/lib/submodules/ably-ruby/lib/ably/models/channel_options.rb +97 -0
  25. data/lib/submodules/ably-ruby/lib/ably/models/channel_status.rb +53 -0
  26. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +8 -0
  27. data/lib/submodules/ably-ruby/lib/ably/models/delta_extras.rb +29 -0
  28. data/lib/submodules/ably-ruby/lib/ably/models/device_details.rb +1 -1
  29. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +6 -2
  30. data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  31. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +28 -3
  32. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +14 -0
  33. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +31 -14
  34. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +7 -2
  35. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +1 -1
  36. data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +11 -1
  37. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +22 -2
  38. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +34 -0
  39. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +2 -2
  40. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +19 -7
  41. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_properties.rb +24 -0
  42. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +10 -1
  43. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/publisher.rb +6 -0
  44. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +56 -28
  45. data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +1 -1
  46. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -6
  47. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +9 -0
  48. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +13 -4
  49. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +4 -0
  50. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +67 -1
  51. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +6 -5
  52. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +0 -14
  53. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +44 -29
  54. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +60 -29
  55. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +1 -1
  56. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +1 -1
  57. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +1 -1
  58. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +5 -2
  59. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +1 -1
  60. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +1 -1
  61. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +1 -1
  62. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +1 -1
  63. data/lib/submodules/ably-ruby/lib/ably/version.rb +2 -14
  64. data/lib/submodules/ably-ruby/lib/ably.rb +1 -0
  65. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +4 -4
  66. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +25 -0
  67. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +476 -21
  68. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +59 -7
  69. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +72 -16
  70. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +85 -13
  71. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +301 -34
  72. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +77 -0
  73. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +3 -59
  74. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +84 -158
  75. data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_admin_spec.rb +3 -19
  76. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +24 -75
  77. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +8 -4
  78. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +141 -10
  79. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +23 -6
  80. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +146 -47
  81. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +61 -3
  82. data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +3 -19
  83. data/lib/submodules/ably-ruby/spec/lib/unit/models/channel_options_spec.rb +52 -0
  84. data/lib/submodules/ably-ruby/spec/run_parallel_tests +2 -7
  85. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +131 -8
  86. data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +1 -1
  87. data/lib/submodules/ably-ruby/spec/spec_helper.rb +12 -2
  88. data/lib/submodules/ably-ruby/spec/support/serialization_helper.rb +21 -0
  89. data/lib/submodules/ably-ruby/spec/support/test_app.rb +3 -3
  90. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +6 -14
  91. data/lib/submodules/ably-ruby/spec/unit/models/channel_details_spec.rb +30 -0
  92. data/lib/submodules/ably-ruby/spec/unit/models/channel_metrics_spec.rb +42 -0
  93. data/lib/submodules/ably-ruby/spec/unit/models/channel_occupancy_spec.rb +17 -0
  94. data/lib/submodules/ably-ruby/spec/unit/models/channel_status_spec.rb +36 -0
  95. data/lib/submodules/ably-ruby/spec/unit/models/delta_extras_spec.rb +14 -0
  96. data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +17 -1
  97. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +97 -0
  98. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +49 -0
  99. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +125 -27
  100. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +14 -0
  101. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +3 -2
  102. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +53 -15
  103. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +19 -6
  104. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +38 -0
  105. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +44 -1
  106. data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +81 -14
  107. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +47 -0
  108. data/spec/spec_helper.rb +5 -0
  109. data/spec/unit/client_spec.rb +30 -0
  110. metadata +88 -25
  111. data/lib/submodules/ably-ruby/.travis.yml +0 -19
@@ -105,6 +105,20 @@ module Ably::Models
105
105
  end.to_json
106
106
  end
107
107
 
108
+ # The size is the sum over name, data, clientId, and extras in bytes (TO3l8a)
109
+ #
110
+ def size
111
+ %w(name data client_id extras).map do |attr|
112
+ if (value = attributes[attr.to_sym]).is_a?(String)
113
+ value.bytesize
114
+ elsif value.nil?
115
+ 0
116
+ else
117
+ value.to_json.bytesize
118
+ end
119
+ end.sum
120
+ end
121
+
108
122
  # Assign this message to a ProtocolMessage before delivery to the Ably system
109
123
  # @api private
110
124
  def assign_to_protocol_message(protocol_message)
@@ -128,6 +142,9 @@ module Ably::Models
128
142
 
129
143
  # Contains any arbitrary key value pairs which may also contain other primitive JSON types, JSON-encodable objects or JSON-encodable arrays.
130
144
  # The extras field is provided to contain message metadata and/or ancillary payloads in support of specific functionality, e.g. push
145
+ # 1.2 adds the delta extension which is of type DeltaExtras, and the headers extension, which contains arbitrary string->string key-value pairs,
146
+ # settable at publish time. Unless otherwise specified, the client library should not attempt to do any filtering or validation of the extras
147
+ # field itself, but should treat it opaquely, encoding it and passing it to realtime unaltered.
131
148
  # @api private
132
149
  def extras
133
150
  attributes[:extras].tap do |val|
@@ -137,15 +154,23 @@ module Ably::Models
137
154
  end
138
155
  end
139
156
 
140
- private
141
- def raw_hash_object
142
- @raw_hash_object
157
+ # Delta extras extension (TM2i)
158
+ # @return [DeltaExtras, nil]
159
+ # @api private
160
+ def delta_extras
161
+ return nil if attributes[:extras][:delta].nil?
162
+ @delta_extras ||= DeltaExtras.new(attributes[:extras][:delta]).freeze
143
163
  end
144
164
 
145
165
  def protocol_message_index
146
166
  protocol_message.messages.map(&:object_id).index(self.object_id)
147
167
  end
148
168
 
169
+ private
170
+ def raw_hash_object
171
+ @raw_hash_object
172
+ end
173
+
149
174
  def set_attributes_object(new_attributes)
150
175
  @attributes = IdiomaticRubyWrapper(new_attributes.clone, stop_at: [:data, :extras])
151
176
  end
@@ -125,6 +125,20 @@ module Ably::Models
125
125
  end.to_json
126
126
  end
127
127
 
128
+ # The size is the sum over data and clientId in bytes (TO3l8a)
129
+ #
130
+ def size
131
+ %w(data client_id).map do |attr|
132
+ if (value = attributes[attr.to_sym]).is_a?(String)
133
+ value.bytesize
134
+ elsif value.nil?
135
+ 0
136
+ else
137
+ value.to_json.bytesize
138
+ end
139
+ end.sum
140
+ end
141
+
128
142
  # Assign this presence message to a ProtocolMessage before delivery to the Ably system
129
143
  # @api private
130
144
  def assign_to_protocol_message(protocol_message)
@@ -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
@@ -19,8 +19,6 @@ module Ably::Models
19
19
  # @!attribute [r] channel_serial
20
20
  # @return [String] Contains a serial number for a message on the current channel
21
21
  # @!attribute [r] connection_id
22
- # @return [String] Contains a string public identifier for the connection
23
- # @!attribute [r] connection_key
24
22
  # @return [String] Contains a string private connection key used to recover this connection
25
23
  # @!attribute [r] connection_serial
26
24
  # @return [Bignum] Contains a serial number for a message sent from the server to the client
@@ -68,6 +66,14 @@ module Ably::Models
68
66
  auth: 17
69
67
  )
70
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
+
71
77
  # Indicates this protocol message action will generate an ACK response such as :message or :presence
72
78
  # @api private
73
79
  def self.ack_required?(for_action)
@@ -98,12 +104,6 @@ module Ably::Models
98
104
  end
99
105
  end
100
106
 
101
- def connection_key
102
- # connection_key in connection details takes precedence over connection_key on the ProtocolMessage
103
- # connection_key in the ProtocolMessage will be deprecated in future protocol versions > 0.8
104
- connection_details.connection_key || attributes[:connection_key]
105
- end
106
-
107
107
  def id!
108
108
  raise RuntimeError, 'ProtocolMessage #id is nil' unless id
109
109
  id
@@ -185,6 +185,18 @@ module Ably::Models
185
185
  end
186
186
  end
187
187
 
188
+ def message_size
189
+ presence.map(&:size).sum + messages.map(&:size).sum
190
+ end
191
+
192
+ def has_correct_message_size?
193
+ message_size <= connection_details.max_message_size
194
+ end
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
@@ -216,24 +228,29 @@ module Ably::Models
216
228
  flags & 16 == 16 # 2^4
217
229
  end
218
230
 
231
+ # @api private
232
+ def has_attach_resume_flag?
233
+ flags & ATTACH_FLAGS_MAPPING[:resume] == ATTACH_FLAGS_MAPPING[:resume] # 2^5
234
+ end
235
+
219
236
  # @api private
220
237
  def has_attach_presence_flag?
221
- flags & 65536 == 65536 # 2^16
238
+ flags & ATTACH_FLAGS_MAPPING[:presence] == ATTACH_FLAGS_MAPPING[:presence] # 2^16
222
239
  end
223
240
 
224
241
  # @api private
225
242
  def has_attach_publish_flag?
226
- flags & 131072 == 131072 # 2^17
243
+ flags & ATTACH_FLAGS_MAPPING[:publish] == ATTACH_FLAGS_MAPPING[:publish] # 2^17
227
244
  end
228
245
 
229
246
  # @api private
230
247
  def has_attach_subscribe_flag?
231
- flags & 262144 == 262144 # 2^18
248
+ flags & ATTACH_FLAGS_MAPPING[:subscribe] == ATTACH_FLAGS_MAPPING[:subscribe] # 2^18
232
249
  end
233
250
 
234
251
  # @api private
235
252
  def has_attach_presence_subscribe_flag?
236
- flags & 524288 == 524288 # 2^19
253
+ flags & ATTACH_FLAGS_MAPPING[:presence_subscribe] == ATTACH_FLAGS_MAPPING[:presence_subscribe] # 2^19
237
254
  end
238
255
 
239
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,8 +6,18 @@
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://ably.com/docs/client-lib-development-guide/features/#RSC15a
9
10
  #
10
- FALLBACK_HOSTS = %w(A.ably-realtime.com B.ably-realtime.com C.ably-realtime.com D.ably-realtime.com E.ably-realtime.com).freeze
11
+ FALLBACK_DOMAIN = 'ably-realtime.com'.freeze
12
+ FALLBACK_IDS = %w(a b c d e).freeze
13
+
14
+ # Default production fallbacks a.ably-realtime.com ... e.ably-realtime.com
15
+ FALLBACK_HOSTS = FALLBACK_IDS.map { |host| "#{host}.#{FALLBACK_DOMAIN}".freeze }.freeze
16
+
17
+ # Custom environment default fallbacks {ENV}-a-fallback.ably-realtime.com ... {ENV}-a-fallback.ably-realtime.com
18
+ CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES = FALLBACK_IDS.map do |host|
19
+ "-#{host}-fallback.#{FALLBACK_DOMAIN}".freeze
20
+ end.freeze
11
21
 
12
22
  INTERNET_CHECK = {
13
23
  url: '//internet-up.ably-realtime.com/is-the-internet-up.txt',
@@ -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)
@@ -37,7 +37,9 @@ module Ably::Realtime
37
37
  # library, such as returning to attached whne detach has failed
38
38
  if attached_protocol_message
39
39
  update_presence_sync_state_following_attached attached_protocol_message
40
- channel.set_attached_serial attached_protocol_message.channel_serial
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
 
@@ -63,6 +65,9 @@ module Ably::Realtime
63
65
  log_channel_error protocol_message.error
64
66
  end
65
67
 
68
+ channel.properties.set_attach_serial(protocol_message.channel_serial)
69
+ channel.options.set_modes_from_flags(protocol_message.flags)
70
+
66
71
  if protocol_message.has_channel_resumed_flag?
67
72
  logger.debug { "ChannelManager: Additional resumed ATTACHED message received for #{channel.state} channel '#{channel.name}'" }
68
73
  else
@@ -75,8 +80,6 @@ module Ably::Realtime
75
80
  )
76
81
  update_presence_sync_state_following_attached protocol_message
77
82
  end
78
-
79
- channel.set_attached_serial protocol_message.channel_serial
80
83
  end
81
84
 
82
85
  # Handle DETACED messages, see #RTL13 for server-initated detaches
@@ -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
 
@@ -0,0 +1,24 @@
1
+ module Ably::Realtime
2
+ class Channel
3
+ # Represents properties of a channel and its state
4
+ class ChannelProperties
5
+ # {Ably::Realtime::Channel} this object associated with
6
+ # @return [Ably::Realtime::Channel]
7
+ attr_reader :channel
8
+
9
+ # Contains the last channelSerial received in an ATTACHED ProtocolMesage for the channel, see RTL15a
10
+ #
11
+ # @return [String]
12
+ attr_reader :attach_serial
13
+
14
+ def initialize(channel)
15
+ @channel = channel
16
+ end
17
+
18
+ # @api private
19
+ def set_attach_serial(attach_serial)
20
+ @attach_serial = attach_serial
21
+ end
22
+ end
23
+ end
24
+ end
@@ -26,18 +26,27 @@ module Ably::Realtime
26
26
  transition :from => :detaching, :to => [:detached, :attaching, :attached, :failed, :suspended]
27
27
  transition :from => :detached, :to => [:attaching, :attached, :failed]
28
28
  transition :from => :suspended, :to => [:attaching, :attached, :detached, :failed]
29
- transition :from => :failed, :to => [:attaching]
29
+ transition :from => :failed, :to => [:attaching, :initialized]
30
30
 
31
31
  after_transition do |channel, transition|
32
32
  channel.synchronize_state_with_statemachine
33
33
  end
34
34
 
35
+ after_transition(to: [:initialized]) do |channel|
36
+ channel.clear_error_reason
37
+ end
38
+
35
39
  after_transition(to: [:attaching]) do |channel|
36
40
  channel.manager.attach
37
41
  end
38
42
 
39
43
  before_transition(to: [:attached]) do |channel, current_transition|
40
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!
41
50
  end
42
51
 
43
52
  after_transition(to: [:detaching]) do |channel, current_transition|
@@ -22,6 +22,12 @@ module Ably::Realtime
22
22
  end
23
23
  end
24
24
 
25
+ max_message_size = connection.details && connection.details.max_message_size || Ably::Models::ConnectionDetails::MAX_MESSAGE_SIZE
26
+ if messages.sum(&:size) > max_message_size
27
+ error = Ably::Exceptions::MaxMessageSizeExceeded.new("Message size exceeded #{max_message_size} bytes.")
28
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
29
+ end
30
+
25
31
  connection.send_protocol_message(
26
32
  action: Ably::Models::ProtocolMessage::ACTION.Message.to_i,
27
33
  channel: channel_name,
@@ -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
@@ -79,6 +80,10 @@ module Ably
79
80
  # @return [Hash]
80
81
  attr_reader :options
81
82
 
83
+ # Properties of a channel and its state
84
+ # @return [{Ably::Realtime::Channel::ChannelProperties}]
85
+ attr_reader :properties
86
+
82
87
  # When a channel failure occurs this attribute contains the Ably Exception
83
88
  # @return [Ably::Models::ErrorInfo,Ably::Exceptions::BaseAblyException]
84
89
  attr_reader :error_reason
@@ -88,22 +93,25 @@ module Ably
88
93
  # @api private
89
94
  attr_reader :manager
90
95
 
91
- # Serial number assigned to this channel when it was attached
92
- # @return [Integer]
96
+ # Flag that specifies whether channel is resuming attachment(reattach) or is doing a 'clean attach' RTL4j1
97
+ # @return [Bolean]
93
98
  # @api private
94
- attr_reader :attached_serial
99
+ attr_reader :attach_resume
100
+
101
+ # ChannelOptions params attrribute (#RTL4k)
102
+ # return [Hash]
103
+ def_delegators :options, :params
95
104
 
96
105
  # Initialize a new Channel object
97
106
  #
98
107
  # @param client [Ably::Rest::Client]
99
108
  # @param name [String] The name of the channel
100
- # @param channel_options [Hash] Channel options, currently reserved for Encryption options
101
- # @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}
102
110
  #
103
111
  def initialize(client, name, channel_options = {})
104
112
  name = ensure_utf_8(:name, name)
105
113
 
106
- update_options channel_options
114
+ @options = Ably::Models::ChannelOptions(channel_options)
107
115
  @client = client
108
116
  @name = name
109
117
  @queue = []
@@ -112,6 +120,8 @@ module Ably
112
120
  @state = STATE(state_machine.current_state)
113
121
  @manager = ChannelManager.new(self, client.connection)
114
122
  @push = PushChannel.new(self)
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.")
@@ -292,7 +304,7 @@ module Ably
292
304
  error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached' )
293
305
  return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
294
306
  end
295
- options[:from_serial] = attached_serial
307
+ options[:from_serial] = properties.attach_serial
296
308
  end
297
309
 
298
310
  async_wrap(callback) do
@@ -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,27 +341,32 @@ module Ably
319
341
  @error_reason = nil
320
342
  end
321
343
 
344
+ # Used by {Ably::Modules::StateEmitter} to debug state changes
322
345
  # @api private
323
- def set_attached_serial(serial)
324
- @attached_serial = serial
346
+ def logger
347
+ client.logger
325
348
  end
326
349
 
327
350
  # @api private
328
- def update_options(channel_options)
329
- @options = channel_options.clone.freeze
351
+ def attach_resume!
352
+ @attach_resume = true
330
353
  end
331
354
 
332
- # Used by {Ably::Modules::StateEmitter} to debug state changes
333
355
  # @api private
334
- def logger
335
- client.logger
356
+ def reset_attach_resume!
357
+ @attach_resume = false
336
358
  end
337
359
 
338
360
  # As we are using a state machine, do not allow change_state to be used
339
361
  # #transition_state_machine must be used instead
340
362
  private :change_state
341
363
 
364
+ def need_reattach?
365
+ !!(attaching? || attached?) && !!(options.modes || options.params)
366
+ end
367
+
342
368
  private
369
+
343
370
  def setup_event_handlers
344
371
  __incoming_msgbus__.subscribe(:message) do |message|
345
372
  message.decode(client.encoders, options) do |encode_error, error_message|
@@ -372,3 +399,4 @@ end
372
399
  require 'ably/realtime/channel/channel_manager'
373
400
  require 'ably/realtime/channel/channel_state_machine'
374
401
  require 'ably/realtime/channel/push_channel'
402
+ require 'ably/realtime/channel/channel_properties'
@@ -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)