I want to make a method that writes some CSV output to a filename if given and stdout if not given.
It seems like I need to treat my calls to CSV
differently depending on if it's a file or to stdout, but I'd really like to treat the output stream z
as something I can write to and not have to whether it's a file on disk or the stdout stream.
Is this possible?
Below is my attempt and errors:
require 'csv'
require 'pathname'
require 'csv'
require 'pathname'
def write_to_csv_or_stdout foo, bar, z=nil
z = Pathname.new(z) if z
z ||= $stdout
res = [[foo, bar, "baz"]]
CSV(z) do |csv|
res.each do |row|
csv << row
end
end
end
write_to_csv_or_stdout "foo", "bar"
# foo
# bar
#=> foo,bar,baz
# write_to_csv_or_stdout "foo", "bar", "baz"
# (NoMethodError)
One solution would be not to use only the Pathname object since it isn't an IO object.
If you open the file then you can use it the same way as you would use the stdout object.
def some_method x, y, z=nil
puts x
puts y
z = Pathname.new(z).open if z # <== here you get an IO Object back
z ||= $stdout
res = [["foo", "bar", "baz"]]
CSV(z) do |csv|
res.each do |row|
csv << row
end
end
end
This works for stdout and filenames.
It uses $stdout.dup
to be able to close io
without closing $stdout
.
require 'csv'
def write_csv(rows, filename = nil)
io = filename ? File.open(filename, 'w+') : $stdout.dup
CSV(io) do |csv|
rows.each do |row|
csv << row
end
end
ensure
io.close
end
rows = [["foo", "bar", "baz"]]
write_csv(rows)
#=> foo,bar,baz
write_csv(rows, 'test.csv')
#=> 'test.csv' written
I'd use a helper method:
def write_csv(io_or_filename, &block)
if io_or_filename.is_a?(IO)
CSV.new(io_or_filename, &block)
else
CSV.open(io_or_filename, 'wb', &block)
end
end
This can be used instead of CSV(...)
:
def some_method(x, y, z = $stdout)
write_csv(z) do |csv|
csv << # ...
end
end