Skip to content

Commit

Permalink
Ferrum - generate PDF and Screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
yshmarov committed Feb 5, 2024
1 parent c49700f commit 34cd066
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Expand Up @@ -68,3 +68,5 @@ group :test do
gem "capybara"
gem "selenium-webdriver"
end

gem "ferrum", "~> 0.14"
6 changes: 6 additions & 0 deletions Gemfile.lock
Expand Up @@ -102,6 +102,11 @@ GEM
drb (2.2.0)
ruby2_keywords
erubi (1.12.0)
ferrum (0.14)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
webrick (~> 1.7)
websocket-driver (>= 0.6, < 0.8)
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.1)
Expand Down Expand Up @@ -256,6 +261,7 @@ DEPENDENCIES
bootsnap
capybara
debug
ferrum (~> 0.14)
importmap-rails
jbuilder
pg (~> 1.1)
Expand Down
6 changes: 6 additions & 0 deletions app/assets/stylesheets/application.css
Expand Up @@ -13,3 +13,9 @@
*= require_tree .
*= require_self
*/

@media print {
.no-print {
display: none;
}
}
12 changes: 12 additions & 0 deletions app/controllers/invoices_controller.rb
Expand Up @@ -5,6 +5,18 @@ def index

def show
@invoice = Invoice.find(params[:id])
respond_to do |format|
format.html
format.pdf do
tmp = Tempfile.new(process_timeout: 30, timeout: 200, pending_connection_errors: true)
browser = Ferrum::Browser.new
browser.go_to(invoice_url(@invoice))
sleep(0.3)
browser.pdf(path: tmp)
browser.quit
send_file tmp, type: "application/pdf", disposition: "inline"
end
end
end

def new
Expand Down
30 changes: 30 additions & 0 deletions app/models/invoice.rb
Expand Up @@ -3,8 +3,38 @@ class Invoice < ApplicationRecord

before_validation :calculate_total

has_one_attached :pdf
has_one_attached :image

after_create_commit :generate_pdf
after_create_commit :generate_image

private

def generate_pdf
url = Rails.application.routes.url_helpers.invoice_url(self)
tmp = Tempfile.new
browser = Ferrum::Browser.new
browser.go_to(url)
browser.pdf(path: tmp.path)
pdf.attach(io: File.open(tmp), filename: "invoice_#{id}.pdf")
browser.quit
tmp.close
tmp.unlink
end

def generate_image
url = Rails.application.routes.url_helpers.invoice_url(self)
tmp = Tempfile.new
browser = Ferrum::Browser.new
browser.go_to(url)
browser.screenshot(path: tmp.path, full: true, quality: 60, format: "png")
image.attach(io: File.open(tmp), filename: "invoice_#{id}.png")
browser.quit
tmp.close
tmp.unlink
end

def calculate_total
return unless price && quantity

Expand Down
1 change: 1 addition & 0 deletions app/views/invoices/_invoice.html.erb
Expand Up @@ -26,5 +26,6 @@

<% if action_name != "show" %>
<%= link_to "Show this invoice", invoice, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to "Open as PDF", invoice_path(invoice, format: :pdf), class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<% end %>
</div>
5 changes: 5 additions & 0 deletions app/views/invoices/show.html.erb
Expand Up @@ -3,3 +3,8 @@
<%= render @invoice %>
</div>
</div>

<%= link_to "Download PDF", rails_blob_path(@invoice.pdf, disposition: "attachment"), class: "no-print", target: :_blank if @invoice.pdf.attached? %>
<%= link_to "View PDF", rails_blob_path(@invoice.pdf, disposition: "inline"), class: "no-print", target: :_blank if @invoice.pdf.attached? %>
<%= link_to "Download as image", rails_blob_path(@invoice.image, disposition: "attachment"), class: "no-print", target: :_blank if @invoice.image.attached? %>
<%= link_to "View as image", rails_blob_path(@invoice.image, disposition: "inline"), class: "no-print", target: :_blank if @invoice.image.attached? %>
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.erb
Expand Up @@ -12,7 +12,7 @@
</head>

<body>
<%= link_to "All invoices", invoices_path %>
<%= link_to "All invoices", invoices_path, class: "no-print" %>
<main class="container mx-auto mt-28 px-5 flex">
<%= yield %>
</main>
Expand Down
2 changes: 2 additions & 0 deletions config/environments/development.rb
@@ -1,5 +1,7 @@
require "active_support/core_ext/integer/time"

Rails.application.routes.default_url_options[:host] = 'localhost:3000'

Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.

Expand Down
@@ -0,0 +1,57 @@
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
def change
# Use Active Record's configured type for primary and foreign keys
primary_key_type, foreign_key_type = primary_and_foreign_key_types

create_table :active_storage_blobs, id: primary_key_type do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.string :service_name, null: false
t.bigint :byte_size, null: false
t.string :checksum

if connection.supports_datetime_with_precision?
t.datetime :created_at, precision: 6, null: false
else
t.datetime :created_at, null: false
end

t.index [ :key ], unique: true
end

create_table :active_storage_attachments, id: primary_key_type do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
t.references :blob, null: false, type: foreign_key_type

if connection.supports_datetime_with_precision?
t.datetime :created_at, precision: 6, null: false
else
t.datetime :created_at, null: false
end

t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end

create_table :active_storage_variant_records, id: primary_key_type do |t|
t.belongs_to :blob, null: false, index: false, type: foreign_key_type
t.string :variation_digest, null: false

t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end

private
def primary_and_foreign_key_types
config = Rails.configuration.generators
setting = config.options[config.orm][:primary_key_type]
primary_key_type = setting || :primary_key
foreign_key_type = setting || :bigint
[primary_key_type, foreign_key_type]
end
end
32 changes: 31 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added goggle.pdf
Binary file not shown.
Binary file added google.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 34cd066

Please sign in to comment.