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