Skip to content

Instantly share code, notes, and snippets.

@ahmadsherif
Created January 29, 2016 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahmadsherif/998858e65899716edd7d to your computer and use it in GitHub Desktop.
Save ahmadsherif/998858e65899716edd7d to your computer and use it in GitHub Desktop.
ARGF#read_nonblock
diff --git a/kernel/common/argf.rb b/kernel/common/argf.rb
index c9f80dd..c710116 100644
--- a/kernel/common/argf.rb
+++ b/kernel/common/argf.rb
@@ -402,6 +402,33 @@ module Rubinius
alias_method :to_a, :readlines
+ def read_nonblock(maxlen, output=nil, opts={})
+ if output.is_a?(Hash)
+ opts = output
+ output = nil
+ end
+
+ output ||= default_value
+
+ unless advance!
+ output.clear
+ raise EOFError, "ARGF at end"
+ end
+
+ begin
+ ret = @stream.read_nonblock(maxlen, output, opts)
+ return ret unless ret.is_a?(Hash)
+ rescue EOFError => e
+ raise e if @use_stdin_only
+
+ @stream.close
+ @advance = true
+ advance! or raise e
+ end
+
+ return output
+ end
+
def readpartial(maxlen, output=nil)
output ||= default_value
diff --git a/spec/custom/helpers/ruby_exe_pipe.rb b/spec/custom/helpers/ruby_exe_pipe.rb
new file mode 100644
index 0000000..6ac99d0
--- /dev/null
+++ b/spec/custom/helpers/ruby_exe_pipe.rb
@@ -0,0 +1,9 @@
+class Object
+ def ruby_exe_pipe(code, opts={})
+ pipe_mode = opts.delete(:pipe_mode) || 'r'
+ f = IO.popen(ruby_cmd(code, opts), pipe_mode)
+ yield f
+ ensure
+ f.close if f && !f.closed?
+ end
+end
diff --git a/spec/custom/mspec.rb b/spec/custom/mspec.rb
index ee009a1..229d6b2 100644
--- a/spec/custom/mspec.rb
+++ b/spec/custom/mspec.rb
@@ -13,3 +13,4 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
end
require 'spec/custom/utils/script'
+require 'spec/custom/helpers/ruby_exe_pipe'
diff --git a/spec/ruby/core/argf/read_nonblock_spec.rb b/spec/ruby/core/argf/read_nonblock_spec.rb
new file mode 100644
index 0000000..7a53161
--- /dev/null
+++ b/spec/ruby/core/argf/read_nonblock_spec.rb
@@ -0,0 +1,78 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../shared/read', __FILE__)
+require File.expand_path('../shared/readpartial', __FILE__)
+
+describe "ARGF.read_nonblock" do
+ it_behaves_like :argf_read, :read_nonblock
+ it_behaves_like :argf_readpartial, :read_nonblock
+
+ context "when ARGF is not readable yet" do
+ context "when exception option is not passed" do
+ it "raises an IO::WaitReadable exception if ARGF is not readable yet" do
+ output = ruby_exe <<-STR, escape: true
+ begin
+ ARGF.read_nonblock(1)
+ rescue IO::WaitReadable => e
+ print "IO::WaitReadable raised"
+ end
+ STR
+ output.should == "IO::WaitReadable raised"
+ end
+ end
+
+ context "when exception option is set to false" do
+ it "returns :wait_readable" do
+ ruby_exe("print ARGF.read_nonblock(1, exception: false).inspect").should == ":wait_readable"
+ end
+ end
+ end
+
+ context "when there is nothing more to read" do
+ context "when exception option is not passed" do
+ it "raises EOFError when there is nothing more to read" do
+ ruby_exe_pipe(<<-STR, escape: true, pipe_mode: "r+") do |f|
+ $stdout.sync
+ begin
+ ARGF.read_nonblock(1)
+ rescue EOFError
+ print "EOFError raised"
+ end
+ STR
+ f.close_write
+ f.read.should == "EOFError raised"
+ end
+ end
+ end
+
+ context "when exception option is set to false" do
+ it "returns nil" do
+ ruby_exe_pipe(<<-STR, escape: true, pipe_mode: "r+") do |f|
+ $stdout.sync
+ print ARGF.read_nonblock(1, exception: false).inspect
+ STR
+ f.close_write
+ f.read.should == "nil"
+ end
+ end
+ end
+ end
+
+ it "reads a maximum number of bytes once it is ready for reading" do
+ ruby_exe_pipe("print ARGF.read_nonblock(1)", escape: true, pipe_mode: "r+") do |f|
+ f.write("a")
+ f.read(1).should == "a"
+ end
+ end
+
+ context "when exception option is set to false" do
+ it "raises an EOFError if the exception was raised while reading the last file" do
+ argv [@file1_name, @file2_name] do
+ ARGF.send(@method, @file1.size)
+ ARGF.send(@method, 1)
+ ARGF.send(@method, @file2.size)
+ ARGF.send(@method, 1, exception: false).should be_nil
+ lambda { ARGF.send(@method, 1, exception: false) }.should raise_error(EOFError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/argf/readpartial_spec.rb b/spec/ruby/core/argf/readpartial_spec.rb
index bd3f8d9..237956b 100644
--- a/spec/ruby/core/argf/readpartial_spec.rb
+++ b/spec/ruby/core/argf/readpartial_spec.rb
@@ -1,75 +1,8 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/read', __FILE__)
+require File.expand_path('../shared/readpartial', __FILE__)
describe "ARGF.readpartial" do
it_behaves_like :argf_read, :readpartial
-
- before :each do
- @file1_name = fixture __FILE__, "file1.txt"
- @file2_name = fixture __FILE__, "file2.txt"
- @stdin_name = fixture __FILE__, "stdin.txt"
-
- @file1 = File.read @file1_name
- @file2 = File.read @file2_name
- @stdin = File.read @stdin_name
- end
-
- it "raises an ArgumentError if called without a maximum read length" do
- argv [@file1_name] do
- lambda { ARGF.readpartial }.should raise_error(ArgumentError)
- end
- end
-
- it "reads maximum number of bytes from one file at a time" do
- argv [@file1_name, @file2_name] do
- len = @file1.size + @file2.size
- ARGF.readpartial(len).should == @file1
- end
- end
-
- it "clears output buffer even if EOFError is raised because ARGF is at end" do
- begin
- output = "to be cleared"
-
- argv [@file1_name] do
- ARGF.read
- ARGF.readpartial(1, output)
- end
- rescue EOFError
- output.should == ""
- end
- end
-
- it "reads maximum number of bytes from one file at a time" do
- argv [@file1_name, @file2_name] do
- len = @file1.size + @file2.size
- ARGF.readpartial(len).should == @file1
- end
- end
-
- it "returns an empty string if EOFError is raised while reading any but the last file" do
- argv [@file1_name, @file2_name] do
- ARGF.readpartial(@file1.size)
- ARGF.readpartial(1).should == ""
- end
- end
-
- it "raises an EOFError if the exception was raised while reading the last file" do
- argv [@file1_name, @file2_name] do
- ARGF.readpartial(@file1.size)
- ARGF.readpartial(1)
- ARGF.readpartial(@file2.size)
- lambda { ARGF.readpartial(1) }.should raise_error(EOFError)
- lambda { ARGF.readpartial(1) }.should raise_error(EOFError)
- end
- end
-
- it "raises an EOFError if the exception was raised while reading STDIN" do
- ruby_str = <<-STR
- print ARGF.readpartial(#{@stdin.size})
- ARGF.readpartial(1) rescue print $!.class
- STR
- stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}", escape: true)
- stdin.should == @stdin + "EOFError"
- end
+ it_behaves_like :argf_readpartial, :readpartial
end
diff --git a/spec/ruby/core/argf/shared/readpartial.rb b/spec/ruby/core/argf/shared/readpartial.rb
new file mode 100644
index 0000000..bd777c1
--- /dev/null
+++ b/spec/ruby/core/argf/shared/readpartial.rb
@@ -0,0 +1,70 @@
+describe :argf_readpartial, shared: true do
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+ @stdin_name = fixture __FILE__, "stdin.txt"
+
+ @file1 = File.read @file1_name
+ @file2 = File.read @file2_name
+ @stdin = File.read @stdin_name
+ end
+
+ it "raises an ArgumentError if called without a maximum read length" do
+ argv [@file1_name] do
+ lambda { ARGF.send(@method) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "reads maximum number of bytes from one file at a time" do
+ argv [@file1_name, @file2_name] do
+ len = @file1.size + @file2.size
+ ARGF.send(@method, len).should == @file1
+ end
+ end
+
+ it "clears output buffer even if EOFError is raised because ARGF is at end" do
+ begin
+ output = "to be cleared"
+
+ argv [@file1_name] do
+ ARGF.read
+ ARGF.send(@method, 1, output)
+ end
+ rescue EOFError
+ output.should == ""
+ end
+ end
+
+ it "reads maximum number of bytes from one file at a time" do
+ argv [@file1_name, @file2_name] do
+ len = @file1.size + @file2.size
+ ARGF.send(@method, len).should == @file1
+ end
+ end
+
+ it "returns an empty string if EOFError is raised while reading any but the last file" do
+ argv [@file1_name, @file2_name] do
+ ARGF.send(@method, @file1.size)
+ ARGF.send(@method, 1).should == ""
+ end
+ end
+
+ it "raises an EOFError if the exception was raised while reading the last file" do
+ argv [@file1_name, @file2_name] do
+ ARGF.send(@method, @file1.size)
+ ARGF.send(@method, 1)
+ ARGF.send(@method, @file2.size)
+ lambda { ARGF.send(@method, 1) }.should raise_error(EOFError)
+ lambda { ARGF.send(@method, 1) }.should raise_error(EOFError)
+ end
+ end
+
+ it "raises an EOFError if the exception was raised while reading STDIN" do
+ ruby_str = <<-STR
+ print ARGF.#{@method}(#{@stdin.size})
+ ARGF.#{@method}(1) rescue print $!.class
+ STR
+ stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}", escape: true)
+ stdin.should == @stdin + "EOFError"
+ end
+end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment