2

In a Ruby script, I want to work on the first few lines of a source file (reading them with .readline) and simply copy the rest of the source file to a target file (with IO.copy_stream on the open files). However, IO.copy_stream seems not to "see" the rest of the source file.

Here's how to reproduce the problem:

require 'rspec'

require 'tempfile'

def file_create(content)
  Tempfile.open('foo') do |src|
    src.print content
    src.close
    yield src.path
  end
end

describe :copy_stream do
  it 'copies the content of an open file' do
    file_create("a\nb\n") do |path|
      open(path) do |from|
        Tempfile.open('bar') do |to|
          IO.copy_stream(from, to)
          to.close
          expect(IO.read(to.path)).to eq "a\nb\n"
        end
      end
    end
  end

  it 'copies the rest of an open file' do
    file_create("a\nb\n") do |path|
      open(path) do |from|
        Tempfile.open('bar') do |to|
          to.print from.readline
          IO.copy_stream(from, to)
          to.close
          expect(IO.read(to.path)).to eq "a\nb\n"
        end
      end
    end
  end
end

That's the output:

Failures:

  1) copy_stream copies the rest of an open file
     Failure/Error: expect(IO.read(to.path)).to eq "a\nb\n"

       expected: "a\nb\n"
            got: "a\n"

       (compared using ==)

       Diff:
       @@ -1,3 +1,2 @@
        a
       -b
[...]
2 examples, 1 failure

Failed examples:

rspec /workspaces/socbm378/johanabt/incstrip/spec/copy_stream_spec.rb:27 # 
copy_stream copies the rest of an open file

Why does copy_stream fail to copy the rest of the file?

I've found a simple work-around, but I'd like to understand the underlying problem. Here is my work-around:

def copy_stream2(from, to)
  while (buf = from.read(16 * 1024))
    to.print buf
  end
end
hagello
  • 2,843
  • 2
  • 27
  • 37
  • what's your while loop doing in `copy_stream2 ` if you're just doing assignment as a condition? – lacostenycoder Dec 20 '18 at 09:34
  • also why would you use `.readline` for this test if you expect test to be the value of `.readlines` ? What exactly are you trying to test here? Seems like you don't trust core ruby. – lacostenycoder Dec 20 '18 at 09:42
  • @lacost (1) The result of any assignment is the value that has been assigned. This is a legacy or a feature inherited from C, I guess, depending on your tastes. (2) `read(n)` returns either data or `nil` (for end-of-file, if n > 0): https://ruby-doc.org/core-2.0.0/IO.html#method-i-read – hagello Dec 20 '18 at 10:31
  • I'm still not sure why you chose to use `.readline` so see answer I posted. – lacostenycoder Dec 20 '18 at 16:13
  • @lacost The second example shows a perceived problem of the interaction between `readline` and `IO.copy_stream`. It reproduces a problem that occurred in a bigger program. That's why using `readlines` instead is not an option, sorry. Don't you think that's is worth a discussion on why Ruby behaves in this way? – hagello Dec 20 '18 at 19:20
  • maybe have look here https://stackoverflow.com/questions/1293695/watch-read-a-growing-log-file – lacostenycoder Dec 20 '18 at 20:46

0 Answers0