问题
I have a large number of products I want to display in a pdf, with category headers. If a category doesn't fit on the current page I want to move it to the next. For this I'm using Prawn's group method.
Product.all.group_by(&:category).each do |category, products|
pdf.group do
# Simplified the data a bit for this example
pdf.text category
pdf.table products.map{ |p| [p.title, p.price] }
end
end
This works very well for small amounts of products, but when I add more than 100 or so it takes a very long time and then ends in "failed to allocate memory". If I don't use the group method it takes about 30 seconds.
Clearly the group method does not manage its memory usage very well. Any suggestions for workarounds would be appreciated.
回答1:
---------- UPDATED ANSWER --------
The previous workaround wasn't good enough for the production server, so I had to use development version from git repo installed as a submodule under vendor/prawn, as described here: https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails
The memory issue with the group method is gone, but the syntax/options for things have changed somewhat. So I had to rewrite the code to generate PDF.
Also, getting the submodule to play nicely with the git repo for the Rails app is difficult. Deployment to production was tough.
---------- ORIGINAL ANSWER --------
This isn't a fix, but it makes the problem take a few more group iterations before it manifests itself:
- override the Prawn::Document instance method named 'group'
- use the code from the 'group' function from the newest development version of prawn (from github.com)
The way I did this is that I added a file to the /lib folder of my Rails app. This file will include the Prawn gems and defined the mime type for a PDF document:
class PdfPrawn
require 'prawn'
require 'prawn/core'
require 'prawn/table'
MIME_TYPE = "application/pdf"
end
class Prawn::Document
def group(second_attempt=false)
old_bounding_box = @bounding_box
@bounding_box = SimpleDelegator.new(@bounding_box)
def @bounding_box.move_past_bottom
raise RollbackTransaction
end
success = transaction { yield }
@bounding_box = old_bounding_box
unless success
raise Prawn::Errors::CannotGroup if second_attempt
old_bounding_box.move_past_bottom
group(second_attempt=true) { yield }
end
success
end
end
And then in a model file, I define a method to generate my PDF and use something like this:
def to_pdf
require "#{File.expand_path(RAILS_ROOT)}/lib/pdf_prawn"
pdf = Prawn::Document.new
# code to add stuff to PDF
pdf.render
end
回答2:
I was using prawn on one project, sorry to tell that, but it was an disaster, finally we had to switch to wicked pdf. I advice you to do the same before you do not have to much code.
来源:https://stackoverflow.com/questions/4210251/ruby-prawn-pdf-out-of-memory-when-using-the-group-method