lightstreamer 0.2 → 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +5 -5
- data/lib/lightstreamer.rb +4 -1
- data/lib/lightstreamer/control_connection.rb +6 -8
- data/lib/lightstreamer/session.rb +28 -37
- data/lib/lightstreamer/stream_connection.rb +100 -35
- data/lib/lightstreamer/stream_connection_header.rb +60 -0
- data/lib/lightstreamer/subscription.rb +3 -16
- data/lib/lightstreamer/utf16.rb +31 -0
- data/lib/lightstreamer/version.rb +1 -1
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f96a70c42b710644985ba440779ec2d73f32ad9e
|
4
|
+
data.tar.gz: 4c0181a0612a67870d3333671ecbb20e0af27f8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5048265eaf80652222323ffad4f4fa4a39343c4adbc8a52ae5b449d43a8c63e343334cab654a2f6fdc3321955b418581661e0c596e23f1f3ea83f953e9a77f02
|
7
|
+
data.tar.gz: 248b20766df771c73189fd3eb61f218a5b016254a2271f965c4076cfde2bd9391eef4ab6afcc1ea5a5424a3ca86c4f15728aeb12bf415bb2b03721722828bced
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Lightstreamer Changelog
|
2
2
|
|
3
|
+
### 0.3 — July 24, 2016
|
4
|
+
|
5
|
+
- Seamlessly rebind the stream connection when a `LOOP` message is received
|
6
|
+
- Correctly handle UTF-16 escape sequences in stream data, including UTF-16 surrogate pairs
|
7
|
+
- Switched to the `typhoeus` library for HTTP support
|
8
|
+
- Improved error handling on the stream thread
|
9
|
+
- Added `Lightstreamer::Session#connected?`
|
10
|
+
|
3
11
|
### 0.2 — July 23, 2016
|
4
12
|
|
5
13
|
- Added complete test suite
|
data/README.md
CHANGED
@@ -43,9 +43,9 @@ session = Lightstreamer::Session.new server_url: 'http://push.lightstreamer.com'
|
|
43
43
|
# Connect the session
|
44
44
|
session.connect
|
45
45
|
|
46
|
-
# Create a new subscription that subscribes to
|
47
|
-
subscription = Lightstreamer::Subscription.new items:
|
48
|
-
fields: [:
|
46
|
+
# Create a new subscription that subscribes to thirty items and to four fields on each item
|
47
|
+
subscription = Lightstreamer::Subscription.new items: (1..30).map { |i| "item#{i}" },
|
48
|
+
fields: [:ask, :bid, :stock_name, :time],
|
49
49
|
mode: :merge, adapter: 'QUOTE_ADAPTER'
|
50
50
|
|
51
51
|
# Create a thread-safe queue
|
@@ -76,10 +76,10 @@ To print streaming data from the demo server run the following command:
|
|
76
76
|
|
77
77
|
```
|
78
78
|
lightstreamer --server-url http://push.lightstreamer.com --adapter-set DEMO --adapter QUOTE_ADAPTER \
|
79
|
-
--items item1 item2 item3 item4 item5 --fields
|
79
|
+
--items item1 item2 item3 item4 item5 --fields ask bid stock_name time
|
80
80
|
```
|
81
81
|
|
82
|
-
To see a full list of available options run the following command:
|
82
|
+
To see a full list of available options for the command-line client run the following command:
|
83
83
|
|
84
84
|
```
|
85
85
|
lightstreamer help stream
|
data/lib/lightstreamer.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require 'rest-client'
|
2
1
|
require 'thor'
|
2
|
+
require 'typhoeus'
|
3
|
+
require 'uri'
|
3
4
|
|
4
5
|
require 'lightstreamer/control_connection'
|
5
6
|
require 'lightstreamer/line_buffer'
|
@@ -7,7 +8,9 @@ require 'lightstreamer/protocol_error'
|
|
7
8
|
require 'lightstreamer/request_error'
|
8
9
|
require 'lightstreamer/session'
|
9
10
|
require 'lightstreamer/stream_connection'
|
11
|
+
require 'lightstreamer/stream_connection_header'
|
10
12
|
require 'lightstreamer/subscription'
|
13
|
+
require 'lightstreamer/utf16'
|
11
14
|
require 'lightstreamer/version'
|
12
15
|
|
13
16
|
require 'lightstreamer/cli/main'
|
@@ -31,16 +31,14 @@ module Lightstreamer
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
-
# Executes a POST request to the control address with the specified payload. Raises
|
35
|
-
# fails. Returns the response
|
34
|
+
# Executes a POST request to the control address with the specified payload. Raises {RequestError} if the HTTP
|
35
|
+
# request fails. Returns the response body split into individual lines.
|
36
36
|
def execute_post_request(payload)
|
37
|
-
response =
|
37
|
+
response = Typhoeus.post @control_url, body: payload
|
38
|
+
|
39
|
+
raise RequestError.new(response.return_message, response.response_code) unless response.success?
|
38
40
|
|
39
41
|
response.body.split("\n").map(&:strip)
|
40
|
-
rescue RestClient::Exception => exception
|
41
|
-
raise RequestError, exception.message
|
42
|
-
rescue SocketError => socket_error
|
43
|
-
raise RequestError, socket_error
|
44
42
|
end
|
45
43
|
|
46
44
|
# Constructs the payload for a Lightstreamer control request based on the given options hash. See {#execute} for
|
@@ -54,7 +52,7 @@ module Lightstreamer
|
|
54
52
|
|
55
53
|
build_optional_payload_fields options, params
|
56
54
|
|
57
|
-
|
55
|
+
params
|
58
56
|
end
|
59
57
|
|
60
58
|
def build_optional_payload_fields(options, params)
|
@@ -36,20 +36,22 @@ module Lightstreamer
|
|
36
36
|
def connect
|
37
37
|
return if @stream_connection
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
create_stream_connection
|
40
|
+
create_control_connection
|
41
|
+
create_processing_thread
|
42
|
+
rescue
|
43
|
+
@stream_connection = nil
|
44
|
+
raise
|
45
|
+
end
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
handle_connection_error
|
49
|
-
end
|
47
|
+
# Returns whether this session is currently connected and has an active stream connection.
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
def connected?
|
51
|
+
!@stream_connection.nil?
|
50
52
|
end
|
51
53
|
|
52
|
-
# Disconnects this session and shuts down its stream and processing threads.
|
54
|
+
# Disconnects this session and shuts down its stream connection and processing threads.
|
53
55
|
def disconnect
|
54
56
|
@stream_connection.disconnect if @stream_connection
|
55
57
|
|
@@ -102,25 +104,20 @@ module Lightstreamer
|
|
102
104
|
|
103
105
|
private
|
104
106
|
|
105
|
-
|
106
|
-
|
107
|
-
@stream_connection.
|
107
|
+
def create_stream_connection
|
108
|
+
@stream_connection = StreamConnection.new self
|
109
|
+
@stream_connection.connect
|
108
110
|
end
|
109
111
|
|
110
|
-
# Attempts to parses the next line of data from the stream connection as a custom control address and then uses this
|
111
|
-
# address to create the control connection. Note that the control address is optional and if it is absent then
|
112
|
-
# {#server_url} will be used instead of a custom control address.
|
113
112
|
def create_control_connection
|
114
|
-
|
115
|
-
control_address = (match && match.captures.first) || server_url
|
116
|
-
|
117
|
-
# The rest of the contents in the header is ignored, so read up until the blank line that marks its ending
|
118
|
-
loop { break if @stream_connection.read_line == '' }
|
113
|
+
control_address = @stream_connection.control_address || server_url
|
119
114
|
|
120
|
-
# If the control
|
121
|
-
|
115
|
+
# If the control address doesn't have a schema then use the same schema as the server URL
|
116
|
+
unless control_address.start_with? 'http'
|
117
|
+
control_address = "#{URI(server_url).scheme}://#{control_address}"
|
118
|
+
end
|
122
119
|
|
123
|
-
@control_connection = ControlConnection.new @session_id, control_address
|
120
|
+
@control_connection = ControlConnection.new @stream_connection.session_id, control_address
|
124
121
|
end
|
125
122
|
|
126
123
|
# Starts the processing thread that reads and processes incoming data from the stream connection.
|
@@ -129,8 +126,14 @@ module Lightstreamer
|
|
129
126
|
loop do
|
130
127
|
line = @stream_connection.read_line
|
131
128
|
|
129
|
+
break if line.nil?
|
130
|
+
|
132
131
|
process_stream_data line unless line.empty?
|
133
132
|
end
|
133
|
+
|
134
|
+
# The stream connection has died, so exit the processing thread
|
135
|
+
warn "Lightstreamer: processing thread exiting, error: #{@stream_connection.error}"
|
136
|
+
@processing_thread = @stream_connection = @control_connection = nil
|
134
137
|
end
|
135
138
|
end
|
136
139
|
|
@@ -145,17 +148,5 @@ module Lightstreamer
|
|
145
148
|
|
146
149
|
warn "Lightstreamer: unprocessed stream data '#{line}'" unless was_processed
|
147
150
|
end
|
148
|
-
|
149
|
-
# Handles a failure to establish a stream connection by reading off the error code and error message then raising
|
150
|
-
# a {ProtocolError}.
|
151
|
-
def handle_connection_error
|
152
|
-
error_code = @stream_connection.read_line
|
153
|
-
error_message = @stream_connection.read_line
|
154
|
-
|
155
|
-
@stream_connection = nil
|
156
|
-
@control_connection = nil
|
157
|
-
|
158
|
-
raise ProtocolError.new(error_message, error_code)
|
159
|
-
end
|
160
151
|
end
|
161
152
|
end
|
@@ -5,6 +5,16 @@ module Lightstreamer
|
|
5
5
|
# @return [Thread] The thread used to process incoming streaming data.
|
6
6
|
attr_reader :thread
|
7
7
|
|
8
|
+
# @return [String] The session ID returned from the server when this stream connection was initiated.
|
9
|
+
attr_reader :session_id
|
10
|
+
|
11
|
+
# @return [String] The control address returned from the server when this stream connection was initiated.
|
12
|
+
attr_reader :control_address
|
13
|
+
|
14
|
+
# @return [ProtocolError, RequestError, String] If an error occurs on the stream thread that causes this stream
|
15
|
+
# to disconnect then the exception or error details will be stored in this attribute.
|
16
|
+
attr_reader :error
|
17
|
+
|
8
18
|
# Establishes a new stream connection using the authentication details from the passed session.
|
9
19
|
#
|
10
20
|
# @param [Session] session The session to create a stream connection for.
|
@@ -12,73 +22,128 @@ module Lightstreamer
|
|
12
22
|
@session = session
|
13
23
|
@queue = Queue.new
|
14
24
|
|
15
|
-
|
25
|
+
@stream_create_url = URI.join(session.server_url, '/lightstreamer/create_session.txt').to_s
|
26
|
+
@stream_bind_url = URI.join(session.server_url, '/lightstreamer/bind_session.txt').to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
# Establishes a new stream connection using the authentication details from the session that was passed to
|
30
|
+
# {#initialize}. Raises {ProtocolError} or {RequestError} on failure.
|
31
|
+
def connect
|
32
|
+
return if @thread
|
33
|
+
@session_id = @error = nil
|
34
|
+
|
16
35
|
create_stream_thread
|
36
|
+
|
37
|
+
# Wait until the connection result is known
|
38
|
+
until @session_id || @error
|
39
|
+
end
|
40
|
+
|
41
|
+
raise @error if @error
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns whether or not this stream connection is connected.
|
45
|
+
#
|
46
|
+
# @return [Boolean]
|
47
|
+
def connected?
|
48
|
+
!@thread.nil?
|
17
49
|
end
|
18
50
|
|
19
51
|
# Disconnects this stream connection by shutting down the streaming thread.
|
20
52
|
def disconnect
|
21
|
-
|
53
|
+
if @thread
|
54
|
+
Thread.kill @thread
|
55
|
+
@thread.join
|
56
|
+
end
|
22
57
|
|
23
|
-
|
24
|
-
@thread.join
|
58
|
+
@session_id = @control_address = @error = @thread = nil
|
25
59
|
end
|
26
60
|
|
27
|
-
# Reads the next line of streaming data.
|
61
|
+
# Reads the next line of streaming data. If the streaming thread is alive then this method blocks the calling thread
|
62
|
+
# until a line of data is available. If the streaming thread is not active then any unconsumed lines will be
|
63
|
+
# returned and after that the return value will be `nil`.
|
64
|
+
#
|
65
|
+
# @return [String, nil]
|
28
66
|
def read_line
|
67
|
+
return nil if @queue.empty? && @thread.nil?
|
68
|
+
|
29
69
|
@queue.pop
|
30
70
|
end
|
31
71
|
|
32
72
|
private
|
33
73
|
|
34
|
-
def create_stream
|
35
|
-
@stream = Net::HTTP.new stream_uri.host, stream_uri.port
|
36
|
-
@stream.use_ssl = true if stream_uri.port == 443
|
37
|
-
end
|
38
|
-
|
39
74
|
def create_stream_thread
|
40
75
|
@thread = Thread.new do
|
41
76
|
begin
|
42
|
-
|
77
|
+
stream_thread_main
|
43
78
|
rescue StandardError => error
|
44
|
-
|
45
|
-
exit 1
|
79
|
+
@error = error
|
46
80
|
end
|
81
|
+
|
82
|
+
@thread = nil
|
47
83
|
end
|
48
84
|
end
|
49
85
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
86
|
+
def stream_thread_main
|
87
|
+
connect_stream_and_process_data stream_create_post_request
|
88
|
+
|
89
|
+
while @loop
|
90
|
+
@loop = false
|
91
|
+
connect_stream_and_process_data stream_bind_post_request
|
53
92
|
end
|
54
93
|
end
|
55
94
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
95
|
+
def stream_create_post_request
|
96
|
+
params = { LS_op2: 'create', LS_cid: 'mgQkwtwdysogQz2BJ4Ji kOj2Bg', LS_user: @session.username,
|
97
|
+
LS_password: @session.password }
|
98
|
+
|
99
|
+
params[:LS_adapter_set] = @session.adapter_set if @session.adapter_set
|
100
|
+
|
101
|
+
Typhoeus::Request.new @stream_create_url, method: :post, params: params
|
102
|
+
end
|
103
|
+
|
104
|
+
def stream_bind_post_request
|
105
|
+
Typhoeus::Request.new @stream_bind_url, method: :post, params: { LS_session: @session_id }
|
106
|
+
end
|
107
|
+
|
108
|
+
def connect_stream_and_process_data(request)
|
109
|
+
@header = StreamConnectionHeader.new
|
110
|
+
|
111
|
+
buffer = LineBuffer.new
|
112
|
+
request.on_body do |data|
|
113
|
+
buffer.process data, &method(:process_stream_line)
|
114
|
+
end
|
115
|
+
|
116
|
+
request.on_complete do |response|
|
117
|
+
@error = RequestError.new(response.return_message, response.response_code) unless response.success?
|
64
118
|
end
|
119
|
+
|
120
|
+
request.run
|
65
121
|
end
|
66
122
|
|
67
|
-
def
|
68
|
-
|
123
|
+
def process_stream_line(line)
|
124
|
+
if @header
|
125
|
+
process_header_line line
|
126
|
+
else
|
127
|
+
process_body_line line
|
128
|
+
end
|
69
129
|
end
|
70
130
|
|
71
|
-
def
|
72
|
-
|
73
|
-
LS_op2: 'create',
|
74
|
-
LS_cid: 'mgQkwtwdysogQz2BJ4Ji kOj2Bg',
|
75
|
-
LS_user: @session.username,
|
76
|
-
LS_password: @session.password
|
77
|
-
}
|
131
|
+
def process_header_line(line)
|
132
|
+
return if @header.process_header_line line
|
78
133
|
|
79
|
-
|
134
|
+
@control_address = @header['ControlAddress']
|
135
|
+
@session_id = @header['SessionId']
|
136
|
+
@error = @header.error
|
137
|
+
|
138
|
+
@header = nil
|
139
|
+
end
|
80
140
|
|
81
|
-
|
141
|
+
def process_body_line(line)
|
142
|
+
if line == 'LOOP'
|
143
|
+
@loop = true
|
144
|
+
elsif !ignore_line?(line)
|
145
|
+
@queue.push line
|
146
|
+
end
|
82
147
|
end
|
83
148
|
|
84
149
|
def ignore_line?(line)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Lightstreamer
|
2
|
+
# Helper class that processes the contents of the header returned by the server when a new stream connection is
|
3
|
+
# created or an existing session is bound to.
|
4
|
+
class StreamConnectionHeader
|
5
|
+
# @return [ProtocolError, RequestError] If there was an error in the header then this value will be set to the
|
6
|
+
# error instance that should be raised in response.
|
7
|
+
attr_reader :error
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@data = {}
|
11
|
+
@lines = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Processes a single line of header information. The return value indicates whether further data is required in
|
15
|
+
# order to complete the header.
|
16
|
+
#
|
17
|
+
# @param [String] line The line of header data to process.
|
18
|
+
#
|
19
|
+
# @return [Boolean] Whether the header is still incomplete and requires further data.
|
20
|
+
def process_header_line(line)
|
21
|
+
@lines << line
|
22
|
+
|
23
|
+
unless %w(OK ERROR).include? @lines.first
|
24
|
+
@error = RequestError.new line
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
return true if @lines.first == 'OK' && !line.empty?
|
29
|
+
return true if @lines.first == 'ERROR' && @lines.size < 3
|
30
|
+
|
31
|
+
parse_header
|
32
|
+
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the value for the item with the specified name in this header, or `nil` if no item with the specified name
|
37
|
+
# was part of this header
|
38
|
+
#
|
39
|
+
# @param [String] item_name The name of the item to return the header value for.
|
40
|
+
#
|
41
|
+
# @return [String, nil] The value of the item as specified in this header, or `nil` if the item name was not
|
42
|
+
# specified in this header.
|
43
|
+
def [](item_name)
|
44
|
+
@data[item_name]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse_header
|
50
|
+
if @lines.first == 'OK'
|
51
|
+
@lines[1..-1].each do |line|
|
52
|
+
match = line.match(/^([^:]*):(.*)$/)
|
53
|
+
@data[match.captures[0]] = match.captures[1] if match
|
54
|
+
end
|
55
|
+
elsif @lines.first == 'ERROR'
|
56
|
+
@error = ProtocolError.new @lines[2], @lines[1]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -134,12 +134,8 @@ module Lightstreamer
|
|
134
134
|
|
135
135
|
private
|
136
136
|
|
137
|
-
# Attempts to parse
|
138
|
-
#
|
139
|
-
# @param [String] line The line of stream data to parse.
|
140
|
-
#
|
141
|
-
# @return [Array] The first value is the item index, and the second is a hash of the values contained in the stream
|
142
|
-
# data. Will be nil if the stream data was not able to be parsed.
|
137
|
+
# Attempts to parse a line of stream data. If parsing is successful then the first return value is the item index,
|
138
|
+
# and the second is a hash of the values contained in the stream data.
|
143
139
|
def parse_stream_data(line)
|
144
140
|
match = line.match stream_data_regex
|
145
141
|
return unless match
|
@@ -152,18 +148,12 @@ module Lightstreamer
|
|
152
148
|
|
153
149
|
# Returns the regular expression that will match a single line of data in the incoming stream that is relevant to
|
154
150
|
# this subscription. The ID at the beginning must match, as well as the number of fields.
|
155
|
-
#
|
156
|
-
# @return [Regexp]
|
157
151
|
def stream_data_regex
|
158
152
|
Regexp.new "^#{id},(\\d+)#{'\|(.*)' * fields.size}"
|
159
153
|
end
|
160
154
|
|
161
155
|
# Parses an array of values from an incoming line of stream data into a hash where the keys are the field names
|
162
156
|
# defined for this subscription.
|
163
|
-
#
|
164
|
-
# @param [String] values The raw values from the incoming stream data.
|
165
|
-
#
|
166
|
-
# @return [Hash] The parsed values as a hash where the keys are the field names of this subscription.
|
167
157
|
def parse_values(values)
|
168
158
|
hash = {}
|
169
159
|
|
@@ -183,10 +173,7 @@ module Lightstreamer
|
|
183
173
|
|
184
174
|
value = value[1..-1] if value =~ /^(\$|#)/
|
185
175
|
|
186
|
-
|
187
|
-
# '\uXXXX\uYYYY'. They need to be transformed into native Unicode characters.
|
188
|
-
|
189
|
-
value
|
176
|
+
UTF16.decode_escape_sequences value
|
190
177
|
end
|
191
178
|
|
192
179
|
# Invokes all of this subscription's data callbacks with the specified arguments. Any exceptions that occur in a
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Lightstreamer
|
2
|
+
# This module supports the decoding of UTF-16 escape sequences
|
3
|
+
module UTF16
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Decodes any UTF-16 escape sequences in the form '\uXXXX' into a new string. Invalid escape sequences are removed.
|
7
|
+
def decode_escape_sequences(string)
|
8
|
+
string = decode_surrogate_pairs_escape_sequences string
|
9
|
+
|
10
|
+
# Match all remaining escape sequences
|
11
|
+
string.gsub(/\\u[A-F\d]{4}/i) do |escape_sequence|
|
12
|
+
codepoint = escape_sequence[2..-1].hex
|
13
|
+
|
14
|
+
# Codepoints greater than 0xD7FF are invalid
|
15
|
+
codepoint < 0xD800 ? [codepoint].pack('U') : ''
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Converts any UTF-16 surrogate pairs escape sequences in the form '\uXXXX\uYYYY' into UTF-8.
|
20
|
+
def decode_surrogate_pairs_escape_sequences(string)
|
21
|
+
string.gsub(/\\uD[89AB][A-F\d]{2}\\uD[C-F][A-F\d]{2}/i) do |escape_sequence|
|
22
|
+
high_surrogate = escape_sequence[2...6].hex
|
23
|
+
low_surrogate = escape_sequence[8...12].hex
|
24
|
+
|
25
|
+
codepoint = 0x10000 + ((high_surrogate - 0xD800) << 10) + (low_surrogate - 0xDC00)
|
26
|
+
|
27
|
+
[codepoint].pack 'U'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lightstreamer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.3'
|
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-
|
11
|
+
date: 2016-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: thor
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0.19'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0.19'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: typhoeus
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.1'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activesupport
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,7 +184,9 @@ files:
|
|
184
184
|
- lib/lightstreamer/request_error.rb
|
185
185
|
- lib/lightstreamer/session.rb
|
186
186
|
- lib/lightstreamer/stream_connection.rb
|
187
|
+
- lib/lightstreamer/stream_connection_header.rb
|
187
188
|
- lib/lightstreamer/subscription.rb
|
189
|
+
- lib/lightstreamer/utf16.rb
|
188
190
|
- lib/lightstreamer/version.rb
|
189
191
|
homepage: https://github.com/rviney/lightstreamer
|
190
192
|
licenses:
|