spockets 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +44 -60
- data/lib/spockets.rb +1 -0
- data/lib/spockets/Exceptions.rb +4 -22
- data/lib/spockets/Spockets.rb +47 -24
- data/lib/spockets/Watcher.rb +45 -15
- metadata +37 -35
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
== Spockets
|
1
|
+
== Spockets
|
2
2
|
|
3
3
|
Spockets is a simple library for dealing with multiple sockets.
|
4
4
|
You supply a socket, and one or more blocks to
|
@@ -6,38 +6,37 @@ execute, and Spockets will make sure those blocks get
|
|
6
6
|
executed when something comes in over the wire. It's just
|
7
7
|
that simple.
|
8
8
|
|
9
|
-
|
10
|
-
This
|
11
|
-
|
12
|
-
|
13
|
-
action can stay in one local pool.
|
9
|
+
This library makes use of a thread pool for running user
|
10
|
+
defined blocks. This setup allows us to use a single thread
|
11
|
+
for socket monitoring, as well as prevents long running blocks
|
12
|
+
from delaying everybody else.
|
14
13
|
|
15
|
-
install (
|
14
|
+
=== install (easy):
|
16
15
|
|
17
|
-
gem install
|
16
|
+
gem install actionpool
|
18
17
|
|
19
|
-
install (
|
18
|
+
=== install (less easy):
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
git clone http://github.com/spox/actionpool.git
|
21
|
+
cd actionpool
|
22
|
+
gem build *.gemspec
|
23
|
+
gem install ./
|
23
24
|
|
24
|
-
|
25
|
+
=== install (less easy that's a little easier)
|
25
26
|
|
26
|
-
|
27
|
-
cd spockets
|
28
|
-
gem build spockets.gemspec
|
29
|
-
gem install ./
|
27
|
+
{rip}[http://hellorip.com/about.html] makes it easy to install directly from a github repository.
|
30
28
|
|
31
|
-
|
32
|
-
should really consider giving them a look. If you want to
|
33
|
-
view them online, you can see them here:
|
29
|
+
=== Testing
|
34
30
|
|
35
|
-
|
31
|
+
ActionPool is currently tested on:
|
36
32
|
|
37
|
-
|
38
|
-
|
33
|
+
* Ruby 1.8.6-p383
|
34
|
+
* Ruby 1.8.7-p248
|
35
|
+
* Ruby 1.9.1-p376
|
36
|
+
* JRuby 1.4.0
|
39
37
|
|
40
|
-
|
38
|
+
It has RDocs. They are short, but will be helpful and you
|
39
|
+
should really consider giving them a look.
|
41
40
|
|
42
41
|
Examples are usually helpful, so here we go:
|
43
42
|
|
@@ -47,40 +46,25 @@ Code:
|
|
47
46
|
require 'spockets'
|
48
47
|
spockets = Spockets::Spockets.new
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
Connected to 192.168.0.95.
|
73
|
-
Escape character is '^]'.
|
74
|
-
foobar
|
75
|
-
complete
|
76
|
-
^]
|
77
|
-
telnet> quit
|
78
|
-
Connection closed.
|
79
|
-
|
80
|
-
Output:
|
81
|
-
|
82
|
-
Socket: #<TCPSocket:0x98ec5ac>
|
83
|
-
Socket: #<TCPSocket:0x98ec37c>
|
84
|
-
#<TCPSocket:0x98ec37c>: foobar
|
85
|
-
#<TCPSocket:0x98ec5ac>: goodbyeworld
|
86
|
-
#<TCPSocket:0x98ec37c>: complete
|
49
|
+
server = TCPServer.new('localhost', 4000)
|
50
|
+
socket = TCPSocket.new('localhost', 4000)
|
51
|
+
rsocket = server.accept
|
52
|
+
output = []
|
53
|
+
spockets.add(rsocket){|s|output << s.strip}
|
54
|
+
spockets.add(rsocket, 'data'){|s, d| output << "#{d}: #{s.strip}"}
|
55
|
+
spockets.on_close(rsocket){|s| output << "closed: #{s}"}
|
56
|
+
socket.puts "fubar"
|
57
|
+
socket.close
|
58
|
+
sleep(0.01)
|
59
|
+
p output
|
60
|
+
|
61
|
+
=> ["fubar", "data: fubar", "closed: #<TCPSocket:0xb7637bdc>"]
|
62
|
+
|
63
|
+
== Last remarks
|
64
|
+
|
65
|
+
If you find any bugs, please report them through {github}[http://github.com/spox/spockets/issues]. If you are in need of any help, you can generally find me on DALnet and Freenode.
|
66
|
+
|
67
|
+
== License
|
68
|
+
|
69
|
+
Spockets is licensed under the LGPLv3
|
70
|
+
Copyright (c) 2009 spox <spox@modspox.com>
|
data/lib/spockets.rb
CHANGED
data/lib/spockets/Exceptions.rb
CHANGED
@@ -1,37 +1,19 @@
|
|
1
1
|
module Spockets
|
2
|
-
|
3
|
-
class DuplicateSocket < Exception
|
4
|
-
attr_reader :socket
|
5
|
-
def initialize(s)
|
6
|
-
@socket = s
|
7
|
-
end
|
8
|
-
end
|
9
2
|
|
10
|
-
class UnknownSocket <
|
3
|
+
class UnknownSocket < StandardError
|
11
4
|
attr_reader :socket
|
12
5
|
def initialize(s)
|
13
6
|
@socket = s
|
14
7
|
end
|
15
8
|
end
|
16
9
|
|
17
|
-
class AlreadyRunning <
|
10
|
+
class AlreadyRunning < StandardError
|
18
11
|
end
|
19
12
|
|
20
|
-
class NotRunning <
|
13
|
+
class NotRunning < StandardError
|
21
14
|
end
|
22
15
|
|
23
|
-
class Resync <
|
24
|
-
end
|
25
|
-
|
26
|
-
class MissingArgument < Exception
|
27
|
-
attr_reader :argument
|
28
|
-
def initialize(a)
|
29
|
-
@argument = a
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_s
|
33
|
-
"Missing required argument: #{@argument}"
|
34
|
-
end
|
16
|
+
class Resync < StandardError
|
35
17
|
end
|
36
18
|
|
37
19
|
end
|
data/lib/spockets/Spockets.rb
CHANGED
@@ -11,32 +11,51 @@ module Spockets
|
|
11
11
|
# creates a new holder
|
12
12
|
def initialize(args={})
|
13
13
|
@sockets = {}
|
14
|
+
@sync = true
|
14
15
|
@watcher = Watcher.new(:sockets => @sockets, :clean => args[:clean], :pool => args[:pool])
|
15
16
|
end
|
17
|
+
|
18
|
+
# Will sockets be resynced
|
19
|
+
def sync?
|
20
|
+
@sync.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
# b:: boolean
|
24
|
+
# Set whether resync on addition
|
25
|
+
def sync=(b)
|
26
|
+
@sync = b != false
|
27
|
+
end
|
28
|
+
|
29
|
+
# b:: boolean to force sync status to true
|
30
|
+
# Resync sockets in Watcher
|
31
|
+
def sync(b = nil)
|
32
|
+
@sync = b != false unless b.nil?
|
33
|
+
if(@sync)
|
34
|
+
begin
|
35
|
+
@watcher.sync
|
36
|
+
rescue NotRunning
|
37
|
+
start
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
16
41
|
|
17
42
|
# socket:: socket to listen to
|
43
|
+
# data:: data to be passed to block. NOTE: string from
|
44
|
+
# socket will be prepended to argument list
|
18
45
|
# block:: block to execute when activity is received
|
19
46
|
# Adds a socket to the list to listen to. When a string
|
20
47
|
# is received on the socket, it will send it to the block
|
21
48
|
# for processing
|
22
|
-
def add(socket, &block)
|
23
|
-
raise
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
@watcher.sync
|
28
|
-
rescue NotRunning
|
29
|
-
start
|
49
|
+
def add(socket, *data, &block)
|
50
|
+
raise ArgumentError.new('Block must be supplied') unless block_given?
|
51
|
+
raise ArgumentError.new('Block must allow at least one argument') if block.arity == 0
|
52
|
+
if(block.arity > 0 && block.arity != (data.size + 1))
|
53
|
+
raise ArgumentError.new('Invalid number of arguments for block')
|
30
54
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# This will add additional blocks to the associated
|
36
|
-
# socket to be executed when a new string is received
|
37
|
-
def extra(socket, &block)
|
38
|
-
raise UnknownSocket.new(socket) unless @sockets.has_key?(socket)
|
39
|
-
@sockets[socket][:procs] << block
|
55
|
+
@sockets[socket] ||= {}
|
56
|
+
@sockets[socket][:procs] ||= []
|
57
|
+
@sockets[socket][:procs] << [data, block]
|
58
|
+
sync
|
40
59
|
end
|
41
60
|
|
42
61
|
# socket:: socket to remove
|
@@ -44,20 +63,24 @@ module Spockets
|
|
44
63
|
def remove(socket)
|
45
64
|
raise UnknownSocket.new(socket) unless @sockets.has_key?(socket)
|
46
65
|
@sockets.delete(socket)
|
47
|
-
|
48
|
-
@watcher.sync
|
49
|
-
rescue NotRunning
|
50
|
-
start
|
51
|
-
end
|
66
|
+
sync
|
52
67
|
end
|
53
68
|
|
54
69
|
# socket:: socket to add close action
|
70
|
+
# data:: data to be passed to the block. NOTE: socket
|
71
|
+
# will be prepended to argument list
|
55
72
|
# block:: action to perform on socket close
|
56
73
|
# Executes block when socket has been closed. Ideal
|
57
74
|
# for reconnection needs
|
58
|
-
def on_close(socket, &block)
|
75
|
+
def on_close(socket, *data, &block)
|
59
76
|
raise UnknownSocket.new(socket) unless @sockets.has_key?(socket)
|
60
|
-
|
77
|
+
raise ArgumentError.new('Block must be supplied') unless block_given?
|
78
|
+
raise ArgumentError.new('Block must allow at least one argument') if block.arity == 0
|
79
|
+
if(block.arity > 0 && block.arity != (data.size + 1))
|
80
|
+
raise ArgumentError.new('Invalid number of arguments for block')
|
81
|
+
end
|
82
|
+
@sockets[socket][:closed] ||= []
|
83
|
+
@sockets[socket][:closed] << [data, block]
|
61
84
|
end
|
62
85
|
|
63
86
|
# remove all sockets
|
data/lib/spockets/Watcher.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'actionpool'
|
2
|
-
require 'actionpool/Exceptions'
|
3
2
|
require 'iconv'
|
4
3
|
|
5
4
|
module Spockets
|
@@ -9,8 +8,9 @@ module Spockets
|
|
9
8
|
# :clean:: clean UTF8 strings or provide block to run on every read string
|
10
9
|
# :pool:: ActionPool to use
|
11
10
|
# Creates a new watcher for sockets
|
12
|
-
def initialize(args)
|
13
|
-
raise
|
11
|
+
def initialize(args={})
|
12
|
+
raise ArgumentError.new('Expecting argument hash') unless args.is_a?(Hash)
|
13
|
+
raise ArgumentError.new('Missing required argument :sockets') unless args[:sockets] && args[:sockets].is_a?(Hash)
|
14
14
|
@sockets = args[:sockets]
|
15
15
|
@runner = nil
|
16
16
|
@clean = args[:clean] && (args[:clean].is_a?(Proc) || args[:clean].is_a?(TrueClass)) ? args[:clean] : nil
|
@@ -21,18 +21,33 @@ module Spockets
|
|
21
21
|
|
22
22
|
# start the watcher
|
23
23
|
def start
|
24
|
-
@
|
25
|
-
|
24
|
+
if(@sockets.size < 0)
|
25
|
+
raise 'No sockets available for listening'
|
26
|
+
elsif(!@runner.nil? && @runner.alive?)
|
27
|
+
raise AlreadyRunning.new
|
28
|
+
else
|
29
|
+
@stop = false
|
30
|
+
@runner = Thread.new{watch}
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
# stop the watcher
|
29
35
|
def stop
|
30
|
-
@
|
31
|
-
|
32
|
-
@runner.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
if(@runner.nil? && @stop)
|
37
|
+
raise NotRunning.new
|
38
|
+
elsif(@runner.nil? || !@runner.alive?)
|
39
|
+
@stop = true
|
40
|
+
@runner = nil
|
41
|
+
else
|
42
|
+
@stop = true
|
43
|
+
if(@runner)
|
44
|
+
@runner.raise Resync.new if @runner.alive? && @runner.stop?
|
45
|
+
@runner.join(0.05) if @runner
|
46
|
+
@runner.kill if @runner && @runner.alive?
|
47
|
+
end
|
48
|
+
@runner = nil
|
49
|
+
end
|
50
|
+
nil
|
36
51
|
end
|
37
52
|
|
38
53
|
# is the watcher running?
|
@@ -52,7 +67,8 @@ module Spockets
|
|
52
67
|
end
|
53
68
|
|
54
69
|
private
|
55
|
-
|
70
|
+
|
71
|
+
# Watch the sockets and send strings for processing
|
56
72
|
def watch
|
57
73
|
until(@stop)
|
58
74
|
begin
|
@@ -60,7 +76,11 @@ module Spockets
|
|
60
76
|
for sock in resultset[0]
|
61
77
|
string = sock.gets
|
62
78
|
if(sock.closed? || string.nil?)
|
63
|
-
@sockets[sock][:closed]
|
79
|
+
if(@sockets[sock][:closed])
|
80
|
+
@sockets[sock][:closed].each do |pr|
|
81
|
+
pr[1].call(*([sock]+pr[0]))
|
82
|
+
end
|
83
|
+
end
|
64
84
|
@sockets.delete(sock)
|
65
85
|
else
|
66
86
|
string = clean? ? do_clean(string) : string
|
@@ -74,10 +94,18 @@ module Spockets
|
|
74
94
|
@runner = nil
|
75
95
|
end
|
76
96
|
|
97
|
+
# string:: String from socket
|
98
|
+
# sock:: Socket string originated from
|
77
99
|
def process(string, sock)
|
78
|
-
@sockets[sock][:procs].each
|
100
|
+
@sockets[sock][:procs].each do |pr|
|
101
|
+
@pool.process do
|
102
|
+
pr[1].call(*([string]+pr[0]))
|
103
|
+
end
|
104
|
+
end
|
79
105
|
end
|
80
106
|
|
107
|
+
# string:: String to be cleaned
|
108
|
+
# Applies clean block to string
|
81
109
|
def do_clean(string)
|
82
110
|
unless(@ic.nil?)
|
83
111
|
return untaint(string)
|
@@ -85,7 +113,9 @@ module Spockets
|
|
85
113
|
return @clean.call(string)
|
86
114
|
end
|
87
115
|
end
|
88
|
-
|
116
|
+
|
117
|
+
# s:: string
|
118
|
+
# Attempt to clean up string
|
89
119
|
def untaint(s)
|
90
120
|
@ic.iconv(s + ' ')[0..-2]
|
91
121
|
end
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spockets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- spox
|
7
|
+
- spox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-15 00:00:00 Z
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: actionpool
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.2.3
|
24
|
+
version:
|
25
25
|
description: Socket helper library
|
26
26
|
email: spox@modspox.com
|
27
27
|
executables: []
|
@@ -29,43 +29,45 @@ executables: []
|
|
29
29
|
extensions: []
|
30
30
|
|
31
31
|
extra_rdoc_files:
|
32
|
-
- README.rdoc
|
32
|
+
- README.rdoc
|
33
33
|
files:
|
34
|
-
- README.rdoc
|
35
|
-
- CHANGELOG
|
36
|
-
- lib/spockets.rb
|
37
|
-
- lib/spockets/Exceptions.rb
|
38
|
-
- lib/spockets/Spockets.rb
|
39
|
-
- lib/spockets/Watcher.rb
|
34
|
+
- README.rdoc
|
35
|
+
- CHANGELOG
|
36
|
+
- lib/spockets.rb
|
37
|
+
- lib/spockets/Exceptions.rb
|
38
|
+
- lib/spockets/Spockets.rb
|
39
|
+
- lib/spockets/Watcher.rb
|
40
40
|
has_rdoc: true
|
41
|
-
homepage: http://
|
41
|
+
homepage: http://github.com/spox/spockets
|
42
|
+
licenses: []
|
43
|
+
|
42
44
|
post_install_message:
|
43
45
|
rdoc_options:
|
44
|
-
- --title
|
45
|
-
- Spockets
|
46
|
-
- --main
|
47
|
-
- README.rdoc
|
48
|
-
- --line-numbers
|
46
|
+
- --title
|
47
|
+
- Spockets
|
48
|
+
- --main
|
49
|
+
- README.rdoc
|
50
|
+
- --line-numbers
|
49
51
|
require_paths:
|
50
|
-
- lib
|
52
|
+
- lib
|
51
53
|
required_ruby_version: !ruby/object:Gem::Requirement
|
52
54
|
requirements:
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
56
58
|
version:
|
57
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
60
|
requirements:
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
62
64
|
version:
|
63
65
|
requirements: []
|
64
66
|
|
65
67
|
rubyforge_project:
|
66
|
-
rubygems_version: 1.3.
|
68
|
+
rubygems_version: 1.3.5
|
67
69
|
signing_key:
|
68
|
-
specification_version:
|
70
|
+
specification_version: 3
|
69
71
|
summary: Socket Helper
|
70
72
|
test_files: []
|
71
73
|
|