ably-rest 0.7.1 → 0.7.3
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 +13 -5
- data/.gitmodules +1 -1
- data/.rspec +1 -0
- data/.travis.yml +7 -3
- data/SPEC.md +495 -419
- data/ably-rest.gemspec +19 -5
- data/lib/ably-rest.rb +9 -1
- data/lib/submodules/ably-ruby/.gitignore +6 -0
- data/lib/submodules/ably-ruby/.rspec +1 -0
- data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -0
- data/lib/submodules/ably-ruby/Gemfile +4 -0
- data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
- data/lib/submodules/ably-ruby/README.md +122 -0
- data/lib/submodules/ably-ruby/Rakefile +34 -0
- data/lib/submodules/ably-ruby/SPEC.md +1794 -0
- data/lib/submodules/ably-ruby/ably.gemspec +36 -0
- data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
- data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
- data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
- data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
- data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
- data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
- data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
- data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
- data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
- data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
- data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
- data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
- metadata +182 -27
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module Ably::Models
|
|
2
|
+
# Convert presence_messsage argument to a {PresenceMessage} object and associate with a protocol message if provided
|
|
3
|
+
#
|
|
4
|
+
# @param presence_message [PresenceMessage,Hash] A presence message object or Hash of presence message properties
|
|
5
|
+
# @param protocol_message [ProtocolMessage] An optional protocol message to assocate the presence message with
|
|
6
|
+
#
|
|
7
|
+
# @return [PresenceMessage]
|
|
8
|
+
def self.PresenceMessage(presence_message, protocol_message = nil)
|
|
9
|
+
case presence_message
|
|
10
|
+
when PresenceMessage
|
|
11
|
+
presence_message.tap do
|
|
12
|
+
presence_message.assign_to_protocol_message protocol_message
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
PresenceMessage.new(presence_message, protocol_message)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# A class representing an individual presence message to be sent or received
|
|
20
|
+
# via the Ably Realtime service.
|
|
21
|
+
#
|
|
22
|
+
# @!attribute [r] action
|
|
23
|
+
# @return [STATE] the state change event signified by a PresenceMessage
|
|
24
|
+
# @!attribute [r] client_id
|
|
25
|
+
# @return [String] The client_id associated with this presence state
|
|
26
|
+
# @!attribute [r] connection_id
|
|
27
|
+
# @return [String] A unique member identifier, disambiguating situations where a given client_id is present on multiple connections simultaneously
|
|
28
|
+
# @!attribute [r] member_key
|
|
29
|
+
# @return [String] A unique connection and client_id identifier ensuring multiple connected clients with the same client_id are unique
|
|
30
|
+
# @!attribute [r] data
|
|
31
|
+
# @return [Object] Optional client-defined status or other event payload associated with this state
|
|
32
|
+
# @!attribute [r] encoding
|
|
33
|
+
# @return [Object] The encoding for the message data. Encoding and decoding of messages is handled automatically by the client library.
|
|
34
|
+
# Therefore, the `encoding` attribute should always be nil unless an Ably library decoding error has occurred.
|
|
35
|
+
# @!attribute [r] timestamp
|
|
36
|
+
# @return [Time] Timestamp when the message was received by the Ably the real-time service
|
|
37
|
+
# @!attribute [r] hash
|
|
38
|
+
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
|
39
|
+
#
|
|
40
|
+
class PresenceMessage
|
|
41
|
+
include Ably::Modules::Conversions
|
|
42
|
+
include Ably::Modules::Encodeable
|
|
43
|
+
include Ably::Modules::ModelCommon
|
|
44
|
+
include EventMachine::Deferrable
|
|
45
|
+
extend Ably::Modules::Enum
|
|
46
|
+
|
|
47
|
+
ACTION = ruby_enum('ACTION',
|
|
48
|
+
:absent,
|
|
49
|
+
:present,
|
|
50
|
+
:enter,
|
|
51
|
+
:leave,
|
|
52
|
+
:update
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# {Message} initializer
|
|
56
|
+
#
|
|
57
|
+
# @param hash_object [Hash] object with the underlying message details
|
|
58
|
+
# @param protocol_message [ProtocolMessage] if this message has been published, then it is associated with a {ProtocolMessage}
|
|
59
|
+
#
|
|
60
|
+
def initialize(hash_object, protocol_message = nil)
|
|
61
|
+
@protocol_message = protocol_message
|
|
62
|
+
@raw_hash_object = hash_object
|
|
63
|
+
|
|
64
|
+
set_hash_object hash_object
|
|
65
|
+
|
|
66
|
+
ensure_utf_8 :client_id, client_id, allow_nil: true
|
|
67
|
+
ensure_utf_8 :connection_id, connection_id, allow_nil: true
|
|
68
|
+
ensure_utf_8 :encoding, encoding, allow_nil: true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
%w( client_id data encoding ).each do |attribute|
|
|
72
|
+
define_method attribute do
|
|
73
|
+
hash[attribute.to_sym]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def id
|
|
78
|
+
hash.fetch(:id) { "#{protocol_message.id!}:#{protocol_message_index}" }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def connection_id
|
|
82
|
+
hash.fetch(:connection_id) { protocol_message.connection_id if assigned_to_protocol_message? }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def member_key
|
|
86
|
+
"#{connection_id}:#{client_id}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def timestamp
|
|
90
|
+
if hash[:timestamp]
|
|
91
|
+
as_time_from_epoch(hash[:timestamp])
|
|
92
|
+
else
|
|
93
|
+
protocol_message.timestamp
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def action
|
|
98
|
+
ACTION(hash[:action])
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def hash
|
|
102
|
+
@hash_object
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Return a JSON ready object from the underlying #hash using Ably naming conventions for keys
|
|
106
|
+
def as_json(*args)
|
|
107
|
+
hash.dup.tap do |presence_message|
|
|
108
|
+
presence_message['action'] = action.to_i
|
|
109
|
+
decode_binary_data_before_to_json presence_message
|
|
110
|
+
end.as_json
|
|
111
|
+
rescue KeyError
|
|
112
|
+
raise KeyError, ':action is missing or invalid, cannot generate a valid Hash for ProtocolMessage'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Assign this presence message to a ProtocolMessage before delivery to the Ably system
|
|
116
|
+
# @api private
|
|
117
|
+
def assign_to_protocol_message(protocol_message)
|
|
118
|
+
@protocol_message = protocol_message
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# True if this presence message is assigned to a ProtocolMessage for delivery to Ably, or received from Ably
|
|
122
|
+
# @return [Boolean]
|
|
123
|
+
# @api private
|
|
124
|
+
def assigned_to_protocol_message?
|
|
125
|
+
!!@protocol_message
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# The optional ProtocolMessage this presence message is assigned to. If ProtocolMessage is nil, an error will be raised.
|
|
129
|
+
# @return [Ably::Models::ProtocolMessage]
|
|
130
|
+
# @api private
|
|
131
|
+
def protocol_message
|
|
132
|
+
raise RuntimeError, 'Presence Message is not yet published with a ProtocolMessage. ProtocolMessage is nil' if @protocol_message.nil?
|
|
133
|
+
@protocol_message
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
attr_reader :raw_hash_object
|
|
138
|
+
|
|
139
|
+
def protocol_message_index
|
|
140
|
+
protocol_message.presence.index(self)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def set_hash_object(hash)
|
|
144
|
+
@hash_object = IdiomaticRubyWrapper(hash.clone.freeze, stop_at: [:data])
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
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://docs.ably.io/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] count
|
|
12
|
+
# @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}
|
|
13
|
+
# @!attribute [r] error_info
|
|
14
|
+
# @return [ErrorInfo] Contains error information
|
|
15
|
+
# @!attribute [r] channel
|
|
16
|
+
# @return [String] Channel name for messages
|
|
17
|
+
# @!attribute [r] channel_serial
|
|
18
|
+
# @return [String] Contains a serial number for a message on the current channel
|
|
19
|
+
# @!attribute [r] connection_id
|
|
20
|
+
# @return [String] Contains a string public identifier for the connection
|
|
21
|
+
# @!attribute [r] connection_key
|
|
22
|
+
# @return [String] Contains a string private connection key used to recover this connection
|
|
23
|
+
# @!attribute [r] connection_serial
|
|
24
|
+
# @return [Bignum] Contains a serial number for a message sent from the server to the client
|
|
25
|
+
# @!attribute [r] message_serial
|
|
26
|
+
# @return [Bignum] Contains a serial number for a message sent from the client to the server
|
|
27
|
+
# @!attribute [r] timestamp
|
|
28
|
+
# @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)
|
|
29
|
+
# @!attribute [r] messages
|
|
30
|
+
# @return [Message] A {ProtocolMessage} with a `:message` action contains one or more messages belonging to a channel
|
|
31
|
+
# @!attribute [r] presence
|
|
32
|
+
# @return [PresenceMessage] A {ProtocolMessage} with a `:presence` action contains one or more presence updates belonging to a channel
|
|
33
|
+
# @!attribute [r] flags
|
|
34
|
+
# @return [Integer] Flags inidicating special ProtocolMessage states
|
|
35
|
+
# @!attribute [r] hash
|
|
36
|
+
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
|
37
|
+
#
|
|
38
|
+
class ProtocolMessage
|
|
39
|
+
include Ably::Modules::ModelCommon
|
|
40
|
+
include EventMachine::Deferrable if defined?(Ably::Realtime)
|
|
41
|
+
extend Ably::Modules::Enum
|
|
42
|
+
|
|
43
|
+
# Actions which are sent by the Ably Realtime API
|
|
44
|
+
#
|
|
45
|
+
# The values correspond to the ints which the API
|
|
46
|
+
# understands.
|
|
47
|
+
ACTION = ruby_enum('ACTION',
|
|
48
|
+
heartbeat: 0,
|
|
49
|
+
ack: 1,
|
|
50
|
+
nack: 2,
|
|
51
|
+
connect: 3,
|
|
52
|
+
connected: 4,
|
|
53
|
+
disconnect: 5,
|
|
54
|
+
disconnected: 6,
|
|
55
|
+
close: 7,
|
|
56
|
+
closed: 8,
|
|
57
|
+
error: 9,
|
|
58
|
+
attach: 10,
|
|
59
|
+
attached: 11,
|
|
60
|
+
detach: 12,
|
|
61
|
+
detached: 13,
|
|
62
|
+
presence: 14,
|
|
63
|
+
message: 15,
|
|
64
|
+
sync: 16
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Indicates this protocol message action will generate an ACK response such as :message or :presence
|
|
68
|
+
def self.ack_required?(for_action)
|
|
69
|
+
[ACTION.Presence, ACTION.Message].include?(ACTION(for_action))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def initialize(hash_object)
|
|
73
|
+
@raw_hash_object = hash_object
|
|
74
|
+
@hash_object = IdiomaticRubyWrapper(@raw_hash_object.clone)
|
|
75
|
+
|
|
76
|
+
raise ArgumentError, 'Invalid ProtocolMessage, action cannot be nil' if @hash_object[:action].nil?
|
|
77
|
+
@hash_object[:action] = ACTION(@hash_object[:action]).to_i unless @hash_object[:action].kind_of?(Integer)
|
|
78
|
+
|
|
79
|
+
@hash_object.freeze
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
%w(id channel channel_serial connection_id connection_key).each do |attribute|
|
|
83
|
+
define_method attribute do
|
|
84
|
+
hash[attribute.to_sym]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def id!
|
|
89
|
+
raise RuntimeError, 'ProtocolMessage #id is nil' unless id
|
|
90
|
+
id
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def action
|
|
94
|
+
ACTION(hash[:action])
|
|
95
|
+
rescue KeyError
|
|
96
|
+
raise KeyError, "Action '#{hash[:action]}' is not supported by ProtocolMessage"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def error
|
|
100
|
+
@error_info ||= ErrorInfo.new(hash[:error]) if hash[:error]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def timestamp
|
|
104
|
+
as_time_from_epoch(hash[:timestamp]) if hash[:timestamp]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def message_serial
|
|
108
|
+
Integer(hash[:msg_serial])
|
|
109
|
+
rescue TypeError
|
|
110
|
+
raise TypeError, "msg_serial '#{hash[:msg_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def connection_serial
|
|
114
|
+
Integer(hash[:connection_serial])
|
|
115
|
+
rescue TypeError
|
|
116
|
+
raise TypeError, "connection_serial '#{hash[:connection_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def count
|
|
120
|
+
[1, hash[:count].to_i].max
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def has_message_serial?
|
|
124
|
+
message_serial && true
|
|
125
|
+
rescue TypeError
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def has_connection_serial?
|
|
130
|
+
connection_serial && true
|
|
131
|
+
rescue TypeError
|
|
132
|
+
false
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def serial
|
|
136
|
+
if has_connection_serial?
|
|
137
|
+
connection_serial
|
|
138
|
+
else
|
|
139
|
+
message_serial
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def has_serial?
|
|
144
|
+
has_connection_serial? || has_message_serial?
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def messages
|
|
148
|
+
@messages ||=
|
|
149
|
+
Array(hash[:messages]).map do |message|
|
|
150
|
+
Ably::Models.Message(message, self)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def add_message(message)
|
|
155
|
+
messages << message
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def presence
|
|
159
|
+
@presence ||=
|
|
160
|
+
Array(hash[:presence]).map do |message|
|
|
161
|
+
Ably::Models.PresenceMessage(message, self)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Flags as bits
|
|
166
|
+
def flags
|
|
167
|
+
Integer(hash[:flags])
|
|
168
|
+
rescue TypeError
|
|
169
|
+
0
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def has_presence_flag?
|
|
173
|
+
flags & 1 == 1
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Indicates this protocol message will generate an ACK response when sent
|
|
177
|
+
# Examples of protocol messages required ACK include :message and :presence
|
|
178
|
+
def ack_required?
|
|
179
|
+
self.class.ack_required?(action)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def hash
|
|
183
|
+
@hash_object
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Return a JSON ready object from the underlying #hash using Ably naming conventions for keys
|
|
187
|
+
def as_json(*args)
|
|
188
|
+
raise TypeError, ':action is missing, cannot generate a valid Hash for ProtocolMessage' unless action
|
|
189
|
+
raise TypeError, ':msg_serial or :connection_serial is missing, cannot generate a valid Hash for ProtocolMessage' if ack_required? && !has_serial?
|
|
190
|
+
|
|
191
|
+
hash.dup.tap do |hash_object|
|
|
192
|
+
hash_object['action'] = action.to_i
|
|
193
|
+
hash_object['messages'] = messages.map(&:as_json) unless messages.empty?
|
|
194
|
+
hash_object['presence'] = presence.map(&:as_json) unless presence.empty?
|
|
195
|
+
end.as_json
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def to_s
|
|
199
|
+
to_json
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# True if the ProtocolMessage appears to be invalid, however this is not a guarantee
|
|
203
|
+
# @return [Boolean]
|
|
204
|
+
# @api private
|
|
205
|
+
def invalid?
|
|
206
|
+
action_enum = action rescue nil
|
|
207
|
+
!action_enum || (ack_required? && !has_serial?)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
module Ably::Models
|
|
2
|
+
# Convert stat argument to a {Stat} object
|
|
3
|
+
#
|
|
4
|
+
# @param stat [Stat,Hash] A Stat object or Hash of stat properties
|
|
5
|
+
#
|
|
6
|
+
# @return [Stat]
|
|
7
|
+
def self.Stat(stat)
|
|
8
|
+
case stat
|
|
9
|
+
when Stat
|
|
10
|
+
stat
|
|
11
|
+
else
|
|
12
|
+
Stat.new(stat)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# A class representing an individual statistic for a specified {#interval_id}
|
|
17
|
+
#
|
|
18
|
+
# @!attribute [r] all
|
|
19
|
+
# @return [Hash] Breakdown of summary stats for all message types
|
|
20
|
+
# @!attribute [r] inbound
|
|
21
|
+
# @return [Hash] Breakdown of summary stats for traffic over various transport types for all inbound messages
|
|
22
|
+
# @!attribute [r] outbound
|
|
23
|
+
# @return [Hash] Breakdown of summary stats for traffic over various transport types for all outbound messages
|
|
24
|
+
# @!attribute [r] persisted
|
|
25
|
+
# @return [Hash] Breakdown of summary stats for all persisted messages
|
|
26
|
+
# @!attribute [r] connections
|
|
27
|
+
# @return [Hash] A breakdown of summary stats data for different (TLS vs non-TLS) connection types.
|
|
28
|
+
# @!attribute [r] channels
|
|
29
|
+
# @return [Hash] Aggregate data for usage of Channels
|
|
30
|
+
# @!attribute [r] api_requests
|
|
31
|
+
# @return [Hash] Aggregate data for numbers of API requests
|
|
32
|
+
# @!attribute [r] token_requests
|
|
33
|
+
# @return [Hash] Aggregate data for numbers of Token requests
|
|
34
|
+
#
|
|
35
|
+
class Stat
|
|
36
|
+
include Ably::Modules::ModelCommon
|
|
37
|
+
extend Ably::Modules::Enum
|
|
38
|
+
|
|
39
|
+
GRANULARITY = ruby_enum('GRANULARITY',
|
|
40
|
+
:minute,
|
|
41
|
+
:hour,
|
|
42
|
+
:day,
|
|
43
|
+
:month
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
INTERVAL_FORMAT_STRING = [
|
|
47
|
+
'%Y-%m-%d:%H:%M',
|
|
48
|
+
'%Y-%m-%d:%H',
|
|
49
|
+
'%Y-%m-%d',
|
|
50
|
+
'%Y-%m'
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
class << self
|
|
54
|
+
# Convert a Time with the specified Granularity into an interval ID based on UTC 0 time
|
|
55
|
+
# @example
|
|
56
|
+
# Stat.to_interval_id(Time.now, :hour) # => '2015-01-01:10'
|
|
57
|
+
#
|
|
58
|
+
# @param time [Time] Time used to determine the interval
|
|
59
|
+
# @param granularity [GRANULARITY] Granularity of the metrics such as :hour, :day
|
|
60
|
+
#
|
|
61
|
+
# @return [String] interval ID used for stats
|
|
62
|
+
#
|
|
63
|
+
def to_interval_id(time, granularity)
|
|
64
|
+
raise ArgumentError, 'Time object required as first argument' unless time.kind_of?(Time)
|
|
65
|
+
|
|
66
|
+
granularity = GRANULARITY(granularity)
|
|
67
|
+
format = INTERVAL_FORMAT_STRING[granularity.to_i]
|
|
68
|
+
|
|
69
|
+
time.utc.strftime(format)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns the UTC 0 start Time of an interval_id
|
|
73
|
+
# @example
|
|
74
|
+
# Stat.from_interval_id('2015-01-01:10') # => 2015-01-01 10:00:00 +0000
|
|
75
|
+
#
|
|
76
|
+
# @param interval_id [String]
|
|
77
|
+
#
|
|
78
|
+
# @return [Time] start time of the provided interval_id
|
|
79
|
+
#
|
|
80
|
+
def from_interval_id(interval_id)
|
|
81
|
+
raise ArgumentError, 'Interval ID must be a string' unless interval_id.kind_of?(String)
|
|
82
|
+
|
|
83
|
+
format = INTERVAL_FORMAT_STRING.find { |format| expected_length(format) == interval_id.length }
|
|
84
|
+
raise ArgumentError, 'Interval ID is an invalid length' unless format
|
|
85
|
+
|
|
86
|
+
Time.strptime("#{interval_id} +0000", "#{format} %z").utc
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns the {GRANULARITY} determined from the interval_id
|
|
90
|
+
# @example
|
|
91
|
+
# Stat.granularity_from_interval_id('2015-01-01:10') # => :hour
|
|
92
|
+
#
|
|
93
|
+
# @param interval_id [String]
|
|
94
|
+
#
|
|
95
|
+
# @return [GRANULARITY] Granularity Enum for the interval_id
|
|
96
|
+
#
|
|
97
|
+
def granularity_from_interval_id(interval_id)
|
|
98
|
+
raise ArgumentError, 'Interval ID must be a string' unless interval_id.kind_of?(String)
|
|
99
|
+
|
|
100
|
+
format = INTERVAL_FORMAT_STRING.find { |format| expected_length(format) == interval_id.length }
|
|
101
|
+
raise ArgumentError, 'Interval ID is an invalid length' unless format
|
|
102
|
+
|
|
103
|
+
GRANULARITY[INTERVAL_FORMAT_STRING.index(format)]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
def expected_length(format)
|
|
108
|
+
format.gsub('%Y', 'YYYY').length
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# {Stat} initializer
|
|
113
|
+
#
|
|
114
|
+
# @param hash_object [Hash] object with the underlying stat details
|
|
115
|
+
#
|
|
116
|
+
def initialize(hash_object)
|
|
117
|
+
@raw_hash_object = hash_object
|
|
118
|
+
|
|
119
|
+
set_hash_object hash_object
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
%w( all inbound outbound persisted connections channels api_requests token_requests ).each do |attribute|
|
|
123
|
+
define_method attribute do
|
|
124
|
+
hash[attribute.to_sym]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @!attribute [r] interval_id
|
|
129
|
+
# @return [String] The interval that this statistic applies to, see {GRANULARITY} and {INTERVAL_FORMAT_STRING}
|
|
130
|
+
def interval_id
|
|
131
|
+
hash.fetch(:interval_id)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @!attribute [r] interval_time
|
|
135
|
+
# @return [Time] A Time object representing the start of the interval
|
|
136
|
+
def interval_time
|
|
137
|
+
self.class.from_interval_id(interval_id)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @!attribute [r] interval_granularity
|
|
141
|
+
# @return [GRANULARITY] The granularity of the interval for the stat such as :day, :hour, :minute, see {GRANULARITY}
|
|
142
|
+
def interval_granularity
|
|
143
|
+
self.class.granularity_from_interval_id(interval_id)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def hash
|
|
147
|
+
@hash_object
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def as_json(*args)
|
|
151
|
+
hash.as_json(*args)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
attr_reader :raw_hash_object
|
|
156
|
+
|
|
157
|
+
def set_hash_object(hash)
|
|
158
|
+
@hash_object = IdiomaticRubyWrapper(hash.clone.freeze)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|