From: "martinjos (Martin Sidaway)" Date: 2013-09-20T07:28:20+09:00 Subject: [ruby-core:57285] [ruby-trunk - Bug #8929] CSV.foreach(filename) without block returns failing Enumerator Issue #8929 has been updated by martinjos (Martin Sidaway). I can see that it is tricky, because a normal enumerator created by calling CSV#each shouldn't necessarily close the file (as the user may want to call #rewind), but when you use CSV.foreach you have no reference to the CSV object, and you don't want the file handle to be left open. However, I don't see why, in principle, CSV.foreach(filename) as an Enumerator shouldn't be supported. It has clear, well-defined semantics: it just has to close the file automatically after yielding the last entry. Also, this would be consistent with, amongst other things, File.foreach(filename). The present behaviour occurs because self.foreach() uses the block form of self.open(), which ensures the csv object is closed after yielding it. It then calls csv.each(&block) inside the open block, which is okay if a block was passed in to self.foreach, but if not, the no-block form of each() simply calls to_enum (from Object). This enumerator then gets passed out of the open block (becoming invalidated in the process), and out of self.foreach. In other words: class CSV def self.foreach(path, options = Hash.new, &block) open(path, options) do |csv| csv.each(&block) end end def self.open(*args) (...) if block_given? begin yield csv ensure csv.close end else csv end end def each if block_given? while row = shift yield row end else to_enum end end end A solution might be to create an alternative version of CSV#each that calls CSV#close after the last entry: def each_closing if block_given? begin while row = shift yield row end ensure close end else to_enum(__method__) # __method__ returns :each_closing end end Then CSV.foreach could be: def self.foreach(path, options = Hash.new, &block) if block_given? open(path, options) do |csv| csv.each(&block) end else open(path, options).each_closing end end ---------------------------------------- Bug #8929: CSV.foreach(filename) without block returns failing Enumerator https://bugs.ruby-lang.org/issues/8929#change-41899 Author: martinjos (Martin Sidaway) Status: Open Priority: Normal Assignee: Category: Target version: ruby -v: 2.0.0-p247 Backport: 1.9.3: UNKNOWN, 2.0.0: UNKNOWN CSV.foreach(filename) {|entry| p entry } => works CSV.foreach(filename).to_a => fails It gives the following error: IOError: closed stream from /home/martin/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/csv.rb:1776:in `gets' from /home/martin/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/csv.rb:1776:in `block in shift' from /home/martin/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/csv.rb:1774:in `loop' from /home/martin/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/csv.rb:1774:in `shift' from /home/martin/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/csv.rb:1716:in `each' from (irb):7:in `each' from (irb):7:in `to_a' (...) -- http://bugs.ruby-lang.org/