ably 0.2.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d814eac0ae04dc623743bbe5973e80a58662056d
4
- data.tar.gz: 95426e76baeaee6fbc297031b83cc128e5bed688
3
+ metadata.gz: ee3e7490507273d4da5f34286db1f6ee1e0bc968
4
+ data.tar.gz: a1558974a048679f9572bb607ce4ea00746364ba
5
5
  SHA512:
6
- metadata.gz: b660702de87dec06d7f982ea110d59310ba1f9a130be320bd2ec7a40f777b2e5452cfa4f97f6a08a0cf751646326f53196e7cf3a39c023ce55795583309ad4db
7
- data.tar.gz: 8088bb09de07b180385a4e817827ef2d867ccb2c6645f55c68888bfaf932df7f2a7c2ec7392b3aa1471a5045beda59d0f93fa5775171ca99b1a1083b3fca2037
6
+ metadata.gz: f8b0da46c04457a191111f3c58a004d8f12dbeee12ad9f47836a86c836132d99b7e4675220644050c8c7ab1342e71c84d1477f5dc1256d593f7e028bbbe94298
7
+ data.tar.gz: da2d88e2cba687467a1b5cd79259cf6ec78cb00de7f9ae22f26641b154197dc3dfded91be3adddceed1e0e48b79ac53055af3b23c6dec3444adf4b039c31e42a
@@ -1,10 +1,11 @@
1
1
  module Ably::Models
2
- # Wraps any Ably HTTP response that supports paging and automatically provides methdos to iterated through
2
+ # Wraps any Ably HTTP response that supports paging and automatically provides methods to iterate through
3
3
  # the array of resources using {#first_page}, {#next_page}, {#first_page?} and {#last_page?}
4
4
  #
5
5
  # Paging information is provided by Ably in the LINK HTTP headers
6
6
  class PaginatedResource
7
7
  include Enumerable
8
+ include Ably::Modules::AsyncWrapper
8
9
 
9
10
  # @param [Faraday::Response] http_response Initial HTTP response from an Ably request to a paged resource
10
11
  # @param [String] base_url Base URL for request that generated the http_response so that subsequent paged requests can be made
@@ -12,6 +13,8 @@ module Ably::Models
12
13
  # @param [Hash] options Options for this paged resource
13
14
  # @option options [Symbol,String] :coerce_into symbol or string representing class that should be used to create each item in the PaginatedResource
14
15
  #
16
+ # @yield [Object] block will be called for each resource object for the current page. This is a useful way to apply a transformation to any page resources after they are retrieved
17
+ #
15
18
  # @return [PaginatedResource]
16
19
  def initialize(http_response, base_url, client, options = {}, &each_block)
17
20
  @http_response = http_response
@@ -20,6 +23,7 @@ module Ably::Models
20
23
  @coerce_into = options[:coerce_into]
21
24
  @raw_body = http_response.body
22
25
  @each_block = each_block
26
+ @make_async = options.fetch(:async_blocking_operations, false)
23
27
 
24
28
  @body = if @coerce_into
25
29
  http_response.body.map do |item|
@@ -34,19 +38,27 @@ module Ably::Models
34
38
  end if block_given?
35
39
  end
36
40
 
37
- # Retrieve the first page of results
41
+ # Retrieve the first page of results.
42
+ # When used as part of the {Ably::Realtime} library, it will return a {EventMachine::Deferrable} object,
43
+ # and allows an optional success callback block to be provided.
38
44
  #
39
- # @return [PaginatedResource]
40
- def first_page
41
- PaginatedResource.new(client.get(pagination_url('first')), base_url, client, coerce_into: coerce_into, &each_block)
45
+ # @return [PaginatedResource,EventMachine::Deferrable]
46
+ def first_page(&success_callback)
47
+ async_wrap_if(make_async, success_callback) do
48
+ PaginatedResource.new(client.get(pagination_url('first')), base_url, client, pagination_options, &each_block)
49
+ end
42
50
  end
43
51
 
44
- # Retrieve the next page of results
52
+ # Retrieve the next page of results.
53
+ # When used as part of the {Ably::Realtime} library, it will return a {EventMachine::Deferrable} object,
54
+ # and allows an optional success callback block to be provided.
45
55
  #
46
- # @return [PaginatedResource]
47
- def next_page
48
- raise Ably::Exceptions::InvalidPageError, 'There are no more pages' if supports_pagination? && last_page?
49
- PaginatedResource.new(client.get(pagination_url('next')), base_url, client, coerce_into: coerce_into, &each_block)
56
+ # @return [PaginatedResource,EventMachine::Deferrable]
57
+ def next_page(&success_callback)
58
+ async_wrap_if(make_async, success_callback) do
59
+ raise Ably::Exceptions::InvalidPageError, 'There are no more pages' if supports_pagination? && last_page?
60
+ PaginatedResource.new(client.get(pagination_url('next')), base_url, client, pagination_options, &each_block)
61
+ end
50
62
  end
51
63
 
52
64
  # True if this is the last page in the paged resource set
@@ -84,7 +96,7 @@ module Ably::Models
84
96
  alias_method :count, :length
85
97
  alias_method :size, :length
86
98
 
87
- # Method ensuring this {PaginatedResource} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
99
+ # Method to allow {PaginatedResource} to be {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
88
100
  def each(&block)
89
101
  body.each do |item|
90
102
  if block_given?
@@ -95,7 +107,7 @@ module Ably::Models
95
107
  end
96
108
  end
97
109
 
98
- # Last item in this page
110
+ # First item in this page
99
111
  def first
100
112
  body.first
101
113
  end
@@ -118,7 +130,7 @@ module Ably::Models
118
130
  end
119
131
 
120
132
  private
121
- attr_reader :body, :http_response, :base_url, :client, :coerce_into, :raw_body, :each_block
133
+ attr_reader :body, :http_response, :base_url, :client, :coerce_into, :raw_body, :each_block, :make_async
122
134
 
123
135
  def pagination_headers
124
136
  link_regex = %r{<(?<url>[^>]+)>; rel="(?<rel>[^"]+)"}
@@ -146,5 +158,20 @@ module Ably::Models
146
158
  pagination_header[id]
147
159
  end
148
160
  end
161
+
162
+ def pagination_options
163
+ {
164
+ coerce_into: coerce_into,
165
+ async_blocking_operations: make_async
166
+ }
167
+ end
168
+
169
+ def async_wrap_if(is_realtime, success_callback, &operation)
170
+ if is_realtime
171
+ async_wrap success_callback, &operation
172
+ else
173
+ yield
174
+ end
175
+ end
149
176
  end
150
177
  end
@@ -0,0 +1,60 @@
1
+ module Ably::Modules
2
+ # Provides methods to convert synchronous operations into async operations through the use of
3
+ # {EventMachine#defer http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine#defer-class_method}.
4
+ # The async_wrap method can only be called from within an EventMachine reactor, and must be thread safe.
5
+ #
6
+ # @note using this AsyncWrapper should only be used for methods that are used less frequently and typically
7
+ # not run with levels of concurrency due to the limited number of threads available to EventMachine by default.
8
+ #
9
+ # @example
10
+ # class BlockingOperation
11
+ # include Aby::Modules::AsyncWrapper
12
+ #
13
+ # def operation(&success_callback)
14
+ # async_wrap(success_callback) do
15
+ # sleep 1
16
+ # 'slept'
17
+ # end
18
+ # end
19
+ # end
20
+ #
21
+ # blocking_object = BlockingOperation.new
22
+ # deferrable = blocking_object.operation do |result|
23
+ # puts "Done with result: #{result}"
24
+ # end
25
+ # puts "Starting"
26
+ #
27
+ # # => 'Starting'
28
+ # # => 'Done with result: slept'
29
+ #
30
+ module AsyncWrapper
31
+ private
32
+
33
+ # Will yield the provided block in a new thread and return an {EventMachine::Deferrable http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Deferrable}
34
+ #
35
+ # @yield [Object] operation block that is run in a thread
36
+ # @return [EventMachine::Deferrable]
37
+ #
38
+ def async_wrap(success_callback = nil, &operation)
39
+ raise ArgumentError, "Operation block is missing" unless block_given?
40
+
41
+ EventMachine::DefaultDeferrable.new.tap do |deferrable|
42
+ deferrable.callback &success_callback if success_callback
43
+
44
+ operation_with_exception_handling = proc do
45
+ begin
46
+ yield
47
+ rescue StandardError => e
48
+ deferrable.fail e
49
+ end
50
+ end
51
+
52
+ complete_callback = proc do |result|
53
+ deferrable.succeed result
54
+ end
55
+
56
+ EventMachine.defer operation_with_exception_handling, complete_callback
57
+ end
58
+ end
59
+ end
60
+ end
@@ -36,6 +36,7 @@ module Ably
36
36
  include Ably::Modules::Conversions
37
37
  include Ably::Modules::EventEmitter
38
38
  include Ably::Modules::EventMachineHelpers
39
+ include Ably::Modules::AsyncWrapper
39
40
  extend Ably::Modules::Enum
40
41
 
41
42
  STATE = ruby_enum('STATE',
@@ -102,6 +103,8 @@ module Ably
102
103
  # @param name [String] The event name of the message to subscribe to if provided. Defaults to all events.
103
104
  # @yield [Ably::Models::Message] For each message received, the block is called
104
105
  #
106
+ # @return [void]
107
+ #
105
108
  def subscribe(name = :all, &blk)
106
109
  attach unless attached? || attaching?
107
110
  subscriptions[message_name_key(name)] << blk
@@ -112,6 +115,8 @@ module Ably
112
115
  #
113
116
  # @param name [String] The event name of the message to subscribe to if provided. Defaults to all events.
114
117
  #
118
+ # @return [void]
119
+ #
115
120
  def unsubscribe(name = :all, &blk)
116
121
  if message_name_key(name) == :all
117
122
  subscriptions.keys
@@ -174,8 +179,14 @@ module Ably
174
179
  #
175
180
  # @param (see Ably::Rest::Channel#history)
176
181
  # @option options (see Ably::Rest::Channel#history)
177
- def history(options = {})
178
- rest_channel.history(options)
182
+ #
183
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
184
+ #
185
+ # @return [EventMachine::Deferrable]
186
+ def history(options = {}, &callback)
187
+ async_wrap(callback) do
188
+ rest_channel.history(options.merge(async_blocking_operations: true))
189
+ end
179
190
  end
180
191
 
181
192
  # @!attribute [r] __incoming_msgbus__
@@ -23,6 +23,7 @@ module Ably
23
23
  # @!attribute [r] protocol_binary?
24
24
  # (see Ably::Rest::Client#protocol_binary?)
25
25
  class Client
26
+ include Ably::Modules::AsyncWrapper
26
27
  extend Forwardable
27
28
 
28
29
  DOMAIN = 'realtime.ably.io'
@@ -66,20 +67,32 @@ module Ably
66
67
  # Return a {Ably::Realtime::Channel Realtime Channel} for the given name
67
68
  #
68
69
  # @param (see Ably::Realtime::Channels#get)
69
- #
70
70
  # @return (see Ably::Realtime::Channels#get)
71
+ #
71
72
  def channel(name, channel_options = {})
72
73
  channels.get(name, channel_options)
73
74
  end
74
75
 
75
- # (see Ably::Rest::Client#time)
76
- def time
77
- rest_client.time
76
+ # Retrieve the Ably service time
77
+ #
78
+ # @yield [Time] The time as reported by the Ably service
79
+ # @return [EventMachine::Deferrable]
80
+ #
81
+ def time(&success_callback)
82
+ async_wrap(success_callback) do
83
+ rest_client.time
84
+ end
78
85
  end
79
86
 
80
- # (see Ably::Rest::Client#stats)
81
- def stats(params = {})
82
- rest_client.stats(params)
87
+ # Retrieve the stats for the application
88
+ #
89
+ # @yield [Array] An Array of hashes representing the stats
90
+ # @return [EventMachine::Deferrable]
91
+ #
92
+ def stats(params = {}, &success_callback)
93
+ async_wrap(success_callback) do
94
+ rest_client.stats(params)
95
+ end
83
96
  end
84
97
 
85
98
  # (see Ably::Realtime::Connection#close)
@@ -103,7 +116,7 @@ module Ably
103
116
  end
104
117
 
105
118
  # @!attribute [r] custom_socket_host
106
- # @return [String,nil] Returns the custom socket host that is being used if it was provided with the option :ws_host when the {Client} was created
119
+ # @return [String,NilClass] Returns the custom socket host that is being used if it was provided with the option :ws_host when the {Client} was created
107
120
  def custom_socket_host
108
121
  @custom_socket_host
109
122
  end
@@ -2,6 +2,7 @@ module Ably::Realtime
2
2
  # Presence provides access to presence operations and state for the associated Channel
3
3
  class Presence
4
4
  include Ably::Modules::EventEmitter
5
+ include Ably::Modules::AsyncWrapper
5
6
  extend Ably::Modules::Enum
6
7
 
7
8
  STATE = ruby_enum('STATE',
@@ -14,12 +15,15 @@ module Ably::Realtime
14
15
  )
15
16
  include Ably::Modules::StateEmitter
16
17
 
17
- # {Ably::Realtime::Channel} this Presence object is assoicated with
18
+ # {Ably::Realtime::Channel} this Presence object is associated with
19
+ # @return {Ably::Realtime::Channel}
18
20
  attr_reader :channel
19
21
 
20
22
  # A unique member identifier for this channel client, disambiguating situations where a given
21
23
  # client_id is present on multiple connections simultaneously.
22
- # TODO: This does not work at present as no ACK is sent from the server with a memberId
24
+ #
25
+ # @note TODO: This does not work at present as no ACK is sent from the server with a memberId
26
+ # @return {String}
23
27
  attr_reader :member_id
24
28
 
25
29
  def initialize(channel)
@@ -36,13 +40,16 @@ module Ably::Realtime
36
40
 
37
41
  # Enter this client into this channel. This client will be added to the presence set
38
42
  # and presence subscribers will see an enter message for this client.
43
+ #
39
44
  # @param [Hash,String] options an options Hash to specify client data and/or client ID, or a String with the client data
40
45
  # @option options [String] :data optional data (eg a status message) for this member
41
46
  # @option options [String] :client_id the optional id of the client.
42
47
  # This option is provided to support connections from server instances that act on behalf of
43
48
  # multiple client_ids. In order to be able to enter the channel with this method, the client
44
49
  # library must have been instanced either with a key, or with a token bound to the wildcard clientId.
50
+ #
45
51
  # @yield [Ably::Realtime::Presence] On success, will call the block with the {Ably::Realtime::Presence}
52
+ #
46
53
  # @return [Ably::Models::PresenceMessage] Deferrable {Ably::Models::PresenceMessage} that supports both success (callback) and failure (errback) callbacks
47
54
  #
48
55
  def enter(options = {}, &blk)
@@ -71,6 +78,7 @@ module Ably::Realtime
71
78
 
72
79
  # Leave this client from this channel. This client will be removed from the presence
73
80
  # set and presence subscribers will see a leave message for this client.
81
+ #
74
82
  # @param (see Presence#enter)
75
83
  # @yield (see Presence#enter)
76
84
  # @return (see Presence#enter)
@@ -101,6 +109,7 @@ module Ably::Realtime
101
109
  # Update the presence data for this client. If the client is not already a member of
102
110
  # the presence set it will be added, and presence subscribers will see an enter or
103
111
  # update message for this client.
112
+ #
104
113
  # @param (see Presence#enter)
105
114
  # @yield (see Presence#enter)
106
115
  # @return (see Presence#enter)
@@ -120,9 +129,15 @@ module Ably::Realtime
120
129
 
121
130
  # Get the presence state for this Channel.
122
131
  # Optionally get a member's {Ably::Models::PresenceMessage} state by member_id
132
+ #
123
133
  # @return [Array<Ably::Models::PresenceMessage>, Ably::Models::PresenceMessage] members on the channel
124
- def get()
125
- members.map { |key, presence| presence }
134
+ #
135
+ def get(member_id = nil)
136
+ if member_id
137
+ members.find { |key, presence| presence.member_id == member_id }
138
+ else
139
+ members.map { |key, presence| presence }
140
+ end
126
141
  end
127
142
 
128
143
  # Subscribe to presence events on the associated Channel.
@@ -131,6 +146,8 @@ module Ably::Realtime
131
146
  # @param action [Ably::Models::PresenceMessage::ACTION] Optional, the state change action to subscribe to. Defaults to all presence actions
132
147
  # @yield [Ably::Models::PresenceMessage] For each presence state change event, the block is called
133
148
  #
149
+ # @return [void]
150
+ #
134
151
  def subscribe(action = :all, &blk)
135
152
  ensure_channel_attached do
136
153
  subscriptions[message_action_key(action)] << blk
@@ -142,6 +159,8 @@ module Ably::Realtime
142
159
  #
143
160
  # @param action [Ably::Models::PresenceMessage::ACTION] Optional, the state change action to subscribe to. Defaults to all presence actions
144
161
  #
162
+ # @return [void]
163
+ #
145
164
  def unsubscribe(action = :all, &blk)
146
165
  if message_action_key(action) == :all
147
166
  subscriptions.keys
@@ -158,8 +177,15 @@ module Ably::Realtime
158
177
  #
159
178
  # @param (see Ably::Rest::Presence#history)
160
179
  # @option options (see Ably::Rest::Presence#history)
161
- def history(options = {})
162
- rest_presence.history(options)
180
+ #
181
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
182
+ #
183
+ # @return [EventMachine::Deferrable]
184
+ #
185
+ def history(options = {}, &callback)
186
+ async_wrap(callback) do
187
+ rest_presence.history(options.merge(async_blocking_operations: true))
188
+ end
163
189
  end
164
190
 
165
191
  # @!attribute [r] __incoming_msgbus__
@@ -57,16 +57,22 @@ module Ably
57
57
  # @option options [Integer] :limit Maximum number of messages to retrieve up to 10,000
58
58
  # @option options [Symbol] :by `:message`, `:bundle` or `:hour`. Defaults to `:message`
59
59
  #
60
- # @return [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of hashes representing the message history that supports paging (next, first)
60
+ # @return [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
61
61
  def history(options = {})
62
62
  url = "#{base_path}/messages"
63
+ options = options.dup
63
64
 
64
65
  merge_options = { live: true } # TODO: Remove live param as all history should be live
65
66
  [:start, :end].each { |option| merge_options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
66
67
 
68
+ paginated_options = {
69
+ coerce_into: 'Ably::Models::Message',
70
+ async_blocking_operations: options.delete(:async_blocking_operations),
71
+ }
72
+
67
73
  response = client.get(url, options.merge(merge_options))
68
74
 
69
- Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::Message') do |message|
75
+ Ably::Models::PaginatedResource.new(response, url, client, paginated_options) do |message|
70
76
  message.tap do |message|
71
77
  message.decode self
72
78
  end
@@ -3,7 +3,13 @@ module Ably
3
3
  class Presence
4
4
  include Ably::Modules::Conversions
5
5
 
6
- attr_reader :client, :channel
6
+ # {Ably::Rest::Client} for this Presence object
7
+ # @return {Ably::Rest::Client}
8
+ attr_reader :client
9
+
10
+ # {Ably::Rest::Channel} this Presence object is associated with
11
+ # @return {Ably::Rest::Channel}
12
+ attr_reader :channel
7
13
 
8
14
  # Initialize a new Presence object
9
15
  #
@@ -16,11 +22,25 @@ module Ably
16
22
 
17
23
  # Obtain the set of members currently present for a channel
18
24
  #
19
- # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of presence-message Hash objects that supports paging (next, first)
25
+ # @param [Hash] options the options for the set of members present
26
+ # @option options [Integer,Time] :start Time or millisecond since epoch
27
+ # @option options [Integer,Time] :end Time or millisecond since epoch
28
+ # @option options [Symbol] :direction `:forwards` or `:backwards`
29
+ # @option options [Integer] :limit Maximum number of members to retrieve up to 10,000
30
+ #
31
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
20
32
  #
21
33
  def get(options = {})
34
+ options = options.dup
35
+
36
+ paginated_options = {
37
+ coerce_into: 'Ably::Models::PresenceMessage',
38
+ async_blocking_operations: options.delete(:async_blocking_operations),
39
+ }
40
+
22
41
  response = client.get(base_path, options)
23
- Ably::Models::PaginatedResource.new(response, base_path, client, coerce_into: 'Ably::Models::PresenceMessage') do |presence_message|
42
+
43
+ Ably::Models::PaginatedResource.new(response, base_path, client, paginated_options) do |presence_message|
24
44
  presence_message.tap do |message|
25
45
  message.decode self.channel
26
46
  end
@@ -35,17 +55,23 @@ module Ably
35
55
  # @option options [Symbol] :direction `:forwards` or `:backwards`
36
56
  # @option options [Integer] :limit Maximum number of presence messages to retrieve up to 10,000
37
57
  #
38
- # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of presence-message Hash objects that supports paging (next, first)
58
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
39
59
  #
40
60
  def history(options = {})
41
61
  url = "#{base_path}/history"
62
+ options = options.dup
42
63
 
43
64
  merge_options = { live: true } # TODO: Remove live param as all history should be live
44
65
  [:start, :end].each { |option| merge_options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
45
66
 
67
+ paginated_options = {
68
+ coerce_into: 'Ably::Models::PresenceMessage',
69
+ async_blocking_operations: options.delete(:async_blocking_operations),
70
+ }
71
+
46
72
  response = client.get(url, options.merge(merge_options))
47
73
 
48
- Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::PresenceMessage') do |presence_message|
74
+ Ably::Models::PaginatedResource.new(response, url, client, paginated_options) do |presence_message|
49
75
  presence_message.tap do |message|
50
76
  message.decode self.channel
51
77
  end
@@ -1,3 +1,3 @@
1
1
  module Ably
2
- VERSION = '0.2.0'
2
+ VERSION = '0.6.2'
3
3
  end
@@ -4,7 +4,7 @@ require 'securerandom'
4
4
  describe Ably::Realtime::Channel do
5
5
  include RSpec::EventMachine
6
6
 
7
- [:msgpack, :json].each do |protocol|
7
+ [:json].each do |protocol| # :msgpack,
8
8
  context "over #{protocol}" do
9
9
  let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) }
10
10
 
@@ -24,25 +24,36 @@ describe Ably::Realtime::Channel do
24
24
 
25
25
  let(:options) { { :protocol => :json } }
26
26
 
27
- it 'retrieves real-time history' do
27
+ it 'returns a Deferrable' do
28
28
  run_reactor do
29
29
  channel.publish('event', payload) do |message|
30
- history = channel.history
31
- expect(history.length).to eql(1)
32
- expect(history[0].data).to eql(payload)
30
+ expect(channel.history).to be_a(EventMachine::Deferrable)
33
31
  stop_reactor
34
32
  end
35
33
  end
36
34
  end
37
35
 
36
+ it 'retrieves real-time history' do
37
+ run_reactor do
38
+ channel.publish('event', payload) do |message|
39
+ channel.history do |history|
40
+ expect(history.length).to eql(1)
41
+ expect(history[0].data).to eql(payload)
42
+ stop_reactor
43
+ end
44
+ end
45
+ end
46
+ end
47
+
38
48
  it 'retrieves real-time history across two channels' do
39
49
  run_reactor do
40
50
  channel.publish('event', payload) do |message|
41
51
  channel2.publish('event', payload) do |message|
42
- history = channel2.history
43
- expect(history.length).to eql(2)
44
- expect(history.map(&:data).uniq).to eql([payload])
45
- stop_reactor
52
+ channel2.history do |history|
53
+ expect(history.length).to eql(2)
54
+ expect(history.map(&:data).uniq).to eql([payload])
55
+ stop_reactor
56
+ end
46
57
  end
47
58
  end
48
59
  end
@@ -53,21 +64,22 @@ describe Ably::Realtime::Channel do
53
64
  let(:limit) { 10 }
54
65
 
55
66
  def check_limited_history(direction)
56
- history = channel.history(direction: direction, limit: limit)
57
- expect(history.length).to eql(limit)
58
- limit.times do |index|
59
- expect(history[index].data).to eql("history#{index}")
60
- end
67
+ channel.history(direction: direction, limit: limit) do |history|
68
+ expect(history.length).to eql(limit)
69
+ limit.times do |index|
70
+ expect(history[index].data).to eql("history#{index}")
71
+ end
61
72
 
62
- history = history.next_page
73
+ history.next_page do |history|
74
+ expect(history.length).to eql(limit)
75
+ limit.times do |index|
76
+ expect(history[index].data).to eql("history#{index + limit}")
77
+ end
78
+ expect(history.last_page?).to eql(true)
63
79
 
64
- expect(history.length).to eql(limit)
65
- limit.times do |index|
66
- expect(history[index].data).to eql("history#{index + limit}")
80
+ stop_reactor
81
+ end
67
82
  end
68
- expect(history.last_page?).to eql(true)
69
-
70
- stop_reactor
71
83
  end
72
84
 
73
85
  context 'as one ProtocolMessage' do
@@ -24,17 +24,18 @@ describe 'Ably::Realtime::Presence Messages' do
24
24
  run_reactor do
25
25
  presence_client_one.enter(data: data) do
26
26
  presence_client_one.leave do
27
- history = presence_client_one.history
28
- expect(history.count).to eql(2)
27
+ presence_client_one.history do |history|
28
+ expect(history.count).to eql(2)
29
29
 
30
- expect(history[1].action).to eq(:enter)
31
- expect(history[1].client_id).to eq(client_one.client_id)
30
+ expect(history[1].action).to eq(:enter)
31
+ expect(history[1].client_id).to eq(client_one.client_id)
32
32
 
33
- expect(history[0].action).to eq(:leave)
34
- expect(history[0].client_id).to eq(client_one.client_id)
35
- expect(history[0].data).to eql(data)
33
+ expect(history[0].action).to eq(:leave)
34
+ expect(history[0].client_id).to eq(client_one.client_id)
35
+ expect(history[0].data).to eql(data)
36
36
 
37
- stop_reactor
37
+ stop_reactor
38
+ end
38
39
  end
39
40
  end
40
41
  end
@@ -43,11 +44,12 @@ describe 'Ably::Realtime::Presence Messages' do
43
44
  it 'ensures REST presence history message IDs match ProtocolMessage wrapped message IDs via Realtime' do
44
45
  run_reactor do
45
46
  presence_client_one.subscribe(:enter) do |message|
46
- history = presence_client_one.history
47
- expect(history.count).to eql(1)
47
+ presence_client_one.history do |history|
48
+ expect(history.count).to eql(1)
48
49
 
49
- expect(history[0].id).to eql(message.id)
50
- stop_reactor
50
+ expect(history[0].id).to eql(message.id)
51
+ stop_reactor
52
+ end
51
53
  end
52
54
 
53
55
  presence_client_one.enter(data: data)
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Ably::Realtime::Client stats' do
4
+ include RSpec::EventMachine
5
+
6
+ [:msgpack, :json].each do |protocol|
7
+ context "over #{protocol}" do
8
+ let(:client) do
9
+ Ably::Realtime::Client.new(api_key: api_key, environment: environment, protocol: protocol)
10
+ end
11
+
12
+ describe 'fetching stats' do
13
+ it 'should return a Hash' do
14
+ run_reactor do
15
+ client.stats do |stats|
16
+ expect(stats).to be_a(Array)
17
+ stop_reactor
18
+ end
19
+ end
20
+ end
21
+
22
+ it 'should return a deferrable object' do
23
+ run_reactor do
24
+ expect(client.stats).to be_a(EventMachine::Deferrable)
25
+ stop_reactor
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Ably::Realtime::Client time' do
4
+ include RSpec::EventMachine
5
+
6
+ [:msgpack, :json].each do |protocol|
7
+ context "over #{protocol}" do
8
+ let(:client) do
9
+ Ably::Realtime::Client.new(api_key: api_key, environment: environment, protocol: protocol)
10
+ end
11
+
12
+ describe 'fetching the service time' do
13
+ it 'should return the service time as a Time object' do
14
+ run_reactor do
15
+ client.time do |time|
16
+ expect(time).to be_within(2).of(Time.now)
17
+ stop_reactor
18
+ end
19
+ end
20
+ end
21
+
22
+ it 'should return a deferrable object' do
23
+ run_reactor do
24
+ expect(client.time).to be_a(EventMachine::Deferrable)
25
+ stop_reactor
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -28,6 +28,8 @@ describe Ably::Rest::Presence do
28
28
  expect(presence_message.data).to eq(fixture[:data])
29
29
  end
30
30
  end
31
+
32
+ skip 'with options'
31
33
  end
32
34
 
33
35
  describe 'presence #history' do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'securerandom'
3
3
 
4
- describe 'Ably::Rest Stats' do
4
+ describe 'Ably::Rest::Client Stats' do
5
5
  [:json, :msgpack].each do |protocol|
6
6
  context "over #{protocol}" do
7
7
  describe 'fetching application stats' do
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
- require 'securerandom'
3
2
 
4
- describe 'Ably::REST time' do
3
+ describe 'Ably::REST::Client time' do
5
4
  [:msgpack, :json].each do |protocol|
6
5
  context "over #{protocol}" do
7
6
  let(:client) do
@@ -9,7 +8,7 @@ describe 'Ably::REST time' do
9
8
  end
10
9
 
11
10
  describe 'fetching the service time' do
12
- it "should return the service time as a Time object" do
11
+ it 'should return the service time as a Time object' do
13
12
  expect(client.time).to be_within(2).of(Time.now)
14
13
  end
15
14
  end
@@ -67,7 +67,7 @@ describe Ably::Models::PaginatedResource do
67
67
  end
68
68
  end
69
69
 
70
- context 'with each block' do
70
+ context 'paged transformations' do
71
71
  let(:headers) do
72
72
  {
73
73
  'link' => [
@@ -93,23 +93,65 @@ describe Ably::Models::PaginatedResource do
93
93
  })
94
94
  end
95
95
 
96
- subject do
97
- paginated_resource_class.new(http_response, full_url, paged_client, paginated_resource_options) do |resource|
98
- resource[:added_attribute_from_block] = "id:#{resource[:id]}"
99
- resource
96
+ context 'with each block' do
97
+ subject do
98
+ paginated_resource_class.new(http_response, full_url, paged_client, paginated_resource_options) do |resource|
99
+ resource[:added_attribute_from_block] = "id:#{resource[:id]}"
100
+ resource
101
+ end
102
+ end
103
+
104
+ it 'calls the block for each resource after retrieving the resources' do
105
+ expect(subject[0][:added_attribute_from_block]).to eql("id:#{body[0][:id]}")
100
106
  end
101
- end
102
107
 
103
- it 'calls the block for each resource after retrieving the resources' do
104
- expect(subject[0][:added_attribute_from_block]).to eql("id:#{body[0][:id]}")
108
+ it 'calls the block for each resource on second page after retrieving the resources' do
109
+ page_1_first_id = subject[0][:id]
110
+ next_page = subject.next_page
111
+
112
+ expect(next_page[0][:added_attribute_from_block]).to eql("id:#{body_page2[0][:id]}")
113
+ expect(next_page[0][:id]).to_not eql(page_1_first_id)
114
+ end
105
115
  end
106
116
 
107
- it 'calls the block for each resource on second page after retrieving the resources' do
108
- page_1_first_id = subject[0][:id]
109
- next_page = subject.next_page
117
+ context 'with option async_blocking_operations: true' do
118
+ include RSpec::EventMachine
119
+
120
+ subject do
121
+ paginated_resource_class.new(http_response, full_url, paged_client, async_blocking_operations: true)
122
+ end
110
123
 
111
- expect(next_page[0][:added_attribute_from_block]).to eql("id:#{body_page2[0][:id]}")
112
- expect(next_page[0][:id]).to_not eql(page_1_first_id)
124
+ context '#next_page' do
125
+ it 'returns a deferrable object' do
126
+ run_reactor do
127
+ expect(subject.next_page).to be_a(EventMachine::Deferrable)
128
+ stop_reactor
129
+ end
130
+ end
131
+
132
+ it 'allows a success callback block to be added' do
133
+ run_reactor do
134
+ subject.next_page do |paginated_resource|
135
+ expect(paginated_resource).to be_a(Ably::Models::PaginatedResource)
136
+ stop_reactor
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ context '#first_page' do
143
+ it 'calls the errback callback when first page headers are missing' do
144
+ run_reactor do
145
+ subject.next_page do |paginated_resource|
146
+ deferrable = subject.first_page
147
+ deferrable.errback do |error|
148
+ expect(error).to be_a(Ably::Exceptions::InvalidPageError)
149
+ stop_reactor
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
113
155
  end
114
156
  end
115
157
 
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ describe Ably::Modules::AsyncWrapper do
5
+ include RSpec::EventMachine
6
+
7
+ let(:class_with_module) do
8
+ Class.new do
9
+ include Ably::Modules::AsyncWrapper
10
+
11
+ def operation(&success_callback)
12
+ async_wrap success_callback, &@block
13
+ end
14
+
15
+ def block=(block)
16
+ @block = block
17
+ end
18
+ end
19
+ end
20
+ let(:subject) { class_with_module.new }
21
+ let(:result) { SecureRandom.hex }
22
+ let(:sleep_time) { 0.1 }
23
+
24
+ before do
25
+ subject.block = block
26
+ end
27
+
28
+ context '#async_wrap blocking block' do
29
+ context 'returns result' do
30
+ let(:block) do
31
+ proc do
32
+ sleep sleep_time
33
+ result
34
+ end
35
+ end
36
+
37
+ it 'calls the success_callback block with result when provided' do
38
+ run_reactor do
39
+ subject.operation do |result|
40
+ expect(result).to eql(result)
41
+ stop_reactor
42
+ end
43
+ end
44
+ end
45
+
46
+ it 'returns a deferrable that succeeds with result' do
47
+ run_reactor do
48
+ deferrable = subject.operation
49
+ expect(deferrable).to be_a(EventMachine::Deferrable)
50
+ deferrable.callback do |result|
51
+ expect(result).to eql(result)
52
+ stop_reactor
53
+ end
54
+ end
55
+ end
56
+
57
+ it 'does not call the errback' do
58
+ run_reactor do
59
+ deferrable = subject.operation
60
+ expect(deferrable).to be_a(EventMachine::Deferrable)
61
+ deferrable.callback do |result|
62
+ expect(result).to eql(result)
63
+ EventMachine.add_timer(sleep_time * 2) { stop_reactor }
64
+ end
65
+ deferrable.errback do |result|
66
+ raise 'Errback should not have been called'
67
+ end
68
+ end
69
+ end
70
+
71
+ it 'does not block EventMachine' do
72
+ run_reactor do
73
+ timers_called = 0
74
+ EventMachine.add_periodic_timer(sleep_time / 5) { timers_called += 1 }
75
+
76
+ subject.operation do |result|
77
+ expect(timers_called).to be >= 4
78
+ stop_reactor
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ context 'raises an Exception' do
85
+ let(:block) do
86
+ proc do
87
+ sleep sleep_time
88
+ raise RuntimeError, 'Intentional'
89
+ end
90
+ end
91
+
92
+ it 'calls the errback block of the deferrable' do
93
+ run_reactor do
94
+ deferrable = subject.operation
95
+ expect(deferrable).to be_a(EventMachine::Deferrable)
96
+ deferrable.errback do |error|
97
+ expect(error).to be_a(RuntimeError)
98
+ expect(error.message).to match(/Intentional/)
99
+ stop_reactor
100
+ end
101
+ end
102
+ end
103
+
104
+ it 'does not call the success_callback block' do
105
+ run_reactor do
106
+ subject.operation do |result|
107
+ raise 'Callback should not have been called'
108
+ end
109
+ EventMachine.add_timer(sleep_time * 2) { stop_reactor }
110
+ end
111
+ end
112
+
113
+ it 'does not call the callback block of the deferrable' do
114
+ run_reactor do
115
+ deferrable = subject.operation
116
+ expect(deferrable).to be_a(EventMachine::Deferrable)
117
+ deferrable.callback do |result|
118
+ raise 'Callback should not have been called'
119
+ end
120
+ EventMachine.add_timer(sleep_time * 2) { stop_reactor }
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -16,16 +16,6 @@ describe Ably::Realtime::Client do
16
16
  subject
17
17
  end
18
18
 
19
- specify '#time' do
20
- expect(subject.rest_client).to receive(:time)
21
- subject.time
22
- end
23
-
24
- specify '#stats' do
25
- expect(subject.rest_client).to receive(:stats).with(options)
26
- subject.stats options
27
- end
28
-
29
19
  context 'for attribute' do
30
20
  [:environment, :use_tls?, :log_level].each do |attribute|
31
21
  specify "##{attribute}" do
@@ -47,7 +47,9 @@ describe Ably::Realtime::Presence do
47
47
  context 'subscriptions' do
48
48
  let(:message_history) { Hash.new { |hash, key| hash[key] = 0 } }
49
49
  let(:presence_action) { Ably::Models::PresenceMessage::ACTION.Enter }
50
- let(:message) { instance_double('Ably::Models::PresenceMessage', action: presence_action, member_id: SecureRandom.hex) }
50
+ let(:message) do
51
+ instance_double('Ably::Models::PresenceMessage', action: presence_action, member_id: SecureRandom.hex, decode: true)
52
+ end
51
53
 
52
54
  context '#subscribe' do
53
55
  specify 'to all presence state actions' do
@@ -3,11 +3,12 @@ require 'support/protocol_msgbus_helper'
3
3
 
4
4
  describe Ably::Realtime::Connection::WebsocketTransport do
5
5
  let(:client_ignored) { double('Ably::Realtime::Client').as_null_object }
6
- let(:connection) { instance_double('Ably::Realtime::Connection', client: client_ignored, id: nil) }
6
+ let(:connection) { instance_double('Ably::Realtime::Connection', client: client_ignored, id: nil) }
7
+ let(:url) { 'http://ably.io/' }
7
8
 
8
9
  let(:websocket_transport_without_eventmachine) do
9
10
  Ably::Realtime::Connection::WebsocketTransport.send(:allocate).tap do |websocket_transport|
10
- websocket_transport.send(:initialize, connection)
11
+ websocket_transport.send(:initialize, connection, url)
11
12
  end
12
13
  end
13
14
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ably
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lewis Marshall
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-12-09 00:00:00.000000000 Z
12
+ date: 2014-12-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine
@@ -210,6 +210,7 @@ files:
210
210
  - lib/ably/models/presence_message.rb
211
211
  - lib/ably/models/protocol_message.rb
212
212
  - lib/ably/models/token.rb
213
+ - lib/ably/modules/async_wrapper.rb
213
214
  - lib/ably/modules/channels_collection.rb
214
215
  - lib/ably/modules/conversions.rb
215
216
  - lib/ably/modules/encodeable.rb
@@ -253,6 +254,8 @@ files:
253
254
  - spec/acceptance/realtime/message_spec.rb
254
255
  - spec/acceptance/realtime/presence_history_spec.rb
255
256
  - spec/acceptance/realtime/presence_spec.rb
257
+ - spec/acceptance/realtime/stats_spec.rb
258
+ - spec/acceptance/realtime/time_spec.rb
256
259
  - spec/acceptance/rest/auth_spec.rb
257
260
  - spec/acceptance/rest/base_spec.rb
258
261
  - spec/acceptance/rest/channel_spec.rb
@@ -284,6 +287,7 @@ files:
284
287
  - spec/unit/models/presence_message_spec.rb
285
288
  - spec/unit/models/protocol_message_spec.rb
286
289
  - spec/unit/models/token_spec.rb
290
+ - spec/unit/modules/async_wrapper_spec.rb
287
291
  - spec/unit/modules/conversions_spec.rb
288
292
  - spec/unit/modules/enum_spec.rb
289
293
  - spec/unit/modules/event_emitter_spec.rb
@@ -330,6 +334,8 @@ test_files:
330
334
  - spec/acceptance/realtime/message_spec.rb
331
335
  - spec/acceptance/realtime/presence_history_spec.rb
332
336
  - spec/acceptance/realtime/presence_spec.rb
337
+ - spec/acceptance/realtime/stats_spec.rb
338
+ - spec/acceptance/realtime/time_spec.rb
333
339
  - spec/acceptance/rest/auth_spec.rb
334
340
  - spec/acceptance/rest/base_spec.rb
335
341
  - spec/acceptance/rest/channel_spec.rb
@@ -361,6 +367,7 @@ test_files:
361
367
  - spec/unit/models/presence_message_spec.rb
362
368
  - spec/unit/models/protocol_message_spec.rb
363
369
  - spec/unit/models/token_spec.rb
370
+ - spec/unit/modules/async_wrapper_spec.rb
364
371
  - spec/unit/modules/conversions_spec.rb
365
372
  - spec/unit/modules/enum_spec.rb
366
373
  - spec/unit/modules/event_emitter_spec.rb