ably 0.1.5 → 0.1.6
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 +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
|