lightstreamer 0.1 → 0.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: bf90908d8a97af1417f8391f16e2c26a10baf73d
4
- data.tar.gz: 6e89354b80f3712ca9c4278f09342123d3844878
3
+ metadata.gz: 7dd1721bffbb480dfa570531e520c727084a58d9
4
+ data.tar.gz: 67ee866151859546536fca838dabfe05e84a1d2d
5
5
  SHA512:
6
- metadata.gz: a17d1e9276c986f6c436e3aad883f4f068b319e9e88e30b7e7eb844b1af33ca18c35dab74a76e2e25763263199c7f251acf45d999a07afeb749b424d6ea58985
7
- data.tar.gz: d359e20a661d5dc953f5ba02d3984f4db0206886b38ebdc1ab555c20c5b367eab9aa8499071eef6ba6f074da116925cf2774742c043e314bdd21f419126535a9
6
+ metadata.gz: 0e52c034fa5c273733b97373038f1ccb9ac18959b9ab8c47b47c515ce64a954f3b397966cb02d5e924183364ec47d0402454faf117e4dbef3d3451fb5cfdaafa
7
+ data.tar.gz: 8a79799ea6def575bd5aed0bbbe9d514512d4db4bd32b5dd6417a320d8b929739931714403f4312b3316f326280dc049c15ca883c5af929be9ba92c62687451a
@@ -1,5 +1,12 @@
1
1
  # Lightstreamer Changelog
2
2
 
3
- ### 0.1 — July 23, 2016
3
+ ### 0.2 — July 23, 2016
4
+
5
+ - Added complete test suite
6
+ - Added `Lightstreamer::Session#subscribed?` and `Lightstreamer::Session#disconnect`
7
+ - Fixed `Lightstreamer::ProtocolError#code` not being set
8
+ - Renamed the command-line client's `--address` option to `--server-url`
9
+
10
+ ### 0.1 — July 22, 2016
4
11
 
5
12
  - Initial release
data/README.md CHANGED
@@ -75,7 +75,7 @@ subscription, then print streaming output from the server as it becomes availabl
75
75
  To print streaming data from the demo server run the following command:
76
76
 
77
77
  ```
78
- lightstreamer --address http://push.lightstreamer.com --adapter-set DEMO --adapter QUOTE_ADAPTER \
78
+ lightstreamer --server-url http://push.lightstreamer.com --adapter-set DEMO --adapter QUOTE_ADAPTER \
79
79
  --items item1 item2 item3 item4 item5 --fields time stock_name bid ask bid
80
80
  ```
81
81
 
@@ -11,7 +11,7 @@ require 'lightstreamer/subscription'
11
11
  require 'lightstreamer/version'
12
12
 
13
13
  require 'lightstreamer/cli/main'
14
- require 'lightstreamer/cli/stream_command'
14
+ require 'lightstreamer/cli/commands/stream_command'
15
15
 
16
16
  # This module contains all the code for the Lightstreamer gem. See `README.md` to get started with using this gem.
17
17
  module Lightstreamer
@@ -4,7 +4,7 @@ module Lightstreamer
4
4
  class Main < Thor
5
5
  desc 'stream', 'Streams a set of items and fields from a Lightstreamer server and prints the live output'
6
6
 
7
- option :address, required: true, desc: 'The address of the Lightstreamer server'
7
+ option :server_url, required: true, desc: 'The URL of the Lightstreamer server'
8
8
  option :username, desc: 'The username for the session'
9
9
  option :password, desc: 'The password for the session'
10
10
  option :adapter_set, desc: 'The name of the adapter set for the session'
@@ -30,7 +30,7 @@ module Lightstreamer
30
30
 
31
31
  # Creates a new session from the specified options.
32
32
  def create_session
33
- Lightstreamer::Session.new server_url: options[:address], username: options[:username],
33
+ Lightstreamer::Session.new server_url: options[:server_url], username: options[:username],
34
34
  password: options[:password], adapter_set: options[:adapter_set]
35
35
  end
36
36
 
@@ -39,12 +39,14 @@ module Lightstreamer
39
39
  subscription = Lightstreamer::Subscription.new items: options[:items], fields: options[:fields],
40
40
  mode: options[:mode], adapter: options[:adapter]
41
41
 
42
- subscription.add_data_callback do |_subscription, item_name, _item_data, new_values|
43
- @queue.push "#{item_name} - #{new_values.map { |key, value| "#{key}: #{value}" }.join ', '}"
44
- end
42
+ subscription.add_data_callback(&method(:subscription_data_callback))
45
43
 
46
44
  subscription
47
45
  end
46
+
47
+ def subscription_data_callback(_subscription, item_name, _item_data, new_values)
48
+ @queue.push "#{item_name} - #{new_values.map { |key, value| "#{key}: #{value}" }.join ', '}"
49
+ end
48
50
  end
49
51
  end
50
52
  end
@@ -26,10 +26,7 @@ module Lightstreamer
26
26
  def execute(options)
27
27
  result = execute_post_request build_payload(options)
28
28
 
29
- return if result.first == 'OK'
30
- raise ProtocolError, result[2], result[1] if result.first == 'ERROR'
31
-
32
- warn "Lightstreamer: unexpected response from server: #{result}"
29
+ raise ProtocolError.new(result[2], result[1]) if result.first == 'ERROR'
33
30
  end
34
31
 
35
32
  private
@@ -14,7 +14,7 @@ module Lightstreamer
14
14
  # @param [Integer] code The numeric code for the error.
15
15
  def initialize(error, code)
16
16
  @error = error.to_s
17
- @http_code = code.to_i
17
+ @code = code.to_i
18
18
 
19
19
  super "Lightstreamer error: #{@error}, code: #{code}"
20
20
  end
@@ -34,8 +34,9 @@ module Lightstreamer
34
34
  # Creates a new Lightstreamer session using the details passed to {#initialize}. If an error occurs then
35
35
  # {ProtocolError} will be raised.
36
36
  def connect
37
+ return if @stream_connection
38
+
37
39
  @stream_connection = StreamConnection.new self
38
- @subscriptions = []
39
40
 
40
41
  first_line = @stream_connection.read_line
41
42
 
@@ -48,6 +49,19 @@ module Lightstreamer
48
49
  end
49
50
  end
50
51
 
52
+ # Disconnects this session and shuts down its stream and processing threads.
53
+ def disconnect
54
+ @stream_connection.disconnect if @stream_connection
55
+
56
+ if @processing_thread
57
+ Thread.kill @processing_thread
58
+ @processing_thread.join
59
+ end
60
+
61
+ @processing_thread = @stream_connection = @control_connection = nil
62
+ @subscriptions = []
63
+ end
64
+
51
65
  # Subscribes this Lightstreamer session to the specified subscription.
52
66
  #
53
67
  # @param [Subscription] subscription The new subscription to subscribe to.
@@ -66,13 +80,20 @@ module Lightstreamer
66
80
  end
67
81
  end
68
82
 
83
+ # Returns whether the specified subscription is currently active on this session.
84
+ #
85
+ # @param [Subscription] subscription The subscription to return the status for.
86
+ #
87
+ # @return [Boolean] Whether the specified subscription is currently active on this session.
88
+ def subscribed?(subscription)
89
+ @subscriptions_mutex.synchronize { @subscriptions.include? subscription }
90
+ end
91
+
69
92
  # Unsubscribes this Lightstreamer session from the specified subscription.
70
93
  #
71
94
  # @param [Subscription] subscription The existing subscription to unsubscribe from.
72
95
  def unsubscribe(subscription)
73
- @subscriptions_mutex.synchronize do
74
- raise ArgumentError, 'Unknown subscription' unless @subscriptions.detect subscription
75
- end
96
+ raise ArgumentError, 'Unknown subscription' unless subscribed? subscription
76
97
 
77
98
  @control_connection.execute table: subscription.id, operation: :delete
78
99
 
@@ -105,13 +126,10 @@ module Lightstreamer
105
126
  # Starts the processing thread that reads and processes incoming data from the stream connection.
106
127
  def create_processing_thread
107
128
  @processing_thread = Thread.new do
108
- begin
109
- loop do
110
- process_stream_data @stream_connection.read_line
111
- end
112
- rescue StandardError => error
113
- warn "Lightstreamer: exception in processing thread: #{error}"
114
- exit 1
129
+ loop do
130
+ line = @stream_connection.read_line
131
+
132
+ process_stream_data line unless line.empty?
115
133
  end
116
134
  end
117
135
  end
@@ -137,7 +155,7 @@ module Lightstreamer
137
155
  @stream_connection = nil
138
156
  @control_connection = nil
139
157
 
140
- raise ProtocolError, error_message, error_code
158
+ raise ProtocolError.new(error_message, error_code)
141
159
  end
142
160
  end
143
161
  end
@@ -2,6 +2,9 @@ module Lightstreamer
2
2
  # Manages a long-running Lightstreamer connection that handles incoming streaming data on a separate thread and
3
3
  # makes it available for consumption via the {#read_line} method.
4
4
  class StreamConnection
5
+ # @return [Thread] The thread used to process incoming streaming data.
6
+ attr_reader :thread
7
+
5
8
  # Establishes a new stream connection using the authentication details from the passed session.
6
9
  #
7
10
  # @param [Session] session The session to create a stream connection for.
@@ -13,6 +16,14 @@ module Lightstreamer
13
16
  create_stream_thread
14
17
  end
15
18
 
19
+ # Disconnects this stream connection by shutting down the streaming thread.
20
+ def disconnect
21
+ return unless @thread
22
+
23
+ Thread.kill @thread
24
+ @thread.join
25
+ end
26
+
16
27
  # Reads the next line of streaming data. This method blocks the calling thread until a line of data is available.
17
28
  def read_line
18
29
  @queue.pop
@@ -29,8 +40,6 @@ module Lightstreamer
29
40
  @thread = Thread.new do
30
41
  begin
31
42
  connect_stream_and_queue_data
32
-
33
- warn 'Lightstreamer: stream connection closed'
34
43
  rescue StandardError => error
35
44
  warn "Lightstreamer: exception in stream thread: #{error}"
36
45
  exit 1
@@ -8,10 +8,10 @@ module Lightstreamer
8
8
  # in incoming Lightstreamer data.
9
9
  attr_reader :id
10
10
 
11
- # @return [Array<String>] The names of the items to subscribe to.
11
+ # @return [Array] The names of the items to subscribe to.
12
12
  attr_reader :items
13
13
 
14
- # @return [Array<String>] The names of the fields to subscribe to on the items.
14
+ # @return [Array] The names of the fields to subscribe to on the items.
15
15
  attr_reader :fields
16
16
 
17
17
  # @return [:distinct, :merge] The operation mode of this subscription.
@@ -25,8 +25,8 @@ module Lightstreamer
25
25
  # {Session#subscribe} to activate the subscription on a Lightstreamer session.
26
26
  #
27
27
  # @param [Hash] options The options to create the subscription with.
28
- # @option options [Array<String>] :items The names of the items to subscribe to.
29
- # @option options [Array<String>] :fields The names of the fields to subscribe to on the items.
28
+ # @option options [Array] :items The names of the items to subscribe to.
29
+ # @option options [Array] :fields The names of the fields to subscribe to on the items.
30
30
  # @option options [:distinct, :merge] :mode The operation mode of this subscription.
31
31
  # @option options [String] :adapter The name of the data adapter from the Lightstreamer session's adapter set that
32
32
  # should be used. If `nil` then the default data adapter will be used.
@@ -55,7 +55,7 @@ module Lightstreamer
55
55
  # Clears the current data stored for the specified item. This is important to do when {#mode} is `:distinct` as
56
56
  # otherwise the incoming data will build up indefinitely.
57
57
  #
58
- # @param [String] item_name The name of the item to clear the current data of.
58
+ # @param [String] item_name The name of the item to clear the current data for.
59
59
  def clear_data_for_item(item_name)
60
60
  index = @items.index item_name
61
61
  raise ArgumentError, 'Unrecognized item name' unless index
@@ -74,8 +74,6 @@ module Lightstreamer
74
74
  # @return [Proc] The same `Proc` object that was passed to this method. This can be used to remove this data
75
75
  # callback at a later stage using {#remove_data_callback}.
76
76
  def add_data_callback(&block)
77
- raise ArgumentError, 'Data callbacks must take four arguments' unless block.arity == 4
78
-
79
77
  @data_mutex.synchronize { @data_callbacks << block }
80
78
 
81
79
  block
@@ -111,16 +109,16 @@ module Lightstreamer
111
109
  # @return [Boolean] Whether the passed line of stream data was relevant to this subscription and was successfully
112
110
  # processed by it.
113
111
  def process_stream_data(line)
114
- item_index, values = parse_stream_data line
112
+ item_index, new_values = parse_stream_data line
115
113
  return false unless item_index
116
114
 
117
115
  @data_mutex.synchronize do
118
116
  data = @data[item_index]
119
117
 
120
- data << values if mode == :distinct
121
- data.merge!(values) if mode == :merge
118
+ data << new_values if mode == :distinct
119
+ data.merge!(new_values) if mode == :merge
122
120
 
123
- @data_callbacks.each { |callback| callback.call self, @items[item_index], data, values }
121
+ call_data_callbacks @items[item_index], data, new_values
124
122
  end
125
123
 
126
124
  true
@@ -172,23 +170,35 @@ module Lightstreamer
172
170
  values.each_with_index do |value, index|
173
171
  next if value == ''
174
172
 
175
- # These conversions are specified in the Lightstreamer specification
176
- value = '' if value == '$'
177
- value = nil if value == '#'
178
- value = value[1..-1] if value =~ /^($|#)/
179
-
180
- hash[fields[index]] = convert_utf16_characters value
173
+ hash[fields[index]] = parse_raw_field_value value
181
174
  end
182
175
 
183
176
  hash
184
177
  end
185
178
 
186
- # Non-ASCII characters are transmitted as UTF-16 escape sequences in the form '\uXXXX' or '\uXXXX\uYYYY'. This
187
- # method is respnsible for converting such escape sequences into native Unicode characters.
188
- #
189
- # @todo Implement this method.
190
- def convert_utf16_characters(value)
179
+ # Parses a raw field value according to to the Lightstreamer specification.
180
+ def parse_raw_field_value(value)
181
+ return '' if value == '$'
182
+ return nil if value == '#'
183
+
184
+ value = value[1..-1] if value =~ /^(\$|#)/
185
+
186
+ # TODO: parse any non-ASCII characters which are specified as UTF-16 escape sequences in the form '\uXXXX' or
187
+ # '\uXXXX\uYYYY'. They need to be transformed into native Unicode characters.
188
+
191
189
  value
192
190
  end
191
+
192
+ # Invokes all of this subscription's data callbacks with the specified arguments. Any exceptions that occur in a
193
+ # data callback are reported on `stderr` but are otherwise ignored.
194
+ def call_data_callbacks(item_name, item_data, new_values)
195
+ @data_callbacks.each do |callback|
196
+ begin
197
+ callback.call self, item_name, item_data, new_values
198
+ rescue StandardError => error
199
+ warn "Lightstreamer: exception occurred in a subscription data callback: #{error}"
200
+ end
201
+ end
202
+ end
193
203
  end
194
204
  end
@@ -1,4 +1,4 @@
1
1
  module Lightstreamer
2
2
  # The version of this gem.
3
- VERSION = '0.1'.freeze
3
+ VERSION = '0.2'.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.1'
4
+ version: '0.2'
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-07-22 00:00:00.000000000 Z
11
+ date: 2016-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -176,8 +176,8 @@ files:
176
176
  - README.md
177
177
  - bin/lightstreamer
178
178
  - lib/lightstreamer.rb
179
+ - lib/lightstreamer/cli/commands/stream_command.rb
179
180
  - lib/lightstreamer/cli/main.rb
180
- - lib/lightstreamer/cli/stream_command.rb
181
181
  - lib/lightstreamer/control_connection.rb
182
182
  - lib/lightstreamer/line_buffer.rb
183
183
  - lib/lightstreamer/protocol_error.rb
@@ -209,5 +209,5 @@ rubyforge_project:
209
209
  rubygems_version: 2.5.1
210
210
  signing_key:
211
211
  specification_version: 4
212
- summary: Ruby client for accessing a Lightstreamer server.
212
+ summary: Library and command-line client for accessing a Lightstreamer server.
213
213
  test_files: []