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