ichannel 6.0.0 → 6.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/.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:
|