spox-spockets 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,11 @@
1
+ 0.0.6
2
+ * Fixed bug exposed when extremely fast input
3
+ is received.
4
+ 0.0.5
5
+
6
+ * Added a delete alias
7
+ * Added an on_close method to set an action
8
+ to run when a closed socket is detected
9
+ * Added an include? method to check if a socket
10
+ is currently being watched
11
+ * Added clear method to remove all sockets
data/README.rdoc ADDED
@@ -0,0 +1,86 @@
1
+ == Spockets ==
2
+
3
+ Spockets is a simple library for dealing with multiple sockets.
4
+ You supply a socket, and one or more blocks to
5
+ execute, and Spockets will make sure those blocks get
6
+ executed when something comes in over the wire. It's just
7
+ that simple.
8
+
9
+ There is one requirement for spockets and that is the ActionPool.
10
+ This just allows spockets to use a thread pool for executing
11
+ blocks so you don't end up having to wait on slow blocks. You
12
+ can even provide your own pool for spockets to use, so all the
13
+ action can stay in one local pool.
14
+
15
+ install (stable):
16
+
17
+ gem install spockets
18
+
19
+ install (unstable):
20
+
21
+ gem sources -a http://gems.github.com
22
+ gem install spox-spockets
23
+
24
+ or
25
+
26
+ git clone http://github.com/spox/spockets.git
27
+ cd spockets
28
+ gem build spockets.gemspec
29
+ gem install ./
30
+
31
+ It has RDocs. They are short, but will be helpful and you
32
+ should really consider giving them a look. If you want to
33
+ view them online, you can see them here:
34
+
35
+ http://dev.modspox.com/~sine/spockets
36
+
37
+ There is also a trac site. It has examples as well as
38
+ a bug tracker. It's located at:
39
+
40
+ http://dev.modspox.com/trac/spockets
41
+
42
+ Examples are usually helpful, so here we go:
43
+
44
+ Code:
45
+
46
+ require 'socket'
47
+ require 'spockets'
48
+ spockets = Spockets::Spockets.new
49
+
50
+ se = TCPServer.new(3000)
51
+ loop do
52
+ s = se.accept
53
+ puts "Socket: #{s}"
54
+ spockets.add(s){|string| puts "#{s}: #{string}" }
55
+ end
56
+ sleep
57
+
58
+
59
+ Connecting:
60
+
61
+ > telnet 192.168.0.95 3000
62
+ Trying 192.168.0.95...
63
+ Connected to 192.168.0.95.
64
+ Escape character is '^]'.
65
+ goodbyeworld
66
+ ^]
67
+ telnet> quit
68
+ Connection closed.
69
+
70
+ > telnet 192.168.0.95 3000
71
+ Trying 192.168.0.95...
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
data/lib/spockets.rb ADDED
@@ -0,0 +1 @@
1
+ require 'spockets/Spockets'
@@ -0,0 +1,37 @@
1
+ module Spockets
2
+
3
+ class DuplicateSocket < Exception
4
+ attr_reader :socket
5
+ def initialize(s)
6
+ @socket = s
7
+ end
8
+ end
9
+
10
+ class UnknownSocket < Exception
11
+ attr_reader :socket
12
+ def initialize(s)
13
+ @socket = s
14
+ end
15
+ end
16
+
17
+ class AlreadyRunning < Exception
18
+ end
19
+
20
+ class NotRunning < Exception
21
+ end
22
+
23
+ class Resync < Exception
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
35
+ end
36
+
37
+ end
@@ -0,0 +1,96 @@
1
+ require 'spockets/Exceptions'
2
+ require 'spockets/Watcher'
3
+
4
+ module Spockets
5
+
6
+ class Spockets
7
+
8
+ # :pool:: ActionPool if you would like to consolidate
9
+ # :clean:: Clean string. Set to true for default or
10
+ # provide a block to clean strings
11
+ # creates a new holder
12
+ def initialize(args={})
13
+ @sockets = {}
14
+ @watcher = Watcher.new(:sockets => @sockets, :clean => args[:clean], :pool => args[:pool])
15
+ end
16
+
17
+ # socket:: socket to listen to
18
+ # block:: block to execute when activity is received
19
+ # Adds a socket to the list to listen to. When a string
20
+ # is received on the socket, it will send it to the block
21
+ # for processing
22
+ def add(socket, &block)
23
+ raise DuplicateSocket.new(socket) if @sockets.has_key?(socket)
24
+ @sockets[socket] = {}
25
+ @sockets[socket][:procs] = [block]
26
+ begin
27
+ @watcher.sync
28
+ rescue NotRunning
29
+ start
30
+ end
31
+ end
32
+
33
+ # socket:: socket in list
34
+ # block:: additional block to execute
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
40
+ end
41
+
42
+ # socket:: socket to remove
43
+ # Removes socket from list
44
+ def remove(socket)
45
+ raise UnknownSocket.new(socket) unless @sockets.has_key?(socket)
46
+ @sockets.delete(socket)
47
+ begin
48
+ @watcher.sync
49
+ rescue NotRunning
50
+ start
51
+ end
52
+ end
53
+
54
+ # socket:: socket to add close action
55
+ # block:: action to perform on socket close
56
+ # Executes block when socket has been closed. Ideal
57
+ # for reconnection needs
58
+ def on_close(socket, &block)
59
+ raise UnknownSocket.new(socket) unless @sockets.has_key?(socket)
60
+ @sockets[socket][:closed] = block
61
+ end
62
+
63
+ # remove all sockets
64
+ def clear
65
+ @sockets.clear
66
+ stop
67
+ end
68
+
69
+ # start spockets
70
+ def start
71
+ raise AlreadyRunning.new if @watcher.running?
72
+ @watcher.start
73
+ end
74
+
75
+ # stop spockets
76
+ def stop
77
+ raise NotRunning.new unless @watcher.running?
78
+ @watcher.stop
79
+ end
80
+
81
+ # currently watching sockets
82
+ def running?
83
+ !@watcher.nil? && @watcher.running?
84
+ end
85
+
86
+ # socket:: a socket
87
+ # check if the given socket is being watched
88
+ def include?(socket)
89
+ @sockets.has_key?(socket)
90
+ end
91
+
92
+ alias :delete :remove
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,93 @@
1
+ require 'actionpool'
2
+ require 'actionpool/Exceptions'
3
+ require 'iconv'
4
+
5
+ module Spockets
6
+ class Watcher
7
+
8
+ # :sockets:: socket list
9
+ # :clean:: clean UTF8 strings or provide block to run on every read string
10
+ # :pool:: ActionPool to use
11
+ # Creates a new watcher for sockets
12
+ def initialize(args)
13
+ raise MissingArgument.new(:sockets) unless args[:sockets]
14
+ @sockets = args[:sockets]
15
+ @runner = nil
16
+ @clean = args[:clean] && (args[:clean].is_a?(Proc) || args[:clean].is_a?(TrueClass)) ? args[:clean] : nil
17
+ @pool = args[:pool] && args[:pool].is_a?(ActionPool::Pool) ? args[:pool] : ActionPool::Pool.new
18
+ @ic = @clean && @clean.is_a?(TrueClass) ? Iconv.new('UTF-8//IGNORE', 'UTF-8') : nil
19
+ @stop = true
20
+ end
21
+
22
+ # start the watcher
23
+ def start
24
+ @stop = false
25
+ @runner = Thread.new{watch} if @runner.nil? && @sockets.size > 0
26
+ end
27
+
28
+ # stop the watcher
29
+ def stop
30
+ @stop = true
31
+ @runner.join(0.1)
32
+ @runner.raise Resync.new
33
+ @runner.join(0.1)
34
+ @runner.kill unless @runner.nil? || @runner.alive?
35
+ @runner = nil
36
+ end
37
+
38
+ # is the watcher running?
39
+ def running?
40
+ !@stop
41
+ end
42
+
43
+ # clean incoming strings
44
+ def clean?
45
+ @clean
46
+ end
47
+
48
+ # Ensure all sockets are being listened to
49
+ def sync
50
+ raise NotRunning.new if @runner.nil?
51
+ @runner.raise Resync.new
52
+ end
53
+
54
+ private
55
+
56
+ def watch
57
+ until(@stop)
58
+ begin
59
+ resultset = Kernel.select(@sockets.keys, nil, nil, nil)
60
+ for sock in resultset[0]
61
+ string = sock.gets
62
+ if(sock.closed? || string.nil?)
63
+ @sockets[sock][:closed].call(sock) if @sockets[sock].has_key?(:closed)
64
+ @sockets.delete(sock)
65
+ else
66
+ string = clean? ? do_clean(string) : string
67
+ process(string.dup, sock)
68
+ end
69
+ end
70
+ rescue Resync
71
+ # break select and relisten #
72
+ end
73
+ end
74
+ @runner = nil
75
+ end
76
+
77
+ def process(string, sock)
78
+ @sockets[sock][:procs].each{|b| @pool.process{ b.call(string)}}
79
+ end
80
+
81
+ def do_clean(string)
82
+ unless(@ic.nil?)
83
+ return untaint(string)
84
+ else
85
+ return @clean.call(string)
86
+ end
87
+ end
88
+
89
+ def untaint(s)
90
+ @ic.iconv(s + ' ')[0..-2]
91
+ end
92
+ end
93
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spox-spockets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - spox
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
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"
24
+ version:
25
+ description: Socket helper library
26
+ email: spox@modspox.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
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
40
+ has_rdoc: true
41
+ homepage: http://dev.modspox.com/trac/spockets
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --title
45
+ - Spockets
46
+ - --main
47
+ - README.rdoc
48
+ - --line-numbers
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Socket Helper
70
+ test_files: []
71
+