mtkcs
mtkcs

Reputation: 1716

Using view_context inside the Mailer

I'm using ActiveJob to send mails:

Using deliver_now method:

invoices_controller.rb

def send_invoice
  #other stuff
  Members::InvoicesMailer.send_invoice(@invoice.id, view_context).deliver_now
end

invoices_mailer.rb

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

Using deliver_later method:

invoices_controller.rb

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?

Edit: This is what the InvoicePdf looks like

invoice_pdf.rb

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

Upvotes: 3

Views: 1450

Answers (1)

Martin Tournoij
Martin Tournoij

Reputation: 27852

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

Upvotes: 4

Related Questions