Writing to a file via Tempfile in Ruby

13,632

Solution 1

The issue is that you're not actually writing csv data to the file. You're sending arrays to the filehandle. I believe you need something like:

Tempfile.open(....) do |fh|
    csv = CSV.new(fh, ...)
    <rest of your code>
end

to properly setup the CSV output filtering.

Solution 2

I prefer to do

tempfile = Tempfile.new(....)
csv = CSV.new(tempfile, ...) do |row|
  <rest of your code>
end

Solution 3

Here's how I did it.

patient_payments = PatientPayment.all

Tempfile.new(['patient_payments', '.csv']).tap do |file|
  CSV.open(file, 'wb') do |csv|
    csv << patient_payments.first.class.attribute_names

    patient_payments.each do |patient_payment|
      csv << patient_payment.attributes.values
    end
  end
end

Solution 4

try this:

Tempfile.open(["#{Rails.root}/tmp/", ".csv"]) do |outfile|
  CSV::Writer.generate(outfile) do |csv|
    csv << ["Application", "Price", "Tax", "Credit", "Base Price", "Billed At", "Order ID", "Payment ID", "Payment Type"]
    #...
  end
end
Share:
13,632
BC00
Author by

BC00

Updated on June 17, 2022

Comments

  • BC00
    BC00 almost 2 years

    I have the below background job that writes to a csv file and emails it out. I am using the Tempfile class so the file is removed after I email it to the user. Currently, when I look at the csv file I am producing the results look like the following:

    ["Client Application"    "Final Price"   "Tax"   "Credit"    "Base Price"    "Billed At"     "Order Guid"    "Method of Payment Guid"    "Method of Payment Type"]
    ["web"   nil     nil     nil     nil     nil     nil     "k32k313k1j3"   "credit card"]
    

    Please ignore the data, but the issue is, it is being written directly to the file in the ruby format and not removing the "" and [] characters.

    Please see the code below:

    class ReportJob
    @queue = :report_job
    
     def self.perform(client_app_id, current_user_id)
      user = User.find(current_user_id)
      client_application = Application.find(client_app_id)
      transactions = client_application.transactions
      file = Tempfile.open(["#{Rails.root}/tmp/", ".csv"]) do |csv|
        begin
         csv << ["Application", "Price", "Tax", "Credit", "Base Price", "Billed At", "Order ID", "Payment ID", "Payment Type"]
         transactions.each do |transaction|
          csv << "\n"
          csv << [application.name, transaction.price, transaction.tax, transaction.credit, transaction.base_price, transaction.billed_at, transaction.order_id, transaction.payment_id, transaction.payment_type]
        end
       ensure
        ReportMailer.send_rev_report(user.email, csv).deliver
        csv.close(unlink_now=false)
        end
      end
    end
    
    end
    

    Would this be an issue with using the tempfile class instead of the csv class? or is there something I could do to change the way it is being written to the file?

    Adding the code for reading the csv file in the mailer. I am currently getting a TypeError that says "can't convert CSV into String".

    class ReportMailer < ActionMailer::Base
     default :from => "[email protected]"
    
      def send_rev_report(email, file)
         attachments['report.csv'] = File.read("#{::Rails.root.join('tmp', file)}")
          mail(:to => email, :subject => "Attached is your report")
        end
      end
    end
    
  • BC00
    BC00 over 11 years
    Thank You! Would you know the proper way to read from an <#CSV io_type:Tempfile object? I am currently getting a TypeError: can't convert CSV into String when trying to read from it...
  • Dave S.
    Dave S. over 11 years
    Can you show me the code for that piece? If you're reading from the same handle as you just wrote to - don't forget to rewind the handle (or seek() to the beginning). Otherwise you'll be trying to read from EOF.
  • Dave S.
    Dave S. over 11 years
    Why bother writing the CSV file here if you're just going to read it back in? You may as well just point the CSV writer at a stringio object and then the mailer can just assign that directly.