xchannel 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3241b5fe46b009d7222e97c76fd892496d354755
4
+ data.tar.gz: bd09019fb519ebac631a621f16af75d0c7c5aab2
5
+ SHA512:
6
+ metadata.gz: a7d37c874cf7b6a3a7a5371863cf2615bbabdd55bb8d16d6ce563407afb171c051b1c79004d1765e47e27ec849b12f5e54034af213a393d7e93bdb90e8cd4ecb
7
+ data.tar.gz: c95e63dc6666bffb84fb62582287e3bf411eece5cbdfcbc623840441844e85a42d5565b183b575c03880a9048bd5d2cd55996424e7226e0bebc7b205dbf5be60
@@ -0,0 +1,3 @@
1
+ ---
2
+ BUNDLE_PATH: ".gems"
3
+ BUNDLE_DISABLE_SHARED_GEMS: '1'
@@ -0,0 +1 @@
1
+ * text eol=lf
@@ -0,0 +1,2 @@
1
+ .gems
2
+ Gemfile.lock
@@ -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
@@ -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,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 rpag
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ __xchannel__
2
+
3
+ xchannel is a library to assist with interprocess communication.
4
+ It implements a channel that can be used to transmit ruby objects
5
+ between ruby processes running on the same machine. It is implemented
6
+ on top of an unbound, streamed unix socket.
7
+
8
+ __Examples__
9
+
10
+ __1.__
11
+
12
+ basic fork example.
13
+ [Marshal](http://rubydoc.info/stdlib/core/Marshal) is the serializer of choice.
14
+
15
+ ```ruby
16
+ channel = XChannel.unix Marshal
17
+ pid = fork do
18
+ channel.put Process.pid
19
+ channel.put 'Hello!'
20
+ end
21
+ Process.wait pid
22
+ channel.get # => Fixnum
23
+ channel.get # => 'Hello!'
24
+ ```
25
+
26
+ __2.__
27
+
28
+ avoid a blocking read
29
+
30
+ ```ruby
31
+ channel = XChannel.unix Marshal
32
+ pid = fork do
33
+ sleep 3
34
+ channel.put 42
35
+ end
36
+ until channel.readable?
37
+ # do something else
38
+ end
39
+ channel.get # => 42
40
+ Process.wait pid
41
+ ```
42
+
43
+ __Install__
44
+
45
+ $ gem install xchannel
46
+
47
+ __Contributing__
48
+
49
+ 1. Fork it ( https://github.com/rpag/xchannel/fork )
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create a new Pull Request
54
+
55
+ __License__
56
+
57
+ MIT. See LICENSE.txt.
@@ -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,15 @@
1
+ module XChannel
2
+ TimeoutError = Class.new(StandardError)
3
+ require_relative "xchannel/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
@@ -0,0 +1,144 @@
1
+ require 'socket'
2
+ class XChannel::UNIXSocket
3
+ SEP = '_$_'
4
+ if respond_to? :private_constant
5
+ private_constant :SEP
6
+ end
7
+
8
+ #
9
+ # @param [#dump,#load] serializer
10
+ # An object who implements `.dump` and `.load` methods
11
+ #
12
+ # @return [XChannel::UNIXSocket]
13
+ #
14
+ def initialize(serializer = Marshal)
15
+ @serializer = serializer
16
+ @last_msg = nil
17
+ @reader, @writer = ::UNIXSocket.pair :STREAM
18
+ end
19
+
20
+ #
21
+ # @return [Boolean]
22
+ # Returns true when a channel is closed
23
+ #
24
+ def closed?
25
+ @reader.closed? and @writer.closed?
26
+ end
27
+
28
+ #
29
+ # Close the channel
30
+ #
31
+ # @raise [IOError]
32
+ # When a channel is already closed
33
+ #
34
+ # @return [Boolean]
35
+ # Returns true on success
36
+ #
37
+ def close
38
+ if closed?
39
+ raise IOError, 'closed channel'
40
+ else
41
+ @reader.close
42
+ @writer.close
43
+ true
44
+ end
45
+ end
46
+
47
+ #
48
+ # @raise [IOError]
49
+ # (see #put!)
50
+ #
51
+ # @param [Object] object
52
+ # An object to add to a channel
53
+ #
54
+ def put(object)
55
+ put!(object, nil)
56
+ end
57
+
58
+ #
59
+ # @param
60
+ # (see XChannel::UNIXSocket#put)
61
+ #
62
+ # @param [Fixnum] timeout
63
+ # Number of seconds to wait before raising an exception
64
+ #
65
+ # @raise [IOError]
66
+ # When channel is closed
67
+ #
68
+ # @raise [XChannel::TimeoutError]
69
+ # When a write doesn't finish within the timeout
70
+ #
71
+ def put!(object, timeout = 0.1)
72
+ if @writer.closed?
73
+ raise IOError, 'closed channel'
74
+ end
75
+ _, writable, _ = IO.select nil, [@writer], nil, timeout
76
+ if writable
77
+ msg = @serializer.dump(object)
78
+ writable[0].syswrite "#{msg}#{SEP}"
79
+ else
80
+ raise XChannel::TimeoutError, "timeout, waited #{timeout} seconds"
81
+ end
82
+ end
83
+
84
+ #
85
+ # Perform a blocking read
86
+ #
87
+ # @raise
88
+ # (see XChannel::UNIXSocket#get)
89
+ #
90
+ # @return [Object]
91
+ #
92
+ def get
93
+ get!(nil)
94
+ end
95
+
96
+ #
97
+ # Perform a read with a timeout
98
+ #
99
+ # @param [Fixnum] timeout
100
+ # Number of seconds to wait before raising an exception
101
+ #
102
+ # @raise [IOError]
103
+ # When channel is closed
104
+ #
105
+ # @raise [XChannel::TimeoutError]
106
+ # When a read doesn't finish within the timeout
107
+ #
108
+ # @return [Object]
109
+ #
110
+ def get!(timeout = 0.1)
111
+ if @reader.closed?
112
+ raise IOError, 'closed channel'
113
+ end
114
+ readable, _ = IO.select [@reader], nil, nil, timeout
115
+ if readable
116
+ msg = readable[0].readline(SEP).chomp SEP
117
+ @last_msg = @serializer.load msg
118
+ else
119
+ raise XChannel::TimeoutError, "timeout, waited #{timeout} seconds"
120
+ end
121
+ end
122
+
123
+ #
124
+ # @return [Object]
125
+ #
126
+ def last_msg
127
+ while readable?
128
+ @last_msg = get
129
+ end
130
+ @last_msg
131
+ end
132
+
133
+ #
134
+ # @return [Boolean]
135
+ #
136
+ def readable?
137
+ if closed?
138
+ false
139
+ else
140
+ readable, _ = IO.select [@reader], nil, nil, 0
141
+ !! readable
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,3 @@
1
+ module XChannel
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,3 @@
1
+ require 'bundler/setup'
2
+ require 'test/unit'
3
+ Bundler.require :default, :test
@@ -0,0 +1,95 @@
1
+ require_relative 'setup'
2
+ class XChannelTest < Test::Unit::TestCase
3
+ def setup
4
+ serializer = Object.const_get ENV["SERIALIZER"] || "Marshal"
5
+ @channel = XChannel.unix serializer
6
+ end
7
+
8
+ def teardown
9
+ @channel.close unless @channel.closed?
10
+ end
11
+
12
+ def test_blocking_get
13
+ assert_raises Timeout::Error do
14
+ Timeout.timeout(1) { @channel.get }
15
+ end
16
+ end
17
+
18
+ def test_timeout_on_get
19
+ assert_raises XChannel::TimeoutError do
20
+ @channel.get! 1
21
+ end
22
+ end
23
+
24
+ def test_last_msg_after_read
25
+ @channel.put [42]
26
+ @channel.get
27
+ assert_equal [42], @channel.last_msg
28
+ end
29
+
30
+ def test_fork
31
+ pid = fork do
32
+ @channel.put [42]
33
+ end
34
+ Process.wait pid
35
+ assert_equal [42], @channel.get
36
+ end
37
+
38
+ def test_last_msg
39
+ @channel.put %w(a)
40
+ @channel.put %w(b)
41
+ assert_equal %w(b), @channel.last_msg
42
+ end
43
+
44
+ def test_last_msg_cache
45
+ @channel.put %w(a)
46
+ 2.times { assert_equal %w(a), @channel.last_msg }
47
+ @channel.close
48
+ assert_equal %w(a), @channel.last_msg
49
+ end
50
+
51
+ def test_bust_last_msg_cache
52
+ @channel.put %w(a)
53
+ assert_equal %w(a), @channel.last_msg
54
+ @channel.put %w(b)
55
+ 2.times { assert_equal %w(b), @channel.last_msg }
56
+ end
57
+
58
+ def test_put_on_closed_channel
59
+ @channel.close
60
+ assert_raises IOError do
61
+ @channel.put %w(a)
62
+ end
63
+ end
64
+
65
+ def test_get_on_closed_channel
66
+ @channel.close
67
+ assert_raises IOError do
68
+ @channel.get
69
+ end
70
+ end
71
+
72
+ def test_queued_messages
73
+ @channel.put %w(a)
74
+ @channel.put %w(b)
75
+ assert_equal %w(a), @channel.get
76
+ assert_equal %w(b), @channel.get
77
+ end
78
+
79
+ def test_readable_on_populated_channel
80
+ @channel.put %w(a)
81
+ @channel.put %w(b)
82
+ assert @channel.readable?
83
+ end
84
+
85
+ def test_readable_on_empty_channel
86
+ @channel.put %w(42)
87
+ @channel.get # discard
88
+ refute @channel.readable?
89
+ end
90
+
91
+ def test_readable_on_closed_channel
92
+ @channel.close
93
+ refute @channel.readable?
94
+ end
95
+ end
@@ -0,0 +1,12 @@
1
+ Kernel.require './lib/xchannel/version'
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "xchannel"
4
+ gem.version = XChannel::VERSION
5
+ gem.authors = ["rpag"]
6
+ gem.email = ["rpag@singletonclass.com"]
7
+ gem.description = "IPC channel"
8
+ gem.summary = gem.description
9
+ gem.homepage = "https://github.com/rpag/xchannel"
10
+ gem.files = `git ls-files`.split($/)
11
+ gem.require_paths = ["lib"]
12
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xchannel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - rpag
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: IPC channel
14
+ email:
15
+ - rpag@singletonclass.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".bundle/config"
21
+ - ".gitattributes"
22
+ - ".gitignore"
23
+ - ".travis.yml"
24
+ - ".yardopts"
25
+ - Gemfile
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - lib/xchannel.rb
30
+ - lib/xchannel/unix_socket.rb
31
+ - lib/xchannel/version.rb
32
+ - test/setup.rb
33
+ - test/xchannel_unix_test.rb
34
+ - xchannel.gemspec
35
+ homepage: https://github.com/rpag/xchannel
36
+ licenses: []
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.2.2
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: IPC channel
58
+ test_files: []
59
+ has_rdoc: