websocket_sequential_client 1.0.0

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 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: []