John
John

Reputation: 6622

Can't resolve image into URL: undefined method `polymorphic_url' for #<ActionView::Base

In my Rails app I have a Job to create PDF files with WickedPDF. This worked fine until I migrated to Rails 5.2, now I get this error:

ActionView::Template::Error (Can't resolve image into URL: undefined method `polymorphic_url' for #<ActionView::Base:0x0000000004854590>):

The code where this happens is this line in the view:

image_tag(image)

This view is rendered from the Job, which I added in full below. The error happens when executing the last method in the class: private def render_image_pdf(view, pdf_name, image), where it is rendering a PDF.

After rolling back to Rails 5.1, everything works fine again, so I'm pretty sure it has something to do with an added/changed feature in Rails 5.2. So what should I change to make this working again?

The PDF Job:

class SendInvoiceAsAttachmentJob < ApplicationJob
  require 'open-uri'
  queue_as :default

  def perform(target, invoice, send_invoice, to, cc, subject, body1, body2, body3)
    view = ActionView::Base.new(ActionController::Base.view_paths, {})
    view.extend(ApplicationHelper)
    view.extend(Rails.application.routes.url_helpers)

    # Create invoice as PDF
    pdf = Rails.root.join('tmp', "tmp_invoice.pdf")

    File.open(pdf, 'wb') do |file|
      file << render_invoice_pdf(invoice, view, "tmp_invoice.pdf")
    end

    # Add (signed) timesheet as PDF
    projectuser = Projectuser.where(project_id: invoice.project_id, user_id: invoice.user_id).order(:updated_at).last
    if projectuser.blank? or invoice.fixed_price? or invoice.project.service?
      pdf_name = "#{invoice.set_pdf_filename}.pdf"
      pdf_to_email = pdf
    else
      if invoice.timesheet and invoice.timesheet.signed_copy?(projectuser)
        # Download signed copy
        timesheet_copy = TimesheetCopy.where(projectuser_id: projectuser.id, timesheet_id: invoice.timesheet_id).first

        timesheet_file = Rails.root.join('tmp', timesheet_copy.attachment_file_name)
        timesheet_name = timesheet_copy.attachment_file_name

        if timesheet_copy.attachment.url.index("http").blank?
          download = open("https:#{timesheet_copy.attachment.url}")
        else
          download = open(timesheet_copy.attachment.url)
        end
        IO.copy_stream(download, timesheet_file)

        # Push into a PDF when image
        if timesheet_copy.attachment_file_name.index(".pdf") == nil
          timesheet_tmp = Rails.root.join('tmp', "tmp_timesheet.pdf")
          File.open(timesheet_tmp, 'wb') do |file|
            file << render_image_pdf(view, "tmp_timesheet.pdf", timesheet_file)
          end
          timesheet_file = timesheet_tmp
          File.delete Rails.root.join('tmp', timesheet_copy.attachment_file_name)
        end
      else
        # Create timesheet PDF
        timesheet = Timesheet.find(invoice.timesheet_id)
        timesheet_builder = TimesheetBuilder.new(timesheet.month, timesheet.year)

        timesheet_name = "Timesheet #{timesheet.user.full_name} #{I18n.t("date.month_names")[timesheet.month]} #{timesheet.year}"
        timesheet_file = Rails.root.join('tmp', timesheet_name)

        File.open(timesheet_file, 'wb') do |file|
          file << render_timesheet_pdf(timesheet, view, timesheet_name, projectuser, timesheet_builder)
        end
      end

      # Combine the 2 PDF's
      combined_pdf = CombinePDF.new
      combined_pdf << CombinePDF.load(pdf, allow_optional_content: true)
      combined_pdf << CombinePDF.load(timesheet_file, allow_optional_content: true)
      pdf_name = "#{invoice.set_pdf_filename}.pdf"
      combined_pdf.save Rails.root.join('tmp', pdf_name)
      pdf_to_email = Rails.root.join('tmp', pdf_name)

      File.delete(timesheet_file)
    end

    # Send email
    if target == "basecone"
      company = Company.find(invoice.company_id)
      UserMailer.send_pdf_to_basecone(company.basecone_email, pdf_to_email, pdf_name).deliver
    else
      UserMailer.invoice_email(invoice, send_invoice, to, cc, subject, body1, body2, body3, pdf_to_email, pdf_name).deliver
    end

    File.delete(pdf_to_email)
  end

  private def render_invoice_pdf(invoice, view, pdf_name)
    WickedPdf.new.pdf_from_string(
      view.render(
        pdf: pdf_name,
        template: 'admin/invoices/show_as_attachment.pdf.haml',
        locals: { invoice: invoice, copy_invoice: nil },
        print_media_type:  true,
        orientation:       'Portrait',
        page_size:         'A4'
      )
    )
  end

  private def render_timesheet_pdf(timesheet, view, pdf_name, projectuser, timesheet_builder)
    WickedPdf.new.pdf_from_string(
      view.render(
        pdf: pdf_name,
        template: 'timesheets/show_as_attachment.pdf.haml',
        locals: { timesheet: timesheet, timesheet_builder: timesheet_builder, projectuser: projectuser },
        print_media_type:  true,
        orientation:       'Portrait',
        page_size:         'A4'
      )
    )
  end

  private def render_image_pdf(view, pdf_name, image)
    WickedPdf.new.pdf_from_string(
      view.render(
        pdf: pdf_name,
        template: 'admin/invoices/image_timesheet_as_attachment.pdf.haml',
        locals: { image: image },
        print_media_type:  true,
        orientation:       'Portrait',
        page_size:         'A4'
      )
    )
  end
end

Upvotes: 5

Views: 3300

Answers (2)

matthewd
matthewd

Reputation: 4435

Instead of manually constructing the ActionView setup (and thus needing to add all the right helpers, which is where you're going wrong -- I think some moved around), you can use the newish ActionController::Renderer API to perform a view render within the context of an arbitrary controller.

# Very untested; please treat this is syntactically-valid pseudocode...

WickedPdf.new.pdf_from_string(
  ApplicationController.render(
    template: 'admin/invoices/show_as_attachment.pdf.haml',
    assigns: {
      pdf: pdf_name,
      print_media_type: true,
      orientation: 'Portrait',
      page_size: 'A4',
    },
    locals: { invoice: invoice, copy_invoice: nil },
  )
)

Upvotes: 1

Anand
Anand

Reputation: 6531

Use wicked_pdf_image_tag Instead of image_tag

<%=wicked_pdf_image_tag image%>

Upvotes: 3

Related Questions