ichannel 5.2.0 → 6.0.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/.travis.yml +3 -0
- data/ChangeLog.txt +26 -11
- data/README.md +3 -3
- data/lib/ichannel/unix_socket.rb +174 -0
- data/lib/ichannel/version.rb +1 -1
- data/lib/ichannel.rb +5 -153
- data/test/ichannel_class_test.rb +24 -1
- metadata +5 -4
data/.travis.yml
CHANGED
data/ChangeLog.txt
CHANGED
@@ -1,50 +1,65 @@
|
|
1
|
+
== v0.6.0
|
2
|
+
- IChannel::UNIXSocket can now be serialized by Marshal.
|
3
|
+
IChannel::UNIXSocket can be serialized by Marshal but there's a gotcha: it
|
4
|
+
only really works on the same machine between one or more Ruby processes.
|
5
|
+
The UNIXSocket#marshal_dump method is implemented to dump the FDs in an array,
|
6
|
+
so this feature won't fair well across different machines or time but I've
|
7
|
+
found this feature useful nonetheless.
|
8
|
+
|
9
|
+
- add IChannel.unix(…)
|
10
|
+
Returns an instance of IChannel::UNIXSocket.
|
11
|
+
|
12
|
+
- add IChannel::UNIXSocket
|
13
|
+
rename IChannel as IChannel::UNIXSocket in preparation for multiple
|
14
|
+
backends(UNIXSocket, Redis, …).
|
15
|
+
|
1
16
|
== v5.2.0
|
2
|
-
|
17
|
+
- add IChannel#last_msg.
|
3
18
|
Reads the last message written to the channel by reading until the channel
|
4
19
|
is empty. The last message is cached and reset to nil on call to #close.
|
5
20
|
|
6
21
|
== v5.1.1.1, v5.1.1.2, v5.1.1.3, v5.1.1.4, v5.1.1.5
|
7
|
-
|
22
|
+
- doc improvements
|
8
23
|
a set of releases that improved the README & api docs.
|
9
24
|
|
10
25
|
== v5.1.1
|
11
|
-
|
26
|
+
- Change socket type to use TCP.
|
12
27
|
The use of UDP could result in bugs because of its unordered nature.
|
13
28
|
|
14
29
|
== v5.1.0
|
15
|
-
|
30
|
+
- Remove restriction on size of message.
|
16
31
|
IChannel#get can read a message of any size(before hand it was limited to
|
17
32
|
1MB in size). Thanks to @quezacoatl.
|
18
33
|
|
19
|
-
|
34
|
+
- IChannel#readable? no longer blocks.
|
20
35
|
IChannel#readable? no longer blocks for 0.1 seconds on IO.select call.
|
21
36
|
Thanks to quezacoatl(https://github.com/quezacoatl) for the initial
|
22
37
|
implementation.
|
23
38
|
|
24
39
|
== v5.0.0
|
25
|
-
|
40
|
+
- Remove IChannel#empty?
|
26
41
|
I think the #readable? method is all you need, and is a much more
|
27
42
|
accurate description of what the method is asking. We cannot determine
|
28
43
|
if the channel is really empty, but we can ask if it is readable at the
|
29
44
|
time you ask.
|
30
45
|
|
31
46
|
== v4.1.0
|
32
|
-
|
47
|
+
- Add IChannel#readable?
|
33
48
|
A method that can tell you whether or not a read would block.
|
34
49
|
When it returns true, a read shouldn't block, on the other hand
|
35
50
|
if it were false it'd likely block by the time you call #get.
|
36
51
|
|
37
52
|
== v4.0.0
|
38
|
-
|
53
|
+
- Modify IChannel#empty?
|
39
54
|
It now returns true in case the underlying UNIXSocket being used as a
|
40
55
|
reader is closed.
|
41
56
|
|
42
57
|
== v3.1.0
|
43
|
-
|
58
|
+
- Add IChannel#empty?.
|
44
59
|
IChannel#empty? returns true when the channel is empty(nothing to read).
|
45
60
|
|
46
|
-
|
61
|
+
- Micro speed improvement on #write!, & #recv! operations.
|
47
62
|
By passing nil instead of creating two empty arrays for every read/write
|
48
63
|
operation we should see a very small improvement in their performance.
|
49
64
|
|
50
|
-
|
65
|
+
- Add ChangeLog.txt
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ A demo of how to pass ruby objects through a channel and also between processes.
|
|
32
32
|
in this example:
|
33
33
|
|
34
34
|
```ruby
|
35
|
-
channel = IChannel.
|
35
|
+
channel = IChannel.unix Marshal
|
36
36
|
pid = fork do
|
37
37
|
channel.put Process.pid
|
38
38
|
channel.put 'Hello!'
|
@@ -48,7 +48,7 @@ Knowing when a channel is readable can be useful so that you can avoid a
|
|
48
48
|
blocking read. This (bad) example demonstrates how to do that:
|
49
49
|
|
50
50
|
```ruby
|
51
|
-
channel = IChannel.
|
51
|
+
channel = IChannel.unix Marshal
|
52
52
|
pid = fork do
|
53
53
|
sleep 3
|
54
54
|
channel.put 42
|
@@ -75,7 +75,7 @@ module MyMessagePack
|
|
75
75
|
MessagePack.unpack(msg)
|
76
76
|
end
|
77
77
|
end
|
78
|
-
channel = IChannel.
|
78
|
+
channel = IChannel.unix MyMessagePack
|
79
79
|
```
|
80
80
|
|
81
81
|
__PLATFORM SUPPORT__
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'socket'
|
2
|
+
class IChannel
|
3
|
+
class UNIXSocket
|
4
|
+
SEP = '_$_'
|
5
|
+
if respond_to? :private_constant
|
6
|
+
private_constant :SEP
|
7
|
+
end
|
8
|
+
|
9
|
+
#
|
10
|
+
# @param [#dump,#load] serializer
|
11
|
+
# Any object that implements dump, & load.
|
12
|
+
#
|
13
|
+
def initialize(serializer = Marshal, adapter_options)
|
14
|
+
@serializer = serializer
|
15
|
+
@last_msg = nil
|
16
|
+
@reader, @writer = ::UNIXSocket.pair :STREAM
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
21
|
+
# Returns true when the channel is closed.
|
22
|
+
#
|
23
|
+
def closed?
|
24
|
+
@reader.closed? && @writer.closed?
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Close the channel.
|
29
|
+
#
|
30
|
+
# @return [Boolean]
|
31
|
+
# Returns true when the channel has been closed.
|
32
|
+
#
|
33
|
+
def close
|
34
|
+
unless closed?
|
35
|
+
@reader.close
|
36
|
+
@writer.close
|
37
|
+
@last_msg = nil
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Add an object to the channel.
|
44
|
+
#
|
45
|
+
# @raise [IOError]
|
46
|
+
# When the channel is closed.
|
47
|
+
#
|
48
|
+
# @param [Object] object
|
49
|
+
# An object to add to the channel.
|
50
|
+
#
|
51
|
+
def write(object)
|
52
|
+
write!(object, nil)
|
53
|
+
end
|
54
|
+
alias_method :put, :write
|
55
|
+
|
56
|
+
#
|
57
|
+
# Add an object to the channel.
|
58
|
+
#
|
59
|
+
# Unlike {#write}, which waits indefinitely until the channel becomes writable,
|
60
|
+
# this method will raise an IOError when _timeout_ seconds elapse and
|
61
|
+
# the channel remains unwritable.
|
62
|
+
#
|
63
|
+
# @param
|
64
|
+
# (see IChannel#write)
|
65
|
+
#
|
66
|
+
# @param [Numeric] timeout
|
67
|
+
# The number of seconds to wait for the channel to become writable.
|
68
|
+
#
|
69
|
+
# @raise (see IChannel#write)
|
70
|
+
#
|
71
|
+
# @raise [IOError]
|
72
|
+
# When _timeout_ seconds elapse & the channel remains unwritable.
|
73
|
+
#
|
74
|
+
def write!(object, timeout = 0.1)
|
75
|
+
if @writer.closed?
|
76
|
+
raise IOError, 'The channel cannot be written to (closed).'
|
77
|
+
end
|
78
|
+
_, writable, _ = IO.select nil, [@writer], nil, timeout
|
79
|
+
if writable
|
80
|
+
msg = @serializer.dump(object)
|
81
|
+
writable[0].syswrite "#{msg}#{SEP}"
|
82
|
+
else
|
83
|
+
raise IOError, 'The channel cannot be written to.'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
alias_method :put!, :write!
|
87
|
+
|
88
|
+
#
|
89
|
+
# Reads the last message written to the channel by reading until the channel
|
90
|
+
# is empty. The last message is cached and reset to nil on call to {#close}.
|
91
|
+
#
|
92
|
+
# @return [Object]
|
93
|
+
# Returns the last message to be written to the channel.
|
94
|
+
#
|
95
|
+
def last_msg
|
96
|
+
while readable?
|
97
|
+
@last_msg = get
|
98
|
+
end
|
99
|
+
@last_msg
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Receive an object from the channel.
|
104
|
+
#
|
105
|
+
# @raise [IOError]
|
106
|
+
# When the channel is closed.
|
107
|
+
#
|
108
|
+
# @return [Object]
|
109
|
+
# The object read from the channel.
|
110
|
+
#
|
111
|
+
def recv
|
112
|
+
recv!(nil)
|
113
|
+
end
|
114
|
+
alias_method :get, :recv
|
115
|
+
|
116
|
+
#
|
117
|
+
# Receive an object from the channel.
|
118
|
+
#
|
119
|
+
# Unlike {#recv}, which waits indefinitely until the channel becomes readable,
|
120
|
+
# this method will raise an IOError when _timeout_ seconds elapse and the
|
121
|
+
# channel remains unreadable.
|
122
|
+
#
|
123
|
+
# @param [Numeric] timeout
|
124
|
+
# The number of seconds to wait for the channel to become readable.
|
125
|
+
#
|
126
|
+
# @raise [IOError]
|
127
|
+
# (see IChannel#recv)
|
128
|
+
#
|
129
|
+
# @raise [IOError]
|
130
|
+
# When _timeout_ seconds elapse & the channel remains unreadable.
|
131
|
+
#
|
132
|
+
# @return [Object]
|
133
|
+
# The object read from the channel.
|
134
|
+
#
|
135
|
+
def recv!(timeout = 0.1)
|
136
|
+
if @reader.closed?
|
137
|
+
raise IOError, 'The channel cannot be read from (closed).'
|
138
|
+
end
|
139
|
+
readable, _ = IO.select [@reader], nil, nil, timeout
|
140
|
+
if readable
|
141
|
+
msg = readable[0].readline(SEP).chomp SEP
|
142
|
+
@serializer.load msg
|
143
|
+
else
|
144
|
+
raise IOError, 'The channel cannot be read from.'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
alias_method :get!, :recv!
|
148
|
+
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
# Returns true when the channel is readable.
|
152
|
+
#
|
153
|
+
def readable?
|
154
|
+
if closed?
|
155
|
+
false
|
156
|
+
else
|
157
|
+
readable, _ = IO.select [@reader], nil, nil, 0
|
158
|
+
!! readable
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @api private
|
163
|
+
def marshal_load(array)
|
164
|
+
@serializer, reader, writer, @last_msg = array
|
165
|
+
@reader = ::UNIXSocket.for_fd(reader)
|
166
|
+
@writer = ::UNIXSocket.for_fd(writer)
|
167
|
+
end
|
168
|
+
|
169
|
+
# @api private
|
170
|
+
def marshal_dump
|
171
|
+
[@serializer, @reader.to_i, @writer.to_i, @last_msg]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/ichannel/version.rb
CHANGED
data/lib/ichannel.rb
CHANGED
@@ -1,159 +1,11 @@
|
|
1
|
-
require 'socket'
|
2
1
|
class IChannel
|
3
|
-
|
4
|
-
if respond_to? :private_constant
|
5
|
-
private_constant :SEP
|
6
|
-
end
|
7
|
-
#
|
8
|
-
# @param [#dump,#load] serializer
|
9
|
-
# Any object that implements dump, & load.
|
10
|
-
#
|
11
|
-
def initialize(serializer)
|
12
|
-
@reader, @writer = UNIXSocket.pair :STREAM
|
13
|
-
@serializer = serializer
|
14
|
-
@last_msg = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
#
|
18
|
-
# @return [Boolean]
|
19
|
-
# Returns true when the channel is closed.
|
20
|
-
#
|
21
|
-
def closed?
|
22
|
-
@reader.closed? && @writer.closed?
|
23
|
-
end
|
24
|
-
|
25
|
-
#
|
26
|
-
# Close the channel.
|
27
|
-
#
|
28
|
-
# @return [Boolean]
|
29
|
-
# Returns true when the channel has been closed.
|
30
|
-
#
|
31
|
-
def close
|
32
|
-
unless closed?
|
33
|
-
@reader.close
|
34
|
-
@writer.close
|
35
|
-
@last_msg = nil
|
36
|
-
true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
#
|
41
|
-
# Add an object to the channel.
|
42
|
-
#
|
43
|
-
# @raise [IOError]
|
44
|
-
# When the channel is closed.
|
45
|
-
#
|
46
|
-
# @param [Object] object
|
47
|
-
# An object to add to the channel.
|
48
|
-
#
|
49
|
-
def write(object)
|
50
|
-
write!(object, nil)
|
51
|
-
end
|
52
|
-
alias_method :put, :write
|
53
|
-
|
54
|
-
#
|
55
|
-
# Add an object to the channel.
|
56
|
-
#
|
57
|
-
# Unlike {#write}, which waits indefinitely until the channel becomes writable,
|
58
|
-
# this method will raise an IOError when _timeout_ seconds elapse and
|
59
|
-
# the channel remains unwritable.
|
60
|
-
#
|
61
|
-
# @param
|
62
|
-
# (see IChannel#write)
|
63
|
-
#
|
64
|
-
# @param [Numeric] timeout
|
65
|
-
# The number of seconds to wait for the channel to become writable.
|
66
|
-
#
|
67
|
-
# @raise (see IChannel#write)
|
68
|
-
#
|
69
|
-
# @raise [IOError]
|
70
|
-
# When _timeout_ seconds elapse & the channel remains unwritable.
|
71
|
-
#
|
72
|
-
def write!(object, timeout = 0.1)
|
73
|
-
if @writer.closed?
|
74
|
-
raise IOError, 'The channel cannot be written to (closed).'
|
75
|
-
end
|
76
|
-
_, writable, _ = IO.select nil, [@writer], nil, timeout
|
77
|
-
if writable
|
78
|
-
msg = @serializer.dump(object)
|
79
|
-
writable[0].syswrite "#{msg}#{SEP}"
|
80
|
-
else
|
81
|
-
raise IOError, 'The channel cannot be written to.'
|
82
|
-
end
|
83
|
-
end
|
84
|
-
alias_method :put!, :write!
|
85
|
-
|
86
|
-
#
|
87
|
-
# Reads the last message written to the channel by reading until the channel
|
88
|
-
# is empty. The last message is cached and reset to nil on call to {#close}.
|
89
|
-
#
|
90
|
-
# @return [Object]
|
91
|
-
# Returns the last message to be written to the channel.
|
92
|
-
#
|
93
|
-
def last_msg
|
94
|
-
while readable?
|
95
|
-
@last_msg = get
|
96
|
-
end
|
97
|
-
@last_msg
|
98
|
-
end
|
99
|
-
|
100
|
-
#
|
101
|
-
# Receive an object from the channel.
|
102
|
-
#
|
103
|
-
# @raise [IOError]
|
104
|
-
# When the channel is closed.
|
105
|
-
#
|
106
|
-
# @return [Object]
|
107
|
-
# The object read from the channel.
|
108
|
-
#
|
109
|
-
def recv
|
110
|
-
recv!(nil)
|
111
|
-
end
|
112
|
-
alias_method :get, :recv
|
2
|
+
require_relative "ichannel/unix_socket"
|
113
3
|
|
114
|
-
|
115
|
-
|
116
|
-
#
|
117
|
-
# Unlike {#recv}, which waits indefinitely until the channel becomes readable,
|
118
|
-
# this method will raise an IOError when _timeout_ seconds elapse and the
|
119
|
-
# channel remains unreadable.
|
120
|
-
#
|
121
|
-
# @param [Numeric] timeout
|
122
|
-
# The number of seconds to wait for the channel to become readable.
|
123
|
-
#
|
124
|
-
# @raise [IOError]
|
125
|
-
# (see IChannel#recv)
|
126
|
-
#
|
127
|
-
# @raise [IOError]
|
128
|
-
# When _timeout_ seconds elapse & the channel remains unreadable.
|
129
|
-
#
|
130
|
-
# @return [Object]
|
131
|
-
# The object read from the channel.
|
132
|
-
#
|
133
|
-
def recv!(timeout = 0.1)
|
134
|
-
if @reader.closed?
|
135
|
-
raise IOError, 'The channel cannot be read from (closed).'
|
136
|
-
end
|
137
|
-
readable, _ = IO.select [@reader], nil, nil, timeout
|
138
|
-
if readable
|
139
|
-
msg = readable[0].readline(SEP).chomp SEP
|
140
|
-
@serializer.load msg
|
141
|
-
else
|
142
|
-
raise IOError, 'The channel cannot be read from.'
|
143
|
-
end
|
4
|
+
def self.unix(options)
|
5
|
+
UNIXSocket.new(options)
|
144
6
|
end
|
145
|
-
alias_method :get!, :recv!
|
146
7
|
|
147
|
-
|
148
|
-
|
149
|
-
# Returns true when the channel is readable.
|
150
|
-
#
|
151
|
-
def readable?
|
152
|
-
if closed?
|
153
|
-
false
|
154
|
-
else
|
155
|
-
readable, _ = IO.select [@reader], nil, nil, 0
|
156
|
-
!! readable
|
157
|
-
end
|
8
|
+
def self.redis(options)
|
9
|
+
raise NotImplementedError
|
158
10
|
end
|
159
11
|
end
|
data/test/ichannel_class_test.rb
CHANGED
@@ -2,13 +2,36 @@ require_relative 'setup'
|
|
2
2
|
class IChannelTest < Test::Unit::TestCase
|
3
3
|
def setup
|
4
4
|
serializer = Object.const_get ENV["SERIALIZER"] || "Marshal"
|
5
|
-
@channel = IChannel.
|
5
|
+
@channel = IChannel.unix serializer
|
6
6
|
end
|
7
7
|
|
8
8
|
def teardown
|
9
9
|
@channel.close
|
10
10
|
end
|
11
11
|
|
12
|
+
def test_interface
|
13
|
+
%w(write
|
14
|
+
write!
|
15
|
+
get
|
16
|
+
get!
|
17
|
+
close
|
18
|
+
closed?
|
19
|
+
readable?
|
20
|
+
last_msg
|
21
|
+
put
|
22
|
+
put!
|
23
|
+
recv
|
24
|
+
recv!).each do |method|
|
25
|
+
assert @channel.respond_to? method
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_serialization
|
30
|
+
@channel.put 42
|
31
|
+
dump = Marshal.dump @channel
|
32
|
+
assert_equal 42, Marshal.load(dump).get
|
33
|
+
end
|
34
|
+
|
12
35
|
def test_last_msg
|
13
36
|
@channel.put %w(a)
|
14
37
|
@channel.put %w(b)
|
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:
|
4
|
+
version: 6.0.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-04-
|
12
|
+
date: 2013-04-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! "A modern and easy-to-use interprocess communication \n primitive."
|
15
15
|
email:
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- Rakefile
|
30
30
|
- ichannel.gemspec
|
31
31
|
- lib/ichannel.rb
|
32
|
+
- lib/ichannel/unix_socket.rb
|
32
33
|
- lib/ichannel/version.rb
|
33
34
|
- test/ichannel_class_test.rb
|
34
35
|
- test/setup.rb
|
@@ -46,7 +47,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
47
|
version: '0'
|
47
48
|
segments:
|
48
49
|
- 0
|
49
|
-
hash:
|
50
|
+
hash: -4148613930352415051
|
50
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
52
|
none: false
|
52
53
|
requirements:
|
@@ -55,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
56
|
version: '0'
|
56
57
|
segments:
|
57
58
|
- 0
|
58
|
-
hash:
|
59
|
+
hash: -4148613930352415051
|
59
60
|
requirements: []
|
60
61
|
rubyforge_project:
|
61
62
|
rubygems_version: 1.8.23
|