diff --git a/README.md b/README.md
index bed73f9b..34ca11f3 100644
--- a/README.md
+++ b/README.md
@@ -201,6 +201,7 @@ class ThingsController < ApplicationController
viewport_size: 'TEXT', # available only with use_xserver or patched QT
extra: '', # directly inserted into the command to wkhtmltopdf
raise_on_all_errors: nil, # raise error for any stderr output. Such as missing media, image assets
+ raise_on_missing_assets: nil, # raise when trying to access a missing asset
log_level: 'info', # Available values: none, error, warn, or info - only available with wkhtmltopdf 0.12.5+
quiet: false, # `false` is same as `log_level: 'info'`, `true` is same as `log_level: 'none'`
outline: { outline: true,
diff --git a/lib/wicked_pdf/wicked_pdf_helper/assets.rb b/lib/wicked_pdf/wicked_pdf_helper/assets.rb
index 5f8820c2..77163265 100644
--- a/lib/wicked_pdf/wicked_pdf_helper/assets.rb
+++ b/lib/wicked_pdf/wicked_pdf_helper/assets.rb
@@ -7,6 +7,27 @@ module WickedPdfHelper
module Assets
ASSET_URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/
+ class MissingAsset < StandardError; end
+
+ class MissingLocalAsset < MissingAsset
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ super("Could not find asset '#{path}'")
+ end
+ end
+
+ class MissingRemoteAsset < MissingAsset
+ attr_reader :url, :response
+
+ def initialize(url, response)
+ @url = url
+ @response = response
+ super("Could not fetch asset '#{url}': server responded with #{response.code} #{response.message}")
+ end
+ end
+
class PropshaftAsset < SimpleDelegator
def content_type
super.to_s
@@ -21,9 +42,39 @@ def filename
end
end
+ class SprocketsEnvironment
+ def self.instance
+ @instance ||= Sprockets::Railtie.build_environment(Rails.application)
+ end
+
+ def self.find_asset(*args)
+ instance.find_asset(*args)
+ end
+ end
+
+ class LocalAsset
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def content_type
+ Mime::Type.lookup_by_extension(File.extname(path).delete('.'))
+ end
+
+ def to_s
+ IO.read(path)
+ end
+
+ def filename
+ path.to_s
+ end
+ end
+
def wicked_pdf_asset_base64(path)
asset = find_asset(path)
- raise "Could not find asset '#{path}'" if asset.nil?
+ raise MissingLocalAsset, path if asset.nil?
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, '')
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
@@ -150,8 +201,11 @@ def find_asset(path)
Rails.application.assets.find_asset(path, :base_path => Rails.application.root.to_s)
elsif defined?(Propshaft::Assembly) && Rails.application.assets.is_a?(Propshaft::Assembly)
PropshaftAsset.new(Rails.application.assets.load_path.find(path))
+ elsif Rails.application.respond_to?(:assets_manifest)
+ asset_path = File.join(Rails.application.assets_manifest.dir, Rails.application.assets_manifest.assets[path])
+ LocalAsset.new(asset_path) if File.file?(asset_path)
else
- Sprockets::Railtie.build_environment(Rails.application).find_asset(path, :base_path => Rails.application.root.to_s)
+ SprocketsEnvironment.find_asset(path, :base_path => Rails.application.root.to_s)
end
end
@@ -175,20 +229,35 @@ def precompiled_or_absolute_asset?(source)
end
def read_asset(source)
- if precompiled_or_absolute_asset?(source)
- pathname = asset_pathname(source)
- if pathname =~ URI_REGEXP
- read_from_uri(pathname)
- elsif File.file?(pathname)
- IO.read(pathname)
- end
- else
- find_asset(source).to_s.force_encoding('UTF-8')
+ asset = find_asset(source)
+ return asset.to_s.force_encoding('UTF-8') if asset
+
+ unless precompiled_or_absolute_asset?(source)
+ raise MissingLocalAsset, source if WickedPdf.config[:raise_on_missing_assets]
+
+ return
+ end
+
+ pathname = asset_pathname(source)
+ if pathname =~ URI_REGEXP
+ read_from_uri(pathname)
+ elsif File.file?(pathname)
+ IO.read(pathname)
+ elsif WickedPdf.config[:raise_on_missing_assets]
+ raise MissingLocalAsset, pathname if WickedPdf.config[:raise_on_missing_assets]
end
end
def read_from_uri(uri)
- asset = Net::HTTP.get(URI(uri))
+ response = Net::HTTP.get_response(URI(uri))
+
+ unless response.is_a?(Net::HTTPSuccess)
+ raise MissingRemoteAsset.new(uri, response) if WickedPdf.config[:raise_on_missing_assets]
+
+ return
+ end
+
+ asset = response.body
asset.force_encoding('UTF-8') if asset
asset = gzip(asset) if WickedPdf.config[:expect_gzipped_remote_assets]
asset
diff --git a/test/functional/wicked_pdf_helper_assets_test.rb b/test/functional/wicked_pdf_helper_assets_test.rb
index d23d18c5..2b8368b1 100644
--- a/test/functional/wicked_pdf_helper_assets_test.rb
+++ b/test/functional/wicked_pdf_helper_assets_test.rb
@@ -4,6 +4,15 @@
class WickedPdfHelperAssetsTest < ActionView::TestCase
include WickedPdf::WickedPdfHelper::Assets
+ setup do
+ @saved_config = WickedPdf.config
+ WickedPdf.config = {}
+ end
+
+ teardown do
+ WickedPdf.config = @saved_config
+ end
+
if Rails::VERSION::MAJOR > 3 || (Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR > 0)
test 'wicked_pdf_asset_base64 returns a base64 encoded asset' do
assert_match %r{data:text\/css;base64,.+}, wicked_pdf_asset_base64('wicked.css')
@@ -15,6 +24,58 @@ class WickedPdfHelperAssetsTest < ActionView::TestCase
wicked_pdf_stylesheet_link_tag('wicked')
end
+ test 'wicked_pdf_stylesheet_link_tag should raise if the stylesheet is not available and config is set' do
+ Rails.configuration.assets.expects(:compile => true)
+ WickedPdf.config[:raise_on_missing_assets] = true
+ assert_raise WickedPdf::WickedPdfHelper::Assets::MissingLocalAsset do
+ wicked_pdf_stylesheet_link_tag('non_existent')
+ end
+ end
+
+ test 'wicked_pdf_stylesheet_link_tag should return empty if the stylesheet is not available' do
+ Rails.configuration.assets.expects(:compile => true)
+ assert_equal "",
+ wicked_pdf_stylesheet_link_tag('non_existent')
+ end
+
+ test 'wicked_pdf_stylesheet_link_tag should raise if the absolute path stylesheet is not available and config is set' do
+ Rails.configuration.assets.expects(:compile => true)
+ WickedPdf.config[:raise_on_missing_assets] = true
+ expects(:precompiled_or_absolute_asset? => true).twice
+ assert_raise WickedPdf::WickedPdfHelper::Assets::MissingLocalAsset do
+ wicked_pdf_stylesheet_link_tag('/non_existent')
+ end
+ end
+
+ test 'wicked_pdf_stylesheet_link_tag should return empty if the absolute path stylesheet is not available' do
+ Rails.configuration.assets.expects(:compile => true).twice
+ assert_equal "",
+ wicked_pdf_stylesheet_link_tag('/non_existent')
+ end
+
+ test 'wicked_pdf_stylesheet_link_tag should inline the stylesheets passed in when assets are remote' do
+ stub_request(:get, 'https://www.example.com/wicked.css').to_return(:status => 200, :body => '/* Wicked styles */')
+ expects(:precompiled_or_absolute_asset? => true).twice
+ assert_equal "",
+ wicked_pdf_stylesheet_link_tag('https://www.example.com/wicked.css')
+ end
+
+ test 'wicked_pdf_stylesheet_link_tag should raise if remote assets are not available and config is set' do
+ WickedPdf.config[:raise_on_missing_assets] = true
+ stub_request(:get, 'https://www.example.com/wicked.css').to_return(:status => 404, :body => 'File not found')
+ expects(:precompiled_or_absolute_asset? => true).twice
+ assert_raise WickedPdf::WickedPdfHelper::Assets::MissingRemoteAsset do
+ wicked_pdf_stylesheet_link_tag('https://www.example.com/wicked.css')
+ end
+ end
+
+ test 'wicked_pdf_stylesheet_link_tag should return empty if remote assets are not available' do
+ stub_request(:get, 'https://www.example.com/wicked.css').to_return(:status => 404, :body => 'File not found')
+ expects(:precompiled_or_absolute_asset? => true).twice
+ assert_equal "",
+ wicked_pdf_stylesheet_link_tag('https://www.example.com/wicked.css')
+ end
+
test 'wicked_pdf_image_tag should return the same as image_tag when passed a full path' do
Rails.configuration.assets.expects(:compile => true)
assert_equal image_tag("file:///#{Rails.root.join('public', 'pdf')}"),
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 15990f93..4720ff73 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -7,6 +7,7 @@
require 'mocha'
require 'rails/test_help'
require 'mocha/test_unit'
+require 'webmock/minitest'
require 'wicked_pdf'
diff --git a/wicked_pdf.gemspec b/wicked_pdf.gemspec
index 3a4819c4..9c7432d3 100644
--- a/wicked_pdf.gemspec
+++ b/wicked_pdf.gemspec
@@ -37,4 +37,5 @@ DESC
spec.add_development_dependency 'rubocop', '~> 1.46'
spec.add_development_dependency 'sqlite3', '~> 1.3'
spec.add_development_dependency 'test-unit'
+ spec.add_development_dependency 'webmock', '~> 3.19'
end