Prawn PDF with Rails mailer? - ruby-on-rails

I have successfully created an email that sends on creation of a Kase, but now I need to attach a PDF that is created on the fly by Prawn and Prawno. Basically when you visit a kase such as application.com/kase/1 you just append the URL with .pdf i.e. application.com/kase/1.
I spent ages getting the PDF to work and look how I wanted, but I can't figure out how to add the PDF to an auto sending email - mainly because I cannot work out how to give it a link as it's auto generated.
Has anyone ever managed to get this to work?
Thanks,
Danny

I suppose it would be better if you store generated pdf somewhere - for caching purposes, etc.
But with current configuration, you can read generated page with Net::HTTP and attach response:
require 'net/http'
def your_mailer_method(record)
#...
attachment "application/pdf" do |a|
a.body = Net::HTTP.get('yourdomain.com', "/kase/#{record.id}.pdf")
a.filename="your_pdf_name.pdf"
end
end

You really should consider just not using Prawnto, and creating a subclass of Prawn::Document to do what you need. Then, in both your controller and your mailer code, it should just be:
MyReport.new.render
See the Prawn documentation on this:
http://wiki.github.com/sandal/prawn/using-prawn-in-rails

For the newer ones, you dont really need to send a request again, when you can ::
mail.attachments["invoice.pdf"] = {:mime_type => "application/pdf" , :content => pdf_generator}
Instead of doing this ::
send_data pdf.render , :filename => file_name_here , :type => "application/pdf"
just do this ::
pdf.render , :filename => file_name_here , :type => "application/pdf"
Do not send_data, just render that pdf in your email attachment as mentioned in the first snippet.
In fact, i just wrote a Gist on github.

This code works for me
def send_file(file, subject, text, to_email)
#subject = subject
#text = text
attachments["#{invoice.invoice_number}.pdf"] = file
from_email = abc#xyz.com
mail(:to => to_email, :from => from_email, :subject=> subject)
end

Related

Rails download http response/displayed site

Instead of displaying the xml file rendered by the index.api.rsb file in my browser, i want to download it. To me this sounds very simple, but I cant find a solution.
I tried the following in the controller-method:
def split
if params[:export] == "yes"
send_file *here comes the path to xml view*, :filename => "filename", :type => :xml
end
respond_to ...
end
The result is a MissingFile exception...
Thanks in advance
Note that :disposition for send_file defaults to 'attachment', so that shouldn't be a problem.
If you have a MissingFile exception, that means the path is incorrect. send_file expects the path to an actual file, not a view that needs to be rendered.
For your case, render_to_string might be what you need. Refer to this related question. It renders the view and returns a string instead of setting the response body.
def split
if params[:export] == "yes"
send_data(render_to_string path_to_view, filename: "object.xml", type: :xml)
end
end
To force it to download it, add :disposition => attachment to your send_file method.
Source: Force a link to download an MP3 rather than play it?

PDF attachment in email is called 'Noname'

When I send an email with a attached pdf file the email only shows a file called 'Noname'. The file itself is the multipart section of the email with the base64 attached pdf. How can I send the PDF so it comes up as an attachment and doesn't corrupt the email?
Here is my code:
attachments['126539_statistics.pdf'] = File.read("app/assets/pdfs/126539_statistics.pdf")
mail(:to => email, :subject => subject, :body => message, :content_type => 'application/pdf')
I had face same problem. I have corrected it by following way
1) As per ActionMailer Guide, You need to make view file into app/views/[action_mailer_model_name]/[method_name]
Here is reference of guide: http://api.rubyonrails.org/classes/ActionMailer/Base.html
2) Action Mailer is smart enough that it identify content-type automatically while reading file. So there is no need to pass explicitly 'application/pdf' in your mail function.
Hope, this information helps to solve your problem
Also experienced the same problem
solution is :
set Attachments before sending mail function.
code is attached below:
attachments["invoice_#{invoice.bill_date.strftime('%B-%Y')}.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string pdf: 'project_report.pdf',
template: 'users/_invoice.html.erb'
)
mail(to: email , from: from, subject: subject) do |format|
format.html
end

Download pdf instead of showing it in the browser

I am using pdfkit on my rails application (I did this according to Ryan Bates railscast)
Currently it's showing the pdf inside the browser.
How can the pdf be downloaded instead ?
Thanks
EDIT:
Thanks for the response!
But how can I create the data of the pdf in the controller?
The pdf shows up automatically when the url has .pdf. for example when going to this link:
http://localhost:3000/home/index.pdf
It opens a pdf version of the the page automatically on the browser (it's a rails middleware setup).
Try using the send_data method that all rails controllers have.
Something like this:
# Generate a PDF document with information on the client and return it.
# The user will get the PDF as a file download.
def download_pdf
send_data(generate_pdf, :filename => "my_file.pdf", :type => "application/pdf")
end
Assuming it a simple rack end point:
class PdfEndpoint
def call(env)
[200, {"Content-Type" => "application/pdf", "Content-Disposition" => "attachment", "filename" => "filename.pdf"}, pdf_data]
end
end
Also the following Railscast will probably help:
http://railscasts.com/episodes/151-rack-middleware

How do I create a temp file and write to it then allow users to download it?

I'm working on my first application and I need some help with allowing my users to download a text file with certain variables that are being displayed on the page.
Take a shopping list for example.
Let's say you allow your users to create a shopping list of products, and then display the shopping list with the items on a shopping list page,
e.g. localhost:3000/list/my-list
Take a look at the example code below (which is probably incorrect):
File.open('shopping_list.txt', 'w') do |file|
file.puts 'Item 1: #{product_1.name}'
file.puts 'Item 2: #{product_2.name}'
file.puts 'Item 3: #{product_3.name}'
end
Which then creates a text file that has the following content:
Item 1: Eggs
Item 2: Butter
Item 3: Bread
Users should then be able to download this file (i don't want this file to be stored on the server) via a download link.
I have no idea how to achieve this, but I'm hoping you guys can guide me. :D
TL;DR
create text files populated with model data (perhaps create a method to achieve this?)
text files should not be stored on the server, but created as users click the download button (not sure if this is the rails way but perhaps someone could show me a better way)
I am assuming there is a resource for List with the attribute name as the name of the list and a list has_many Item which has an attribute description
First off, create a download path change your routes config/routes.rb
resources :lists do
member {get "download"}
end
Now if you run a rake routes in the console you should see a route like
/lists/:id/download
Whats more you should now have the helpers download_list_url & download_list_path to use in your view like
<ul>
<% #lists.each do |list| %>
<li> <%= list.name %> - <%= link_to 'Download List', download_list_path(list) %> </li>
<% end %>
</ul>
In your lists_controller add the action, and as you dont actually want to keep the file on the server disk just stream the data as a string
def download
list = List.find(params[:id])
send_data list.as_file,
:filename => "#{list.name}.txt",
:type => "text/plain"
end
Finally you see I have used a as_file method which you should add to the model (I prefer not to do this stuff in controllers, fat models, skinny controllers). So in the List model
def as_file
output = [self.name]
self.items.each {|item| output << item.description }
output.join("\n")
end
You say you don't want to store the file on the server, but "download" it on request; this sounds like you just want to generate and deliver a text document in response to the download link. There are several approaches, but you want to be sure of setting the mime-type so the browser sees it as a text file instead of an html document.
product_info = [
"Item 1: #{product_1.name}",
"Item 2: #{product_2.name}",
"Item 3: #{product_3.name}",
].join("\n")
render :text => product_info # implies :content_type => Mime::Type["text/plain"]
BTW, your example with open/puts above won't output what you think since single-quoted strings don't interpolate.
so, you wish to :
create text files populated with model data (perhaps create a method
to achieve this?)
text files should not be stored on the server, but
created as users click the download button (not sure if this is the
rails way but perhaps someone could show me a better way)
You have the right idea, here's what to do :
Create a method in your model to generate the text file contents. Let's say this method is called list_data
It seems like you have an existing controller action called my_list. Hence we can call our new method in the controller like so :
.
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end
To link to the download, just use link_to :action => my_list, :format => 'text'
See http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_data for full docs on send_data
Caveat & explanations : Using the method above, there isn't really an explicit creation of files, Rails is streaming it for you. Hence this method is not suitable for very large files, or when the generation of the file content will take a while. Use a delayed method to generate the file and store it - the file contents somewhere if that's the case - but we can use send_data once it has been generated
You could try a combination of TempFile and send_file. In your controller action ..
file = Tempfile.new('foo')
file.write("hello world")
file.close
send_file file.path
At Rails 2.3 you can use Template Streaming. Working with Redmine I can remember something like that, you have to adapt for your case. Reference: Streaming and file downloads
require "prawn"
class ClientsController < ApplicationController
# Generate a PDF document with information on the client and return it.
# The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
end
private
def generate_pdf(client)
Prawn::Document.new do
text client.name, :align => :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
Using the Thong Kuah you must just change the "content_type" param:
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end

Saving XML files with Rails

im working on a Rails project that should create XMl files, or to be more specific
use existing XMl templates and put content from the database in it.
So i dont need to create the xml structure, basically just rendering a template with content.
What would be the smartest way to do that?
So far i have a file.xml.erb in my layout folder
and i have a custom route "/renderXML" that does
def renderXML
#reading_question = ReadingQuestion.find(params[:id])
render :file => 'layouts/question.xml'
end
This works, but i also want to save the file, not only show it (actually viewing it is not really needed).
For saving i found this
File.open('fixed.xml','w'){|f| f.write builder.to_xml}
How do i access the rendered file and save it with some method like above?
Perhaps something like:
s = render_to_string :file => 'layouts/question.xml'
File.open('fixed.xml','w'){|f| f.write s}
render :text => s
Another approach :
send_data fixed, :type => 'text/xml; charset=UTF-8;', :disposition =>
"attachment; filename=fixed.xml"

Resources