Prawn

Prawn is a direct PDF generation ruby library, with wide usage and good documentation support.

Step 1 - Add Dependencies to gemfile

gem 'prawn'
gem 'prawn-tables'

run bundle to install the gems

$ bundle install

Step 2 – Register the PDF MIME Type in the initializer

# file: rails-generate-pdf/config/initializer/mime_types.rb
Mime::Type.register application/pdf, :pdf

Step 3 - create a template for your pdf’s

create a directory in your app folder to store your pdf’s.

class PdfTemplate < Prawn::Document
  TABLE_ROW_COLORS = ["FFFFFF","DDDDDD"]
  TABLE_FONT_SIZE = 9
  TABLE_BORDER_STYLE = :grid
  INFO = {
      :Creator => "JourneyCTRL App",
      :Producer => "Prawn",
      :CreationDate => Time.now}

  PDF_OPTIONS = {
      :info => INFO,
      :page_size   => "A4",
      :page_layout => :portrait,
      :margin      => [40, 75]
  }

  def initialize(default_options={})
    default_options ? default_prawn_options = default_options : default_prawn_options = PDF_OPTIONS
    super(default_prawn_options)
    font_size 10
  end

  def header(title=nil)
    text "JourneyCTRL", size: 15, style: :bold, align: :left
    if title
      text title, size: 14, style: :bold_italic, align: :left
    end
    move_up 12
    text "Date: #{ApplicationController.helpers.format_date_time(Time.now)}", style: :normal, align: :right
    move_down 7
    stroke_horizontal_rule
    move_down 10
  end

  def footer
    # ...
  end
end

Create your pdf’s

class AccountabilitySummaryReport < PdfTemplate
  TABLE_WIDTHS = [120, 120, 100, 80, 80]
  EXPENSE_HEADERS = ["Category", "Payment Mode", "Value", "Currency"]

  def initialize(acc)
    super()
    @acc = acc
    @exp = ExpenseSummary.where(...)
    header 'Accountability Summary Report'
    display_user_info
    display_expenses
    display_balance

    #footer
  end

  private

  def display_user_info
    text "Responsible: #{}", size: 11, style: :normal, align: :left
    move_down 5
  end

  def display_expenses
    text "Expenses:", size: 12, style: :bold, align: :left
    @expense_data = [EXPENSE_HEADERS]
    @expense_data += @exp.map { |e| [e.expense_category_name,
                                     e.payment_mode_name,
                                     e.value,
                                     e.currency] }
    total = 0
    @total_expenses = @exp.map{ |e| total += e.value}
    @expense_data += [[{content: "Total", colspan: 2}, @acc.payment_mode_name, total, @acc.currency]]
    table @expense_data,
          header: true,
          column_widths: TABLE_WIDTHS,
          row_colors: TABLE_ROW_COLORS do
      row(0).font_style = :bold
    end
    move_down 10
  end

  def display_balance
    ...
  end
end

Rails integration

in your controller:

def show
  respond_to do |format|
    format.js
    format.pdf do
      if params[:tipo] == "summary"
        pdf = AccountabilitySummaryReport.new(acc)
      else
        pdf = AccountabilityDetailReport.new(acc)
      end
      send_data pdf.render,
                filename: "accountability_sumary_report_#{current_user.id}.pdf",
                type: 'application/pdf',
                disposition: 'inline'
    end
  end

end

Calling your controller:

  link_to accountability_report_path(acc, format: "pdf", :tipo => "summary"),
             target: "_blank",
             :class => 'btn btn-primary btn-sm',
             title: "Print",
             data: { toggle: 'tooltip', placement: 'top' }

Testing

Install the pdf-reader gem https://github.com/yob/pdf-reader, that can convert the renderred PDF into a string and assert that the proper content is included.
RSpec:

describe AccountabilitySummaryReport do
  context 'Given an that there is a single expense' do
    let(:accountabilities) { [{id: 1, currency: "BRL", value: 100, payment_mode_name: 'CC', expense_category_name: 'Lunch' ] }
    context 'The rendered pdf content' do
      let(:pdf) { AccountabilitySummaryReport.new(accountabilities) }
      let(:pdf_content) { PDF::Reader.new(StringIO.new(pdf.render)).page(1).to_s }

      it 'contains the category of the expense' do
        expect(pdf_content).to include('Lunch')
      end
      it 'contains the expense currency' do
        expect(pdf_content).to include('BRL')
      end
    end
  end
end

References

Prawn Manual Prawn-tables manual

Other options

wkhtmltopdf and wicked_pdf:
https://www.pdftron.com/blog/rails/how-to-generate-pdf-with-ruby-on-rails/