Question

The problem

I want to send emails with an invoice PDF attached to it.

I already have a invoice show view, with styling and javascript.

The javascript is making calculations for the total field, formatting the currency and dates and splitting at page breaks.

The show view is presenting the invoice just like it should be on the pdf.

Attempts

  1. I have tried wicked_pdf and pdfkit. The problem is that when doing this..

    html = render_to_string(:action => "../invoices/show_pdf.html.erb") 
    mail(:to => @registration.billing_email, :subject => "Faktura") do |format|
      format.pdf do
        attachments['faktura.pdf'] = PDFKit.new(html).to_pdf
      end
    

    ..I don't have a browser to interpreter the javascript. So all calculations and formats is gone. When I open the pdf in the browser it's correct.

  2. I also tried shrimp which uses phantomjs to generate the PDF's. The problem with this solution is that I am using devise and the phantomjs browser has to be signed in in some way.

I need some suggestions on a elegant solution for this problem.

Either

Some way to render the view with all the javascript before I generate the PDF with pdfkit or wicked_pdf

or

How to assign a session cookie to phantomjs?

I am aware that this is two questions, but as it solves one problem I hope it is ok.

Was it helpful?

Solution

I think shrimp would be your best bet. Since you need your javascript to be executed a solution that runs PhantomJS is probably the only way you can go.

Shrimp provides an easy way to add your session cookie in the request. Just to something like the following:

Shrimp::Phantom.new(url, options, {user_session: "a session"})

As you can understand the initializer accepts an options hash and after that a hash with your cookies and their values.

Now the hard part would be to always have a valid session at your server to do the requests. Depending on your session management (e.g. you might have auto expiration) this could be a bit tricky.


EDIT: Adding Andreas's findings about how to hack user session in an Rails application using Devise based on a relevant blog post by the Shrimp creators:

lib/devise/sign_in_interceptor.rb

module Devise
  class SignInInterceptor
    def initialize(app, opts)
      @app    = app
      @scope  =opts[:scope]
      @secret = opts[:secret]
      @klass  = opts[:klass]
    end

    def call(env)
      if user = Rack::Request.new(env).cookies[@secret]
        env['warden'].session_serializer.store(@klass.constantize.find(user), @scope)
      end

      @app.call(env)
    end
  end
end

config/application.rb

require File.expand_path('../../lib/devise/sign_in_interceptor', __FILE__)
config.middleware.use Devise::SignInInterceptor, { :scope  => :user, :klass => 'User',
                                                    :secret => "our_very_very_long_secret" }

With this setup we can add a to_pdf method to our resource invoice.rb

def to_pdf
  host        = Rails.env.production? ? 'www.adjust.io' : 'localhost:3000'
  url         = Rails.application.routes.url_helpers.reports_url(self, :host => host)
  cookie      = { 'our_very_very_long_secret' => user_id } #user_id is a instance method on the invoice class 
  options     = { :margin => "1cm"}
  res         = Shrimp::Phantom.new(url, options, cookie).to_pdf("#{Rails.root}/invoices/invoice_#{self.id}.pdf")
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top