lightstreamer 0.10 → 0.11

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 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