diff --git a/Gemfile b/Gemfile
index 44a8bba..a1226ad 100644
--- a/Gemfile
+++ b/Gemfile
@@ -68,3 +68,5 @@ group :test do
gem "capybara"
gem "selenium-webdriver"
end
+
+gem "ferrum", "~> 0.14"
diff --git a/Gemfile.lock b/Gemfile.lock
index b386632..82edddc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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)
@@ -256,6 +261,7 @@ DEPENDENCIES
bootsnap
capybara
debug
+ ferrum (~> 0.14)
importmap-rails
jbuilder
pg (~> 1.1)
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 288b9ab..2936f16 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -13,3 +13,9 @@
*= require_tree .
*= require_self
*/
+
+@media print {
+ .no-print {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/invoices_controller.rb b/app/controllers/invoices_controller.rb
index d340081..ade87f7 100644
--- a/app/controllers/invoices_controller.rb
+++ b/app/controllers/invoices_controller.rb
@@ -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
diff --git a/app/models/invoice.rb b/app/models/invoice.rb
index cf6d171..8ea5cec 100644
--- a/app/models/invoice.rb
+++ b/app/models/invoice.rb
@@ -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
diff --git a/app/views/invoices/_invoice.html.erb b/app/views/invoices/_invoice.html.erb
index cf8365a..bdd2579 100644
--- a/app/views/invoices/_invoice.html.erb
+++ b/app/views/invoices/_invoice.html.erb
@@ -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 %>
diff --git a/app/views/invoices/show.html.erb b/app/views/invoices/show.html.erb
index b83470a..d27c1cb 100644
--- a/app/views/invoices/show.html.erb
+++ b/app/views/invoices/show.html.erb
@@ -3,3 +3,8 @@
<%= render @invoice %>
+
+<%= 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? %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 6b3c14c..c13670d 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -12,7 +12,7 @@
- <%= link_to "All invoices", invoices_path %>
+ <%= link_to "All invoices", invoices_path, class: "no-print" %>
<%= yield %>
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 2e7fb48..f00d5c7 100644
--- a/config/environments/development.rb
+++ b/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.
diff --git a/db/migrate/20240204193222_create_active_storage_tables.active_storage.rb b/db/migrate/20240204193222_create_active_storage_tables.active_storage.rb
new file mode 100644
index 0000000..e4706aa
--- /dev/null
+++ b/db/migrate/20240204193222_create_active_storage_tables.active_storage.rb
@@ -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
diff --git a/db/schema.rb b/db/schema.rb
index e54abf4..1f716c7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,10 +10,38 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2024_02_04_111538) do
+ActiveRecord::Schema[7.1].define(version: 2024_02_04_193222) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "active_storage_attachments", force: :cascade do |t|
+ t.string "name", null: false
+ t.string "record_type", null: false
+ t.bigint "record_id", null: false
+ t.bigint "blob_id", null: false
+ t.datetime "created_at", null: false
+ t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
+ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
+ end
+
+ create_table "active_storage_blobs", force: :cascade 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"
+ t.datetime "created_at", null: false
+ t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
+ end
+
+ create_table "active_storage_variant_records", force: :cascade do |t|
+ t.bigint "blob_id", null: false
+ t.string "variation_digest", null: false
+ t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
+ end
+
create_table "invoices", force: :cascade do |t|
t.string "email"
t.string "product"
@@ -24,4 +52,6 @@
t.datetime "updated_at", null: false
end
+ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
end
diff --git a/goggle.pdf b/goggle.pdf
new file mode 100644
index 0000000..c23c0ea
Binary files /dev/null and b/goggle.pdf differ
diff --git a/google.png b/google.png
new file mode 100644
index 0000000..e786e82
Binary files /dev/null and b/google.png differ