fernandoalmeida/receipt

View on GitHub
lib/receipt/pdf.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# -*- coding: utf-8 -*-
require 'ostruct'
require 'i18n'

module Receipt
  class Pdf
    extend Forwardable

    include Prawn::View

    attr_reader :params, :errors

    def_delegators :I18n, :t, :l

    def_delegators :params, *[
      :id,
      :payer,
      :receiver,
      :amount,
      :date,
      :description,
      :logo,
      :location,
      :locale,
      :filepath
    ]

    def initialize(params)
      @params = OpenStruct.new(params)
      setup_i18n

      @errors = {}
    end

    def data
      return unless valid?

      generate
      render
    end

    def file
      return unless valid?

      @file ||= lambda do
        generate
        save_as(path)
        path
      end.call
    end

    def before_receipt_box(&block)
      @before ||= block
    end

    def after_receipt_box(&block)
      @after ||= block
    end

    def valid?
      [
        :id,
        :amount,
        :payer,
        :receiver,
        :description
      ].each do |p|
        if params.send(p).to_s.empty?
          @errors[p] = t('receipt.errors.required_param_not_found')
        end
      end

      @errors.size == 0
    end

    def filename
      File.basename(path)
    end

    def mimetype
      'application/pdf'
    end

    def currency
      params.currency || t('receipt.currency')
    end
    private

    def generate
      valid?

      before_receipt_box.call if before_receipt_box

      move_down 20
      receipt_box

      move_down 20
      after_receipt_box.call if after_receipt_box
    end

    def header
      pad(10) do
        text(t('receipt.title').upcase, align: :center, style: :bold, size: 20)

        move_up 20
        text("#{t('receipt.number')}: <b>#{id}</b>", inline_format: true)

        move_up 15
        text("#{t('receipt.amount')}: <b>#{formated_amount}</b>",
             inline_format: true, align: :right)
        pad(10) { stroke_horizontal_rule }
      end
    end

    def body
      text(
        [
          "#{t('receipt.received_from')} <b>#{payer}</b>",
          "#{t('receipt.the_amount_of')} <b>#{formated_amount}</b>",
          "#{t('receipt.relating_to')} <b>#{description}</b>."
        ].join(' '),
        inline_format: true
      )
    end

    def footer
      move_cursor_to 70
      stroke_horizontal_rule

      pad(35) do
        position = cursor
        width = bounds.width / 2

        date_box(0, position, width)
        signature_box(width, position, width)
      end
    end

    def formated_amount
      [
        currency,
        amount
      ].compact.join(' ')
    end

    def date_box(x, y, width)
      bounding_box [x, y], width: width do
        text(
          [
            location,
            l(date, format: :long)
          ].compact.join(', ')
        )
      end
    end

    def receipt_box
      padded_bounding_box(10, [30, cursor], width: 500, height: 250) do
        header
        body
        footer
      end
    end

    def signature_box(x, y, width)
      bounding_box [x, y], width: width do
        stroke_horizontal_rule
        move_down 10
        centered_text(receiver)
      end
    end

    def centered_text(s)
      text(s, align: :center)
    end

    def padded_bounding_box(padding, *args)
      bounding_box(*args) do
        stroke_bounds

        bounding_box(
          [padding, bounds.height - padding],
          width: bounds.width - (2 * padding),
          height: bounds.height - (2 * padding)
        ) do
          yield
        end
      end
    end

    def setup_i18n
      load_translations
      set_locale
    end

    def load_translations
      locales = Dir[File.expand_path('../../../config/locale/*.yml', __FILE__)]
      I18n.load_path += locales
      I18n.backend.load_translations
    end

    def set_locale
      I18n.locale = (locale || I18n.default_locale)
    end

    def path
      @path ||= (params.filepath || tempfilepath)
    end

    def tempfilepath
      @tempfilepath ||= File.join(
        Dir.tmpdir,
        Dir::Tmpname.make_tmpname("#{t('receipt.title')}", "#{id}.pdf")
      )
    end
  end
end