Unable to render PDF to browser using Prawn PDF for Ruby on Rails - ruby-on-rails-3.1

I am creating a pdf file in the latest version of the Prawn library (v1.0.1rc) in Rails (3.1.1) and when I run my code it generates the PDF into the root of the application.
I don't want this. I want it to render the output into user's browser window, without saving it locally to the server.
Please tell me how I can achieve this. Here are my files:
views/foo/show.pdf.erb:
<%=
require 'prawn'
pdf = Prawn::Document.new(:page_size => 'LETTER', :page_layout => :landscape, :margin => 50, :top_margin => 20, :bottom_margin => 50)
.....
render_file("foo.pdf")
%>
controllers/foo_controller:
class AuditsController < ApplicationController
before_filter :authenticate_user!
layout 'application'
can_edit_on_the_spot
respond_to :html, :xml, :js, :pdf
def index
#audits = Audit.all
respond_with #audits
end
def show
#audit = Audit.find(params[:id])
respond_with #audit do |format|
format.pdf { render :layour => false }
end
end

Gemfile
gem 'prawn'
/config/initializers/mime_types.rb
Mime::Type.register "application/pdf", :pdf
AuditsController
def show
#audit = Audit.find(params[:id])
respond_to do |format|
format.html
format.pdf do
pdf = Prawn::Document.new
pdf.text "This is an audit."
# Use whatever prawn methods you need on the pdf object to generate the PDF file right here.
send_data pdf.render, type: "application/pdf", disposition: "inline"
# send_data renders the pdf on the client side rather than saving it on the server filesystem.
# Inline disposition renders it in the browser rather than making it a file download.
end
end
end
I used to use the prawnto gem before Rails 3.1, but it doesn't work without a bit of hacking anymore. This is a much cleaner way to instantiate and display the PDF object in 3.1 by accessing Prawn directly.
I got this technique straight from one of Ryan Bates' Railscasts. Been using it ever since. You can view that specific episode here. He goes into much more detail about subclassing Prawn and moving the PDF generating code out of the controller. Also shows a lot of useful Prawn methods to get you started. Highly recommended.
A lot of the episodes are free, but that revised Prawn episode is one of those that are only available with a paid subscription. At $9/month though, a subscription quickly pays for itself.

I find the best way to send a pdf to the client's browser is to put the download into a link. Often you need to generate a pdf after form submission, but also need to redirect to another page.
You can't redirect and send the pdf simultaneously, but you can redirect and then provide a download link, like so:
First add gem 'prawn' to your gemfile. Bundle. Then do the following:
Link to your special printing action in your view
<%= link_to 'print ticket', print_ticket_path %>
route to special printing action in routes.rb
match 'print_ticket', to: 'tickets#print_ticket'
action that sends the outputted file (change per your needs):
def print_ticket
if session[:token]
#pdf = generate_pdf(session[:token])
send_data(#pdf, :filename => "output.pdf", :type => "application/pdf")
end
end
private
def generate_pdf(token)
Prawn::Document.new do
formatted_text [ { :text=>"xxx.org", :styles => [:bold], :size => 30 } ]
move_down 20
text "Please proceed to the following web address:"
move_down 20
text "http://xxx.org/finder"
move_down 20
text "and enter this code:"
move_down 20
formatted_text [ { :text=>token, :styles => [:bold], :size => 20 } ]
end.render
end

Related

Run controller as Ruby script?

In my application I setup a news controller to handle news feeds using Nokogiri:
class NewsController < InheritedResources::Base
def new
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::XML(open("http://www.themusicvoid.com/feed"))
#info = doc.xpath('//item').take(5).map do |i|
News.create(:title => i.xpath('title').inner_text,
:description => i.xpath('description').inner_text,
:link => i.xpath('link').inner_text,
:image => i.xpath('image').inner_text)
end
end
end
This functions by running <%= debug #info %> in the new.html.erb file. It searches the RSS feed and automatically saves to my database.
The problem is that it would do this whenever ANYBODY loaded new.html.erb (although I could add that file to my robots.txt so at least bots wouldn't trigger it.)
What I actually want is some sort of a Ruby script or Rake task that would do the exact same thing without loading a page. If I just run the code above as a script, it works, it would output the data if I wanted to echo it, but it does not save the information to the database. Any ideas on how I might do that?
You can create a rake task under lib/tasks directory:
rss_fetcher.rake
require 'nokogiri'
require 'open-uri'
namespace :rss do
desc "Fetch rss feed"
task :fetch => :environment do
doc = Nokogiri::XML(open("http://www.themusicvoid.com/feed"))
#info = doc.xpath('//item').take(5).map do |i|
News.create(:title => i.xpath('title').inner_text, :description => i.xpath('description').inner_text, :link => i.xpath('link').inner_text, :image => i.xpath('image').inner_text)
end
end
end
then somewhere call rake 'rss:fetch'
I highly recommend the whenever gem to do that kind of job
gem install whenever
config/schedule.rb
every 2.hours do
rake "rss:fetch"
end

Why doesn't the sinatra-redirect-with-flash gem work with shotgun?

I want to show flash messages using sinatra-redirect-with-flash gem.
Here's my ruby code:
require 'sinatra'
require 'sinatra/base'
require 'sinatra/flash'
require 'sinatra/redirect_with_flash'
require 'data_mapper'
require 'haml'
require 'builder'
# ...
class App < Sinatra::Base
enable :sessions
register Sinatra::Flash
helpers Sinatra::RedirectWithFlash
use Rack::MethodOverride
get '/' do
#notes = Note.all :order => :id.desc
#title = 'All TODOs'
if #notes.empty?
flash.now[:error] = 'No TODOs found. Add your first below.'
end
haml :home
end
post '/' do
n = Note.new
n.content = params[:content]
n.created_at = Time.now
n.updated_at = Time.now
if n.save
redirect '/', :notice => 'TODO saved successfully.'
else
redirect '/', :error => 'Failed to save TODO.'
end
end
# ...
end
And views/layout.haml is:
!!! 5
%html{:lang => "en"}
%head
%meta{:charset => "utf8"}
%body
%header
%hgroup
%h1
%a{:href => "/"}= SITE_TITLE
%h2= SITE_DESCRIPTION
#main
=styled_flash
=yield
After adding a TODO successfully, I expected to see the flash message 'TODO saved successfully.' on the home page. But no flash messages are shown after redirection when I run my app using shotgun. Flash messages are shown well when I run ruby app.rb or rackup.
How can I solve this problem?
Another problem is also happening when I run the app using shotgun. In get '/' method, if I use flash[:error] instead of flash.now[:error], the flash message doesn't show up on the page.
I am shadowning this tutorial, but I made some differences:
erb -> haml
Classic Sinatra app -> Subclassing Sinatra::Base
rack-flash -> sinatra-flash
You can browse whole codes here.
Thanks for any answers/comments.
The shotgun gem reloads Sinatra after every request. The README says:
Each time a request is received, it forks, loads the application in
the child process, processes the request, and exits the child process. The
result is clean, application-wide reloading of all source files and templates on
each request.
As a result, you will need some sort of mechanism to preserve state between requests that doesn't rely on data stored in each child process.

Rails 3 - custom ajax action is not working

I want to create a custom action in Rails which will update views and print some info on div.
I use that gem for file upload:
https://github.com/valums/file-uploader/blob/master/client/fileuploader.js
After successful upload I want to update with ajax page how many miliseconds it takes.
In old Rails I would write that with:
def set_tab
#diff = count_miliseconds_method
render :update do |page|
page.replace_html "place_menu", render( :partial => 'place_menu')
end
end
But I cant figure out how to do that in Rails 3.1.
My custom action controller code:
def custom
[...] # Here everything works OK
start_time = Time.now
Some_method
end_time = Time.now
#diff = ((end_time - start_time)*100).to_i # counted miliseconds
respond_to do |format|
format.json {render :json => {:success => true, :time => #diff}, :status => :created, :location => custom_words_path}
end
end
My custom.js.erb code
var el = $('#upload-log');
el.append("#{#diff} ms");
Unfortunately this doesnt work. I get response e.g.
{"success":true, "time":324}
but js.erb file doesnt get executed and page doesnt containt information about miliseconds.
Any idea how to fix that?
Update
Github repo:
https://github.com/there-is-no-spoon/Anagram
To execute js.erb file you have to pass
:format => :js
to your path generating method - for example:
link_to "My custom action", my_action_path(:format => :js)
You're returning JSON now (in Rails 3.1) not a chunk of string containing Javascript (as was the case before).
You need to write your code which handles your result where you make the Ajax Call. I assume you're using jQuery. So where you make the Ajax call, implement the success handler and do the
var el = $("#upload-log");
...
stuff there.
Basically server does not return Javascript anymore, it is completely on the client side only.
You need to implement the onComplete method of your file upload plugin. Read the manual of your js plugin, its mentioned clearly.

Include picture url to xml file

I have the following which renders information from my groups in through xml, this code works well.
respond_to do |format|
format.html # index.html.erb
format.xml {
groups_xml = #groups.to_xml(:include => [:enrolled_users, :tracks, :events])
render :xml => courses_xml
}
end
end
In a second part I want add url picture to this xml.
Currently I use the following code to get the picture url, and know I need to add it to the xml render but I don't know how
picture = Groups.find(params[:id]).groups.logo.public_filename(:avatar)
I'm looking for an answer for 1 month and now I don't know where I can find out.
Assuming that each group has a logo that you're trying to include in the xml, you could add a method to the group model like:
class Group
def avatar_filename
logo.public_filename :avatar
end
end
and then add :avatar_filename to the :include option you are already passing to to_xml, like this:
#groups.to_xml(:include => [:enrolled_users, :tracks, :events, :avatar_filename])
Is this what you're looking for?

Ruby: Paperclip, S3, and Deep-cloning

What I have is a Theme model, which contains many Assets. Assets are using Paperclip and storing their file content in my Amazon AWS-S3 system. I'm also using deep_clone because my customers have the ability to copy built in Themes and then modify them to their hearts content. All the deep_clone stuff is working great, but when I deep_clone the assets, the old file contents don't get added to my S3 buckets. The record gets saved to the database, but since the file-contents don't get saved with the new ID the file.url property points to a dead file.
I've tried calling paperclip's save and create method manually but I can't figure out how to get paperclip to "push" the file back to the bucket since it now has a new ID, etc....
require 'open-uri'
class Asset < ActiveRecord::Base
belongs_to :theme
attr_accessor :old_id
has_attached_file :file,
:storage => "s3",
:s3_credentials => YAML.load_file("#{RAILS_ROOT}/config/aws.yml")[RAILS_ENV],
:bucket => "flavorpulse-" + RAILS_ENV,
:path => ":class/:id/:style.:extension"
validates_attachment_presence :file
validates_attachment_size :file, :less_than => 5.megabytes
before_save :delete_assets_in_same_theme_with_same_name
after_create :copy_from_cloned_asset
private
def delete_assets_in_same_theme_with_same_name
Asset.destroy_all({:theme_id => self.theme_id, :file_file_name => self.file_file_name})
end
def copy_from_cloned_asset
if (!old_id.blank?)
if (old_id > 0)
old_asset = Asset.find(old_id)
if (!old_asset.blank?)
self.file = do_download_remote_image(old_asset.file.url)
self.file.save
end
end
end
end
def do_download_remote_image (image_url)
io = open(URI.parse(image_url))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
Any ideas on how I can get paperclip to push the file? I also wouldn't be opposed to doing this using Amazon's aws-s3 gem but I couldn't seem to get that to work either.
According to this former question/answer, it should be possible with this simple line of code:
self.file = old_asset.file

Resources