diff --git a/io.c b/io.c index 366225089c..ec8a385cdc 100644 --- a/io.c +++ b/io.c @@ -4072,14 +4072,20 @@ rb_io_each_line(int argc, VALUE *argv, VALUE io) { VALUE str; struct getline_arg args; + rb_io_t *fptr; RETURN_ENUMERATOR(io, argc, argv); prepare_getline_args(argc, argv, &args, io); if (args.limit == 0) rb_raise(rb_eArgError, "invalid limit: 0 for each_line"); + + fptr = rb_io_get_fptr(io); + while (!NIL_P(str = rb_io_getline_1(args.rs, args.limit, args.chomp, io))) { rb_yield(str); + if (fptr->fd < 0) break; } + return io; } diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb index 91766fbe03..c8f3d327b6 100644 --- a/spec/ruby/core/io/shared/each.rb +++ b/spec/ruby/core/io/shared/each.rb @@ -41,6 +41,32 @@ -> { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError) end + ruby_version_is ""..."3.1" do + it "raises an IOError when the block closes self" do + lines_count = 0 + + -> do + @io.send(@method) do + lines_count += 1 + @io.close if lines_count >= 3 + end + end.should raise_error(IOError) + end + end + + ruby_version_is "3.1" do + it "does not raise IOError when the block closes self" do + lines_count = 0 + + @io.send(@method) do + lines_count += 1 + @io.close if lines_count >= 3 + end.should equal(@io) + + lines_count.should equal(3) + end + end + it "makes line count accessible via lineno" do @io.send(@method) { ScratchPad << @io.lineno } ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ]