spockets 0.0.6 → 0.1.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.
- 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
|
|