zchannel 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b734e305c5b58ff2748a914726a87b17096ae28
4
+ data.tar.gz: fbc41b5a2ffd5323197761ffcb3298c829dd79db
5
+ SHA512:
6
+ metadata.gz: ac889c3e6f4704c42e97e58da6268c5a06e1b6defd670b2c43984e008def714ef9b59f1ef1041454f3005e62ffde5f4eca65359a7d135bda64c4447cc8d808c9
7
+ data.tar.gz: 9a5c36e1c85da9783fb05703eb7271b951aec14e99008db97bdfc6e0d5bfa0954ea7f4a2634abbb1db98d428460d06754a49881d58eff796e635033238857da9
data/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ * text eol=lf
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .gems
2
+ .bundle
3
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - rbx-19mode
6
+ # - ruby-head
7
+
8
+ env:
9
+ - REDIS=1 SERIALIZER=YAML
10
+ - REDIS=1 SERIALIZER=Marshal
11
+ - REDIS=1 SERIALIZER=JSON
12
+
13
+ services:
14
+ - redis-server
15
+
16
+ notifications:
17
+ email: true
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ -m markdown -M redcarpet
2
+ -
3
+ README.md
4
+ LICENSE.txt
5
+ ChangeLog.txt
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
3
+ gem "test-unit"
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ __zchannel__
2
+
3
+ zchannel is a library that assists with InterProcess Communication, or
4
+ IPC for short. It provides a high level and easy to use API for sending
5
+ Ruby objects between processes that are running on the same machine.
6
+ A channel is implemented on top of an unbound UNIXSocket, and objects
7
+ are serialized with a serializer of your choosing although the examples
8
+ use the "Marshal" serializer, which is available without any extra
9
+ dependencies.
10
+
11
+ __Examples__
12
+
13
+ __1.__
14
+
15
+ ```ruby
16
+ chan = ZChannel.unix Marshal
17
+ Process.wait fork { chan.send "Hello, world!" }
18
+ chan.recv # => "Hello, world!"
19
+ ```
20
+
21
+ __Install__
22
+
23
+ * Rubygems
24
+
25
+ ```
26
+ $ gem install zchannel
27
+ ```
28
+
29
+ * Bundler
30
+
31
+ ```ruby
32
+ gem "zchannel", "~> 0.1"
33
+ ```
34
+
35
+ __License__
36
+
37
+ ```
38
+ This is free and unencumbered software released into the public domain.
39
+
40
+ Anyone is free to copy, modify, publish, use, compile, sell, or
41
+ distribute this software, either in source code form or as a compiled
42
+ binary, for any purpose, commercial or non-commercial, and by any
43
+ means.
44
+
45
+ In jurisdictions that recognize copyright laws, the author or authors
46
+ of this software dedicate any and all copyright interest in the
47
+ software to the public domain.
48
+
49
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
52
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
53
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
54
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
55
+ OTHER DEALINGS IN THE SOFTWARE.
56
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.test_files = Dir['test/*_test.rb']
5
+ end
6
+ task default: :test
@@ -0,0 +1,139 @@
1
+ require 'socket'
2
+ class ZChannel::UNIXSocket
3
+ SEP = '_$_' if respond_to? :private_constant
4
+
5
+ #
6
+ # @param [#dump,#load] serializer
7
+ # An object who implements `.dump` and `.load` methods
8
+ #
9
+ # @return [ZChannel::UNIXSocket]
10
+ #
11
+ def initialize(serializer = Marshal)
12
+ @serializer = serializer
13
+ @last_msg = nil
14
+ @reader, @writer = ::UNIXSocket.pair :STREAM
15
+ end
16
+
17
+ #
18
+ # @return [Boolean]
19
+ # Returns true when a channel is closed
20
+ #
21
+ def closed?
22
+ @reader.closed? and @writer.closed?
23
+ end
24
+
25
+ #
26
+ # Close the channel
27
+ #
28
+ # @raise [IOError]
29
+ # When a channel is already closed
30
+ #
31
+ # @return [Boolean]
32
+ # Returns true on success
33
+ #
34
+ def close
35
+ if closed?
36
+ raise IOError, 'closed channel'
37
+ else
38
+ @reader.close
39
+ @writer.close
40
+ true
41
+ end
42
+ end
43
+
44
+ #
45
+ # @raise [IOError]
46
+ # (see #send!)
47
+ #
48
+ # @param [Object] object
49
+ # An object to add to a channel
50
+ #
51
+ def send(object)
52
+ send!(object, nil)
53
+ end
54
+
55
+ #
56
+ # @param
57
+ # (see ZChannel::UNIXSocket#send)
58
+ #
59
+ # @param [Fixnum] timeout
60
+ # Number of seconds to wait before raising an exception
61
+ #
62
+ # @raise [IOError]
63
+ # When channel is closed
64
+ #
65
+ # @raise [ZChannel::TimeoutError]
66
+ # When a write doesn't finish within the timeout
67
+ #
68
+ def send!(object, timeout = 0.1)
69
+ if @writer.closed?
70
+ raise IOError, 'closed channel'
71
+ end
72
+ _, writable, _ = IO.select nil, [@writer], nil, timeout
73
+ if writable
74
+ msg = @serializer.dump(object)
75
+ writable[0].syswrite "#{msg}#{SEP}"
76
+ else
77
+ raise ZChannel::TimeoutError, "timeout, waited #{timeout} seconds"
78
+ end
79
+ end
80
+
81
+ #
82
+ # Perform a blocking read
83
+ #
84
+ # @raise
85
+ # (see ZChannel::UNIXSocket#recv)
86
+ #
87
+ # @return [Object]
88
+ #
89
+ def recv
90
+ recv!(nil)
91
+ end
92
+
93
+ #
94
+ # Perform a read with a timeout
95
+ #
96
+ # @param [Fixnum] timeout
97
+ # Number of seconds to wait before raising an exception
98
+ #
99
+ # @raise [IOError]
100
+ # When channel is closed
101
+ #
102
+ # @raise [ZChannel::TimeoutError]
103
+ # When a read doesn't finish within the timeout
104
+ #
105
+ # @return [Object]
106
+ #
107
+ def recv!(timeout = 0.1)
108
+ if @reader.closed?
109
+ raise IOError, 'closed channel'
110
+ end
111
+ readable, _ = IO.select [@reader], nil, nil, timeout
112
+ if readable
113
+ msg = readable[0].readline(SEP).chomp SEP
114
+ @last_msg = @serializer.load msg
115
+ else
116
+ raise ZChannel::TimeoutError, "timeout, waited #{timeout} seconds"
117
+ end
118
+ end
119
+
120
+ #
121
+ # @return [Object]
122
+ #
123
+ def last_msg
124
+ @last_msg = recv while readable?
125
+ @last_msg
126
+ end
127
+
128
+ #
129
+ # @return [Boolean]
130
+ #
131
+ def readable?
132
+ if closed?
133
+ false
134
+ else
135
+ readable, _ = IO.select [@reader], nil, nil, 0
136
+ !! readable
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,3 @@
1
+ module ZChannel
2
+ VERSION = "0.1.0"
3
+ end
data/lib/zchannel.rb ADDED
@@ -0,0 +1,15 @@
1
+ module ZChannel
2
+ TimeoutError = Class.new(StandardError)
3
+ require_relative "zchannel/unix_socket"
4
+
5
+ #
6
+ # @param
7
+ # (see UNIXSocket#initialize).
8
+ #
9
+ # @return
10
+ # (see UNIXSocket#initialize)
11
+ #
12
+ def self.unix(serializer = Marshal)
13
+ UNIXSocket.new(serializer)
14
+ end
15
+ end
data/test/setup.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler/setup'
2
+ require 'test/unit'
3
+ Bundler.require :default, :test
@@ -0,0 +1,94 @@
1
+ require_relative 'setup'
2
+ class ZChannelTest < Test::Unit::TestCase
3
+ def setup
4
+ @chan = ZChannel.unix Object.const_get(ENV["SERIALIZER"] || "Marshal")
5
+ end
6
+
7
+ def teardown
8
+ @chan.close unless @chan.closed?
9
+ end
10
+
11
+ def test_blocking_recv
12
+ assert_raises Timeout::Error do
13
+ Timeout.timeout(1) { @chan.recv }
14
+ end
15
+ end
16
+
17
+ def test_timeout_on_recv
18
+ assert_raises ZChannel::TimeoutError do
19
+ @chan.recv! 1
20
+ end
21
+ end
22
+
23
+ def test_last_msg_after_read
24
+ @chan.send [42]
25
+ @chan.recv
26
+ assert_equal [42], @chan.last_msg
27
+ end
28
+
29
+ def test_fork
30
+ pid = fork do
31
+ @chan.send [42]
32
+ end
33
+ Process.wait pid
34
+ assert_equal [42], @chan.recv
35
+ end
36
+
37
+ def test_last_msg
38
+ @chan.send %w(a)
39
+ @chan.send %w(b)
40
+ assert_equal %w(b), @chan.last_msg
41
+ end
42
+
43
+ def test_last_msg_cache
44
+ @chan.send %w(a)
45
+ 2.times { assert_equal %w(a), @chan.last_msg }
46
+ @chan.close
47
+ assert_equal %w(a), @chan.last_msg
48
+ end
49
+
50
+ def test_bust_last_msg_cache
51
+ @chan.send %w(a)
52
+ assert_equal %w(a), @chan.last_msg
53
+ @chan.send %w(b)
54
+ 2.times { assert_equal %w(b), @chan.last_msg }
55
+ end
56
+
57
+ def test_send_on_closed_channel
58
+ @chan.close
59
+ assert_raises IOError do
60
+ @chan.send %w(a)
61
+ end
62
+ end
63
+
64
+ def test_recv_on_closed_channel
65
+ @chan.close
66
+ assert_raises IOError do
67
+ @chan.recv
68
+ end
69
+ end
70
+
71
+ def test_queued_messages
72
+ @chan.send %w(a)
73
+ @chan.send %w(b)
74
+ assert_equal %w(a), @chan.recv
75
+ assert_equal %w(b), @chan.recv
76
+ end
77
+
78
+ def test_readable_on_populated_channel
79
+ @chan.send %w(a)
80
+ @chan.send %w(b)
81
+ assert @chan.readable?
82
+ end
83
+
84
+ def test_readable_on_empty_channel
85
+ @chan.send %w(42)
86
+ @chan.recv # discard
87
+ refute @chan.readable?
88
+ end
89
+
90
+ def test_readable_on_closed_channel
91
+ @chan.close
92
+ refute @chan.readable?
93
+ end
94
+ end
data/zchannel.gemspec ADDED
@@ -0,0 +1,13 @@
1
+ Kernel.require './lib/zchannel/version'
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "zchannel"
4
+ gem.version = ZChannel::VERSION
5
+ gem.authors = ["jazzonmymind"]
6
+ gem.email = ["robert@jazzonmymind.xyz"]
7
+ gem.description = "Provides a high level and easy to use API for sending objects between Ruby processes"
8
+ gem.summary = gem.description
9
+ gem.homepage = "https://github.com/jazzonmymind/zchannel.rb"
10
+ gem.licenses = ["Nonstandard"]
11
+ gem.files = `git ls-files`.split($/)
12
+ gem.require_paths = ["lib"]
13
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zchannel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - jazzonmymind
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Provides a high level and easy to use API for sending objects between
14
+ Ruby processes
15
+ email:
16
+ - robert@jazzonmymind.xyz
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitattributes"
22
+ - ".gitignore"
23
+ - ".travis.yml"
24
+ - ".yardopts"
25
+ - Gemfile
26
+ - README.md
27
+ - Rakefile
28
+ - lib/zchannel.rb
29
+ - lib/zchannel/unix_socket.rb
30
+ - lib/zchannel/version.rb
31
+ - test/setup.rb
32
+ - test/zchannel_test.rb
33
+ - zchannel.gemspec
34
+ homepage: https://github.com/jazzonmymind/zchannel.rb
35
+ licenses:
36
+ - Nonstandard
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.5.1
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Provides a high level and easy to use API for sending objects between Ruby
58
+ processes
59
+ test_files: []
60
+ has_rdoc: