class Mailings::InlineImages BLOB_PATH = %r{/rails/active_storage/blobs/(?:redirect/|proxy/)?([^/?#]+)} def self.call(mailer, html) new(mailer).call(html) end def initialize(mailer) @mailer = mailer end def call(html) fragment = Nokogiri::HTML::DocumentFragment.parse(html.to_s) fragment.css("img").each_with_index do |img, index| blob = blob_from(img["src"]) next unless blob name = "inline-#{index}-#{blob.filename}" @mailer.attachments.inline[name] = { mime_type: blob.content_type, content: blob.download } img["src"] = @mailer.attachments[name].url width = img["width"].presence styles = ["max-width: 100%", "height: auto"] styles << "width: #{width}px" if width.present? img["style"] = [img["style"], *styles].compact.join("; ") end fragment.css("figure").each do |figure| image = figure.at_css("img") image ? figure.replace(image) : figure.remove end fragment.to_html end private def blob_from(src) signed_id = src.to_s[BLOB_PATH, 1] return if signed_id.blank? ActiveStorage::Blob.find_signed(signed_id) rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveRecord::RecordNotFound nil end end