websocket_sequential_client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c86e2bfd2241079309ed7daceed67c861a0e6e27
4
+ data.tar.gz: f1f3b91c65126b6885bd5c6b735e44c350ada22c
5
+ SHA512:
6
+ metadata.gz: 85b89614c39101a15684018881338f6aa747faeeb34e909f112d6fbc2762bd7000c04e9902ad42ed93bb9b35222ab668d2c84553c74adfbb42e5fba8e4f43c3c
7
+ data.tar.gz: e43c3706bef4b80a410ad6946958e608e59a77bff13b79a582a3a1b35b57fb0b83ba4293e254b7db59d487237ec34233c3f775f0f695b77c9c0a4fdaab32cecb
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /.rspec
4
+ /bin/
5
+ /Gemfile.lock
6
+ /_yardoc/
7
+ /coverage/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ script: bundle exec rspec spec/websocket_sequential_client_spec.rb
3
+ before_install:
4
+ - gem update --system
5
+ - gem --version
6
+ - gem update bundler
7
+ rvm:
8
+ - 2.0.0
9
+ - 2.1
10
+ - 2.2
11
+ - ruby-head
12
+ - rbx
13
+ - jruby
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: ruby-head
17
+ - rvm: rbx
18
+ - rvm: jruby
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in websocket_sequential_client.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Masamitsu MURASE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # WebsocketSequentialClient [![Build Status](https://travis-ci.org/masamitsu-murase/websocket_sequential_client.svg)](https://travis-ci.org/masamitsu-murase/websocket_sequential_client)
2
+
3
+ This ruby gem library allows you to access a WebSocket server in a **sequential** way instead of an event-driven way.
4
+
5
+ ## Features
6
+
7
+ * Provides access to a WebSocket server in a *sequential* way.
8
+ Many gem libraries for WebSocket client use *event-driven* style.
9
+ This style has many advantages, but it's not so easy to understand.
10
+ This gem library provides a *simple* and *sequential* access to a WebSocket server.
11
+
12
+ * Independent from eventmachine and other frameworks.
13
+ You can use this gem library with/without eventmachine and other frameworks.
14
+ Eventmachine is a great and useful library, but it uses some global functions, such as `EM.run`, `EM.stop` and so on, so it might conflicts with *your* environment.
15
+ This gem library does not depends on any frameworks, so you can use it in your environment.
16
+
17
+ If you need performance and/or scalability, you should use the other great libraries, such as em-websocket.
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'websocket_sequential_client'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install websocket_sequential_client
34
+
35
+ ## Usage
36
+
37
+ Call `send` and `recv` to communicate with the server.
38
+
39
+ ```ruby
40
+ require "websocket_sequential_client"
41
+
42
+ WebSocketSequentialClient::WebSocket.open "ws://server-url" do |ws|
43
+ ws.send "Hello"
44
+ puts ws.recv
45
+ end
46
+ ```
47
+
48
+ **Note**: `recv` method is a *blocking* method. It will wait until a message is sent from the server.
49
+
50
+ ## More examples
51
+
52
+ ### Send and receive *text* and *binary* messages.
53
+
54
+ ```ruby
55
+ # coding: utf-8
56
+
57
+ require "websocket_sequential_client"
58
+
59
+ ws = WebSocketSequentialClient::WebSocket.new "ws://server-url"
60
+
61
+ # `send` method guesses whether the argument is text or binary based on the encoding.
62
+ # The encoding of "Hello" is UTF-8, so the messages is sent as text.
63
+ ws.send "Hello"
64
+
65
+ # You can also use `send_text` method.
66
+ ws.send_text "World"
67
+
68
+
69
+ # The following lines send binary messages
70
+ # because the argument has ASCII-8BIT encoding.
71
+ ws.send [0,1,2,3].pack("C*")
72
+ ws.send "example".b
73
+
74
+ # Of course, `send_binary` is supported.
75
+ ws.send_binary "binary message"
76
+
77
+
78
+ # You can receive messages from the server with `recv`.
79
+ # If the server sent "text" message, msg has UTF-8 encoding.
80
+ # In the case of "binary" message, msg has ASCII-8BIT encoding.
81
+ msg = ws.recv
82
+
83
+ ws.close
84
+ ```
85
+
86
+ ### Ping and pong.
87
+
88
+ `ping` and `pong` frames are sent in background, so you need not to handle these frames.
89
+
90
+ ```ruby
91
+ require "websocket_sequential_client"
92
+
93
+ # `ping` is enabled as default. The interval is 20s.
94
+ ws = WebSocketSequentialClient::WebSocket.new "ws://server-url"
95
+ #...
96
+ ws.close
97
+
98
+ # You can disable `ping`.
99
+ ws = WebSocketSequentialClient::WebSocket.new "ws://server-url", ping: false
100
+ #...
101
+ ws.close
102
+
103
+ # You can change the interval of `ping` to 10s.
104
+ ws = WebSocketSequentialClient::WebSocket.new "ws://server-url", ping: { interval: 10 }
105
+ #...
106
+ ws.close
107
+ ```
108
+
109
+ ### Send additional headers.
110
+
111
+ You can send additional headers with the request of HTTP handshake.
112
+
113
+ ```ruby
114
+ require "websocket_sequential_client"
115
+
116
+ # Add "Cookie" field to the HTTP request.
117
+ ws = WebSocketSequentialClient::WebSocket.new "ws://server-url", headers: { "Cookie" => "value1" }
118
+ #...
119
+ ws.close
120
+ ```
121
+
122
+
123
+ ## Versions
124
+
125
+ * 1.0.0
126
+ Initial release
127
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ require "websocket_sequential_client/version"
2
+ require "websocket_sequential_client/web_socket"
3
+ require "websocket_sequential_client/write_queue"
4
+ require "websocket_sequential_client/read_queue"
5
+ require "websocket_sequential_client/exception"
6
+
7
+ module WebsocketSequentialClient
8
+ end
9
+
10
+ WebSocketSequentialClient = WebsocketSequentialClient
11
+
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module WebsocketSequentialClient
4
+ class Error < StandardError
5
+ end
6
+
7
+ class SocketAlreadyClosed < Error
8
+ end
9
+
10
+ class HandshakeFailed < Error
11
+ end
12
+
13
+ class InvalidDataReceived < Error
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require "thread"
4
+
5
+ module WebsocketSequentialClient
6
+ class ReadQueue
7
+ def initialize
8
+ @mutex = Mutex.new
9
+ @cond_var = ConditionVariable.new
10
+ @frame_list = []
11
+ @closed_value = nil
12
+ end
13
+
14
+ def close value
15
+ @mutex.synchronize do
16
+ @closed_value = value
17
+ @cond_var.broadcast
18
+ end
19
+ end
20
+
21
+ def available?
22
+ @mutex.synchronize do
23
+ return !(@frame_list.empty?)
24
+ end
25
+ end
26
+
27
+ def push frame
28
+ @mutex.synchronize do
29
+ return if @closed_value
30
+
31
+ @frame_list.push frame
32
+ @cond_var.broadcast
33
+ end
34
+ end
35
+
36
+ def pop
37
+ @mutex.synchronize do
38
+ until !(@frame_list.empty?) or @closed_value
39
+ @cond_var.wait @mutex
40
+ end
41
+
42
+ if @frame_list.empty?
43
+ return @closed_value
44
+ else
45
+ return @frame_list.shift
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module WebsocketSequentialClient
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,349 @@
1
+ require("websocket")
2
+ require("uri")
3
+ require("socket")
4
+ require("thread")
5
+
6
+ module WebsocketSequentialClient
7
+ class WebSocket
8
+ DEFAULT_PING_INTERVAL = 20
9
+ DEFAULT_CLOSE_CODE = 1000
10
+ DEFAULT_CLOSE_TIMEOUT = 20
11
+
12
+ RECV_SIZE = 1024
13
+
14
+ WS_PORT = 80
15
+
16
+ def self.open *args, &block
17
+ if block
18
+ ws = self.new *args
19
+ begin
20
+ block.call ws
21
+ ensure
22
+ ws.close
23
+ end
24
+ else
25
+ self.new *args
26
+ end
27
+ end
28
+
29
+ def initialize url, opt = { ping: true, headers: {} }
30
+ @read_queue = ReadQueue.new
31
+ @write_queue = WriteQueue.new
32
+
33
+ @closed_status_mutex = Mutex.new
34
+ @closed_status_cond_var = ConditionVariable.new
35
+ @closed_status = nil
36
+ @close_timeout = DEFAULT_CLOSE_TIMEOUT
37
+
38
+ @close_code = nil
39
+ @close_reason = nil
40
+
41
+ @ping_th = nil
42
+
43
+ url = URI.parse url.to_s unless url.kind_of? URI
44
+ @url = url
45
+
46
+ case url.scheme
47
+ when "ws"
48
+ @socket = TCPSocket.new(url.host, url.port || WS_PORT)
49
+ else
50
+ raise ArgumentError, "URL scheme must be 'ws'."
51
+ end
52
+
53
+ hs = handshake(opt[:headers])
54
+
55
+ @version = hs.version
56
+
57
+ Thread.start{ send_background }
58
+ Thread.start{ receive_background }
59
+ start_ping_thread opt[:ping] if opt[:ping]
60
+
61
+ rescue
62
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
63
+ raise
64
+ end
65
+ attr_reader :close_code, :close_reason
66
+
67
+ def available?
68
+ @read_queue.available?
69
+ end
70
+ alias data_available? available?
71
+
72
+ def recv
73
+ data = @read_queue.pop
74
+ raise data if data.kind_of? StandardError
75
+
76
+ case data.type
77
+ when :text
78
+ data.to_s.force_encoding Encoding::UTF_8
79
+ when :binary
80
+ data.to_s.force_encoding Encoding::BINARY
81
+ else
82
+ raise NotImplementedError
83
+ end
84
+ end
85
+ alias receive recv
86
+
87
+ def send data, type = :guess
88
+ case type
89
+ when :guess
90
+ if data.encoding == Encoding::BINARY
91
+ type = :binary
92
+ elsif Encoding.compatible?(data.encoding, Encoding::UTF_8)
93
+ type = :text
94
+ else
95
+ raise ArgumentError, "Invalid encoding."
96
+ end
97
+ when :text
98
+ unless Encoding.compatible?(data.encoding, Encoding::UTF_8)
99
+ raise ArgumentError, "Invalid encoding"
100
+ end
101
+ when :binary
102
+ #
103
+ else
104
+ raise ArgumentError, "Invalid type specified"
105
+ end
106
+
107
+ # data.b is necessary for the current implementation of websocket gem library.
108
+ frame = ::WebSocket::Frame::Outgoing::Client.new(data: data.b, type: type, version: @version)
109
+
110
+ result = @write_queue.process_frame frame
111
+ raise result if result.kind_of? StandardError
112
+ end
113
+
114
+ def send_text data
115
+ send data, :text
116
+ end
117
+
118
+ def send_binary data
119
+ send data, :binary
120
+ end
121
+
122
+ def close code = nil, reason = nil, opt = { timeout: DEFAULT_CLOSE_TIMEOUT, wait_for_response: true }
123
+ code ||= DEFAULT_CLOSE_CODE
124
+ param = {
125
+ code: code,
126
+ type: :close,
127
+ version: @version
128
+ }
129
+ param[:data] = reason if reason
130
+ frame = ::WebSocket::Frame::Outgoing::Client.new(param)
131
+
132
+ @close_timeout = (opt.key?(:timeout) ? opt[:timeout] : DEFAULT_CLOSE_CODE)
133
+ @write_queue.process_frame frame
134
+
135
+ wait_for_response = (opt.key?(:wait_for_response) ? opt[:wait_for_response] : true)
136
+ if wait_for_response
137
+ @closed_status_mutex.synchronize do
138
+ @closed_status_cond_var.wait @closed_status_mutex until @closed_status == :closed
139
+ end
140
+ end
141
+ end
142
+
143
+ private
144
+ def handshake headers
145
+ hs = ::WebSocket::Handshake::Client.new(url: @url.to_s, headers: headers)
146
+
147
+ @socket.send(hs.to_s, 0)
148
+
149
+ begin
150
+ data = @socket.recv(1)
151
+ raise HandshakeFailed if data.empty?
152
+ hs << data
153
+ end until hs.finished?
154
+
155
+ raise HandshakeFailed unless hs.valid?
156
+
157
+ hs
158
+ end
159
+
160
+ def start_ping_thread ping_opt
161
+ interval = (ping_opt[:interval] rescue DEFAULT_PING_INTERVAL)
162
+ raise ArgumentError, "opt[:ping][:interval] must be a positive number." if interval <= 0
163
+ @ping_th = Thread.start(interval) do |i|
164
+ while true
165
+ sleep i
166
+ return if @closed_status
167
+
168
+ frame = ::WebSocket::Frame::Outgoing::Client.new(type: :ping, version: @version)
169
+ @write_queue.push frame
170
+ end
171
+ end
172
+ end
173
+
174
+ def kill_ping_thread
175
+ return unless @ping_th
176
+
177
+ @ping_th.kill rescue nil
178
+ @ping_th = nil
179
+ end
180
+
181
+ def receive_next_data
182
+ data = @socket.recv(RECV_SIZE)
183
+ if data.empty?
184
+ # Unexpected close
185
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
186
+ return nil
187
+ end
188
+ return data
189
+ rescue => e
190
+ @read_queue.push e
191
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
192
+ return nil
193
+ end
194
+
195
+ def receive_background
196
+ input = ::WebSocket::Frame::Incoming::Client.new
197
+
198
+ data = receive_next_data
199
+ return unless data
200
+
201
+ input << data
202
+
203
+ while true
204
+ error = false
205
+ begin
206
+ next_frame = input.next
207
+ error = true if input.error
208
+ rescue
209
+ error = true
210
+ end
211
+ if error
212
+ @read_queue.push InvalidDataReceived.new
213
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
214
+ return
215
+ end
216
+
217
+ unless next_frame
218
+ data = receive_next_data
219
+ return unless data
220
+
221
+ input << data
222
+ next
223
+ end
224
+
225
+ case next_frame.type
226
+ when :text, :binary
227
+ @read_queue.push next_frame
228
+
229
+ when :close
230
+ @read_queue.close SocketAlreadyClosed.new
231
+ @close_code = next_frame.code
232
+ @close_reason = (next_frame.data && next_frame.data.to_s.force_encoding(Encoding::UTF_8))
233
+
234
+ @closed_status_mutex.synchronize do
235
+ unless @closed_status
236
+ @closed_status = :close_frame_received
237
+ @closed_status_cond_var.broadcast
238
+ end
239
+ end
240
+
241
+ # After close_frame_received is set to true, all frames are ignored.
242
+ kill_ping_thread
243
+
244
+ # If @write_queue is already closed, frame will be ignored.
245
+ frame = ::WebSocket::Frame::Outgoing::Client.new(code: next_frame.code,
246
+ type: :close, version: @version)
247
+ @write_queue.push frame # Ignore result.
248
+
249
+ return
250
+
251
+ when :ping
252
+ f = ::WebSocket::Frame::Outgoing::Client.new(data: next_frame.to_s,
253
+ type: :pong, version: @version)
254
+ @write_queue.push f
255
+
256
+ when :pong
257
+ # Ignore
258
+
259
+ else
260
+ # Unknown packet.
261
+ @read_queue.push InvalidDataReceived.new
262
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
263
+ return
264
+
265
+ end
266
+ end
267
+ rescue SignalException, StandardError
268
+ critical_close
269
+ end
270
+
271
+ def send_background
272
+ while true
273
+ frame = @write_queue.pop
274
+
275
+ begin
276
+ @socket.send(frame.to_s, 0)
277
+ rescue => e
278
+ @write_queue.push_result frame, e
279
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
280
+ return
281
+ end
282
+
283
+ case frame.type
284
+ when :text, :binary
285
+ @write_queue.push_result frame, true
286
+ when :close
287
+ @write_queue.push_result frame, true
288
+ close_process
289
+ return
290
+ else
291
+ # ping and pong are ignored.
292
+ end
293
+ end
294
+ rescue SignalException, StandardError
295
+ critical_close
296
+ end
297
+
298
+ def critical_close
299
+ begin
300
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
301
+ rescue
302
+ end
303
+ end
304
+
305
+ def close_process
306
+ kill_ping_thread
307
+
308
+ # Always start with shutdown of write.
309
+ @socket.shutdown Socket::SHUT_WR rescue nil
310
+ @write_queue.close SocketAlreadyClosed.new
311
+ # Then, try to receive a close frame from the server.
312
+ @closed_status_mutex.synchronize do
313
+ unless @closed_status
314
+ @closed_status_cond_var.wait(@closed_status_mutex, @close_timeout)
315
+ end
316
+ end
317
+
318
+ case @closed_status
319
+ when nil
320
+ # An error occurs and a close frame is not sent from the server,
321
+ # so close anyway.
322
+ @socket.shutdown Socket::SHUT_RD
323
+ close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
324
+ when :close_frame_received
325
+ begin
326
+ true until @socket.recv(RECV_SIZE).empty?
327
+ @socket.shutdown Socket::SHUT_RD
328
+ rescue
329
+ end
330
+ close_socket(nil, nil)
331
+ when :closed
332
+ # Nothing to do.
333
+ end
334
+ end
335
+
336
+ def close_socket(wr_queue_error, rd_queue_error)
337
+ @write_queue.close wr_queue_error if wr_queue_error
338
+ @read_queue.close rd_queue_error if rd_queue_error
339
+
340
+ @socket.close rescue nil
341
+ @closed_status_mutex.synchronize do
342
+ @closed_status = :closed
343
+ @closed_status_cond_var.broadcast
344
+ end
345
+
346
+ kill_ping_thread # Fail safe
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,65 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require "thread"
4
+
5
+ module WebsocketSequentialClient
6
+ class WriteQueue
7
+ def initialize
8
+ @mutex = Mutex.new
9
+ @cond_var = ConditionVariable.new
10
+ @result_mutex = Mutex.new
11
+ @result_cond_var = ConditionVariable.new
12
+ @frame_list = []
13
+ @frame_result = {}
14
+ @closed_value = nil
15
+ end
16
+
17
+ def close value
18
+ @result_mutex.synchronize do
19
+ @closed_value = value
20
+ @result_cond_var.broadcast
21
+ end
22
+ end
23
+
24
+ def push frame
25
+ return if @closed_value
26
+
27
+ @mutex.synchronize do
28
+ @frame_list.push frame
29
+ @cond_var.broadcast
30
+ end
31
+ end
32
+
33
+ def pop
34
+ @mutex.synchronize do
35
+ @cond_var.wait @mutex while @frame_list.empty?
36
+
37
+ return @frame_list.shift
38
+ end
39
+ end
40
+
41
+ def push_result frame, result
42
+ @result_mutex.synchronize do
43
+ return if @closed_value
44
+
45
+ @frame_result[frame.object_id] = result
46
+ @result_cond_var.broadcast
47
+ end
48
+ end
49
+
50
+ def pop_result frame
51
+ @result_mutex.synchronize do
52
+ until @frame_result.key?(frame.object_id) or @closed_value
53
+ @result_cond_var.wait @result_mutex
54
+ end
55
+
56
+ return (@frame_result.delete(frame.object_id) || @closed_value)
57
+ end
58
+ end
59
+
60
+ def process_frame frame
61
+ push frame
62
+ pop_result frame
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'websocket_sequential_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "websocket_sequential_client"
8
+ spec.version = WebsocketSequentialClient::VERSION
9
+ spec.authors = ["Masamitsu MURASE"]
10
+ spec.email = ["masamitsu.murase@gmail.com"]
11
+
12
+ spec.summary = %q{A simple WebSocket client to access a server in a sequential way}
13
+ spec.description = %q{This gem library allows you to access a WebSocket server in a sequential way instead of an event-driven way.}
14
+ spec.homepage = "https://github.com/masamitsu-murase/websocket_sequential_client"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.required_ruby_version = ">= 2.0"
23
+
24
+ spec.add_dependency "websocket"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.9"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.2"
29
+ spec.add_development_dependency "em-websocket"
30
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: websocket_sequential_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Masamitsu MURASE
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-05-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: em-websocket
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: This gem library allows you to access a WebSocket server in a sequential
84
+ way instead of an event-driven way.
85
+ email:
86
+ - masamitsu.murase@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/websocket_sequential_client.rb
98
+ - lib/websocket_sequential_client/exception.rb
99
+ - lib/websocket_sequential_client/read_queue.rb
100
+ - lib/websocket_sequential_client/version.rb
101
+ - lib/websocket_sequential_client/web_socket.rb
102
+ - lib/websocket_sequential_client/write_queue.rb
103
+ - websocket_sequential_client.gemspec
104
+ homepage: https://github.com/masamitsu-murase/websocket_sequential_client
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '2.0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.4.6
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: A simple WebSocket client to access a server in a sequential way
128
+ test_files: []