added pipe class. docced and tested
This commit is contained in:
parent
7031cb8c4c
commit
63809a7258
2
Rakefile
2
Rakefile
|
@ -10,7 +10,7 @@ begin
|
||||||
gem.email = "nick@smartlogicsolutions.com"
|
gem.email = "nick@smartlogicsolutions.com"
|
||||||
gem.homepage = "http://github.com/ngauthier/hydra"
|
gem.homepage = "http://github.com/ngauthier/hydra"
|
||||||
gem.authors = ["Nick Gauthier"]
|
gem.authors = ["Nick Gauthier"]
|
||||||
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
gem.add_development_dependency "shoulda", "= 2.10.3"
|
||||||
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
||||||
end
|
end
|
||||||
Jeweler::GemcutterTasks.new
|
Jeweler::GemcutterTasks.new
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
require 'hydra/pipe'
|
|
@ -0,0 +1,109 @@
|
||||||
|
module Hydra #:nodoc:
|
||||||
|
# Read and write between two processes via pipes. For example:
|
||||||
|
# @pipe = Hydra::Pipe.new
|
||||||
|
# Process.fork do
|
||||||
|
# @pipe.identify_as_child
|
||||||
|
# sleep(1)
|
||||||
|
# puts "A message from my parent:\n#{@pipe.gets}"
|
||||||
|
# @pipe.close
|
||||||
|
# end
|
||||||
|
# @pipe.identify_as_parent
|
||||||
|
# @pipe.write "Hello, Child!"
|
||||||
|
# @pipe.close
|
||||||
|
# When the process forks, the pipe is copied. When a pipe is
|
||||||
|
# identified as a parent or child, it is choosing which ends
|
||||||
|
# of the pipe to use.
|
||||||
|
#
|
||||||
|
# A pipe is actually two pipes:
|
||||||
|
#
|
||||||
|
# Parent === Pipe 1 ==> Child
|
||||||
|
# Parent <== Pipe 2 === Child
|
||||||
|
class Pipe
|
||||||
|
# Creates a new uninitialized pipe pair.
|
||||||
|
def initialize
|
||||||
|
@child_read, @parent_write = IO.pipe
|
||||||
|
@parent_read, @child_write = IO.pipe
|
||||||
|
[@parent_write, @child_write].each{|io| io.sync = true}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read a line from a pipe. It will have a trailing newline.
|
||||||
|
def gets
|
||||||
|
force_identification
|
||||||
|
@reader.gets
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write a line to a pipe. It must have a trailing newline.
|
||||||
|
def write(str)
|
||||||
|
force_identification
|
||||||
|
begin
|
||||||
|
@writer.write(str)
|
||||||
|
return str
|
||||||
|
rescue Errno::EPIPE
|
||||||
|
raise Hydra::PipeError::Broken
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if there is nothing to read (right now). However it is
|
||||||
|
# not exactly eof, if the other side writes, this will return false.
|
||||||
|
#
|
||||||
|
# It's a good way to tell if there is anything to process right now,
|
||||||
|
# otherwise, you can sleep.
|
||||||
|
def eof?
|
||||||
|
@reader.eof?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Identify this side of the pipe as the child.
|
||||||
|
def identify_as_child
|
||||||
|
@parent_write.close
|
||||||
|
@parent_read.close
|
||||||
|
@reader = @child_read
|
||||||
|
@writer = @child_write
|
||||||
|
end
|
||||||
|
|
||||||
|
# Identify this side of the pipe as the parent
|
||||||
|
def identify_as_parent
|
||||||
|
@child_write.close
|
||||||
|
@child_read.close
|
||||||
|
@reader = @parent_read
|
||||||
|
@writer = @parent_write
|
||||||
|
end
|
||||||
|
|
||||||
|
# closes the pipes. Once a pipe is closed on one end, the other
|
||||||
|
# end will get a PipeError::Broken if it tries to write.
|
||||||
|
def close
|
||||||
|
done_reading
|
||||||
|
done_writing
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def done_writing #:nodoc:
|
||||||
|
@writer.close unless @writer.closed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def done_reading #:nodoc:
|
||||||
|
@reader.close unless @reader.closed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def force_identification #:nodoc:
|
||||||
|
raise PipeError::Unidentified if @reader.nil? or @writer.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module PipeError #:nodoc:
|
||||||
|
# Raised if you try to read or write to a pipe when it is unidentified.
|
||||||
|
# Use identify_as_parent and identify_as_child to identify a pipe.
|
||||||
|
class Unidentified < RuntimeError
|
||||||
|
def message #:nodoc:
|
||||||
|
"Must identify as child or parent"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# Raised when a pipe has been broken between two processes.
|
||||||
|
# This happens when a process exits, and is a signal that
|
||||||
|
# there is no more data to communicate.
|
||||||
|
class Broken < RuntimeError
|
||||||
|
def message #:nodoc:
|
||||||
|
"Other side closed the connection"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
require 'helper'
|
|
||||||
|
|
||||||
class TestHydra < Test::Unit::TestCase
|
|
||||||
should "probably rename this file and start testing for real" do
|
|
||||||
flunk "hey buddy, you should probably rename this file and start testing for real"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
require 'helper'
|
||||||
|
|
||||||
|
class TestPipeDream < Test::Unit::TestCase
|
||||||
|
context "a pipe" do
|
||||||
|
setup do
|
||||||
|
@pipe = Hydra::Pipe.new
|
||||||
|
end
|
||||||
|
should "be able to write messages" do
|
||||||
|
Process.fork do
|
||||||
|
@pipe.identify_as_child
|
||||||
|
assert_equal "Test Message\n", @pipe.gets
|
||||||
|
@pipe.write "Message Received\n"
|
||||||
|
@pipe.write "Second Message\n"
|
||||||
|
@pipe.close
|
||||||
|
end
|
||||||
|
@pipe.identify_as_parent
|
||||||
|
@pipe.write "Test Message\n"
|
||||||
|
assert_equal "Message Received\n", @pipe.gets
|
||||||
|
assert !@pipe.eof?
|
||||||
|
assert_equal "Second Message\n", @pipe.gets
|
||||||
|
assert @pipe.eof?
|
||||||
|
assert_raise Hydra::PipeError::Broken do
|
||||||
|
@pipe.write "anybody home?"
|
||||||
|
end
|
||||||
|
@pipe.close
|
||||||
|
end
|
||||||
|
should "not allow writing if unidentified" do
|
||||||
|
assert_raise Hydra::PipeError::Unidentified do
|
||||||
|
@pipe.write "hey\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
should "not allow reading if unidentified" do
|
||||||
|
assert_raise Hydra::PipeError::Unidentified do
|
||||||
|
@pipe.gets
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue