cod 0.2.0 → 0.3.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/Gemfile +4 -0
- data/HISTORY.txt +9 -0
- data/README +2 -2
- data/Rakefile +4 -4
- data/examples/example_scaffold.rb +15 -0
- data/examples/ping.rb +1 -1
- data/examples/pong.rb +1 -1
- data/examples/tcp.rb +21 -0
- data/lib/at_fork.rb +37 -13
- data/lib/cod.rb +69 -3
- data/lib/cod/channel/abstract.rb +32 -0
- data/lib/cod/channel/base.rb +96 -33
- data/lib/cod/channel/beanstalk.rb +8 -9
- data/lib/cod/channel/pipe.rb +46 -46
- data/lib/cod/channel/tcp.rb +16 -0
- data/lib/cod/channel/tcpconnection.rb +73 -0
- data/lib/cod/channel/tcpserver.rb +84 -0
- data/lib/cod/connection/beanstalk.rb +28 -7
- data/lib/cod/context.rb +8 -55
- data/lib/cod/object_io.rb +3 -0
- data/lib/cod/objectio/connection.rb +0 -0
- data/lib/cod/objectio/reader.rb +129 -0
- data/lib/cod/objectio/serializer.rb +26 -0
- data/lib/cod/objectio/writer.rb +32 -0
- metadata +56 -51
data/lib/cod/context.rb
CHANGED
@@ -1,72 +1,25 @@
|
|
1
1
|
require 'weakref'
|
2
2
|
|
3
3
|
module Cod
|
4
|
-
#
|
5
|
-
#
|
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
|
-
|
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
|
60
|
-
|
17
|
+
def tcp(destination)
|
18
|
+
Cod::Channel::TCPConnection.new(destination)
|
61
19
|
end
|
62
|
-
|
63
|
-
def
|
64
|
-
|
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
|
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
|
-
|
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:
|
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
|
-
|
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:
|
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
|
-
|
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:
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
47
45
|
type: :development
|
48
|
-
|
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:
|
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:
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
103
110
|
requirements: []
|
104
|
-
|
105
111
|
rubyforge_project:
|
106
|
-
rubygems_version: 1.
|
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
|
-
|