ably-pubsub-core 0.0.1.pre
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.
- checksums.yaml +7 -0
- data/lib/ably/agent.rb +8 -0
- data/lib/ably/auth.rb +791 -0
- data/lib/ably/exceptions.rb +169 -0
- data/lib/ably/logger.rb +119 -0
- data/lib/ably/models/auth_details.rb +45 -0
- data/lib/ably/models/channel_details.rb +69 -0
- data/lib/ably/models/channel_metrics.rb +107 -0
- data/lib/ably/models/channel_occupancy.rb +50 -0
- data/lib/ably/models/channel_options.rb +120 -0
- data/lib/ably/models/channel_state_change.rb +84 -0
- data/lib/ably/models/channel_status.rb +63 -0
- data/lib/ably/models/cipher_params.rb +137 -0
- data/lib/ably/models/connection_details.rb +149 -0
- data/lib/ably/models/connection_state_change.rb +83 -0
- data/lib/ably/models/delta_extras.rb +28 -0
- data/lib/ably/models/device_details.rb +126 -0
- data/lib/ably/models/device_push_details.rb +94 -0
- data/lib/ably/models/error_info.rb +104 -0
- data/lib/ably/models/http_paginated_response.rb +112 -0
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +235 -0
- data/lib/ably/models/message.rb +223 -0
- data/lib/ably/models/message_encoders/base.rb +135 -0
- data/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/ably/models/message_encoders/cipher.rb +84 -0
- data/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_result.rb +201 -0
- data/lib/ably/models/presence_message.rb +244 -0
- data/lib/ably/models/protocol_message.rb +288 -0
- data/lib/ably/models/push_channel_subscription.rb +114 -0
- data/lib/ably/models/stats.rb +242 -0
- data/lib/ably/models/stats_types.rb +107 -0
- data/lib/ably/models/token_details.rb +148 -0
- data/lib/ably/models/token_request.rb +180 -0
- data/lib/ably/modules/ably.rb +26 -0
- data/lib/ably/modules/async_wrapper.rb +69 -0
- data/lib/ably/modules/channels_collection.rb +105 -0
- data/lib/ably/modules/conversions.rb +153 -0
- data/lib/ably/modules/encodeable.rb +111 -0
- data/lib/ably/modules/enum.rb +230 -0
- data/lib/ably/modules/event_emitter.rb +200 -0
- data/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/ably/modules/message_emitter.rb +78 -0
- data/lib/ably/modules/message_pack.rb +14 -0
- data/lib/ably/modules/model_common.rb +84 -0
- data/lib/ably/modules/safe_deferrable.rb +83 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +181 -0
- data/lib/ably/modules/state_machine.rb +59 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +111 -0
- data/lib/ably/realtime/auth.rb +283 -0
- data/lib/ably/realtime/channel/channel_manager.rb +272 -0
- data/lib/ably/realtime/channel/channel_properties.rb +40 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +104 -0
- data/lib/ably/realtime/channel/publisher.rb +82 -0
- data/lib/ably/realtime/channel/push_channel.rb +69 -0
- data/lib/ably/realtime/channel.rb +440 -0
- data/lib/ably/realtime/channels.rb +71 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +225 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +87 -0
- data/lib/ably/realtime/client.rb +363 -0
- data/lib/ably/realtime/connection/connection_manager.rb +594 -0
- data/lib/ably/realtime/connection/connection_state_machine.rb +144 -0
- data/lib/ably/realtime/connection/websocket_transport.rb +287 -0
- data/lib/ably/realtime/connection.rb +738 -0
- data/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/ably/realtime/presence/members_map.rb +395 -0
- data/lib/ably/realtime/presence/presence_manager.rb +78 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +57 -0
- data/lib/ably/realtime/presence.rb +502 -0
- data/lib/ably/realtime/push/admin.rb +75 -0
- data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/ably/realtime/push.rb +25 -0
- data/lib/ably/realtime/recovery_key_context.rb +36 -0
- data/lib/ably/realtime.rb +60 -0
- data/lib/ably/rest/channel/push_channel.rb +59 -0
- data/lib/ably/rest/channel.rb +194 -0
- data/lib/ably/rest/channels.rb +47 -0
- data/lib/ably/rest/client.rb +727 -0
- data/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/ably/rest/middleware/exceptions.rb +53 -0
- data/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +22 -0
- data/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/ably/rest/middleware/parse_message_pack.rb +43 -0
- data/lib/ably/rest/presence.rb +113 -0
- data/lib/ably/rest/push/admin.rb +58 -0
- data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/ably/rest/push/device_registrations.rb +114 -0
- data/lib/ably/rest/push.rb +25 -0
- data/lib/ably/rest.rb +40 -0
- data/lib/ably/util/ably_extensions.rb +29 -0
- data/lib/ably/util/crypto.rb +134 -0
- data/lib/ably/util/pub_sub.rb +45 -0
- data/lib/ably/util/safe_deferrable.rb +44 -0
- data/lib/ably/version.rb +7 -0
- data/lib/ably.rb +16 -0
- metadata +497 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
module Ably::Models
|
|
2
|
+
# A message sent and received over the Realtime protocol.
|
|
3
|
+
# A ProtocolMessage always relates to a single channel only, but
|
|
4
|
+
# can contain multiple individual Messages or PresenceMessages.
|
|
5
|
+
# ProtocolMessages are serially numbered on a connection.
|
|
6
|
+
# See the {http://ably.com/docs/client-lib-development-guide/protocol/ Ably client library developer documentation}
|
|
7
|
+
# for further details on the members of a ProtocolMessage
|
|
8
|
+
#
|
|
9
|
+
# @!attribute [r] action
|
|
10
|
+
# @return [ACTION] Protocol Message action {Ably::Modules::Enum} from list of {ACTION}. Returns nil if action is unsupported by protocol
|
|
11
|
+
# @!attribute [r] auth
|
|
12
|
+
# @return [Ably::Models::AuthDetails] Authentication details used to perform authentication upgrades over an existing transport
|
|
13
|
+
# @!attribute [r] count
|
|
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
|
+
# @!attribute [r] error
|
|
16
|
+
# @return [ErrorInfo] Contains error information
|
|
17
|
+
# @!attribute [r] channel
|
|
18
|
+
# @return [String] Channel name for messages
|
|
19
|
+
# @!attribute [r] channel_serial
|
|
20
|
+
# @return [String] Contains a serial number for a message on the current channel
|
|
21
|
+
# @!attribute [r] connection_id
|
|
22
|
+
# @return [String] Contains a string private connection key used to recover this connection
|
|
23
|
+
# @!attribute [r] message_serial
|
|
24
|
+
# @return [Bignum] Contains a serial number for a message sent from the client to the server
|
|
25
|
+
# @!attribute [r] timestamp
|
|
26
|
+
# @return [Time] An optional timestamp, applied by the service in messages sent to the client, to indicate the system time at which the message was sent (milliseconds past epoch)
|
|
27
|
+
# @!attribute [r] messages
|
|
28
|
+
# @return [Array<Message>] A {ProtocolMessage} with a `:message` action contains one or more messages belonging to the channel
|
|
29
|
+
# @!attribute [r] presence
|
|
30
|
+
# @return [Array<PresenceMessage>] A {ProtocolMessage} with a `:presence` action contains one or more presence updates belonging to the channel
|
|
31
|
+
# @!attribute [r] flags
|
|
32
|
+
# @return [Integer] Flags indicating special ProtocolMessage states
|
|
33
|
+
# @!attribute [r] attributes
|
|
34
|
+
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
|
35
|
+
#
|
|
36
|
+
class ProtocolMessage
|
|
37
|
+
include Ably::Modules::ModelCommon
|
|
38
|
+
include Ably::Modules::Encodeable
|
|
39
|
+
include Ably::Modules::SafeDeferrable if defined?(Ably::Realtime)
|
|
40
|
+
extend Ably::Modules::Enum
|
|
41
|
+
|
|
42
|
+
# Actions which are sent by the Ably Realtime API
|
|
43
|
+
#
|
|
44
|
+
# The values correspond to the ints which the API
|
|
45
|
+
# understands.
|
|
46
|
+
ACTION = ruby_enum('ACTION',
|
|
47
|
+
heartbeat: 0,
|
|
48
|
+
ack: 1,
|
|
49
|
+
nack: 2,
|
|
50
|
+
connect: 3,
|
|
51
|
+
connected: 4,
|
|
52
|
+
disconnect: 5,
|
|
53
|
+
disconnected: 6,
|
|
54
|
+
close: 7,
|
|
55
|
+
closed: 8,
|
|
56
|
+
error: 9,
|
|
57
|
+
attach: 10,
|
|
58
|
+
attached: 11,
|
|
59
|
+
detach: 12,
|
|
60
|
+
detached: 13,
|
|
61
|
+
presence: 14,
|
|
62
|
+
message: 15,
|
|
63
|
+
sync: 16,
|
|
64
|
+
auth: 17
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
ATTACH_FLAGS_MAPPING = {
|
|
68
|
+
resume: 32, # 2^5
|
|
69
|
+
presence: 65536, # 2^16
|
|
70
|
+
publish: 131072, # 2^17
|
|
71
|
+
subscribe: 262144, # 2^18
|
|
72
|
+
presence_subscribe: 524288, # 2^19
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Indicates this protocol message action will generate an ACK response such as :message or :presence
|
|
76
|
+
# @api private
|
|
77
|
+
def self.ack_required?(for_action)
|
|
78
|
+
ACTION(for_action).match_any?(ACTION.Presence, ACTION.Message)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# {ProtocolMessage} initializer
|
|
82
|
+
#
|
|
83
|
+
# @param hash_object [Hash] object with the underlying protocol message data
|
|
84
|
+
# @param [Hash] options an options Hash for this initializer
|
|
85
|
+
# @option options [Logger] :logger An optional Logger to be used by {Ably::Modules::SafeDeferrable} if an exception is caught in a callback
|
|
86
|
+
#
|
|
87
|
+
def initialize(hash_object, options = {})
|
|
88
|
+
@logger = options[:logger] # Logger expected for SafeDeferrable
|
|
89
|
+
|
|
90
|
+
@raw_hash_object = hash_object
|
|
91
|
+
@hash_object = IdiomaticRubyWrapper(@raw_hash_object.clone)
|
|
92
|
+
|
|
93
|
+
raise ArgumentError, 'Invalid ProtocolMessage, action cannot be nil' if @hash_object[:action].nil?
|
|
94
|
+
@hash_object[:action] = ACTION(@hash_object[:action]).to_i unless @hash_object[:action].kind_of?(Integer)
|
|
95
|
+
|
|
96
|
+
@hash_object.freeze
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
%w(id channel channel_serial connection_id).each do |attribute|
|
|
100
|
+
define_method attribute do
|
|
101
|
+
attributes[attribute.to_sym]
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def id!
|
|
106
|
+
raise RuntimeError, 'ProtocolMessage #id is nil' unless id
|
|
107
|
+
id
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def action
|
|
111
|
+
ACTION(attributes[:action])
|
|
112
|
+
rescue KeyError
|
|
113
|
+
raise KeyError, "Action '#{attributes[:action]}' is not supported by ProtocolMessage"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def error
|
|
117
|
+
@error ||= ErrorInfo.new(attributes[:error]) if attributes[:error]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def timestamp
|
|
121
|
+
as_time_from_epoch(attributes[:timestamp]) if attributes[:timestamp]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def message_serial
|
|
125
|
+
Integer(attributes[:msg_serial])
|
|
126
|
+
rescue TypeError
|
|
127
|
+
raise TypeError, "msg_serial '#{attributes[:msg_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def count
|
|
131
|
+
[1, attributes[:count].to_i].max
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @api private
|
|
135
|
+
def has_message_serial?
|
|
136
|
+
message_serial && true
|
|
137
|
+
rescue TypeError
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def has_channel_serial?
|
|
142
|
+
channel_serial && true
|
|
143
|
+
rescue TypeError
|
|
144
|
+
false
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def messages
|
|
148
|
+
@messages ||=
|
|
149
|
+
Array(attributes[:messages]).map do |message|
|
|
150
|
+
Ably::Models.Message(message, protocol_message: self)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# @api private
|
|
155
|
+
def add_message(message)
|
|
156
|
+
messages << message
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def presence
|
|
160
|
+
@presence ||=
|
|
161
|
+
Array(attributes[:presence]).map do |message|
|
|
162
|
+
Ably::Models.PresenceMessage(message, protocol_message: self)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def message_size
|
|
167
|
+
presence.map(&:size).sum + messages.map(&:size).sum
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def params
|
|
171
|
+
@params ||= attributes[:params].to_h
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def flags
|
|
175
|
+
Integer(attributes[:flags])
|
|
176
|
+
rescue TypeError
|
|
177
|
+
0
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @api private
|
|
181
|
+
def has_presence_flag?
|
|
182
|
+
flags & 1 == 1
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @api private
|
|
186
|
+
def has_backlog_flag?
|
|
187
|
+
flags & 2 == 2 # 2^1
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @api private
|
|
191
|
+
def has_channel_resumed_flag?
|
|
192
|
+
flags & 4 == 4 # 2^2
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @api private
|
|
196
|
+
def has_local_presence_flag?
|
|
197
|
+
flags & 8 == 8 # 2^3
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# @api private
|
|
201
|
+
def has_transient_flag?
|
|
202
|
+
flags & 16 == 16 # 2^4
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# @api private
|
|
206
|
+
def has_attach_resume_flag?
|
|
207
|
+
flags & ATTACH_FLAGS_MAPPING[:resume] == ATTACH_FLAGS_MAPPING[:resume] # 2^5
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# @api private
|
|
211
|
+
def has_attach_presence_flag?
|
|
212
|
+
flags & ATTACH_FLAGS_MAPPING[:presence] == ATTACH_FLAGS_MAPPING[:presence] # 2^16
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# @api private
|
|
216
|
+
def has_attach_publish_flag?
|
|
217
|
+
flags & ATTACH_FLAGS_MAPPING[:publish] == ATTACH_FLAGS_MAPPING[:publish] # 2^17
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# @api private
|
|
221
|
+
def has_attach_subscribe_flag?
|
|
222
|
+
flags & ATTACH_FLAGS_MAPPING[:subscribe] == ATTACH_FLAGS_MAPPING[:subscribe] # 2^18
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# @api private
|
|
226
|
+
def has_attach_presence_subscribe_flag?
|
|
227
|
+
flags & ATTACH_FLAGS_MAPPING[:presence_subscribe] == ATTACH_FLAGS_MAPPING[:presence_subscribe] # 2^19
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def connection_details
|
|
231
|
+
@connection_details ||= Ably::Models::ConnectionDetails(attributes[:connection_details])
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def auth
|
|
235
|
+
@auth ||= Ably::Models::AuthDetails(attributes[:auth])
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Indicates this protocol message will generate an ACK response when sent
|
|
239
|
+
# Examples of protocol messages required ACK include :message and :presence
|
|
240
|
+
# @api private
|
|
241
|
+
def ack_required?
|
|
242
|
+
self.class.ack_required?(action)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def attributes
|
|
246
|
+
@hash_object
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Return a JSON ready object from the underlying #attributes using Ably naming conventions for keys
|
|
250
|
+
def as_json(*args)
|
|
251
|
+
raise TypeError, ':action is missing, cannot generate a valid Hash for ProtocolMessage' unless action
|
|
252
|
+
|
|
253
|
+
attributes.dup.tap do |hash_object|
|
|
254
|
+
hash_object['action'] = action.to_i
|
|
255
|
+
hash_object['messages'] = messages.map(&:as_json) unless messages.empty?
|
|
256
|
+
hash_object['presence'] = presence.map(&:as_json) unless presence.empty?
|
|
257
|
+
end.as_json
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def to_s
|
|
261
|
+
json_hash = as_json
|
|
262
|
+
|
|
263
|
+
# Decode any binary data to before converting to a JSON string representation
|
|
264
|
+
%w(messages presence).each do |message_type|
|
|
265
|
+
if json_hash[message_type] && !json_hash[message_type].empty?
|
|
266
|
+
json_hash[message_type].each do |message|
|
|
267
|
+
decode_binary_data_before_to_json message
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
json_hash.to_json
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# True if the ProtocolMessage appears to be invalid, however this is not a guarantee
|
|
276
|
+
# Used for validating incoming protocol messages, so no need to add unnecessary checks
|
|
277
|
+
# @return [Boolean]
|
|
278
|
+
# @api private
|
|
279
|
+
def invalid?
|
|
280
|
+
action_enum = action rescue nil
|
|
281
|
+
!action_enum
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# @!attribute [r] logger
|
|
285
|
+
# @api private
|
|
286
|
+
attr_reader :logger
|
|
287
|
+
end
|
|
288
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module Ably::Modules
|
|
2
|
+
module Conversions
|
|
3
|
+
private
|
|
4
|
+
# Convert push_channel_subscription argument to a {Ably::Models::PushChannelSubscription} object
|
|
5
|
+
#
|
|
6
|
+
# @param push_channel_subscription [Ably::Models::PushChannelSubscription,Hash,nil] A device details object
|
|
7
|
+
#
|
|
8
|
+
# @return [Ably::Models::PushChannelSubscription]
|
|
9
|
+
#
|
|
10
|
+
def PushChannelSubscription(push_channel_subscription)
|
|
11
|
+
case push_channel_subscription
|
|
12
|
+
when Ably::Models::PushChannelSubscription
|
|
13
|
+
push_channel_subscription
|
|
14
|
+
else
|
|
15
|
+
Ably::Models::PushChannelSubscription.new(push_channel_subscription)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Ably::Models
|
|
22
|
+
# Contains the subscriptions of a device, or a group of devices sharing the same clientId,
|
|
23
|
+
# has to a channel in order to receive push notifications.
|
|
24
|
+
#
|
|
25
|
+
class PushChannelSubscription < Ably::Exceptions::BaseAblyException
|
|
26
|
+
include Ably::Modules::ModelCommon
|
|
27
|
+
|
|
28
|
+
# @param hash_object [Hash,nil] Device detail attributes
|
|
29
|
+
#
|
|
30
|
+
def initialize(hash_object = {})
|
|
31
|
+
@raw_hash_object = hash_object || {}
|
|
32
|
+
@hash_object = IdiomaticRubyWrapper(hash_object)
|
|
33
|
+
|
|
34
|
+
if !attributes[:client_id] && !attributes[:device_id]
|
|
35
|
+
raise ArgumentError, 'Either client_id or device_id must be provided'
|
|
36
|
+
end
|
|
37
|
+
if attributes[:client_id] && attributes[:device_id]
|
|
38
|
+
raise ArgumentError, 'client_id and device_id cannot both be provided, they are mutually exclusive'
|
|
39
|
+
end
|
|
40
|
+
if !attributes[:channel]
|
|
41
|
+
raise ArgumentError, 'channel is required'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# A static factory method to create a PushChannelSubscription object for a channel and single device.
|
|
46
|
+
#
|
|
47
|
+
# @spec PSC5
|
|
48
|
+
#
|
|
49
|
+
# @param channel [String] the realtime pub/sub channel this subscription is registered to
|
|
50
|
+
# @param device_id [String] Unique device identifier assigned to the push device
|
|
51
|
+
#
|
|
52
|
+
# @return [PushChannelSubscription]
|
|
53
|
+
#
|
|
54
|
+
def self.for_device(channel, device_id)
|
|
55
|
+
PushChannelSubscription.new(channel: channel, device_id: device_id)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# A static factory method to create a PushChannelSubscription object for a channel and group of devices sharing the same clientId.
|
|
59
|
+
#
|
|
60
|
+
# @spec PSC5
|
|
61
|
+
#
|
|
62
|
+
# @param channel [String] the realtime pub/sub channel this subscription is registered to
|
|
63
|
+
# @param client_id [String] Client ID that is assigned to one or more registered push devices
|
|
64
|
+
#
|
|
65
|
+
# @return [PushChannelSubscription]
|
|
66
|
+
#
|
|
67
|
+
def self.for_client_id(channel, client_id)
|
|
68
|
+
PushChannelSubscription.new(channel: channel, client_id: client_id)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The channel the push notification subscription is for.
|
|
72
|
+
#
|
|
73
|
+
# @spec PCS4
|
|
74
|
+
#
|
|
75
|
+
# @return [String]
|
|
76
|
+
#
|
|
77
|
+
def channel
|
|
78
|
+
attributes[:channel]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The ID of the client the device, or devices are associated to.
|
|
82
|
+
#
|
|
83
|
+
# @spec PCS3, PCS6
|
|
84
|
+
#
|
|
85
|
+
# @return [String]
|
|
86
|
+
#
|
|
87
|
+
def client_id
|
|
88
|
+
attributes[:client_id]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The unique ID of the device.
|
|
92
|
+
#
|
|
93
|
+
# @spec PCS2, PCS5, PCS6
|
|
94
|
+
#
|
|
95
|
+
# @return [String]
|
|
96
|
+
#
|
|
97
|
+
def device_id
|
|
98
|
+
attributes[:device_id]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
%w(channel client_id device_id).each do |attribute|
|
|
102
|
+
define_method "#{attribute}=" do |val|
|
|
103
|
+
unless val.nil? || val.kind_of?(String)
|
|
104
|
+
raise ArgumentError, "#{attribute} must be nil or a string value"
|
|
105
|
+
end
|
|
106
|
+
attributes[attribute.to_sym] = val
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def attributes
|
|
111
|
+
@hash_object
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
require 'ably/models/stats_types'
|
|
2
|
+
|
|
3
|
+
module Ably::Models
|
|
4
|
+
# Convert stat argument to a {Stats} object
|
|
5
|
+
#
|
|
6
|
+
# @param stat [Stats,Hash] A Stats object or Hash of stat properties
|
|
7
|
+
#
|
|
8
|
+
# @return [Stats]
|
|
9
|
+
def self.Stats(stat)
|
|
10
|
+
case stat
|
|
11
|
+
when Stats
|
|
12
|
+
stat
|
|
13
|
+
else
|
|
14
|
+
Stats.new(stat)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# A class representing an individual statistic for a specified {#interval_id}
|
|
19
|
+
#
|
|
20
|
+
class Stats
|
|
21
|
+
include Ably::Modules::ModelCommon
|
|
22
|
+
extend Ably::Modules::Enum
|
|
23
|
+
|
|
24
|
+
# Describes the interval unit over which statistics are gathered.
|
|
25
|
+
#
|
|
26
|
+
# MINUTE Interval unit over which statistics are gathered as minutes.
|
|
27
|
+
# HOUR Interval unit over which statistics are gathered as hours.
|
|
28
|
+
# DAY Interval unit over which statistics are gathered as days.
|
|
29
|
+
# MONTH Interval unit over which statistics are gathered as months.
|
|
30
|
+
#
|
|
31
|
+
GRANULARITY = ruby_enum('GRANULARITY',
|
|
32
|
+
:minute,
|
|
33
|
+
:hour,
|
|
34
|
+
:day,
|
|
35
|
+
:month
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
INTERVAL_FORMAT_STRING = [
|
|
39
|
+
'%Y-%m-%d:%H:%M',
|
|
40
|
+
'%Y-%m-%d:%H',
|
|
41
|
+
'%Y-%m-%d',
|
|
42
|
+
'%Y-%m'
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
# Convert a Time with the specified Granularity into an interval ID based on UTC 0 time
|
|
47
|
+
# @example
|
|
48
|
+
# Stats.to_interval_id(Time.now, :hour) # => '2015-01-01:10'
|
|
49
|
+
#
|
|
50
|
+
# @param time [Time] Time used to determine the interval
|
|
51
|
+
# @param granularity [GRANULARITY] Granularity of the metrics such as :hour, :day
|
|
52
|
+
#
|
|
53
|
+
# @return [String] interval ID used for stats
|
|
54
|
+
#
|
|
55
|
+
def to_interval_id(time, granularity)
|
|
56
|
+
raise ArgumentError, 'Time object required as first argument' unless time.kind_of?(Time)
|
|
57
|
+
|
|
58
|
+
granularity = GRANULARITY(granularity)
|
|
59
|
+
format = INTERVAL_FORMAT_STRING[granularity.to_i]
|
|
60
|
+
|
|
61
|
+
time.utc.strftime(format)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns the UTC 0 start Time of an interval_id
|
|
65
|
+
# @example
|
|
66
|
+
# Stats.from_interval_id('2015-01-01:10') # => 2015-01-01 10:00:00 +0000
|
|
67
|
+
#
|
|
68
|
+
# @param interval_id [String]
|
|
69
|
+
#
|
|
70
|
+
# @return [Time] start time of the provided interval_id
|
|
71
|
+
#
|
|
72
|
+
def from_interval_id(interval_id)
|
|
73
|
+
raise ArgumentError, 'Interval ID must be a string' unless interval_id.kind_of?(String)
|
|
74
|
+
|
|
75
|
+
format = INTERVAL_FORMAT_STRING.find { |fmt| expected_length(fmt) == interval_id.length }
|
|
76
|
+
raise ArgumentError, 'Interval ID is an invalid length' unless format
|
|
77
|
+
|
|
78
|
+
Time.strptime("#{interval_id} +0000", "#{format} %z").utc
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns the {Symbol} determined from the interval_id
|
|
82
|
+
# @example
|
|
83
|
+
# Stats.granularity_from_interval_id('2015-01-01:10') # => :hour
|
|
84
|
+
#
|
|
85
|
+
# @param interval_id [String]
|
|
86
|
+
#
|
|
87
|
+
# @return [Symbol]
|
|
88
|
+
#
|
|
89
|
+
def granularity_from_interval_id(interval_id)
|
|
90
|
+
raise ArgumentError, 'Interval ID must be a string' unless interval_id.kind_of?(String)
|
|
91
|
+
|
|
92
|
+
format = INTERVAL_FORMAT_STRING.find { |fmt| expected_length(fmt) == interval_id.length }
|
|
93
|
+
raise ArgumentError, 'Interval ID is an invalid length' unless format
|
|
94
|
+
|
|
95
|
+
GRANULARITY[INTERVAL_FORMAT_STRING.index(format)]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
def expected_length(format)
|
|
100
|
+
format.gsub('%Y', 'YYYY').length
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# {Stats} initializer
|
|
105
|
+
#
|
|
106
|
+
# @param hash_object [Hash] object with the underlying stat details
|
|
107
|
+
#
|
|
108
|
+
def initialize(hash_object)
|
|
109
|
+
@raw_hash_object = hash_object
|
|
110
|
+
set_attributes_object hash_object
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# A {Ably::Models::Stats::MessageTypes} object containing the aggregate count of all message stats.
|
|
114
|
+
#
|
|
115
|
+
# @spec TS12e
|
|
116
|
+
#
|
|
117
|
+
# @return [Stats::MessageTypes]
|
|
118
|
+
#
|
|
119
|
+
def all
|
|
120
|
+
@all ||= Stats::MessageTypes.new(attributes[:all])
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# A {Ably::Models::Stats::MessageTraffic} object containing the aggregate count of inbound message stats.
|
|
124
|
+
#
|
|
125
|
+
# @spec TS12f
|
|
126
|
+
#
|
|
127
|
+
# @return [Ably::Models::Stats::MessageTraffic]
|
|
128
|
+
#
|
|
129
|
+
def inbound
|
|
130
|
+
@inbound ||= Stats::MessageTraffic.new(attributes[:inbound])
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# A {Ably::Models::Stats::MessageTraffic} object containing the aggregate count of outbound message stats.
|
|
134
|
+
#
|
|
135
|
+
# @spec TS12g
|
|
136
|
+
#
|
|
137
|
+
# @return [Ably::Models::Stats::MessageTraffic]
|
|
138
|
+
#
|
|
139
|
+
def outbound
|
|
140
|
+
@outbound ||= Stats::MessageTraffic.new(attributes[:outbound])
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# A {Ably::Models::Stats::MessageTraffic} object containing the aggregate count of persisted message stats.
|
|
144
|
+
#
|
|
145
|
+
# @spec TS12h
|
|
146
|
+
#
|
|
147
|
+
# @return [Ably::Models::Stats::MessageTraffic]
|
|
148
|
+
#
|
|
149
|
+
def persisted
|
|
150
|
+
@persisted ||= Stats::MessageTypes.new(attributes[:persisted])
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# A {Ably::Models::Stats::ConnectionTypes} object containing a breakdown of connection related stats, such as min, mean and peak connections.
|
|
154
|
+
#
|
|
155
|
+
# @spec TS12i
|
|
156
|
+
#
|
|
157
|
+
# @return [Ably::Models::Stats::ConnectionTypes]
|
|
158
|
+
#
|
|
159
|
+
def connections
|
|
160
|
+
@connections ||= Stats::ConnectionTypes.new(attributes[:connections])
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# A {Ably::Models::Stats::ResourceCount} object containing a breakdown of connection related stats, such as min, mean and peak connections.
|
|
164
|
+
#
|
|
165
|
+
# @spec TS12j
|
|
166
|
+
#
|
|
167
|
+
# @return [Ably::Models::Stats::ResourceCount]
|
|
168
|
+
#
|
|
169
|
+
def channels
|
|
170
|
+
@channels ||= Stats::ResourceCount.new(attributes[:channels])
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# A {Ably::Models::Stats::RequestCount} object containing a breakdown of API Requests.
|
|
174
|
+
#
|
|
175
|
+
# @spec TS12k
|
|
176
|
+
#
|
|
177
|
+
# @return [Ably::Models::Stats::RequestCount]
|
|
178
|
+
#
|
|
179
|
+
def api_requests
|
|
180
|
+
@api_requests ||= Stats::RequestCount.new(attributes[:api_requests])
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# A {Ably::Models::Stats::RequestCount} object containing a breakdown of Ably Token requests.
|
|
184
|
+
#
|
|
185
|
+
# @spec TS12l
|
|
186
|
+
#
|
|
187
|
+
# @return [Ably::Models::Stats::RequestCount]
|
|
188
|
+
#
|
|
189
|
+
def token_requests
|
|
190
|
+
@token_requests ||= Stats::RequestCount.new(attributes[:token_requests])
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# The UTC time at which the time period covered begins. If unit is set to minute this will be in
|
|
194
|
+
# the format YYYY-mm-dd:HH:MM, if hour it will be YYYY-mm-dd:HH, if day it will be YYYY-mm-dd:00
|
|
195
|
+
# and if month it will be YYYY-mm-01:00.
|
|
196
|
+
#
|
|
197
|
+
# @spec TS12a
|
|
198
|
+
#
|
|
199
|
+
# @return [String]
|
|
200
|
+
#
|
|
201
|
+
def interval_id
|
|
202
|
+
attributes.fetch(:interval_id)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Represents the intervalId as a time object.
|
|
206
|
+
#
|
|
207
|
+
# @spec TS12b
|
|
208
|
+
#
|
|
209
|
+
# @return [Time]
|
|
210
|
+
#
|
|
211
|
+
def interval_time
|
|
212
|
+
self.class.from_interval_id(interval_id)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# The length of the interval the stats span. Values will be a [StatsIntervalGranularity]{@link StatsIntervalGranularity}.
|
|
216
|
+
#
|
|
217
|
+
# @spec TS12c
|
|
218
|
+
#
|
|
219
|
+
# @return [GRANULARITY] The granularity of the interval for the stat such as :day, :hour, :minute, see {GRANULARITY}
|
|
220
|
+
#
|
|
221
|
+
def interval_granularity
|
|
222
|
+
self.class.granularity_from_interval_id(interval_id)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def attributes
|
|
226
|
+
@attributes
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def as_json(*args)
|
|
230
|
+
attributes.as_json(*args).reject { |key, val| val.nil? }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
private
|
|
234
|
+
def raw_hash_object
|
|
235
|
+
@raw_hash_object
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def set_attributes_object(new_attributes)
|
|
239
|
+
@attributes = IdiomaticRubyWrapper(new_attributes.clone.freeze)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|