lightstreamer 0.10 → 0.11

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: 3c3233354e9c3f4b4200e693ad85cca5c0608a5a
4
- data.tar.gz: 84d6e92653bf54b3bd74de5ca789714c7f47e011
3
+ metadata.gz: 712d0f7cae7367c26efde5f75b0481bb5a3d90a3
4
+ data.tar.gz: 77a6882d44273754bb5c85a7739bb614860b7b82
5
5
  SHA512:
6
- metadata.gz: 012b09b619a2c3c21913096639fcbd3962f6ad34618e5629a85434062f289308711aa546928e3b12d98d847f3d4e175988db70c02086cbaf2f3f6f9d741d634d
7
- data.tar.gz: 3dce2858bcbc74bf1b35304f23fb600e68e846c4cfbc0916358a3f8308d5367e3cd69f5c7f7a1e92ed01d88869973bfba294f3eaae7e212742996ceaab78028f
6
+ metadata.gz: e6eec1efbbd9e79c891798f8833a820396caa40b0bbb275eee686b95cc93689651e7703495949953d66ea80e95278e0b3f2203e2ad032a1861981426d082473b
7
+ data.tar.gz: 6164a9f932129439f3870081d55962ebae8f35f44169269f366bd64d2222d66d6bbd14e25fd92b9d59bfe7d3bff8ec52f8f8a5af8ee44a4cd6c144ccd9635377
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Lightstreamer Changelog
2
2
 
3
+ ### 0.11 — August 20, 2016
4
+
5
+ - Added `Lightstreamer::Session#stop_subscriptions` for stopping multiple subscriptions in one request
6
+ - Renamed `Lightstreamer::Session#bulk_subscription_start` to `Lightstreamer::Session#start_subscriptions`
7
+ - Added support for combining all subscription actions via `Lightstreamer::Session#perform_subscription_actions`
8
+ which allows subscription start, unsilence and stop requests to be easily bundled together
9
+ - Fixed `Subscription#id` only being callable on the main thread
10
+
3
11
  ### 0.10 — August 5, 2016
4
12
 
5
13
  - Added support for shared subscription start options to `Lightstreamer::Session#bulk_subscription_start`
data/README.md CHANGED
@@ -20,7 +20,7 @@ Includes support for:
20
20
  - Silent subscriptions
21
21
  - Item snapshots and end-of-snapshot notifications
22
22
  - Unfiltered subscriptions and overflow notifications
23
- - Bulk subscription creation
23
+ - Performing multiple subscription actions in a single request
24
24
  - Synchronous and asynchronous message sending
25
25
  - Detailed error reporting and error handling callbacks
26
26
 
@@ -64,7 +64,7 @@ session.connect
64
64
  # Create a new subscription that subscribes to thirty items and to four fields on each item
65
65
  subscription = session.build_subscription data_adapter: 'QUOTE_ADAPTER', mode: :merge,
66
66
  items: (1..30).map { |i| "item#{i}" },
67
- fields: [:ask, :bid, :stock_name, :time],
67
+ fields: [:ask, :bid, :stock_name, :time]
68
68
 
69
69
  # Create a thread-safe queue
70
70
  queue = Queue.new
@@ -1,5 +1,5 @@
1
1
  module Lightstreamer
2
- # This module contains helper methods for sending single and bulk POST requests to a Lightstreamer server and
2
+ # This module contains helper methods for sending single and multiple POST requests to a Lightstreamer server and
3
3
  # handling the possible error responses.
4
4
  #
5
5
  # @private
@@ -12,7 +12,7 @@ module Lightstreamer
12
12
  # @param [String] url The URL to send the POST request to.
13
13
  # @param [Hash] query The POST request's query params.
14
14
  def execute(url, query)
15
- errors = bulk_execute url, [request_body(query)]
15
+ errors = execute_multiple url, [request_body(query)]
16
16
  raise errors.first if errors.first
17
17
  end
18
18
 
@@ -26,7 +26,7 @@ module Lightstreamer
26
26
  #
27
27
  # @return [Array<LightstreamerError, nil>] The execution result of each of the passed bodies. If an entry is `nil`
28
28
  # then no error occurred when executing that body.
29
- def bulk_execute(url, bodies)
29
+ def execute_multiple(url, bodies)
30
30
  response = Excon.post url, body: bodies.join("\r\n"), expects: 200, connect_timeout: 15
31
31
 
32
32
  response_lines = response.body.split("\n").map(&:strip)
@@ -2,7 +2,7 @@ module Lightstreamer
2
2
  # This class is responsible for managing a Lightstreamer session, and along with the {Subscription} class forms the
3
3
  # primary API for working with Lightstreamer. Start by calling {#initialize} with the desired server URL and other
4
4
  # options, then call {#connect} to initiate the session. Once connected create subscriptions using
5
- # {#build_subscription} and then start streaming data by calling {Subscription#start} or {#bulk_subscription_start}.
5
+ # {#build_subscription} and then start streaming data by calling {Subscription#start} or {#start_subscriptions}.
6
6
  # See the {Subscription} class for details on how to consume the streaming data as it arrives.
7
7
  class Session
8
8
  # The URL of the Lightstreamer server to connect to. Set by {#initialize}.
@@ -94,14 +94,14 @@ module Lightstreamer
94
94
 
95
95
  # Disconnects this Lightstreamer session and terminates the session on the server. All worker threads are exited.
96
96
  def disconnect
97
- control_request :destroy if @stream_connection
97
+ control_request LS_op: :destroy if @stream_connection
98
98
 
99
99
  @processing_thread.join 5 if @processing_thread
100
100
  ensure
101
101
  @stream_connection.disconnect if @stream_connection
102
102
  @processing_thread.exit if @processing_thread
103
103
 
104
- @subscriptions.each { |subscription| subscription.instance_variable_set :@active, false }
104
+ @subscriptions.each { |subscription| subscription.after_control_request :stop }
105
105
 
106
106
  @processing_thread = @stream_connection = nil
107
107
  end
@@ -113,7 +113,7 @@ module Lightstreamer
113
113
  # it forces the stream connection to rebind using the new setting. If an error occurs then a {LightstreamerError}
114
114
  # subclass will be raised.
115
115
  def force_rebind
116
- control_request :force_rebind if @stream_connection
116
+ control_request LS_op: :force_rebind if @stream_connection
117
117
  end
118
118
 
119
119
  # Builds a new subscription for this session with the specified options. Note that ths does not activate the
@@ -152,29 +152,62 @@ module Lightstreamer
152
152
  @mutex.synchronize { @subscriptions.delete subscription }
153
153
  end
154
154
 
155
- # This method performs a bulk {Subscription#start} on all the passed subscriptions. Calling {Subscription#start} on
156
- # each subscription individually would also work but requires a separate POST request to be sent for every
157
- # subscription, whereas this request starts all of the passed subscriptions in a single POST request which is
158
- # significantly faster for a large number of subscriptions. The return value is an array with one entry per
159
- # subscription and indicates the error state returned by the server for that subscription's start request, or `nil`
160
- # if no error occurred.
155
+ # This method performs {Subscription#start} on all the passed subscriptions. Calling {Subscription#start} on each
156
+ # subscription individually would also work but requires a separate POST request to be sent for every subscription,
157
+ # whereas this method starts all of the passed subscriptions in a single POST request which is significantly faster
158
+ # for a large number of subscriptions. The return value is an array with one entry per subscription and indicates
159
+ # the error state returned by the server for that subscription's start request, or `nil` if no error occurred.
161
160
  #
162
161
  # @param [Array<Subscription>] subscriptions The subscriptions to start.
163
- # @param [Hash] options The options to start all subscriptions with. See {Subscription#start} for details on the
162
+ # @param [Hash] options The options to start the subscriptions with. See {Subscription#start} for details on the
164
163
  # supported options.
165
164
  #
166
165
  # @return [Array<LightstreamerError, nil>]
167
- def bulk_subscription_start(subscriptions, options = {})
168
- request_bodies = subscriptions.map do |subscription|
169
- args = subscription.start_control_request_args options
170
- PostRequest.request_body({ LS_session: session_id, LS_op: args.first }.merge(args[1]))
166
+ def start_subscriptions(subscriptions, options = {})
167
+ details = subscriptions.map { |subscription| { subscription: subscription, action: :start, options: options } }
168
+
169
+ perform_subscription_actions details
170
+ end
171
+
172
+ # This method performs {Subscription#stop} on all the passed subscriptions. Calling {Subscription#stop} on each
173
+ # subscription individually would also work but requires a separate POST request to be sent for every subscription,
174
+ # whereas this method stops all of the passed subscriptions in a single POST request which is significantly faster
175
+ # for a large number of subscriptions. The return value is an array with one entry per subscription and indicates
176
+ # the error state returned by the server for that subscription's stop request, or `nil` if no error occurred.
177
+ #
178
+ # @param [Array<Subscription>] subscriptions The subscriptions to stop.
179
+ #
180
+ # @return [Array<LightstreamerError, nil>]
181
+ def stop_subscriptions(subscriptions)
182
+ details = subscriptions.map { |subscription| { subscription: subscription, action: :stop } }
183
+
184
+ perform_subscription_actions details
185
+ end
186
+
187
+ # This method takes an array of subscriptions and actions to perform on those subscriptions. The supported actions
188
+ # are `:start`, `:unsilence` and `:stop`. Calling {Subscription#start}, {Subscription#unsilence} or
189
+ # {Subscription#stop} on each subscription individually would also work but requires a separate POST request to be
190
+ # sent for each action, whereas this method performs all of the specified actions in a single POST request which is
191
+ # significantly faster for a large number of actions. The return value is an array with one entry per action and
192
+ # indicates the error state returned by the server for that action, or `nil` if no error occurred. It will have the
193
+ # same number of entries as the passed `details` array.
194
+ #
195
+ # @param [Array<Hash>] actions This array describes the subscription actions to perform. Each entry must be a hash
196
+ # containing a `:subscription` key specifying the {Subscription}, and an `:action` key specifying the action
197
+ # to perform on the subscription (either `:start`, `:unsilence` or `:stop`). If `:action` is `:start` then an
198
+ # `:options` key can be specified, and the supported options are the same as for {Subscription#start}.
199
+ #
200
+ # @return [Array<LightstreamerError, nil>]
201
+ def perform_subscription_actions(actions)
202
+ request_bodies = actions.map do |hash|
203
+ PostRequest.request_body hash.fetch(:subscription).control_request_options(hash.fetch(:action), hash[:options])
171
204
  end
172
205
 
173
- errors = PostRequest.bulk_execute control_request_url, request_bodies
206
+ errors = PostRequest.execute_multiple control_request_url, request_bodies
174
207
 
175
- # Set @active to true on all subscriptions that did not have an error
208
+ # Update the state of subscriptions that did not have an error
176
209
  errors.each_with_index do |error, index|
177
- subscriptions[index].instance_variable_set :@active, true if error.nil?
210
+ actions[index][:subscription].after_control_request actions[index][:action] unless error
178
211
  end
179
212
  end
180
213
 
@@ -183,7 +216,7 @@ module Lightstreamer
183
216
  #
184
217
  # @param [Float] bandwidth The new requested maximum bandwidth, expressed in kbps.
185
218
  def requested_maximum_bandwidth=(bandwidth)
186
- control_request :constrain, LS_requested_max_bandwidth: bandwidth if connected?
219
+ control_request LS_op: :constrain, LS_requested_max_bandwidth: bandwidth if connected?
187
220
  @requested_maximum_bandwidth = bandwidth.to_f
188
221
  end
189
222
 
@@ -228,16 +261,15 @@ module Lightstreamer
228
261
  # Sends a request to this session's control connection. If an error occurs then a {LightstreamerError} subclass will
229
262
  # be raised.
230
263
  #
231
- # @param [Symbol] operation The control operation to perform.
232
- # @param [Hash] options The options to send with the control request.
233
- def control_request(operation, options = {})
234
- PostRequest.execute control_request_url, options.merge(LS_session: session_id, LS_op: operation)
264
+ # @param [Hash] query The details of the control request query.
265
+ def control_request(query = {})
266
+ PostRequest.execute control_request_url, query.merge(LS_session: session_id)
235
267
  end
236
268
 
237
269
  # Adds the passed block to the list of callbacks that will be run when this session encounters an error on its
238
- # processing thread caused by an error with the steam connection. The block will be called on a worker thread and so
239
- # the code that is run by the block must be thread-safe. The argument passed to the block is `|error|`, which will
240
- # be a {LightstreamerError} subclass detailing the error that occurred.
270
+ # processing thread caused by an error with the stream connection. The block will be called on a worker thread and
271
+ # so the code that is run by the block must be thread-safe. The argument passed to the block is `|error|`, which
272
+ # will be a {LightstreamerError} subclass detailing the error that occurred.
241
273
  #
242
274
  # @param [Proc] callback The callback that is to be run.
243
275
  def on_error(&callback)
@@ -76,7 +76,7 @@ module Lightstreamer
76
76
  #
77
77
  # @private
78
78
  def id
79
- @id ||= ID_GENERATOR.next
79
+ @id ||= self.class.next_id
80
80
  end
81
81
 
82
82
  # Starts streaming data for this Lightstreamer subscription. If an error occurs then a {LightstreamerError} subclass
@@ -97,38 +97,23 @@ module Lightstreamer
97
97
  def start(options = {})
98
98
  return if @active
99
99
 
100
- session.control_request(*start_control_request_args(options))
101
- @active = true
102
- end
103
-
104
- # Returns the arguments to pass to to {Session#control_request} in order to start this subscription with the given
105
- # options.
106
- #
107
- # @param [Hash] options The options to start the subscription with.
108
- #
109
- # @private
110
- def start_control_request_args(options = {})
111
- operation = options[:silent] ? :add_silent : :add
112
-
113
- options = { LS_table: id, LS_mode: mode.to_s.upcase, LS_id: items, LS_schema: fields, LS_selector: selector,
114
- LS_data_adapter: data_adapter, LS_requested_max_frequency: maximum_update_frequency,
115
- LS_snapshot: options.fetch(:snapshot, false) }
116
-
117
- [operation, options]
100
+ session.control_request control_request_options(:start, options)
101
+ after_control_request :start
118
102
  end
119
103
 
120
104
  # Unsilences this subscription if it was initially started in silent mode by passing `silent: true` to {#start}. If
121
105
  # this subscription was not started in silent mode then this method has no effect. If an error occurs then a
122
106
  # {LightstreamerError} subclass will be raised.
123
107
  def unsilence
124
- session.control_request :start, LS_table: id
108
+ session.control_request control_request_options(:unsilence)
109
+ after_control_request :unsilence
125
110
  end
126
111
 
127
112
  # Stops streaming data for this Lightstreamer subscription. If an error occurs then a {LightstreamerError} subclass
128
113
  # will be raised.
129
114
  def stop
130
- session.control_request :delete, LS_table: id if @active
131
- @active = false
115
+ session.control_request control_request_options(:stop) if @active
116
+ after_control_request :stop
132
117
  end
133
118
 
134
119
  # Sets this subscription's maximum update frequency. This can be done while a subscription is streaming data in
@@ -140,7 +125,7 @@ module Lightstreamer
140
125
  # details.
141
126
  def maximum_update_frequency=(new_frequency)
142
127
  new_frequency = sanitize_frequency new_frequency
143
- session.control_request :reconf, LS_table: id, LS_requested_max_frequency: new_frequency if @active
128
+ session.control_request LS_op: :reconf, LS_table: id, LS_requested_max_frequency: new_frequency if @active
144
129
  @maximum_update_frequency = new_frequency
145
130
  end
146
131
 
@@ -181,7 +166,8 @@ module Lightstreamer
181
166
 
182
167
  # Adds the passed block to the list of callbacks that will be run when new data for this subscription arrives. The
183
168
  # block will be called on a worker thread and so the code that is run by the block must be thread-safe. The
184
- # arguments passed to the block are `|subscription, item_name, item_data, new_data|`. If {#mode} is `:distinct`
169
+ # arguments passed to the block are `|subscription, item_name, item_data, new_data|`. The `item_data` argument will
170
+ # be an array if {#mode} is `:command`, for all other modes it will be a hash. Note that if {#mode} is `:distinct`
185
171
  # or `:raw` then `item_data` and `new_data` will be the same.
186
172
  #
187
173
  # @param [Proc] callback The callback that is to be run when new data arrives.
@@ -230,9 +216,39 @@ module Lightstreamer
230
216
  return true if process_end_of_snapshot_message EndOfSnapshotMessage.parse(line, id, items)
231
217
  end
232
218
 
219
+ # Returns the control request arguments to use to perform the specified action on this subscription.
220
+ #
221
+ # @private
222
+ def control_request_options(action, options = nil)
223
+ case action.to_sym
224
+ when :start
225
+ start_control_request_options options
226
+ when :unsilence
227
+ { LS_session: session.session_id, LS_op: :start, LS_table: id }
228
+ when :stop
229
+ { LS_session: session.session_id, LS_op: :delete, LS_table: id }
230
+ end
231
+ end
232
+
233
+ # Performs any required updates to this subscription's state after a control request succeeds.
234
+ #
235
+ # @private
236
+ def after_control_request(action)
237
+ @active = true if action == :start
238
+ @active = false if action == :stop
239
+ end
240
+
233
241
  private
234
242
 
235
- ID_GENERATOR = (1..Float::INFINITY).each
243
+ class << self
244
+ # Returns the next unique numeric subscription ID.
245
+ #
246
+ # @private
247
+ def next_id
248
+ @next_id ||= 0
249
+ @next_id += 1
250
+ end
251
+ end
236
252
 
237
253
  def sanitize_frequency(frequency)
238
254
  frequency.to_s == 'unfiltered' ? :unfiltered : frequency.to_f
@@ -264,5 +280,15 @@ module Lightstreamer
264
280
 
265
281
  true
266
282
  end
283
+
284
+ def start_control_request_options(options)
285
+ options ||= {}
286
+
287
+ operation = options[:silent] ? :add_silent : :add
288
+
289
+ { LS_session: session.session_id, LS_op: operation, LS_table: id, LS_mode: mode.to_s.upcase, LS_id: items,
290
+ LS_schema: fields, LS_selector: selector, LS_snapshot: options.fetch(:snapshot, false),
291
+ LS_requested_max_frequency: maximum_update_frequency, LS_data_adapter: data_adapter }
292
+ end
267
293
  end
268
294
  end
@@ -1,4 +1,4 @@
1
1
  module Lightstreamer
2
2
  # The version of this gem.
3
- VERSION = '0.10'.freeze
3
+ VERSION = '0.11'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lightstreamer
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.10'
4
+ version: '0.11'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Viney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-05 00:00:00.000000000 Z
11
+ date: 2016-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon