cod 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,72 +1,25 @@
1
1
  require 'weakref'
2
2
 
3
3
  module Cod
4
- # Channels inside a context know each other and can be looked up by their
5
- # identifier. Context is also responsible for holding connections and for
6
- # doing background work. For most purposes, you will only need one context;
7
- # by using methods on the Cod module directly, you implicitly hold a context
8
- # and call methods there.
4
+ # Context will allow to produce channels retaining some state. Until now,
5
+ # this hasn't been neccessary.
9
6
  #
10
7
  class Context
11
-
12
- def self.install_at_fork(ref)
13
- at_fork do |old_handler|
14
- old_handler.call rescue nil
15
-
16
- begin
17
- ref.reset
18
- rescue WeakRef::RefError
19
- # IGNORED EXCEPTION
20
- end
21
- end
22
- end
23
-
24
- def initialize
25
- @connections = {}
26
-
27
- self.class.install_at_fork(WeakRef.new(self))
28
- end
29
-
30
8
  def pipe(name=nil)
31
9
  Cod::Channel::Pipe.new(name)
32
10
  end
33
11
 
34
12
  def beanstalk(url, name=nil)
35
13
  Cod::Channel::Beanstalk.new(
36
- connection(:beanstalk, url), name)
37
- end
38
-
39
- def reset
40
- @connections = {}
41
- end
42
-
43
- private
44
- # Returns a connection to a system identified by type and url. Currently,
45
- # connections are never released or closed. This is only a minor drawback
46
- # since there will be few of them. (considering we only use this for
47
- # beanstalk)
48
- #
49
- def connection(type, url)
50
- key = connection_key(type, url)
51
-
52
- connection = @connections[key]
53
- return connection if connection
54
-
55
- produce_connection(type, url).tap { |connection|
56
- @connections.store(key, connection) }
14
+ Connection::Beanstalk.new(url), name)
57
15
  end
58
16
 
59
- def connection_key(type, url)
60
- [type, url]
17
+ def tcp(destination)
18
+ Cod::Channel::TCPConnection.new(destination)
61
19
  end
62
-
63
- def produce_connection(type, url)
64
- case type
65
- when :beanstalk
66
- return Connection::Beanstalk.new(url)
67
- end
68
-
69
- fail "Tried to produce a connection of unknown type #{type.inspect}."
20
+
21
+ def tcpserver(bind_to)
22
+ Cod::Channel::TCPServer.new(bind_to)
70
23
  end
71
24
  end
72
25
  end
@@ -0,0 +1,3 @@
1
+ require 'cod/objectio/reader'
2
+ require 'cod/objectio/writer'
3
+ require 'cod/objectio/serializer'
File without changes
@@ -0,0 +1,129 @@
1
+ module Cod::ObjectIO
2
+ # Reads objects from one or more IO streams.
3
+ #
4
+ class Reader
5
+ attr_reader :waiting_messages
6
+ attr_reader :registered_ios
7
+
8
+ # Initializes an object reader that reads from one or several IO objects.
9
+ # You can either pass the io object in the constructor (io) or you can
10
+ # provide the instance with a block that is called each time a read is
11
+ # attempted. The block should return an array of IO objects to also read
12
+ # from.
13
+ #
14
+ # Example:
15
+ # reader = Reader.new { make_connection }
16
+ #
17
+ def initialize(serializer, io=nil, &block)
18
+ @serializer = serializer
19
+ @waiting_messages = []
20
+ @establish_block = block
21
+ @registered_ios = Set.new
22
+
23
+ register io if io
24
+ end
25
+
26
+ # Called before each attempt to read from the wire. This should return
27
+ # the IO objects that need to be considered when reading.
28
+ #
29
+ def establish
30
+ sockets = @establish_block && @establish_block.call(@io) ||
31
+ nil
32
+
33
+ [sockets].flatten
34
+ end
35
+
36
+ def register(ios)
37
+ return unless ios
38
+ ios.each do |io|
39
+ registered_ios << io
40
+ end
41
+ end
42
+
43
+ def unregister(ios)
44
+ ios.each do |io|
45
+ registered_ios.delete(io)
46
+ end
47
+ end
48
+
49
+ def get(opts={})
50
+ return waiting_messages.shift if queued?
51
+
52
+ start_time = Time.now
53
+ loop do
54
+ # p [:looping, opts]
55
+ read_from_wire opts
56
+
57
+ # Early return in case we have a message waiting
58
+ return waiting_messages.shift if queued?
59
+
60
+ if opts[:timeout] && (Time.now-start_time) > opts[:timeout]
61
+ raise Cod::Channel::TimeoutError,
62
+ "No messages waiting in pipe."
63
+ end
64
+ end
65
+
66
+ fail "NOTREACHED"
67
+ end
68
+
69
+ def waiting?
70
+ read_from_wire
71
+ queued?
72
+ end
73
+
74
+ def queued?
75
+ ! waiting_messages.empty?
76
+ end
77
+
78
+ def close
79
+ @registered_ios.each { |io| io.close }
80
+ end
81
+
82
+ private
83
+
84
+ # Checks if data is waiting and processes messages.
85
+ #
86
+ def read_from_wire(opts={})
87
+ # Establish new connections and register them
88
+ register establish
89
+
90
+ # Wait for sockets to have data
91
+ ready_read, _, _ = IO.select(Array(registered_ios), nil, nil, 0.1)
92
+
93
+ # Read all ready sockets
94
+ process_nonblock(ready_read) if ready_read
95
+ end
96
+
97
+ # Reads all data waiting in each io in the ios array.
98
+ #
99
+ def process_nonblock(ios)
100
+ ios.each do |io|
101
+ process_nonblock_single(io)
102
+ end
103
+ end
104
+
105
+ # Reads all data waiting in a single io.
106
+ #
107
+ def process_nonblock_single(io)
108
+ buffer = io.read_nonblock(1024*1024*1024)
109
+
110
+ sio = StringIO.new(buffer)
111
+ while not sio.eof?
112
+ waiting_messages << deserialize(io, sio)
113
+ end
114
+ rescue EOFError
115
+ # Connection has failed/ been disconnected.
116
+ # We will need to reconnect this. If possible.
117
+ registered_ios.delete(io)
118
+ raise
119
+ end
120
+
121
+ # Deserializes a message (in message format, string) into the object that
122
+ # was transmitted. Overwrite this message if you want to control the
123
+ # message format.
124
+ #
125
+ def deserialize(*args)
126
+ @serializer.deserialize(*args)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,26 @@
1
+ module Cod::ObjectIO
2
+ class Serializer
3
+ attr_reader :transformer
4
+
5
+ def initialize(transformer=nil)
6
+ @transformer = transformer
7
+ end
8
+
9
+ # NOTE: source_io is provided to be able to provide back-channels through
10
+ # that same, not to read from it. Reading from this IO object will block
11
+ # you.
12
+ #
13
+ def deserialize(source_io, buffer_io)
14
+ if @transformer
15
+ Marshal.load(buffer_io, proc {
16
+ |obj| transformer.transform(source_io, obj) })
17
+ else
18
+ Marshal.load(buffer_io)
19
+ end
20
+ end
21
+
22
+ def serialize(message)
23
+ Marshal.dump(message)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ module Cod::ObjectIO
2
+ # Writes objects to an IO stream.
3
+ #
4
+ class Writer
5
+ def initialize(serializer, io=nil, &block)
6
+ @serializer = serializer
7
+ @io = io
8
+ @reconnect_block = block
9
+ end
10
+
11
+ def put(message)
12
+ attempt_reconnect
13
+
14
+ @io.write(serialize(message)) if @io
15
+ end
16
+
17
+ def close
18
+ @io.close
19
+ end
20
+
21
+ private
22
+ def attempt_reconnect
23
+ if @reconnect_block
24
+ @io = @reconnect_block[]
25
+ end
26
+ end
27
+
28
+ def serialize(message)
29
+ @serializer.serialize(message)
30
+ end
31
+ end
32
+ end
metadata CHANGED
@@ -1,111 +1,116 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: cod
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
4
5
  prerelease:
5
- version: 0.2.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Kaspar Schiess
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-04-27 00:00:00 +02:00
12
+ date: 2011-07-20 00:00:00.000000000 +02:00
14
13
  default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
17
16
  name: rspec
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2154614480 !ruby/object:Gem::Requirement
20
18
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: "0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
25
23
  type: :development
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: flexmock
29
24
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2154614480
26
+ - !ruby/object:Gem::Dependency
27
+ name: flexmock
28
+ requirement: &2154476000 !ruby/object:Gem::Requirement
31
29
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: "0"
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
36
34
  type: :development
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: sdoc
40
35
  prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
36
+ version_requirements: *2154476000
37
+ - !ruby/object:Gem::Dependency
38
+ name: sdoc
39
+ requirement: &2154475480 !ruby/object:Gem::Requirement
42
40
  none: false
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: "0"
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
47
45
  type: :development
48
- version_requirements: *id003
46
+ prerelease: false
47
+ version_requirements: *2154475480
49
48
  description:
50
49
  email: kaspar.schiess@absurd.li
51
50
  executables: []
52
-
53
51
  extensions: []
54
-
55
- extra_rdoc_files:
52
+ extra_rdoc_files:
56
53
  - README
57
- files:
54
+ files:
58
55
  - Gemfile
59
56
  - HISTORY.txt
60
57
  - LICENSE
61
58
  - Rakefile
62
59
  - README
63
60
  - lib/at_fork.rb
61
+ - lib/cod/channel/abstract.rb
64
62
  - lib/cod/channel/base.rb
65
63
  - lib/cod/channel/beanstalk.rb
66
64
  - lib/cod/channel/pipe.rb
65
+ - lib/cod/channel/tcp.rb
66
+ - lib/cod/channel/tcpconnection.rb
67
+ - lib/cod/channel/tcpserver.rb
67
68
  - lib/cod/channel.rb
68
69
  - lib/cod/client.rb
69
70
  - lib/cod/connection/beanstalk.rb
70
71
  - lib/cod/context.rb
71
72
  - lib/cod/directory/subscription.rb
72
73
  - lib/cod/directory.rb
74
+ - lib/cod/object_io.rb
75
+ - lib/cod/objectio/connection.rb
76
+ - lib/cod/objectio/reader.rb
77
+ - lib/cod/objectio/serializer.rb
78
+ - lib/cod/objectio/writer.rb
73
79
  - lib/cod/service.rb
74
80
  - lib/cod/topic.rb
75
81
  - lib/cod.rb
82
+ - examples/example_scaffold.rb
76
83
  - examples/master_child.rb
77
84
  - examples/ping.rb
78
85
  - examples/pong.rb
79
86
  - examples/service.rb
80
87
  - examples/service_directory.rb
88
+ - examples/tcp.rb
81
89
  has_rdoc: true
82
90
  homepage: http://kschiess.github.com/cod
83
91
  licenses: []
84
-
85
92
  post_install_message:
86
- rdoc_options:
93
+ rdoc_options:
87
94
  - --main
88
95
  - README
89
- require_paths:
96
+ require_paths:
90
97
  - lib
91
- required_ruby_version: !ruby/object:Gem::Requirement
98
+ required_ruby_version: !ruby/object:Gem::Requirement
92
99
  none: false
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: "0"
97
- required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
105
  none: false
99
- requirements:
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: "0"
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
103
110
  requirements: []
104
-
105
111
  rubyforge_project:
106
- rubygems_version: 1.5.2
112
+ rubygems_version: 1.6.2
107
113
  signing_key:
108
114
  specification_version: 3
109
115
  summary: Really simple IPC.
110
116
  test_files: []
111
-