Jake Smith Jake Smith - 17 days ago 5
Ruby Question

Ruby peek with include? acts like next

I'm having trouble understanding my own ruby script at the moment. If I check the contents of the next position with

peek
, intending not to move the position yet, using
include?
, the position of my enumerator is moved to the next one anyway.

For example:

print @file.each_line.peek
if @file.each_line.peek.include? 'State'
...


outputs

State


but this:

if @file.each_line.peek.include? 'State'
print @file.each_line.peek
...


outputs

CO


The contents of my file look like

...
Extension Date
State
CO
COLORADO
...


I am opening this file in the following manner:
@file = File.open(file)
and using the
@file.each_line
enumerator.

To me, this looks like using
@file.each_line.peek.include? 'State'
is actually causing the position to move by one. Does anyone know why this is and how I can avoid it?

How to reproduce



Create a file called test.txt with the following contents:

Extension Date
State
CO
COLORADO


Create a file called test.rb with the following contents:

file = File.open('./test.txt')
until file.each_line.next.include? 'Extension Date' do ; end
print file.each_line.peek
if file.each_line.peek.include? 'State'
end


When you run with
ruby test.rb
, you should get the output
State
.

If you then move line 3 so that it is inside the if block, the output (for me) is
CO
.

Answer

It's not the .include?, it's how you get your enumerator (a new one each time). Observe:

@file.each_line.peek # => "Extension Date\n"
@file.each_line.peek # => "State\n"
@file.each_line.peek # => "CO\n"
@file.each_line.peek # => "COLORADO\n"
@file.each_line.peek # => "\n"

The problem here is that when each_line is called, it reads a line. And since file position is maintained between invocations, the second time you call it, it reads one more line. And so on.

Get enumerator once and hold on to it.

enum = @file.each_line

enum.peek # => "Extension Date\n"
enum.peek # => "Extension Date\n"
enum.peek # => "Extension Date\n"
enum.peek # => "Extension Date\n"
enum.peek.include?('foo') # => false
enum.peek # => "Extension Date\n"