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 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
- 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.
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 (stable):
14
+ === install (easy):
16
15
 
17
- gem install spockets
16
+ gem install actionpool
18
17
 
19
- install (unstable):
18
+ === install (less easy):
20
19
 
21
- gem sources -a http://gems.github.com
22
- gem install spox-spockets
20
+ git clone http://github.com/spox/actionpool.git
21
+ cd actionpool
22
+ gem build *.gemspec
23
+ gem install ./
23
24
 
24
- or
25
+ === install (less easy that's a little easier)
25
26
 
26
- git clone http://github.com/spox/spockets.git
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
- 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:
29
+ === Testing
34
30
 
35
- http://dev.modspox.com/~sine/spockets
31
+ ActionPool is currently tested on:
36
32
 
37
- There is also a trac site. It has examples as well as
38
- a bug tracker. It's located at:
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
- http://dev.modspox.com/trac/spockets
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
- 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
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
@@ -1 +1,2 @@
1
+ require 'rubygems'
1
2
  require 'spockets/Spockets'
@@ -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 < Exception
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 < Exception
10
+ class AlreadyRunning < StandardError
18
11
  end
19
12
 
20
- class NotRunning < Exception
13
+ class NotRunning < StandardError
21
14
  end
22
15
 
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
16
+ class Resync < StandardError
35
17
  end
36
18
 
37
19
  end
@@ -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 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
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
- 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
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
- begin
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
- @sockets[socket][:closed] = block
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
@@ -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 MissingArgument.new(:sockets) unless args[:sockets]
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
- @stop = false
25
- @runner = Thread.new{watch} if @runner.nil? && @sockets.size > 0
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
- @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
+ 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].call(sock) if @sockets[sock].has_key?(: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{|b| @pool.process{ b.call(string)}}
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.6
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: 2009-06-27 00:00:00 -07:00
12
+ date: 2010-01-15 00:00:00 Z
13
13
  default_executable:
14
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:
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://dev.modspox.com/trac/spockets
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
- - !ruby/object:Gem::Version
55
- version: "0"
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
- - !ruby/object:Gem::Version
61
- version: "0"
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.1
68
+ rubygems_version: 1.3.5
67
69
  signing_key:
68
- specification_version: 2
70
+ specification_version: 3
69
71
  summary: Socket Helper
70
72
  test_files: []
71
73