How to create download button for CSV and PDF using Ruby on Rails

Hey Guys !

Let's cut to the chase, hands on code !

  • First generate scaffold
rails g scaffold Note title
  • run db:migrate
rails db:migrate
  • now we are going to install the gem to generate PDF, we will use the gem 'prawn' and the 'prawn-table':
bundle add prawn prawn-table
  • now we're going to add the route and buttons for downloads nor csv and pdf format:
# add route in file config/routes.rb
resources :notes
root 'notes#index'
post 'download/notes', to: 'notes#download'

# add method downlod in controller app/controllers/notes_controller.rb
def download
    respond_to do |format|
      format.csv { send_data Note.to_csv, filename: "notes-#{Date.today}.csv" }
      format.pdf { send_data Note.to_pdf, filename: "notes-#{Date.today}.pdf" }
    end
end

# adds concerns to the note model to export the data
class Note < ApplicationRecord
  include ExportCsv
  include ExportPdf
end

# now let's create the modules in app/models/concerns
# first export_csv.rb
# frozen_string_literal: true

# module ExportCSV
module ExportCsv
  extend ActiveSupport::Concern

  # module ClassMethods
  module ClassMethods
    def to_csv
      require 'csv'
      options = { col_sep: ';', encoding: 'utf-8' }
      headers = %i[id title]

      CSV.generate(headers: true, **options) do |csv|
        csv << headers

        all.each do |note|
          csv << [note.id, note.title]
        end
      end
    end
  end
end

# second export_pdf.rb
# frozen_string_literal: true

# module ExportPdf
module ExportPdf
  extend ActiveSupport::Concern

  # module ClassMethods
  module ClassMethods
    def to_pdf
      require 'prawn'
      require 'prawn/table'

      options = { position: :center, column_widths: [300, 300], width: 600 }

      header = %w[ID Title]
      data = all.map { |note| [note.id, note.title] }
      Prawn::Document.new do
        text 'All Notes', align: :center, size: 18, style: :bold
        table([header, *data], header: true, **options)
      end.render
    end
  end
end

# and finally add the links on the app/views/notes/index.html.erb page
<%= button_to "Download CSV", download_notes_path(request.params.merge(format: :csv)), data: {turbo: :false} %>
<%= button_to "Download PDF", download_notes_path(request.params.merge(format: :pdf)), data: {turbo: :false} %>

And that's it, we're done!

61