spox-spockets 0.0.6

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/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
+