Search code examples
ruby-on-railspdfsidekiqwicked-pdf

Generating a PDF with Sidekiq & Wicked PDF: Undefined local variable or method for worker#


I've been trying to get PDF's generated via sidekiq and wicked_pdf in a rails 5.1 app but keep getting this error:

2017-11-01T02:20:33.339Z 1780 TID-ovys2t32c GeneratePdfWorker JID-b3e9487113db23d65b179b1c INFO: start
2017-11-01T02:20:33.369Z 1780 TID-ovys2t32c GeneratePdfWorker JID-b3e9487113db23d65b179b1c INFO: fail: 0.03 sec
2017-11-01T02:20:33.371Z 1780 TID-ovys2t32c WARN: {"class":"GeneratePdfWorker","args":[2,1],"retry":false,"queue":"default","jid":"b3e9487113db23d65b179b1c","created_at":1509502833.334234,"enqueued_at":1509502833.3345}
2017-11-01T02:20:33.380Z 1780 TID-ovys2t32c WARN: NameError: undefined local variable or method `quote' for #<GeneratePdfWorker:0x007fb6d5cea070>
Did you mean?  @quote
2017-11-01T02:20:33.380Z 1780 TID-ovys2t32c WARN: /Users/stefanbullivant/quottes/app/workers/generate_pdf_worker.rb:18:in `perform'

I get this error even with no locals being passed in the av.render method. Any ideas on what's causing it are appreciated. quotes_controller.rb calling the worker

  def create_pdf
    @quote = Quote.find(params[:id])
    GeneratePdfWorker.perform_async(@quote.id, current_account.id)
    redirect_to @quote
  end

Generate_pdf_worker.rb

class GeneratePdfWorker
  include Sidekiq::Worker
  sidekiq_options retry: false

  def perform(quote_id, account_id)
    @quote = Quote.find(quote_id)
    @account = Account.find(account_id)

    # create an instance of ActionView, so we can use the render method outside of a controller
    av = ActionView::Base.new()
    av.view_paths = ActionController::Base.view_paths

    # need these in case your view constructs any links or references any helper methods.
    av.class_eval do
      include Rails.application.routes.url_helpers
      include ApplicationHelper
    end

    pdf = av.render pdf: "Quote ##{ @quote.id } for #{ @quote.customer_name }",
                   file: "#{ Rails.root }/tmp/pdfs/quote_#{@quote.id}_#{@quote.customer_name}.pdf",
               template: 'quotes/create_pdf.html.erb',
                 layout: 'layouts/quotes_pdf.html.erb',
            disposition: 'attachment',
     disable_javascript: true,
         enable_plugins: false,
                 locals: {
                          quote: @quote,
                          account: @account
                        }

    # pdf_html = av.render :template => "quotes/create_pdf.html.erb",
    # :layout => "layouts/quotes_pdf.html.erb",
    # :locals => {
    #   quote: @quote,
    #   account: @account
    # }

    # use wicked_pdf gem to create PDF from the doc HTML
    quote_pdf = WickedPdf.new.pdf_from_string(pdf, :page_size => 'A4')

    # save PDF to disk
    pdf_path = Rails.root.join('tmp', "quote.pdf")
    File.open(pdf_path, 'wb') do |file|
      file << quote_pdf
    en
  end
end

quotes_pdf.html.erb PDF Layout

<!DOCTYPE html>
<html>
  <head>
    <title>Quottes</title>
    <meta charset="utf-8" />
    <meta name="ROBOTS" content="NOODP" />

    <style>
      <%= wicked_pdf_stylesheet_link_tag 'quote_pdf' -%>
    </style>
  </head>

  <body>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>

create_pdf.html.erb PDF View (for the sake of getting things running first, just two lines using each local)

<%= account.brand_name %>
<%= quote.customer_name %>

Any advice on getting this running is much appreciated. I have played around with simply generating plain html text in the pdf view without passing any variables and still get this error so I'm confused as to what's causing it.


Solution

  • I sometimes forget about this as well - It seems like it's very insistent that line 18 (which I can only assume is render) is looking up the quote local and can't find it, and @quote is defined at that time. If that is all your code, then I would presume the changes are not being picked up.

    My best suggestion (which I hope works) is you need to restart sidekiq!