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,33 @@
|
|
|
1
|
+
module Ably::Modules
|
|
2
|
+
# @api private
|
|
3
|
+
module StatesmanMonkeyPatch
|
|
4
|
+
# Override Statesman's #before_transition to support :from arrays
|
|
5
|
+
# This can be removed once https://github.com/gocardless/statesman/issues/95 is solved
|
|
6
|
+
def before_transition(options = nil, &block)
|
|
7
|
+
arrayify_transition(options) do |options_without_from_array|
|
|
8
|
+
super *options_without_from_array, &block
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Override Statesman's #after_transition to support :from arrays
|
|
13
|
+
# This can be removed once https://github.com/gocardless/statesman/issues/95 is solved
|
|
14
|
+
def after_transition(options = nil, &block)
|
|
15
|
+
arrayify_transition(options) do |options_without_from_array|
|
|
16
|
+
super *options_without_from_array, &block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def arrayify_transition(options, &block)
|
|
22
|
+
if options.nil?
|
|
23
|
+
yield []
|
|
24
|
+
elsif options.fetch(:from, nil).kind_of?(Array)
|
|
25
|
+
options[:from].each do |from_state|
|
|
26
|
+
yield [options.merge(from: from_state)]
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
yield [options]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Ably::Modules
|
|
2
|
+
# Mixing module that assists with {https://github.com/gocardless/statesman Statemans State Machine} state transitions
|
|
3
|
+
# and maintaining state of this object's #state.
|
|
4
|
+
#
|
|
5
|
+
# Expects:
|
|
6
|
+
# - @state_machine is set to the StateMachine
|
|
7
|
+
# - StateEmitter is included in the object
|
|
8
|
+
#
|
|
9
|
+
module UsesStateMachine
|
|
10
|
+
extend Forwardable
|
|
11
|
+
|
|
12
|
+
# Call #transition_to on the StateMachine
|
|
13
|
+
#
|
|
14
|
+
# @return [Boolean] true if new_state can be transitioned to by state machine
|
|
15
|
+
# @api private
|
|
16
|
+
def transition_state_machine(new_state, emit_object = nil)
|
|
17
|
+
state_machine.transition_state(new_state, emit_object)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Call #transition_to! on the StateMachine
|
|
21
|
+
# An exception wil be raised if new_state cannot be transitioned to by state machine
|
|
22
|
+
#
|
|
23
|
+
# @return [void]
|
|
24
|
+
# @api private
|
|
25
|
+
def transition_state_machine!(new_state, emit_object = nil)
|
|
26
|
+
state_machine.transition_to!(new_state, emit_object)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Provides an internal method for this object's state to match the StateMachine's current state.
|
|
30
|
+
# The current object's state will be changed to the StateMachine state and will emit an event
|
|
31
|
+
# @api private
|
|
32
|
+
def synchronize_state_with_statemachine(*args)
|
|
33
|
+
log_state_machine_state_change
|
|
34
|
+
change_state state_machine.current_state, state_machine.last_transition.metadata
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @!attribute [r] previous_state
|
|
38
|
+
# @return [STATE,nil] The previous state for this connection
|
|
39
|
+
# @api private
|
|
40
|
+
def previous_state
|
|
41
|
+
if state_machine.previous_state
|
|
42
|
+
STATE(state_machine.previous_state)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @!attribute [r] state_history
|
|
47
|
+
# @return [Array<Hash>] All previous states including the current state in date ascending order with Hash properties :state, :metadata, :transitioned_at
|
|
48
|
+
# @api private
|
|
49
|
+
def state_history
|
|
50
|
+
state_machine.history.map do |transition|
|
|
51
|
+
{
|
|
52
|
+
state: STATE(transition.to_state),
|
|
53
|
+
metadata: transition.metadata,
|
|
54
|
+
transitioned_at: transition.created_at
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def_delegators :state_machine, :can_transition_to?
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
attr_reader :state_machine
|
|
63
|
+
|
|
64
|
+
def_delegators :state_machine, :exception_for_state_change_to
|
|
65
|
+
|
|
66
|
+
def log_state_machine_state_change
|
|
67
|
+
if state_machine.previous_state
|
|
68
|
+
logger.debug "#{self.class.name}: Transitioned from #{state_machine.previous_state} => #{state_machine.current_state}"
|
|
69
|
+
else
|
|
70
|
+
logger.debug "#{self.class.name}: Transitioned to #{state_machine.current_state}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'eventmachine'
|
|
2
|
+
require 'websocket/driver'
|
|
3
|
+
|
|
4
|
+
require 'ably/modules/event_emitter'
|
|
5
|
+
|
|
6
|
+
require 'ably/realtime/channel'
|
|
7
|
+
require 'ably/realtime/channels'
|
|
8
|
+
require 'ably/realtime/channel/channel_manager'
|
|
9
|
+
require 'ably/realtime/channel/channel_state_machine'
|
|
10
|
+
require 'ably/realtime/client'
|
|
11
|
+
require 'ably/realtime/connection'
|
|
12
|
+
require 'ably/realtime/connection/connection_manager'
|
|
13
|
+
require 'ably/realtime/connection/connection_state_machine'
|
|
14
|
+
require 'ably/realtime/connection/websocket_transport'
|
|
15
|
+
require 'ably/realtime/presence'
|
|
16
|
+
|
|
17
|
+
Dir.glob(File.expand_path("models/*.rb", File.dirname(__FILE__))).each do |file|
|
|
18
|
+
require file
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Dir.glob(File.expand_path("realtime/models/*.rb", File.dirname(__FILE__))).each do |file|
|
|
22
|
+
require file
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
require 'ably/models/message_encoders/base'
|
|
26
|
+
|
|
27
|
+
require 'ably/realtime/client/incoming_message_dispatcher'
|
|
28
|
+
require 'ably/realtime/client/outgoing_message_dispatcher'
|
|
29
|
+
|
|
30
|
+
module Ably
|
|
31
|
+
# Realtime provides the top-level class to be instanced for the Ably Realtime library
|
|
32
|
+
#
|
|
33
|
+
# @example
|
|
34
|
+
# client = Ably::Realtime.new("xxxxx")
|
|
35
|
+
# channel = client.channel("test")
|
|
36
|
+
# channel.subscribe do |message|
|
|
37
|
+
# message[:name] #=> "greeting"
|
|
38
|
+
# end
|
|
39
|
+
# channel.publish "greeting", "data"
|
|
40
|
+
#
|
|
41
|
+
module Realtime
|
|
42
|
+
# Convenience method providing an alias to {Ably::Realtime::Client} constructor.
|
|
43
|
+
#
|
|
44
|
+
# @param (see Ably::Realtime::Client#initialize)
|
|
45
|
+
# @option options (see Ably::Realtime::Client#initialize)
|
|
46
|
+
#
|
|
47
|
+
# @yield (see Ably::Realtime::Client#initialize)
|
|
48
|
+
# @yieldparam (see Ably::Realtime::Client#initialize)
|
|
49
|
+
# @yieldreturn (see Ably::Realtime::Client#initialize)
|
|
50
|
+
#
|
|
51
|
+
# @return [Ably::Realtime::Client]
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# # create a new client authenticating with basic auth
|
|
55
|
+
# client = Ably::Realtime.new('key.id:secret')
|
|
56
|
+
#
|
|
57
|
+
# # create a new client authenticating with basic auth and a client_id
|
|
58
|
+
# client = Ably::Realtime.new(api_key: 'key.id:secret', client_id: 'john')
|
|
59
|
+
#
|
|
60
|
+
def self.new(options, &token_request_block)
|
|
61
|
+
Ably::Realtime::Client.new(options, &token_request_block)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
module Ably
|
|
2
|
+
module Realtime
|
|
3
|
+
# The Channel class represents a Channel belonging to this application.
|
|
4
|
+
# The Channel instance allows messages to be published and
|
|
5
|
+
# received, and controls the lifecycle of this instance's
|
|
6
|
+
# attachment to the channel.
|
|
7
|
+
#
|
|
8
|
+
# Channels will always be in one of the following states:
|
|
9
|
+
#
|
|
10
|
+
# initialized: 0
|
|
11
|
+
# attaching: 1
|
|
12
|
+
# attached: 2
|
|
13
|
+
# detaching: 3
|
|
14
|
+
# detached: 4
|
|
15
|
+
# failed: 5
|
|
16
|
+
#
|
|
17
|
+
# Note that the states are available as Enum-like constants:
|
|
18
|
+
#
|
|
19
|
+
# Channel::STATE.Initialized
|
|
20
|
+
# Channel::STATE.Attaching
|
|
21
|
+
# Channel::STATE.Attached
|
|
22
|
+
# Channel::STATE.Detaching
|
|
23
|
+
# Channel::STATE.Detached
|
|
24
|
+
# Channel::STATE.Failed
|
|
25
|
+
#
|
|
26
|
+
# Channels emit errors - use `on(:error)` to subscribe to errors
|
|
27
|
+
#
|
|
28
|
+
# @!attribute [r] state
|
|
29
|
+
# @return {Ably::Realtime::Connection::STATE} channel state
|
|
30
|
+
#
|
|
31
|
+
class Channel
|
|
32
|
+
include Ably::Modules::Conversions
|
|
33
|
+
include Ably::Modules::EventEmitter
|
|
34
|
+
include Ably::Modules::EventMachineHelpers
|
|
35
|
+
include Ably::Modules::AsyncWrapper
|
|
36
|
+
extend Ably::Modules::Enum
|
|
37
|
+
|
|
38
|
+
STATE = ruby_enum('STATE',
|
|
39
|
+
:initialized,
|
|
40
|
+
:attaching,
|
|
41
|
+
:attached,
|
|
42
|
+
:detaching,
|
|
43
|
+
:detached,
|
|
44
|
+
:failed
|
|
45
|
+
)
|
|
46
|
+
include Ably::Modules::StateEmitter
|
|
47
|
+
include Ably::Modules::UsesStateMachine
|
|
48
|
+
|
|
49
|
+
# Max number of messages to bundle in a single ProtocolMessage
|
|
50
|
+
MAX_PROTOCOL_MESSAGE_BATCH_SIZE = 50
|
|
51
|
+
|
|
52
|
+
# {Ably::Realtime::Client} associated with this channel
|
|
53
|
+
# @return [Ably::Realtime::Client]
|
|
54
|
+
attr_reader :client
|
|
55
|
+
|
|
56
|
+
# Channel name
|
|
57
|
+
# @return [String]
|
|
58
|
+
attr_reader :name
|
|
59
|
+
|
|
60
|
+
# Channel options configured for this channel, see {#initialize} for channel_options
|
|
61
|
+
# @return [Hash]
|
|
62
|
+
attr_reader :options
|
|
63
|
+
|
|
64
|
+
# When a channel failure occurs this attribute contains the Ably Exception
|
|
65
|
+
# @return [Ably::Models::ErrorInfo,Ably::Exceptions::BaseAblyException]
|
|
66
|
+
attr_reader :error_reason
|
|
67
|
+
|
|
68
|
+
# The Channel manager responsible for attaching, detaching and handling failures for this channel
|
|
69
|
+
# @return [Ably::Realtime::Channel::ChannelManager]
|
|
70
|
+
# @api private
|
|
71
|
+
attr_reader :manager
|
|
72
|
+
|
|
73
|
+
# Initialize a new Channel object
|
|
74
|
+
#
|
|
75
|
+
# @param client [Ably::Rest::Client]
|
|
76
|
+
# @param name [String] The name of the channel
|
|
77
|
+
# @param channel_options [Hash] Channel options, currently reserved for Encryption options
|
|
78
|
+
# @option channel_options [Boolean] :encrypted setting this to true for this channel will encrypt & decrypt all messages automatically
|
|
79
|
+
# @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of `cipher_params` options
|
|
80
|
+
#
|
|
81
|
+
def initialize(client, name, channel_options = {})
|
|
82
|
+
ensure_utf_8 :name, name
|
|
83
|
+
|
|
84
|
+
@client = client
|
|
85
|
+
@name = name
|
|
86
|
+
@options = channel_options.clone.freeze
|
|
87
|
+
@subscriptions = Hash.new { |hash, key| hash[key] = [] }
|
|
88
|
+
@queue = []
|
|
89
|
+
|
|
90
|
+
@state_machine = ChannelStateMachine.new(self)
|
|
91
|
+
@state = STATE(state_machine.current_state)
|
|
92
|
+
@manager = ChannelManager.new(self, client.connection)
|
|
93
|
+
|
|
94
|
+
setup_event_handlers
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Publish a message on the channel
|
|
98
|
+
#
|
|
99
|
+
# @param name [String] The event name of the message
|
|
100
|
+
# @param data [String,ByteArray] payload for the message
|
|
101
|
+
# @yield [Ably::Models::Message] On success, will call the block with the {Ably::Models::Message}
|
|
102
|
+
# @return [Ably::Models::Message] Deferrable {Ably::Models::Message} that supports both success (callback) and failure (errback) callbacks
|
|
103
|
+
#
|
|
104
|
+
# @example
|
|
105
|
+
# channel.publish('click', 'body')
|
|
106
|
+
#
|
|
107
|
+
# channel.publish('click', 'body') do |message|
|
|
108
|
+
# puts "#{message.name} event received with #{message.data}"
|
|
109
|
+
# end
|
|
110
|
+
#
|
|
111
|
+
# channel.publish('click', 'body').errback do |message, error|
|
|
112
|
+
# puts "#{message.name} was not received, error #{error.message}"
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
def publish(name, data, &success_block)
|
|
116
|
+
ensure_utf_8 :name, name
|
|
117
|
+
|
|
118
|
+
create_message(name, data).tap do |message|
|
|
119
|
+
message.callback(&success_block) if block_given?
|
|
120
|
+
queue_message message
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Subscribe to messages matching providing event name, or all messages if event name not provided
|
|
125
|
+
#
|
|
126
|
+
# @param name [String] The event name of the message to subscribe to if provided. Defaults to all events.
|
|
127
|
+
# @yield [Ably::Models::Message] For each message received, the block is called
|
|
128
|
+
#
|
|
129
|
+
# @return [void]
|
|
130
|
+
#
|
|
131
|
+
def subscribe(name = :all, &callback)
|
|
132
|
+
attach unless attached? || attaching?
|
|
133
|
+
subscriptions[message_name_key(name)] << callback
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Unsubscribe the matching block for messages matching providing event name, or all messages if event name not provided.
|
|
137
|
+
# If a block is not provided, all subscriptions will be unsubscribed
|
|
138
|
+
#
|
|
139
|
+
# @param name [String] The event name of the message to subscribe to if provided. Defaults to all events.
|
|
140
|
+
#
|
|
141
|
+
# @return [void]
|
|
142
|
+
#
|
|
143
|
+
def unsubscribe(name = :all, &callback)
|
|
144
|
+
if message_name_key(name) == :all
|
|
145
|
+
subscriptions.keys
|
|
146
|
+
else
|
|
147
|
+
Array(message_name_key(name))
|
|
148
|
+
end.each do |key|
|
|
149
|
+
subscriptions[key].delete_if do |block|
|
|
150
|
+
!block_given? || callback == block
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Attach to this channel, and call the block if provided when attached.
|
|
156
|
+
# Attaching to a channel is implicit in when a message is published or #subscribe is called, so it is uncommon
|
|
157
|
+
# to need to call attach explicitly.
|
|
158
|
+
#
|
|
159
|
+
# @yield [Ably::Realtime::Channel] Block is called as soon as this channel is in the Attached state
|
|
160
|
+
# @return [EventMachine::Deferrable] Deferrable that supports both success (callback) and failure (errback) callback
|
|
161
|
+
#
|
|
162
|
+
def attach(&success_block)
|
|
163
|
+
transition_state_machine :attaching if can_transition_to?(:attaching)
|
|
164
|
+
deferrable_for_state_change_to(STATE.Attached, &success_block)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Detach this channel, and call the block if provided when in a Detached or Failed state
|
|
168
|
+
#
|
|
169
|
+
# @yield [Ably::Realtime::Channel] Block is called as soon as this channel is in the Detached or Failed state
|
|
170
|
+
# @return [void]
|
|
171
|
+
#
|
|
172
|
+
def detach(&success_block)
|
|
173
|
+
raise exception_for_state_change_to(:detaching) if failed? || initialized?
|
|
174
|
+
transition_state_machine :detaching if can_transition_to?(:detaching)
|
|
175
|
+
deferrable_for_state_change_to(STATE.Detached, &success_block)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Presence object for this Channel. This controls this client's
|
|
179
|
+
# presence on the channel and may also be used to obtain presence information
|
|
180
|
+
# and change events for other members of the channel.
|
|
181
|
+
#
|
|
182
|
+
# @return {Ably::Realtime::Presence}
|
|
183
|
+
#
|
|
184
|
+
def presence
|
|
185
|
+
attach if initialized?
|
|
186
|
+
@presence ||= Presence.new(self)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Return the message history of the channel
|
|
190
|
+
#
|
|
191
|
+
# @param (see Ably::Rest::Channel#history)
|
|
192
|
+
# @option options (see Ably::Rest::Channel#history)
|
|
193
|
+
#
|
|
194
|
+
# @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
|
|
195
|
+
#
|
|
196
|
+
# @return [EventMachine::Deferrable]
|
|
197
|
+
def history(options = {}, &callback)
|
|
198
|
+
async_wrap(callback) do
|
|
199
|
+
rest_channel.history(options.merge(async_blocking_operations: true))
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @!attribute [r] __incoming_msgbus__
|
|
204
|
+
# @return [Ably::Util::PubSub] Client library internal channel incoming message bus
|
|
205
|
+
# @api private
|
|
206
|
+
def __incoming_msgbus__
|
|
207
|
+
@__incoming_msgbus__ ||= Ably::Util::PubSub.new(
|
|
208
|
+
coerce_into: Proc.new { |event| Ably::Models::ProtocolMessage::ACTION(event) }
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @api private
|
|
213
|
+
def set_failed_channel_error_reason(error)
|
|
214
|
+
@error_reason = error
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Used by {Ably::Modules::StateEmitter} to debug state changes
|
|
218
|
+
# @api private
|
|
219
|
+
def logger
|
|
220
|
+
client.logger
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
private
|
|
224
|
+
attr_reader :queue, :subscriptions
|
|
225
|
+
|
|
226
|
+
def setup_event_handlers
|
|
227
|
+
__incoming_msgbus__.subscribe(:message) do |message|
|
|
228
|
+
message.decode self
|
|
229
|
+
|
|
230
|
+
subscriptions[:all].each { |cb| cb.call(message) }
|
|
231
|
+
subscriptions[message.name].each { |cb| cb.call(message) }
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
on(STATE.Attached) do
|
|
235
|
+
process_queue
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Queue message and process queue if channel is attached.
|
|
240
|
+
# If channel is not yet attached, attempt to attach it before the message queue is processed.
|
|
241
|
+
def queue_message(message)
|
|
242
|
+
queue << message
|
|
243
|
+
|
|
244
|
+
if attached?
|
|
245
|
+
process_queue
|
|
246
|
+
else
|
|
247
|
+
attach
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def messages_in_queue?
|
|
252
|
+
!queue.empty?
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Move messages from Channel Queue into Outgoing Connection Queue
|
|
256
|
+
def process_queue
|
|
257
|
+
condition = -> { attached? && messages_in_queue? }
|
|
258
|
+
non_blocking_loop_while(condition) do
|
|
259
|
+
send_messages_within_protocol_message queue.shift(MAX_PROTOCOL_MESSAGE_BATCH_SIZE)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def send_messages_within_protocol_message(messages)
|
|
264
|
+
client.connection.send_protocol_message(
|
|
265
|
+
action: Ably::Models::ProtocolMessage::ACTION.Message.to_i,
|
|
266
|
+
channel: name,
|
|
267
|
+
messages: messages
|
|
268
|
+
)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def create_message(name, data)
|
|
272
|
+
message = { name: name }
|
|
273
|
+
message.merge!(data: data) unless data.nil?
|
|
274
|
+
message.merge!(clientId: client.client_id) if client.client_id
|
|
275
|
+
|
|
276
|
+
Ably::Models::Message.new(message, nil).tap do |message|
|
|
277
|
+
message.encode self
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def rest_channel
|
|
282
|
+
client.rest_client.channel(name)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def connection
|
|
286
|
+
client.connection
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def message_name_key(name)
|
|
290
|
+
if name == :all
|
|
291
|
+
:all
|
|
292
|
+
else
|
|
293
|
+
name.to_s
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|