title: Getting PDF from WickedPDF for attachment via Carrierwave
body: <p>In Rails3, I am using the <code>WickedPDF gem</code> to render a PDF format of one of my models. This is working fine: <code>/invoices/123</code> returns HTML, <code>/invoices/123.pdf</code> downloads a PDF.</p>
  
  <p>In my Invoice model, I am using the state_machine gem to keep track of Invoice status. When an invoice goes from "unbilled" to "billed" state, I would like to grab a copy of the invoice PDF and attach it to the invoice model using CarrierWave.</p>
  
  <p>I have the three parts working separately: the controller creates a PDF view, the model tracks state and triggers a callback when making the correct transition, and CarrierWave is set up properly. However, I'm having a heck of a time getting them to play nicely together. </p>
  
  <p>If I just wanted to grab the HTML version of the invoice, I could call <code>render_to_string</code> from the model. But <code>render_to_string</code> seems to choke on receiving a PDF binary file. If I can get a stream of data back, it's pretty easy to write that data to a tempfile and attach it to the uploader, but I can't figure out how to get the data stream.</p>
  
  <p>Any thoughts? Code below:</p>
  
  <p>Invoice controller</p>
  
  <pre><code>def show
    @invoice = @agg_class.find(params[:id])
  
    respond_to do |format|
      format.pdf do
        render_pdf
      end
      format.html # show.html.erb
      format.json { render json: @aggregation }
    end
  end
  </code></pre>
  
  <p>...</p>
  
  <pre><code>def render_pdf(options = {})
    options[:pdf] = pdf_filename
    options[:layout] = 'pdf.html'
    options[:page_size] = 'Letter'
    options[:wkhtmltopdf] = '/usr/local/bin/wkhtmltopdf'
    options[:margin] = {
      :top      =&gt; '0.5in',
      :bottom   =&gt; '1in',
      :left     =&gt; '0in',
      :right    =&gt; '0in'
    }
    options[:footer] = {
      :html =&gt; {
        :template   =&gt; 'aggregations/footer.pdf.haml',
        :layout     =&gt; false,
      }
    }
    options[:header] = {
      :html =&gt; {
        :template   =&gt; 'aggregations/header.pdf.haml',
        :layout     =&gt; false,
      }
    }
    render options
  end
  </code></pre>
  
  <p>Invoice.rb</p>
  
  <pre><code>def create_pdf_copy
  
     # This does not work.    
     pdf_file = ApplicationController.new.render_to_string(
      :action =&gt; 'aggregations/show',
      :format =&gt; :pdf,
      :locals =&gt; { 
        :invoice =&gt; self
      }
    )
  
    # This part works fine if the above works.
    unless pdf_file.blank?
      self.uploads.clear
      self.uploads.create(:fileinfo =&gt; File.new(pdf_file), :job_id =&gt; self.job.id)
    end
  
  end
  </code></pre>
  
  <p><strong>UPDATE</strong> Found a solution.</p>
  
  <pre><code>def create_pdf_copy
  
      wicked = WickedPdf.new
  
      # Make a PDF in memory
      pdf_file = wicked.pdf_from_string( 
          ActionController::Base.new().render_to_string(
              :template   =&gt; 'aggregations/show.pdf.haml', 
              :layout     =&gt; 'layouts/pdf.html.haml',
              :locals     =&gt; { 
                  :aggregation =&gt; self
              } 
          ),
          :pdf =&gt; "#{self.type}-#{self}",
          :layout =&gt; 'pdf.html',
          :page_size =&gt; 'Letter',
          :wkhtmltopdf =&gt; '/usr/local/bin/wkhtmltopdf',
          :margin =&gt; {
              :top      =&gt; '0.5in',
              :bottom   =&gt; '1in',
              :left     =&gt; '0in',
              :right    =&gt; '0in'
          },
          :footer =&gt; {
              :content =&gt; ActionController::Base.new().render_to_string({
                  :template =&gt; 'aggregations/footer.pdf.haml', 
                  :layout =&gt; false
              })
          },
          :header =&gt; {
              :content =&gt; ActionController::Base.new().render_to_string({
                  :template =&gt; 'aggregations/header.pdf.haml', 
                  :layout =&gt; false
              })
          }
      )
  
      # Write it to tempfile
      tempfile = Tempfile.new(['invoice', '.pdf'], Rails.root.join('tmp'))
      tempfile.binmode
      tempfile.write pdf_file
      tempfile.close
  
      # Attach that tempfile to the invoice
      unless pdf_file.blank?
          self.uploads.clear
          self.uploads.create(:fileinfo =&gt; File.open(tempfile.path), :job_id =&gt; self.job.id)
          tempfile.unlink
      end
  
  end
  </code></pre>