ably 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -1
- data/ably.gemspec +4 -3
- data/lib/ably.rb +6 -2
- data/lib/ably/auth.rb +24 -16
- data/lib/ably/exceptions.rb +16 -5
- data/lib/ably/{realtime/models → models}/error_info.rb +9 -11
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +57 -26
- data/lib/ably/{realtime/models → models}/message.rb +45 -38
- data/lib/ably/{realtime/models → models}/nil_channel.rb +4 -4
- data/lib/ably/{rest/models/paged_resource.rb → models/paginated_resource.rb} +21 -10
- data/lib/ably/models/presence_message.rb +126 -0
- data/lib/ably/{realtime/models → models}/protocol_message.rb +76 -38
- data/lib/ably/models/token.rb +74 -0
- data/lib/ably/modules/channels_collection.rb +49 -0
- data/lib/ably/modules/conversions.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +43 -8
- data/lib/ably/modules/event_machine_helpers.rb +1 -0
- data/lib/ably/modules/http_helpers.rb +9 -2
- data/lib/ably/modules/message_pack.rb +14 -0
- data/lib/ably/modules/model_common.rb +29 -0
- data/lib/ably/modules/{state.rb → state_emitter.rb} +8 -7
- data/lib/ably/realtime.rb +37 -7
- data/lib/ably/realtime/channel.rb +154 -31
- data/lib/ably/realtime/channels.rb +47 -0
- data/lib/ably/realtime/client.rb +39 -33
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +50 -21
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +9 -11
- data/lib/ably/realtime/connection.rb +148 -79
- data/lib/ably/realtime/connection/connection_state_machine.rb +111 -0
- data/lib/ably/realtime/connection/websocket_transport.rb +161 -0
- data/lib/ably/realtime/presence.rb +270 -0
- data/lib/ably/rest.rb +14 -3
- data/lib/ably/rest/channel.rb +3 -3
- data/lib/ably/rest/channels.rb +26 -12
- data/lib/ably/rest/client.rb +42 -25
- data/lib/ably/rest/middleware/exceptions.rb +21 -23
- data/lib/ably/rest/middleware/external_exceptions.rb +8 -10
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
- data/lib/ably/rest/middleware/parse_json.rb +9 -2
- data/lib/ably/rest/middleware/parse_message_pack.rb +6 -2
- data/lib/ably/rest/presence.rb +4 -4
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +125 -0
- data/spec/acceptance/realtime/channel_spec.rb +135 -63
- data/spec/acceptance/realtime/connection_spec.rb +86 -0
- data/spec/acceptance/realtime/message_spec.rb +116 -94
- data/spec/acceptance/realtime/presence_history_spec.rb +0 -0
- data/spec/acceptance/realtime/presence_spec.rb +277 -0
- data/spec/acceptance/rest/auth_spec.rb +351 -347
- data/spec/acceptance/rest/base_spec.rb +43 -26
- data/spec/acceptance/rest/channel_spec.rb +88 -83
- data/spec/acceptance/rest/channels_spec.rb +32 -28
- data/spec/acceptance/rest/presence_spec.rb +83 -63
- data/spec/acceptance/rest/stats_spec.rb +38 -37
- data/spec/acceptance/rest/time_spec.rb +10 -6
- data/spec/integration/modules/{state_spec.rb → state_emitter_spec.rb} +16 -2
- data/spec/spec_helper.rb +14 -0
- data/spec/support/api_helper.rb +4 -0
- data/spec/support/model_helper.rb +28 -9
- data/spec/support/protocol_msgbus_helper.rb +8 -1
- data/spec/support/test_app.rb +24 -14
- data/spec/unit/{realtime → models}/error_info_spec.rb +4 -4
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +46 -9
- data/spec/unit/models/message_spec.rb +229 -0
- data/spec/unit/{rest/paged_resource_spec.rb → models/paginated_resource_spec.rb} +19 -11
- data/spec/unit/models/presence_message_spec.rb +230 -0
- data/spec/unit/models/protocol_message_spec.rb +280 -0
- data/spec/unit/{token_spec.rb → models/token_spec.rb} +18 -22
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/event_emitter_spec.rb +36 -4
- data/spec/unit/realtime/channel_spec.rb +76 -2
- data/spec/unit/realtime/channels_spec.rb +50 -0
- data/spec/unit/realtime/client_spec.rb +31 -1
- data/spec/unit/realtime/connection_spec.rb +8 -15
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +6 -6
- data/spec/unit/realtime/presence_spec.rb +100 -0
- data/spec/unit/rest/channels_spec.rb +48 -0
- metadata +72 -38
- data/lib/ably/realtime/models/shared.rb +0 -17
- data/lib/ably/rest/models/message.rb +0 -64
- data/lib/ably/rest/models/presence_message.rb +0 -21
- data/lib/ably/token.rb +0 -80
- data/spec/unit/realtime/message_spec.rb +0 -117
- data/spec/unit/realtime/protocol_message_spec.rb +0 -172
- data/spec/unit/rest/message_spec.rb +0 -75
@@ -1,7 +1,13 @@
|
|
1
|
-
module Ably::
|
1
|
+
module Ably::Models
|
2
|
+
# Convert messsage argument to a {Message} object and associate with a protocol message if provided
|
3
|
+
#
|
4
|
+
# @param message [Message,Hash] A message object or Hash of message properties
|
5
|
+
# @param protocol_message [ProtocolMessage] An optional protocol message to assocate the message with
|
6
|
+
#
|
7
|
+
# @return [Message]
|
2
8
|
def self.Message(message, protocol_message = nil)
|
3
9
|
case message
|
4
|
-
when
|
10
|
+
when Message
|
5
11
|
message.tap do
|
6
12
|
message.assign_to_protocol_message protocol_message
|
7
13
|
end
|
@@ -19,80 +25,81 @@ module Ably::Realtime::Models
|
|
19
25
|
# @return [String] The id of the publisher of this message
|
20
26
|
# @!attribute [r] data
|
21
27
|
# @return [Object] The message payload. See the documentation for supported datatypes.
|
22
|
-
# @!attribute [r]
|
23
|
-
# @return [Time] Timestamp when the message was
|
24
|
-
# @!attribute [r]
|
25
|
-
# @return [Time] Timestamp when the message was received by the Ably the service for publishing
|
26
|
-
# @!attribute [r] message_id
|
28
|
+
# @!attribute [r] timestamp
|
29
|
+
# @return [Time] Timestamp when the message was received by the Ably the real-time service
|
30
|
+
# @!attribute [r] id
|
27
31
|
# @return [String] A globally unique message ID
|
28
|
-
# @!attribute [r]
|
32
|
+
# @!attribute [r] hash
|
29
33
|
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
30
34
|
#
|
31
35
|
class Message
|
32
|
-
include
|
33
|
-
include Ably::Modules::Conversions
|
36
|
+
include Ably::Modules::ModelCommon
|
34
37
|
include EventMachine::Deferrable
|
35
38
|
|
36
39
|
# {Message} initializer
|
37
40
|
#
|
38
|
-
# @param
|
41
|
+
# @param hash_object [Hash] object with the underlying message details
|
39
42
|
# @param protocol_message [ProtocolMessage] if this message has been published, then it is associated with a {ProtocolMessage}
|
40
43
|
#
|
41
|
-
def initialize(
|
44
|
+
def initialize(hash_object, protocol_message = nil)
|
42
45
|
@protocol_message = protocol_message
|
43
|
-
@
|
44
|
-
@
|
46
|
+
@raw_hash_object = hash_object
|
47
|
+
@hash_object = IdiomaticRubyWrapper(hash_object.clone.freeze, stop_at: [:data])
|
45
48
|
end
|
46
49
|
|
47
50
|
%w( name client_id ).each do |attribute|
|
48
51
|
define_method attribute do
|
49
|
-
|
52
|
+
hash[attribute.to_sym]
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
53
56
|
def data
|
54
|
-
@data ||=
|
55
|
-
end
|
56
|
-
|
57
|
-
def message_id
|
58
|
-
"#{connection_id}:#{message_serial}:#{protocol_message_index}"
|
59
|
-
end
|
60
|
-
|
61
|
-
def sender_timestamp
|
62
|
-
as_time_from_epoch(json[:timestamp]) if json[:timestamp]
|
57
|
+
@data ||= hash[:data].freeze
|
63
58
|
end
|
64
59
|
|
65
|
-
def
|
66
|
-
protocol_message.
|
60
|
+
def id
|
61
|
+
hash[:id] || "#{protocol_message.id!}:#{protocol_message_index}"
|
67
62
|
end
|
68
63
|
|
69
|
-
def
|
70
|
-
|
64
|
+
def timestamp
|
65
|
+
if hash[:timestamp]
|
66
|
+
as_time_from_epoch(hash[:timestamp])
|
67
|
+
else
|
68
|
+
protocol_message.timestamp
|
69
|
+
end
|
71
70
|
end
|
72
71
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
json.dup.tap do |json_object|
|
77
|
-
json_object[:timestamp] = as_since_epoch(Time.now) unless sender_timestamp
|
78
|
-
end
|
72
|
+
def hash
|
73
|
+
@hash_object
|
79
74
|
end
|
80
75
|
|
81
|
-
def
|
82
|
-
|
76
|
+
def as_json(*args)
|
77
|
+
raise RuntimeError, ":name is missing, cannot generate a valid Hash for Message" unless name
|
78
|
+
super
|
83
79
|
end
|
84
80
|
|
81
|
+
# Assign this message to a ProtocolMessage before delivery to the Ably system
|
82
|
+
# @api private
|
85
83
|
def assign_to_protocol_message(protocol_message)
|
86
84
|
@protocol_message = protocol_message
|
87
85
|
end
|
88
86
|
|
89
|
-
|
87
|
+
# True if this message is assigned to a ProtocolMessage for delivery to Ably, or received from Ably
|
88
|
+
# @return [Boolean]
|
89
|
+
# @api private
|
90
|
+
def assigned_to_protocol_message?
|
91
|
+
!!@protocol_message
|
92
|
+
end
|
90
93
|
|
94
|
+
# The optional ProtocolMessage this message is assigned to. If ProtocolMessage is nil, an error will be raised.
|
95
|
+
# @return [Ably::Models::ProtocolMessage]
|
96
|
+
# @api private
|
91
97
|
def protocol_message
|
92
|
-
raise RuntimeError, "Message is not yet published with a ProtocolMessage.
|
98
|
+
raise RuntimeError, "Message is not yet published with a ProtocolMessage. ProtocolMessage is nil" if @protocol_message.nil?
|
93
99
|
@protocol_message
|
94
100
|
end
|
95
101
|
|
102
|
+
private
|
96
103
|
def protocol_message_index
|
97
104
|
protocol_message.messages.index(self)
|
98
105
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
module Ably::
|
1
|
+
module Ably::Models
|
2
2
|
# Nil object for Channels, this object is only used within the internal API of this client library
|
3
3
|
class NilChannel
|
4
4
|
include Ably::Modules::EventEmitter
|
5
5
|
extend Ably::Modules::Enum
|
6
6
|
STATE = ruby_enum('STATE', Ably::Realtime::Channel::STATE)
|
7
|
-
include Ably::Modules::
|
7
|
+
include Ably::Modules::StateEmitter
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
@state = STATE.Initialized
|
@@ -14,8 +14,8 @@ module Ably::Realtime::Models
|
|
14
14
|
'Nil channel'
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
@
|
17
|
+
def __incoming_msgbus__
|
18
|
+
@__incoming_msgbus__ ||= Ably::Util::PubSub.new
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -1,23 +1,24 @@
|
|
1
|
-
module Ably::
|
1
|
+
module Ably::Models
|
2
2
|
# Wraps any Ably HTTP response that supports paging and automatically provides methdos to iterated through
|
3
3
|
# the array of resources using {#first}, {#next}, {#last?} and {#first?}
|
4
4
|
#
|
5
5
|
# Paging information is provided by Ably in the LINK HTTP headers
|
6
|
-
class
|
6
|
+
class PaginatedResource
|
7
7
|
include Enumerable
|
8
8
|
|
9
9
|
# @param [Faraday::Response] http_response Initial HTTP response from an Ably request to a paged resource
|
10
10
|
# @param [String] base_url Base URL for request that generated the http_response so that subsequent paged requests can be made
|
11
11
|
# @param [Client] client {Ably::Client} used to make the request to Ably
|
12
12
|
# @param [Hash] options Options for this paged resource
|
13
|
-
# @option options [Symbol,String] :coerce_into symbol or string representing class that should be used to create each item in the
|
13
|
+
# @option options [Symbol,String] :coerce_into symbol or string representing class that should be used to create each item in the PaginatedResource
|
14
14
|
#
|
15
|
-
# @return [
|
15
|
+
# @return [PaginatedResource]
|
16
16
|
def initialize(http_response, base_url, client, options = {})
|
17
17
|
@http_response = http_response
|
18
18
|
@client = client
|
19
19
|
@base_url = "#{base_url.gsub(%r{/[^/]*$}, '')}/"
|
20
20
|
@coerce_into = options[:coerce_into]
|
21
|
+
@raw_body = http_response.body
|
21
22
|
|
22
23
|
@body = if @coerce_into
|
23
24
|
http_response.body.map do |item|
|
@@ -30,17 +31,17 @@ module Ably::Rest::Models
|
|
30
31
|
|
31
32
|
# Retrieve the first page of results
|
32
33
|
#
|
33
|
-
# @return [
|
34
|
+
# @return [PaginatedResource]
|
34
35
|
def first_page
|
35
|
-
|
36
|
+
PaginatedResource.new(client.get(pagination_url('first')), base_url, client, coerce_into: coerce_into)
|
36
37
|
end
|
37
38
|
|
38
39
|
# Retrieve the next page of results
|
39
40
|
#
|
40
|
-
# @return [
|
41
|
+
# @return [PaginatedResource]
|
41
42
|
def next_page
|
42
43
|
raise Ably::Exceptions::InvalidPageError, "There are no more pages" if supports_pagination? && last_page?
|
43
|
-
|
44
|
+
PaginatedResource.new(client.get(pagination_url('next')), base_url, client, coerce_into: coerce_into)
|
44
45
|
end
|
45
46
|
|
46
47
|
# True if this is the last page in the paged resource set
|
@@ -78,7 +79,7 @@ module Ably::Rest::Models
|
|
78
79
|
alias_method :count, :length
|
79
80
|
alias_method :size, :length
|
80
81
|
|
81
|
-
# Method ensuring this {
|
82
|
+
# Method ensuring this {PaginatedResource} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
|
82
83
|
def each(&block)
|
83
84
|
body.each do |item|
|
84
85
|
if block_given?
|
@@ -89,8 +90,18 @@ module Ably::Rest::Models
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
93
|
+
# Last item in this page
|
94
|
+
def first
|
95
|
+
body.first
|
96
|
+
end
|
97
|
+
|
98
|
+
# Last item in this page
|
99
|
+
def last
|
100
|
+
body.last
|
101
|
+
end
|
102
|
+
|
92
103
|
private
|
93
|
-
attr_reader :body, :http_response, :base_url, :client, :coerce_into
|
104
|
+
attr_reader :body, :http_response, :base_url, :client, :coerce_into, :raw_body
|
94
105
|
|
95
106
|
def pagination_headers
|
96
107
|
link_regex = %r{<(?<url>[^>]+)>; rel="(?<rel>[^"]+)"}
|
@@ -0,0 +1,126 @@
|
|
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] member_id
|
27
|
+
# @return [String] A unique member identifier, disambiguating situations where a given client_id is present on multiple connections simultaneously
|
28
|
+
# @!attribute [r] client_data
|
29
|
+
# @return [Object] Optional client-defined status or other event payload associated with this state
|
30
|
+
# @!attribute [r] timestamp
|
31
|
+
# @return [Time] Timestamp when the message was received by the Ably the real-time service
|
32
|
+
# @!attribute [r] hash
|
33
|
+
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
34
|
+
#
|
35
|
+
class PresenceMessage
|
36
|
+
include Ably::Modules::ModelCommon
|
37
|
+
include EventMachine::Deferrable
|
38
|
+
extend Ably::Modules::Enum
|
39
|
+
|
40
|
+
ACTION = ruby_enum('ACTION',
|
41
|
+
:enter,
|
42
|
+
:leave,
|
43
|
+
:update
|
44
|
+
)
|
45
|
+
|
46
|
+
# {Message} initializer
|
47
|
+
#
|
48
|
+
# @param hash_object [Hash] object with the underlying message details
|
49
|
+
# @param protocol_message [ProtocolMessage] if this message has been published, then it is associated with a {ProtocolMessage}
|
50
|
+
#
|
51
|
+
def initialize(hash_object, protocol_message = nil)
|
52
|
+
@protocol_message = protocol_message
|
53
|
+
@raw_hash_object = hash_object
|
54
|
+
@hash_object = IdiomaticRubyWrapper(hash_object.clone.freeze, stop_at: [:data])
|
55
|
+
end
|
56
|
+
|
57
|
+
%w( client_id member_id client_data ).each do |attribute|
|
58
|
+
define_method attribute do
|
59
|
+
hash[attribute.to_sym]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def id
|
64
|
+
hash[:id] || "#{protocol_message.id!}:#{protocol_message_index}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def timestamp
|
68
|
+
if hash[:timestamp]
|
69
|
+
as_time_from_epoch(hash[:timestamp])
|
70
|
+
else
|
71
|
+
protocol_message.timestamp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def action
|
76
|
+
ACTION(hash[:action])
|
77
|
+
end
|
78
|
+
|
79
|
+
def hash
|
80
|
+
@hash_object
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a JSON ready object from the underlying #hash using Ably naming conventions for keys
|
84
|
+
def as_json(*args)
|
85
|
+
hash.dup.tap do |hash|
|
86
|
+
hash['action'] = action.to_i
|
87
|
+
end.as_json
|
88
|
+
rescue KeyError
|
89
|
+
raise KeyError, ":action is missing or invalid, cannot generate a valid Hash for ProtocolMessage"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Assign this presence message to a ProtocolMessage before delivery to the Ably system
|
93
|
+
# @api private
|
94
|
+
def assign_to_protocol_message(protocol_message)
|
95
|
+
@protocol_message = protocol_message
|
96
|
+
end
|
97
|
+
|
98
|
+
# True if this presence message is assigned to a ProtocolMessage for delivery to Ably, or received from Ably
|
99
|
+
# @return [Boolean]
|
100
|
+
# @api private
|
101
|
+
def assigned_to_protocol_message?
|
102
|
+
!!@protocol_message
|
103
|
+
end
|
104
|
+
|
105
|
+
# The optional ProtocolMessage this presence message is assigned to. If ProtocolMessage is nil, an error will be raised.
|
106
|
+
# @return [Ably::Models::ProtocolMessage]
|
107
|
+
# @api private
|
108
|
+
def protocol_message
|
109
|
+
raise RuntimeError, "Presence Message is not yet published with a ProtocolMessage. ProtocolMessage is nil" if @protocol_message.nil?
|
110
|
+
@protocol_message
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def protocol_message_index
|
115
|
+
protocol_message.presence.index(self)
|
116
|
+
end
|
117
|
+
|
118
|
+
def connection_id
|
119
|
+
protocol_message.connection_id
|
120
|
+
end
|
121
|
+
|
122
|
+
def message_serial
|
123
|
+
protocol_message.message_serial
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Ably::
|
1
|
+
module Ably::Models
|
2
2
|
# A message sent and received over the Realtime protocol.
|
3
3
|
# A ProtocolMessage always relates to a single channel only, but
|
4
4
|
# can contain multiple individual Messages or PresenceMessages.
|
@@ -15,26 +15,25 @@ module Ably::Realtime::Models
|
|
15
15
|
# @!attribute [r] channel
|
16
16
|
# @return [String] Channel name for messages
|
17
17
|
# @!attribute [r] channel_serial
|
18
|
-
# @return [String] Contains a serial number for
|
18
|
+
# @return [String] Contains a serial number for a message on the current channel
|
19
19
|
# @!attribute [r] connection_id
|
20
20
|
# @return [String] Contains a string connection ID
|
21
21
|
# @!attribute [r] connection_serial
|
22
|
-
# @return [Bignum] Contains a serial number for a message
|
22
|
+
# @return [Bignum] Contains a serial number for a message sent from the server to the client
|
23
23
|
# @!attribute [r] message_serial
|
24
|
-
# @return [Bignum] Contains a serial number for a message sent from the client to the
|
24
|
+
# @return [Bignum] Contains a serial number for a message sent from the client to the server
|
25
25
|
# @!attribute [r] timestamp
|
26
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
27
|
# @!attribute [r] messages
|
28
28
|
# @return [Message] A {ProtocolMessage} with a `:message` action contains one or more messages belonging to a channel.
|
29
29
|
# @!attribute [r] presence
|
30
30
|
# @return [PresenceMessage] A {ProtocolMessage} with a `:presence` action contains one or more presence updates belonging to a channel.
|
31
|
-
# @!attribute [r]
|
31
|
+
# @!attribute [r] hash
|
32
32
|
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
33
33
|
#
|
34
34
|
class ProtocolMessage
|
35
|
-
include
|
35
|
+
include Ably::Modules::ModelCommon
|
36
36
|
extend Ably::Modules::Enum
|
37
|
-
include Ably::Modules::Conversions
|
38
37
|
|
39
38
|
# Actions which are sent by the Ably Realtime API
|
40
39
|
#
|
@@ -64,40 +63,55 @@ module Ably::Realtime::Models
|
|
64
63
|
[ACTION.Presence, ACTION.Message].include?(ACTION(for_action))
|
65
64
|
end
|
66
65
|
|
67
|
-
def initialize(
|
68
|
-
@
|
69
|
-
@
|
66
|
+
def initialize(hash_object)
|
67
|
+
@raw_hash_object = hash_object
|
68
|
+
@hash_object = IdiomaticRubyWrapper(@raw_hash_object.clone)
|
69
|
+
|
70
|
+
raise ArgumentError, "Invalid ProtocolMessage, action cannot be nil" if @hash_object[:action].nil?
|
71
|
+
@hash_object[:action] = ACTION(@hash_object[:action]).to_i unless @hash_object[:action].kind_of?(Integer)
|
72
|
+
|
73
|
+
@hash_object.freeze
|
70
74
|
end
|
71
75
|
|
72
|
-
%w( channel channel_serial
|
73
|
-
connection_id connection_serial ).each do |attribute|
|
76
|
+
%w( id channel channel_serial connection_id ).each do |attribute|
|
74
77
|
define_method attribute do
|
75
|
-
|
78
|
+
hash[attribute.to_sym]
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
82
|
+
def id!
|
83
|
+
raise RuntimeError, "ProtocolMessage #id is nil" unless id
|
84
|
+
id
|
85
|
+
end
|
86
|
+
|
79
87
|
def action
|
80
|
-
ACTION(
|
88
|
+
ACTION(hash[:action])
|
81
89
|
rescue KeyError
|
82
|
-
raise KeyError, "Action '#{
|
90
|
+
raise KeyError, "Action '#{hash[:action]}' is not supported by ProtocolMessage"
|
83
91
|
end
|
84
92
|
|
85
93
|
def error
|
86
|
-
@error_info ||= ErrorInfo.new(
|
94
|
+
@error_info ||= ErrorInfo.new(hash[:error]) if hash[:error]
|
87
95
|
end
|
88
96
|
|
89
97
|
def timestamp
|
90
|
-
as_time_from_epoch(
|
98
|
+
as_time_from_epoch(hash[:timestamp]) if hash[:timestamp]
|
91
99
|
end
|
92
100
|
|
93
101
|
def message_serial
|
94
|
-
Integer(
|
102
|
+
Integer(hash[:msg_serial])
|
103
|
+
rescue TypeError
|
104
|
+
raise TypeError, "msg_serial '#{hash[:msg_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
105
|
+
end
|
106
|
+
|
107
|
+
def connection_serial
|
108
|
+
Integer(hash[:connection_serial])
|
95
109
|
rescue TypeError
|
96
|
-
raise TypeError, "
|
110
|
+
raise TypeError, "connection_serial '#{hash[:connection_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
97
111
|
end
|
98
112
|
|
99
113
|
def count
|
100
|
-
[1,
|
114
|
+
[1, hash[:count].to_i].max
|
101
115
|
end
|
102
116
|
|
103
117
|
def has_message_serial?
|
@@ -106,10 +120,28 @@ module Ably::Realtime::Models
|
|
106
120
|
false
|
107
121
|
end
|
108
122
|
|
123
|
+
def has_connection_serial?
|
124
|
+
connection_serial && true
|
125
|
+
rescue TypeError
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def serial
|
130
|
+
if has_connection_serial?
|
131
|
+
connection_serial
|
132
|
+
else
|
133
|
+
message_serial
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def has_serial?
|
138
|
+
has_connection_serial? || has_message_serial?
|
139
|
+
end
|
140
|
+
|
109
141
|
def messages
|
110
142
|
@messages ||=
|
111
|
-
Array(
|
112
|
-
Ably::
|
143
|
+
Array(hash[:messages]).map do |message|
|
144
|
+
Ably::Models.Message(message, self)
|
113
145
|
end
|
114
146
|
end
|
115
147
|
|
@@ -119,37 +151,43 @@ module Ably::Realtime::Models
|
|
119
151
|
|
120
152
|
def presence
|
121
153
|
@presence ||=
|
122
|
-
Array(
|
123
|
-
PresenceMessage(message, self)
|
154
|
+
Array(hash[:presence]).map do |message|
|
155
|
+
Ably::Models.PresenceMessage(message, self)
|
124
156
|
end
|
125
157
|
end
|
126
158
|
|
127
|
-
def json
|
128
|
-
@json_object
|
129
|
-
end
|
130
|
-
|
131
159
|
# Indicates this protocol message will generate an ACK response when sent
|
132
160
|
# Examples of protocol messages required ACK include :message and :presence
|
133
161
|
def ack_required?
|
134
162
|
self.class.ack_required?(action)
|
135
163
|
end
|
136
164
|
|
137
|
-
def
|
138
|
-
|
139
|
-
raise TypeError, ":msg_serial is missing, cannot generate valid JSON for ProtocolMessage" if ack_required? && !has_message_serial?
|
140
|
-
|
141
|
-
json.dup.tap do |json_object|
|
142
|
-
json_object[:messages] = messages.map(&:to_json_object) unless messages.empty?
|
143
|
-
json_object[:presence] = presence.map(&:to_json_object) unless presence.empty?
|
144
|
-
end
|
165
|
+
def hash
|
166
|
+
@hash_object
|
145
167
|
end
|
146
168
|
|
147
|
-
|
148
|
-
|
169
|
+
# Return a JSON ready object from the underlying #hash using Ably naming conventions for keys
|
170
|
+
def as_json(*args)
|
171
|
+
raise TypeError, ":action is missing, cannot generate a valid Hash for ProtocolMessage" unless action
|
172
|
+
raise TypeError, ":msg_serial or :connection_serial is missing, cannot generate a valid Hash for ProtocolMessage" if ack_required? && !has_serial?
|
173
|
+
|
174
|
+
hash.dup.tap do |hash_object|
|
175
|
+
hash_object['action'] = action.to_i
|
176
|
+
hash_object['messages'] = messages.map(&:as_json) unless messages.empty?
|
177
|
+
hash_object['presence'] = presence.map(&:as_json) unless presence.empty?
|
178
|
+
end.as_json
|
149
179
|
end
|
150
180
|
|
151
181
|
def to_s
|
152
182
|
to_json
|
153
183
|
end
|
184
|
+
|
185
|
+
# True if the ProtocolMessage appears to be invalid, however this is not a guarantee
|
186
|
+
# @return [Boolean]
|
187
|
+
# @api private
|
188
|
+
def invalid?
|
189
|
+
action_enum = action rescue nil
|
190
|
+
!action_enum || (ack_required? && !has_serial?)
|
191
|
+
end
|
154
192
|
end
|
155
193
|
end
|