ichannel 6.0.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.pryrc +1 -0
- data/ChangeLog.txt +11 -0
- data/Gemfile +6 -3
- data/Procfile +1 -0
- data/README.md +42 -14
- data/lib/ichannel/redis.rb +154 -0
- data/lib/ichannel/unix_socket.rb +4 -2
- data/lib/ichannel/version.rb +2 -2
- data/lib/ichannel.rb +24 -5
- data/test/ichannel_redis_test.rb +17 -0
- data/test/{ichannel_class_test.rb → ichannel_unix_test.rb} +16 -1
- data/test/setup.rb +2 -1
- metadata +11 -6
data/.pryrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "./lib/ichannel"
|
data/ChangeLog.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== HEAD
|
2
|
+
- Redis#last_msg, UNIXSocket#last_msg changes.
|
3
|
+
The last_msg method returns the last value read by #get when a channel is
|
4
|
+
not readable.
|
5
|
+
|
6
|
+
- change IChannel from being a class to a module.
|
7
|
+
There's no need to create an instance of IChannel anymore.
|
8
|
+
|
9
|
+
- Add IChannel::Redis.
|
10
|
+
Add Redis as a backend.
|
11
|
+
|
1
12
|
== v0.6.0
|
2
13
|
- IChannel::UNIXSocket can now be serialized by Marshal.
|
3
14
|
IChannel::UNIXSocket can be serialized by Marshal but there's a gotcha: it
|
data/Gemfile
CHANGED
data/Procfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
redis: redis-server
|
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
__OVERVIEW__
|
2
2
|
|
3
|
-
|
4
3
|
| Project | ichannel
|
5
4
|
|:----------------|:--------------------------------------------------
|
6
5
|
| Homepage | https://github.com/robgleeson/ichannel
|
@@ -11,25 +10,33 @@ __OVERVIEW__
|
|
11
10
|
|
12
11
|
__DESCRIPTION__
|
13
12
|
|
14
|
-
ichannel
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
ichannel is a channel for interprocess communication between ruby processes on
|
14
|
+
the same machine(or network). The basic premise is that you can "put" a ruby
|
15
|
+
object onto the channel and on the other end(maybe in a different process,
|
16
|
+
or maybe on a different machine) you can "get" the object from the channel.
|
17
|
+
|
18
|
+
The two main modes of transport are a UNIXSocket(streamed) or [redis](https://redis.io).
|
19
|
+
A unix socket is fast and operates without any external dependencies but it
|
20
|
+
can't go beyond a single machine. A channel that uses redis can operate between
|
21
|
+
different machines. And incase you're wondering ichannel uses a
|
22
|
+
redis [list](http://redis.io/commands#list) to queue messages.
|
23
|
+
|
24
|
+
The last topic I feel I should talk about before the examples is serialization.
|
25
|
+
A ruby object is serialized(on write) and deserialized(on read) when passing
|
26
|
+
through a channel. A channel can use any serializer that implements the dump and
|
27
|
+
load methods but the default is [Marshal](http://ruby-doc.org/core-2.0/Marshal.html).
|
28
|
+
There are also a number of objects that cannot be serialized (such as IO,
|
29
|
+
anonymous classes/modules, Proc, …) but I've found most of the time I send
|
30
|
+
simple objects like Hash.
|
19
31
|
|
20
|
-
Underneath the hood ruby objects are serialized when writing and reading from the
|
21
|
-
underlying UNIXSocket. A ruby object is serialized before a write, and it is
|
22
|
-
deserialized after a read. The choice of serializer is left up to you, though.
|
23
|
-
A serializer can be any object that implements `dump` and `load` -- two methods
|
24
|
-
that are usually implemented by serializers written in ruby.
|
25
32
|
|
26
33
|
__EXAMPLES__
|
27
34
|
|
28
35
|
__1.__
|
29
36
|
|
30
37
|
A demo of how to pass ruby objects through a channel and also between processes.
|
31
|
-
[Marshal](http://rubydoc.info/stdlib/core/Marshal) is the serializer of choice
|
32
|
-
|
38
|
+
[Marshal](http://rubydoc.info/stdlib/core/Marshal) is the serializer of choice,
|
39
|
+
and a streamed UNIXSocket is mode of transport:
|
33
40
|
|
34
41
|
```ruby
|
35
42
|
channel = IChannel.unix Marshal
|
@@ -45,7 +52,8 @@ channel.get # => 'Hello!'
|
|
45
52
|
__2.__
|
46
53
|
|
47
54
|
Knowing when a channel is readable can be useful so that you can avoid a
|
48
|
-
blocking read. This (bad) example demonstrates
|
55
|
+
blocking read on the underlying UNIXSocket. This (bad) example demonstrates
|
56
|
+
how to do that:
|
49
57
|
|
50
58
|
```ruby
|
51
59
|
channel = IChannel.unix Marshal
|
@@ -62,6 +70,19 @@ Process.wait pid
|
|
62
70
|
|
63
71
|
__3.__
|
64
72
|
|
73
|
+
A demo of how to use ichannel with Redis:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
channel = IChannel.redis
|
77
|
+
channel.put %w(a)
|
78
|
+
|
79
|
+
# In another process, far away…
|
80
|
+
channel = IChannel.redis
|
81
|
+
channel.get # => ["a"]
|
82
|
+
```
|
83
|
+
|
84
|
+
__4.__
|
85
|
+
|
65
86
|
MessagePack doesn't implement `dump` or `load` but a wrapper can be easily
|
66
87
|
written:
|
67
88
|
|
@@ -93,6 +114,13 @@ _unsupported_
|
|
93
114
|
|
94
115
|
__INSTALL__
|
95
116
|
|
117
|
+
If you plan on using redis you'll need to install the 'redis' gem. It's
|
118
|
+
optional:
|
119
|
+
|
120
|
+
$ gem install redis
|
121
|
+
|
122
|
+
And to finish:
|
123
|
+
|
96
124
|
$ gem install ichannel
|
97
125
|
|
98
126
|
__SEE ALSO__
|
@@ -0,0 +1,154 @@
|
|
1
|
+
class IChannel::Redis
|
2
|
+
|
3
|
+
#
|
4
|
+
# @param [#dump,#load] serializer
|
5
|
+
# A serializer.
|
6
|
+
#
|
7
|
+
# @param [Hash] options
|
8
|
+
# A Hash of options to pass onto the redis-rb client.
|
9
|
+
#
|
10
|
+
# @return [IChannel::Redis]
|
11
|
+
#
|
12
|
+
def initialize(serializer, options)
|
13
|
+
@serializer = serializer
|
14
|
+
@key = options.delete(:key) || "channel"
|
15
|
+
@redis = ::Redis.new(options)
|
16
|
+
@last_msg = nil
|
17
|
+
@closed = false
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# @return [Boolean]
|
22
|
+
# Returns true when the channel is closed.
|
23
|
+
#
|
24
|
+
def closed?
|
25
|
+
@closed
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Close the channel.
|
30
|
+
#
|
31
|
+
# @return [Boolean]
|
32
|
+
# Returns true when the channel has been closed.
|
33
|
+
#
|
34
|
+
def close
|
35
|
+
unless closed?
|
36
|
+
@redis.quit
|
37
|
+
@last_msg = nil
|
38
|
+
@closed = true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Add an object to the channel.
|
44
|
+
#
|
45
|
+
# @param [Object] object
|
46
|
+
# The object to add to the channel.
|
47
|
+
#
|
48
|
+
# @raise [IOError]
|
49
|
+
# When the channel is closed.
|
50
|
+
#
|
51
|
+
# @return
|
52
|
+
# (see Redis#write!)
|
53
|
+
#
|
54
|
+
def write(object)
|
55
|
+
write!(object, nil)
|
56
|
+
end
|
57
|
+
alias_method :put, :write
|
58
|
+
|
59
|
+
#
|
60
|
+
# Add an object to the channel.
|
61
|
+
#
|
62
|
+
# @param [Object] object
|
63
|
+
# The object to add to the channel.
|
64
|
+
#
|
65
|
+
# @param [Fixnum] timeout
|
66
|
+
# The amount of time to wait for the write to complete.
|
67
|
+
#
|
68
|
+
# @raise [IOError]
|
69
|
+
# When the channel is closed.
|
70
|
+
#
|
71
|
+
# @raise [Timeout::Error]
|
72
|
+
# When the write does not complete in time.
|
73
|
+
#
|
74
|
+
# @return [void]
|
75
|
+
#
|
76
|
+
def write!(object, timeout = 0.1)
|
77
|
+
if closed?
|
78
|
+
raise IOError, 'The channel cannot be written to (closed)'
|
79
|
+
end
|
80
|
+
Timeout.timeout(timeout) do
|
81
|
+
dump = @serializer.dump object
|
82
|
+
@redis.lpush @key, dump
|
83
|
+
end
|
84
|
+
end
|
85
|
+
alias_method :put!, :write!
|
86
|
+
|
87
|
+
#
|
88
|
+
# Read an object from the channel.
|
89
|
+
#
|
90
|
+
# @raise [IOError]
|
91
|
+
# When the channel is closed or empty.
|
92
|
+
#
|
93
|
+
# @return
|
94
|
+
# (see Redis#recv!)
|
95
|
+
#
|
96
|
+
def recv
|
97
|
+
recv! nil
|
98
|
+
end
|
99
|
+
alias_method :get, :recv
|
100
|
+
|
101
|
+
#
|
102
|
+
# @param [Fixnum] timeout
|
103
|
+
# The amount of time to wait for the read to complete.
|
104
|
+
#
|
105
|
+
# @raise [IOError]
|
106
|
+
# When the channel is closed or empty.
|
107
|
+
#
|
108
|
+
# @raise [Timeout::Error]
|
109
|
+
# When the read does not complete in time.
|
110
|
+
#
|
111
|
+
# @return [Object]
|
112
|
+
# The object read from the channel.
|
113
|
+
#
|
114
|
+
def recv!(timeout = 0.1)
|
115
|
+
if closed?
|
116
|
+
raise IOError, 'The channel cannot be read from (closed).'
|
117
|
+
elsif empty?
|
118
|
+
raise IOError, 'The channel cannot be read from (empty).'
|
119
|
+
else
|
120
|
+
Timeout.timeout(timeout) do
|
121
|
+
dump = @redis.rpop @key
|
122
|
+
@last_msg = @serializer.load dump
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
alias_method :get!, :recv!
|
127
|
+
|
128
|
+
#
|
129
|
+
# @return [Object]
|
130
|
+
# Returns the last message written to the channel.
|
131
|
+
#
|
132
|
+
def last_msg
|
133
|
+
while readable?
|
134
|
+
@last_msg = get
|
135
|
+
end
|
136
|
+
@last_msg
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# @return [Boolean]
|
141
|
+
# Returns true when the channel is empty.
|
142
|
+
#
|
143
|
+
def empty?
|
144
|
+
@redis.llen(@key) == 0
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# @return [Boolean]
|
149
|
+
# Returns true when the channel is readable.
|
150
|
+
#
|
151
|
+
def readable?
|
152
|
+
!closed? && !empty?
|
153
|
+
end
|
154
|
+
end
|
data/lib/ichannel/unix_socket.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'socket'
|
2
|
-
|
2
|
+
module IChannel
|
3
3
|
class UNIXSocket
|
4
4
|
SEP = '_$_'
|
5
5
|
if respond_to? :private_constant
|
@@ -10,6 +10,8 @@ class IChannel
|
|
10
10
|
# @param [#dump,#load] serializer
|
11
11
|
# Any object that implements dump, & load.
|
12
12
|
#
|
13
|
+
# @return [IChannel::UNIXSocket]
|
14
|
+
#
|
13
15
|
def initialize(serializer = Marshal, adapter_options)
|
14
16
|
@serializer = serializer
|
15
17
|
@last_msg = nil
|
@@ -139,7 +141,7 @@ class IChannel
|
|
139
141
|
readable, _ = IO.select [@reader], nil, nil, timeout
|
140
142
|
if readable
|
141
143
|
msg = readable[0].readline(SEP).chomp SEP
|
142
|
-
@serializer.load msg
|
144
|
+
@last_msg = @serializer.load msg
|
143
145
|
else
|
144
146
|
raise IOError, 'The channel cannot be read from.'
|
145
147
|
end
|
data/lib/ichannel/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "6.
|
1
|
+
module IChannel
|
2
|
+
VERSION = "6.1.0"
|
3
3
|
end
|
data/lib/ichannel.rb
CHANGED
@@ -1,11 +1,30 @@
|
|
1
|
-
|
1
|
+
module IChannel
|
2
|
+
require 'redis'
|
2
3
|
require_relative "ichannel/unix_socket"
|
4
|
+
require_relative "ichannel/redis"
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
#
|
7
|
+
# @param
|
8
|
+
# (see UNIXSocket#initialize)
|
9
|
+
#
|
10
|
+
# @return
|
11
|
+
# (see UNIXSocket#initialize)
|
12
|
+
#
|
13
|
+
def self.unix(serializer = Marshal, options = {})
|
14
|
+
UNIXSocket.new serializer, options
|
6
15
|
end
|
7
16
|
|
8
|
-
|
9
|
-
|
17
|
+
#
|
18
|
+
# @param
|
19
|
+
# (see Redis#initialize)
|
20
|
+
#
|
21
|
+
# @return
|
22
|
+
# (see Redis#initialize)
|
23
|
+
#
|
24
|
+
def self.redis(serializer = Marshal, options = {})
|
25
|
+
unless defined?(::Redis)
|
26
|
+
require 'redis'
|
27
|
+
end
|
28
|
+
Redis.new serializer, options
|
10
29
|
end
|
11
30
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'setup'
|
2
|
+
require_relative 'ichannel_unix_test'
|
3
|
+
class IChannelRedisTest < IChannelUNIXTest
|
4
|
+
def setup
|
5
|
+
serializer = Object.const_get ENV["SERIALIZER"] || "Marshal"
|
6
|
+
@channel = IChannel.redis serializer
|
7
|
+
end
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
key = @channel.instance_variable_get :@key
|
11
|
+
@channel.instance_variable_get(:@redis).del(key)
|
12
|
+
@channel.close
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_serialization() end
|
16
|
+
def test_serialization_in_fork() end
|
17
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'setup'
|
2
|
-
class
|
2
|
+
class IChannelUNIXTest < Test::Unit::TestCase
|
3
3
|
def setup
|
4
4
|
serializer = Object.const_get ENV["SERIALIZER"] || "Marshal"
|
5
5
|
@channel = IChannel.unix serializer
|
@@ -26,6 +26,21 @@ class IChannelTest < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def test_last_msg_after_read
|
30
|
+
@channel.put 42
|
31
|
+
@channel.get
|
32
|
+
assert_equal 42, @channel.last_msg
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_serialization_in_fork
|
36
|
+
dump = Marshal.dump(@channel)
|
37
|
+
pid = fork do
|
38
|
+
Marshal.load(dump).put 42
|
39
|
+
end
|
40
|
+
Process.wait pid
|
41
|
+
assert_equal 42, @channel.get
|
42
|
+
end
|
43
|
+
|
29
44
|
def test_serialization
|
30
45
|
@channel.put 42
|
31
46
|
dump = Marshal.dump @channel
|
data/test/setup.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ichannel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-03 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! "A modern and easy-to-use interprocess communication \n primitive."
|
15
15
|
email:
|
@@ -20,18 +20,22 @@ extra_rdoc_files: []
|
|
20
20
|
files:
|
21
21
|
- .gitattributes
|
22
22
|
- .gitignore
|
23
|
+
- .pryrc
|
23
24
|
- .travis.yml
|
24
25
|
- .yardopts
|
25
26
|
- ChangeLog.txt
|
26
27
|
- Gemfile
|
27
28
|
- LICENSE.txt
|
29
|
+
- Procfile
|
28
30
|
- README.md
|
29
31
|
- Rakefile
|
30
32
|
- ichannel.gemspec
|
31
33
|
- lib/ichannel.rb
|
34
|
+
- lib/ichannel/redis.rb
|
32
35
|
- lib/ichannel/unix_socket.rb
|
33
36
|
- lib/ichannel/version.rb
|
34
|
-
- test/
|
37
|
+
- test/ichannel_redis_test.rb
|
38
|
+
- test/ichannel_unix_test.rb
|
35
39
|
- test/setup.rb
|
36
40
|
homepage: https://github.com/robgleeson/ichannel
|
37
41
|
licenses: []
|
@@ -47,7 +51,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
51
|
version: '0'
|
48
52
|
segments:
|
49
53
|
- 0
|
50
|
-
hash:
|
54
|
+
hash: 3267569127984942618
|
51
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
56
|
none: false
|
53
57
|
requirements:
|
@@ -56,7 +60,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
60
|
version: '0'
|
57
61
|
segments:
|
58
62
|
- 0
|
59
|
-
hash:
|
63
|
+
hash: 3267569127984942618
|
60
64
|
requirements: []
|
61
65
|
rubyforge_project:
|
62
66
|
rubygems_version: 1.8.23
|
@@ -64,6 +68,7 @@ signing_key:
|
|
64
68
|
specification_version: 3
|
65
69
|
summary: A modern and easy-to-use interprocess communication primitive.
|
66
70
|
test_files:
|
67
|
-
- test/
|
71
|
+
- test/ichannel_redis_test.rb
|
72
|
+
- test/ichannel_unix_test.rb
|
68
73
|
- test/setup.rb
|
69
74
|
has_rdoc:
|