I'm using ActiveJob
to send mails:
deliver_now
method:def send_invoice
#other stuff
Members::InvoicesMailer.send_invoice(@invoice.id, view_context).deliver_now
end
require 'open-uri'
class Members::InvoicesMailer < ApplicationMailer
def send_invoice(invoice_id, view_context)
@invoice = Invoice.find(invoice_id)
attachments["#{@invoice.identifier}.pdf"] = InvoicePdf.new(@invoice, view_context).render
mail :to => @invoice.client.email, :subject => "Invoice"
end
end
Notice here that I'm sending the view_context
from the controller to the mailer, that will again pass it to the InvoicePdf
class to generate the invoice.
Result: Email sent correctly
deliver_later
method:def send_invoice
#other stuff
Members::InvoicesMailer.send_invoice(@invoice.id, view_context).deliver_later
end
Result: ActiveJob::SerializationError in Members::InvoicesController#send_invoice Unsupported argument type:
view_context
.
How to inject the view_context
inside the InvoicePdf
, either loading it from inside InvoicePdf
, or InvoiceMailer
?
class InvoicePdf < Prawn::Document
def initialize(invoice, view_context)
@invoice, @view_context = invoice, view_context
generate_pdf
end
def generate_pdf
# calling some active_support helpers:
# @view_context.number_to_currency(//)
# calling some helpers I created
end
end
The problem with passing an object like the view context and then using deliver_later
is that the parameters you give it are serialized to some backend (redis, MySQL), and another ruby background process picks it up later.
Objects like a view context are not really things you can serialize. It's not really data.
You can just use ActionView::Base.new
, for example from rails console
:
# New ActionView::Base instance
vagrant :002 > view = ActionView::Base.new
# Include some helper classes
vagrant :003 > view.class_eval { include ApplicationHelper }
vagrant :004 > view.class_eval { include Rails.application.routes.url_helpers }
# Now you can run helpers from `ApplicationHelper`
vagrant :005 > view.page_title 'Test'
"Test"
# And from url_helpers
vagrant :006 > view.link_to 'Title', [:admin, :organisations]
=> "<a href=\"/admin/organisations\">Title</a>"
Here's what I do in my PdfMaker
class, which is probably similar to your InvoicePdf
class.
def action_view
@action_view ||= begin
view = ActionView::Base.new ActionController::Base.view_paths
view.class_eval do
include Rails.application.routes.url_helpers
include ApplicationHelper
include FontAwesome::Rails::IconHelper
include Pundit
def self.helper_method *name; end
def view_context; self; end
def self.before_action f; end
def protect_against_forgery?; end
end
view
end
end