generate ZIP from generated PDFs with wicked_pdf - ruby-on-rails-3

In my invoice system, I want a backup function to download all invoices at once in one zip file.
This system is running on heroku - so it's only possible to save the pdfs temporary.
I've the rubyzip and wicked_pdf gem installed.
My current code in the controller:
def zip_all_bills
#bill = Bill.all
if #bill.count > 0
t = Tempfile.new("bill_tmp_#{Time.now}")
Zip::ZipOutputStream.open(t.path) do |z|
#bill.each do |bill|
#bills = bill
#customer = #bills.customer
#customer_name = #customer.name_company_id
t = WickedPdf.new.pdf_from_string(
render :template => '/bills/printing.html.erb',
:disposition => "attachment",
:margin => { :bottom => 23 },
:footer => { :html => { :template => 'pdf/footer.pdf.erb' } }
)
z.puts("invoice_#{bill.id}")
z.print IO.read(t.path)
end
end
send_file t.path, :type => "application/zip",
:disposition => "attachment",
:filename => "bills_backup"
t.close
end
respond_to do |format|
format.html { redirect_to bills_url }
end
end
This ends with the message
IOError in BillsController#zip_all_bills closed stream

I think what is wrong in your code is that you have t for your zip but you also also use it for individual pdfs. So I think when you try to use the tempfile for a pdf it is a problem because you are already using it for the zip.
But I don't think you need to use tempfiles at all (and I never actually got a tempfile solution working on Heroku)
Here is a controller method that works for me--also using wickedpdf and rubyzip on heroku. Note that I'm not using a Tempfile instead I'm just doing everything with StringIO (at least I think that's the underlying tech).
def dec_zip
require 'zip'
#grab some test records
#coverages = Coverage.all.limit(10)
stringio = Zip::OutputStream.write_buffer do |zio|
#coverages.each do |coverage|
#create and add a text file for this record
zio.put_next_entry("#{coverage.id}_test.txt")
zio.write "Hello #{coverage.agency_name}!"
#create and add a pdf file for this record
dec_pdf = render_to_string :pdf => "#{coverage.id}_dec.pdf", :template => 'coverages/dec_page', :locals => {coverage: coverage}, :layout => 'print'
zio.put_next_entry("#{coverage.id}_dec.pdf")
zio << dec_pdf
end
end
# This is needed because we are at the end of the stream and
# will send zero bytes otherwise
stringio.rewind
#just using variable assignment for clarity here
binary_data = stringio.sysread
send_data(binary_data, :type => 'application/zip', :filename => "test_dec_page.zip")
end

Related

How to use interpolation to make upload path dynamic paperclip ruby on rails?

I have following model and controller setup on my app
attr_accessible :upload
has_attached_file :upload,
:url => "/files/docs/:basename.:extension"
:path => "/files/docs/:basename.:extension"
include Rails.application.routes.url_helpers
def to_jq_upload
{
"name" => read_attribute(:upload_file_name),
"size" => read_attribute(:upload_file_size),
"url" => upload.url(:original),
"delete_url" => upload_path(self),
"delete_type" => "DELETE"
}
end
and controller
def create
#upload = Upload.new(params[:upload])
respond_to do |format|
if #upload.save
format.html {
render :json => [#upload.to_jq_upload].to_json,
:content_type => 'text/html',
:layout => false
}
format.json { render json: {files: [#upload.to_jq_upload],param:params}, status: :created, location: #upload }
else
format.html { render action: "new" }
format.json { render json: #upload.errors, status: :unprocessable_entity }
end
end
end
now I want to upload my files to different folders like docs, images etc, so I need to make /docs dynamic in path
In file upload form I have added a hidden field with name folder and set value "docs" but when I use it in model to make path dynamic it gives me error following is the code I tried
has_attached_file :upload,
:url => "/files/#{params[:folder]}/:basename.:extension"
:path => "/files/#{params[:folder]}/:basename.:extension"
when I check I can see folder name in params but I am not able to use it in model.
I also tried interpolation
Paperclip.interpolates :folder do |attachment, style|
attachment.instance.params[:folder]
end
but no result.
Is there any way to make path dynamic using params ?
You need to do some changes to get your desired result.
Add the field folder to your model so that it gets saved when you save your form.
Update your model and remove the path and url options form has_attached_file:
has_attached_file: :upload
Update your environment (e.g. config/environments/development.rb) configuration of paperclip to use the interpolation and a dynamic path:
# Paperclip defaults
config.paperclip_defaults = {
# other paperclip options for storage type e.g. file or S3
# important part
path: '/files/:dynamic_path'
}
Implement the interpolation to create your dynamic path within your config/initializers/paperclip.rb initializer
Paperclip.interpolates :dynamic_path do |attachment, style|
file_name = attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, '')
folder = attachment.instance.folder
"#{folder}/#{file_name}"
end

Issue updating file attachment(image) using Attachment_fu

I am able to upload image files as attachments using Attachment_fu but when I try to edit/ modify images which were already uploaded, it does not work.
In my Controller I have this:
def update
#sponsor_account = SponsorAccount.find( params[:id] )
if params[:showcase_category_image_file] &&
!params[:showcase_category_image_file].blank?
#sponsor_account.showcase_category_image =
ShowcaseCategoryImage.new(:uploaded_data =>
params[:showcase_category_image_file])
***This logs - Now the file name is: ***
Rails.logger.info("Now the file name is: #
{#sponsor_account.showcase_category_image.filename}")
end
#sponsor_account.update_attributes( params[:sponsor_account] )
if #sponsor_account.save
flash[:notice] = "Showcase category #{#sponsor_account.name} was updated!"
else
flash[:errors] = #sponsor_account.errors
end
redirect_to sponsor_accounts_path
end
ShowcaseCategoryImage is defined as follows:
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 5.megabytes,
:thumbnails => { :large => [350, 100], :medium => [200, 90], :thumb =>
[35,35], :preview => [60,60] }
validates_as_attachment
The view has a file_field_tag as follows:
<%= file_field_tag :showcase_category_image_file%>
and my SponsorAccount model says:
has_one :showcase_category_image, :as => :owner, :dependent => :destroy
validates_presence_of :showcase_category_image, :on => :create
Almost similar code works perfectly ok in 'create' but here in 'update' action where there is already a value, this code is not working.
I am getting the below error msgs:
Completed 500 Internal Server Error in 1089ms
ActionView::Template::Error (undefined method `public_filename' for nil:NilClass):
Obviously this error is in the index action where it tries to list all records and their attachments. Since this attachment is empty after the update, this error is thrown in the redirect_to part.
I am using REE1.8.7 and rails 3.2.9
Please help!
This issue was solved when I added :multipart => true in the 'View'. I am using rails 3.2.9 and the rails api has this to say about the 'file_field_tag':
file_field_tag(name, options = {}) Link
Creates a file upload field. If you are using file uploads then you will also need to set the multipart option for the form tag:

Rails 3.0.12 remote form failing with AJAX update

Using Ruby 1.8.7-p358, Rails 3.0.12, gem responder, gem simple-form; jquery_ujs.js
I have a form where :remote=>true and also uploading a file
= simple_form_for :resume, :url => {:controller => :resumes,
action => :upload_resume, :id => #job_seeker.id}, :html => { :multipart => true }, :remote => true do |f|
etc
The Resume controller method looks like this:
respond_to :html, :xml, :json, :js
def upload_resume
r = #job_seeker.resumes.new
r.name = params[:resume][:name]
r.source_path = params[:resume][:content].original_filename
r.doc_type = params[:resume][:content].content_type
r.content = params[:resume][:content].tempfile.read
r.size = r.content.size
r.source_ip = request.remote_ip
r.save
,end
which it does, but will not respond to upload_resume.js.erb, which looks like this:
$('#resume_list').html("<%= escape_javascript(render(:partial => 'resumes/resumes' ))%>");
Is there anything about data-remote & file uploads that causes the render to misbehave?
Any help would be appreciated.
solved the issue by converting erb to haml

Sending email with Rails 3 error

I am learning to how send email with Rails 3 with Agile Web Development Book, Task H. However, I keep getting the following error:
ArgumentError in OrdersController#create
wrong number of arguments (1 for 0)
Rails.root: /Applications/XAMPP/xamppfiles/htdocs/rails_projects/TUTORIALS/depot
Application Trace | Framework Trace | Full Trace
app/mailers/notifier.rb:4:in `order_received'
app/controllers/orders_controller.rb:57:in `block in create'
app/controllers/orders_controller.rb:52:in `create'
I have looked at similar discussions on gmail configuration, using setup_mail.rb here and there, but wasn't able to remove the error.
My config/environment.rb file (because i want same for dev/test/production)has my gmail details with xxxx and yyyyy:
Depot::Application.configure do
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "gmail.com",
:authentication => "plain",
:user_name => "xxxxxx#gmail.com",
:password => "yyyyyyy",
:enable_starttls_auto => true
}
end
model/notifier/order_received.text.erb has this:
Dear <%= #order.name %>
Thank you for your recent order from The Pragmatic Store.
You ordered the following items:
<%= render #order.line_items %>
We'll send you a separate e-mail when your order ships.
and finally, models/controller/orders_controller has def create method with the line Notifier:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
respond_to do |format|
if #order.save Cart.destroy(session[:cart_id])
session[:cart_id] = nil
Notifier.order_received(#order).deliver
format.html { redirect_to(store_url, :notice => 'Thank you for your order.') }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end
I feel my email configuration was probably not done correctly, but not sure which one. Thanks!
EDIT: I managed to solve it! Instead of smtp i used sendmail.
As for the number of arguments error, the app/mailers/notifer.rb looks like this:
class Notifier < ActionMailer::Base
default :from => 'Sam Ruby <depot#example.com>'
def order_received(order)
#order = order
mail :to => order.email, :subject => 'Pragmatic Store Order Confirmation'
end
def order_shipped(order)
#order = order
mail :to => order.email, :subject => 'Pragmatic Store Order Shipped'
end
end
Although my emails and everything still works, i'm curious whether anyones know why the smtp doesn't work, and sendmail does.
You've got a space in your order_received definition:
def order_received (order)
That should be this:
def order_received(order)
Is it this line in create?
if #order.save Cart.destroy(session[:cart_id])
If that's what you really have then Ruby will try to pass whatever Cart.destroy returns to #order.save as an argument, the above is equivalent to this:
if(#order.save(Cart.destroy(session[:cart_id])))
However, the save method doesn't take any arguments so you get a "wrong number of arguments (1 for 0) in OrdersController#create" error message. I'd guess that you mean:
if #order.save
Cart.destroy(session[:cart_id])
# etc.

Rails 3 ActionMailer and Wicked_PDF

I'm trying to generate emails with rendered PDF attachements using ActionMailer and wicked_pdf.
On my site, I'm using already both wicked_pdf and actionmailer separately. I can use wicked_pdf to serve up a pdf in the web app, and can use ActionMailer to send mail, but I'm having trouble attaching rendered pdf content to an ActionMailer (edited for content):
class UserMailer < ActionMailer::Base
default :from => "webadmin#mydomain.com"
def generate_pdf(invoice)
render :pdf => "test.pdf",
:template => 'invoices/show.pdf.erb',
:layout => 'pdf.html'
end
def email_invoice(invoice)
#invoice = invoice
attachments["invoice.pdf"] = {:mime_type => 'application/pdf',
:encoding => 'Base64',
:content => generate_pdf(#invoice)}
mail :subject => "Your Invoice", :to => invoice.customer.email
end
end
Using Railscasts 206 (Action Mailer in Rails 3) as a guide, I can send email with my desired rich content, only if I don't try to add my rendered attachment.
If I try to add the attachment (as shown above), I get an attachement of what looks to be the right size, only the name of the attachment doesn't come across as expected, nor is it readable as a pdf. In addition to that, the content of my email is missing...
Does anyone have any experience using ActionMailer while rendering the PDF on the fly in Rails 3.0?
Thanks in advance!
--Dan
WickedPDF can render to a file just fine to attach to an email or save to the filesystem.
Your method above won't work for you because generate_pdf is a method on the mailer, that returns a mail object (not the PDF you wanted)
Also, there is a bug in ActionMailer that causes the message to be malformed if you try to call render in the method itself
http://chopmode.wordpress.com/2011/03/25/render_to_string-causes-subsequent-mail-rendering-to-fail/
https://rails.lighthouseapp.com/projects/8994/tickets/6623-render_to_string-in-mailer-causes-subsequent-render-to-fail
There are 2 ways you can make this work,
The first is to use the hack described in the first article above:
def email_invoice(invoice)
#invoice = invoice
attachments["invoice.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
)
self.instance_variable_set(:#lookup_context, nil)
mail :subject => "Your Invoice", :to => invoice.customer.email
end
Or, you can set the attachment in a block like so:
def email_invoice(invoice)
#invoice = invoice
mail(:subject => 'Your Invoice', :to => invoice.customer.email) do |format|
format.text
format.pdf do
attachments['invoice.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
)
end
end
end
I used of Unixmonkey's solutions above, but then when I upgraded to rails 3.1.rc4 setting the #lookup_context instance variable no longer worked. Perhaps there's another way to achieve the same clearing of the lookup context, but for now, setting the attachment in the mail block works fine like so:
def results_email(participant, program)
mail(:to => participant.email,
:subject => "my subject") do |format|
format.text
format.html
format.pdf do
attachments['trust_quotient_results.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string :pdf => "results",
:template => '/test_sessions/results.pdf.erb',
:layout => 'pdf.html')
end
end
end
Heres' how I fixed this issue:
Removed wicked_pdf
Installed prawn (https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails)
While Prawn is/was a bit more cumbersome in laying out a document, it can easily sling around mail attachments...
Better to use PDFKit, for example:
class ReportMailer < ApplicationMailer
def report(users:, amounts:)
#users = users
#amounts = amounts
attachments["proveedores.pdf"] = PDFKit.new(
render_to_string(
pdf: 'bcra',
template: 'reports/users.html.haml',
layout: false,
locals: { users: #users }
)
).to_pdf
mail subject: "Report - #{Date.today}"
end
end