Rails 4 , Heroku, PDF Generation, PDFkit - ruby-on-rails

I am trying to make a flyer creator app and am having a hard time getting the page to save as a pdf.
After doing a bit of googling I found a couple options and landed on pdfkit. The rails casts video made it look very simple, then like always, being on a windows machine, I found it didn't exactly apply. I have tried a ton of tutorials but am still unable to get my local environment working.
I eventually would like to deploy this solution to heroku and that seams to also require some extra steps.
My gems include,
gem "pdfkit"
my application.rb includes
config.middleware.use "PDFKit::Middleware", :print_media_type => true
I have in my /config/initializers I have pdfkit.rb that contains
PDFKit.configure do |config|
config.wkhtmltopdf = 'C:/wkhtmltopdf/bin/wkhtmltopdf.exe'
config.default_options = {
:page_size => 'Legal',
:print_media_type => true
}
end
Is there anything else needed I may have missed?
I have seen the gem "wkhtmltopdf-binary" referenced in some tutorials, do I need this gem? what is it doing?
I have installed the wkhtmltopdf application all over my computer at this point, I saw one tutorial that placed the install in the actual root of the app, in the root of my C: drive and elsewhere. Even when I think I got this part right at best I got the message
"Exit with code 1 due to network error: ContentNotFoundError"
I would really like some help getting PDFKit to work on my local environment, and then deploy that solution to heroku. Can anyone help?
I can show any code snippets needed I am just unsure what would be helpful.
In case it helps anyone here is a link to my work in progress site / an example page that I would like turned into a pdf.
http://www.easyflyerceator.com/carpet_cleaning_flyers/1/carpet_cleaning1s/4

Problem is with wkhtmltopdf.exe path in pdfkit.rb, move your binary file to bin folder of the project(Create one, if it’s not there. You can create any other folder too but then remember to change the path in the initializers.) and then specify same path in pdfkit.rb as follow:
PDFKit.configure do |config|
config.wkhtmltopdf = Rails.root.join('bin', 'wkhtmltopdf').to_s
config.default_options = {
:page_size => 'Legal',
:print_media_type => true
}
end
You can refer Converting HTML to PDF.

Related

How to use pdfkit to render image in rails app?

Can anyone step-by-step or with example explain how to install and use pdfkit in rails to render images present in html.?
I have installed pdfkit along with wkhtmltopdf-binary. When i do
require 'pdfkit'
kit = PDFKit.new('http://google.com')
gpdf = kit.to_file('/home/blackat/Desktop/gpdf.pdf')
it will generate a pdf of google home page perfectly. So it is meant that pdfkit installed correctly in my system(correct me if it is wrong).
As mentioned in the railcast i did all the changes.
gem 'pdfkit' # in my Gemfile
bundle install
require 'pdfkit' # in application.rb
config.middleware.use "PDFKit::Middleware", :print_media_type => true #in application.rb
to check weather it works or not i did some workaround in my controller
like this
html_data = render_to_string :layout => false
html_file_path = "demo.html"
pdf_file_path = "demo.pdf"
f = File.open(html_file_path, "w")
f.write(html_data)
f.close
kit = PDFKit.new(html_data,:page_size => 'Letter')
pdf_file = kit.to_file(pdf_file_path)
When i remove images from my html.erb template, a pdf will generate. If i specify any images in my erb template or if i include ".pdf" in browser execution hangs for lifetime.
Where i am doing wrong?
is there any blog/example to refer?
Does the 'wkhtmltopdf' installed correctly?
Please help me in this. i am trying this from 2 weeks.
I am using rails '3.2.11'
and ruby 1.9.3
Thanks in advance.
Try giving absolute path of image file in your view like:
%img{src: "file://#{Rails.root}/public/imagesmyImage.png"}

RoR, Fog: Getting Started with Fog

I just installed the gem asset_sync and I am trying to get set up with my AWS account. When I run bundle exec rake assets:precompile I get the following errror:
AssetSync::Config::Invalid: Fog provider can't be blank, Fog directory can't be blank
I understand the simply reason that I am getting this error, namely that I havent pushed the Fog provider or directory to heroku. What I am stumped about is where to put the Following code (Taken from the Fog README). In config/initializers/fog.rb? Is this all I need to do to start using fog, other than installing the gem?
require 'rubygems'
require 'fog'
# create a connection
connection = Fog::Storage.new({
:provider => 'AWS',
:aws_access_key_id => YOUR_AWS_ACCESS_KEY_ID,
:aws_secret_access_key => YOUR_AWS_SECRET_ACCESS_KEY
})
# First, a place to contain the glorious details
directory = connection.directories.create(
:key => "fog-demo-#{Time.now.to_i}", # globally unique name
:public => true
)
not a problem, getting started tends to be the hardest part.
The answer is, it depends. I'd actually venture to say it would be best to put this in your environment based initializers, ie config/init/development or config/init/production, etc. Relatedly, you probably will not want to generate a new directory every time you start your app (there is an account level limit of 100 total I believe). So you might want to either set a key for each environment for that create or simply create the directory somewhere outside the initializers (and within the initializer you can assume it exists).
If you want to use that directory directly, you'll still need to create a reference, but you can create a local reference without making any api calls with #new like this:
directory = connection.directories.new(:key => ...)
As for asset_sync, it needs those keys and a reference to the directory key, which you will probably want to provide via ENV vars (to avoid checking your credentials into version control). You can find details on which keys and how to set them here: https://github.com/rumblelabs/asset_sync#built-in-initializer-environment-variables (the readme also describes how to do it via initializers, but that probably isn't the best plan).
Hope that helps!

How can I get Rspec2 to support models and specs in a different path?

I have two rails projects which share the same models (a public website and a an admin site) via a git submodule.
In my application.rb file I have added the following line: config.autoload_paths += ["shared/models", "shared/lib"], and this works fine while running the Rails applications, however when I try to run specs it seems to load the routes.rb file first, and then the application.rb file - which means that the specs error out - especially with devise.
In addition to this, Rspec wont pickup the specs in the shared/spec path - is there any way to add this path to the spec task, like do I need to setup my own rspec.rake file duplicating the task or something like that?
Cheers
To load shared/models, you do have to add it to config.autoload_paths.
Then to load your spec from shared/spec, add this to spec_helper.rb:
shared_model_specs = config.filename_pattern.split(",").collect do |pattern|
Dir["shared/spec/models/#{pattern.strip}"]
end.flatten
config.files_to_run.concat shared_model_specs
Just a side note for other guys interested, if your spec files are in the normal spec folder but under a customized sub folder, you can load it like this:
config.include RSpec::Rails::ModelExampleGroup, :type => :model, :example_group => {
:file_path => config.escaped_path(%w[spec shared models])
}
PS: I would recommend putting the shared code or modules into a gem, then use them in the two projects. This way the gem contains its own tests and referencing it from multiple projects is much easier and organized.

Rails - Paper_Clip - Support for Multi File Uploads

I have paper_clip installed on my Rails 3 app, and can upload a file - wow that was fun and easy!
Challenge now is, allowing a user to upload multiple objects.
Whether it be clicking select fileS and being able to select more than one. Or clicking a more button and getting another file upload button.
I can't find any tutorials or gems to support this out of the box. Shocking I know...
Any suggestions or solutions. Seems like a common need?
Thanks
Okay, this is a complex one but it is doable. Here's how I got it to work.
On the client side I used http://github.com/valums/file-uploader, a javascript library which allows multiple file uploads with progress-bar and drag-and-drop support. It's well supported, highly configurable and the basic implementation is simple:
In the view:
<div id='file-uploader'><noscript><p>Please Enable JavaScript to use the file uploader</p></noscript></div>
In the js:
var uploader = new qq.FileUploader({
element: $('#file-uploader')[0],
action: 'files/upload',
onComplete: function(id, fileName, responseJSON){
// callback
}
});
When handed files, FileUploader posts them to the server as an XHR request where the POST body is the raw file data while the headers and filename are passed in the URL string (this is the only way to upload a file asyncronously via javascript).
This is where it gets complicated, since Paperclip has no idea what to do with these raw requests, you have to catch and convert them back to standard files (preferably before they hit your Rails app), so that Paperclip can work it's magic. This is done with some Rack Middleware which creates a new Tempfile (remember: Heroku is read only):
# Embarrassing note: This code was adapted from an example I found somewhere online
# if you recoginize any of it please let me know so I pass credit.
module Rack
class RawFileStubber
def initialize(app, path=/files\/upload/) # change for your route, careful.
#app, #path = app, path
end
def call(env)
if env["PATH_INFO"] =~ #path
convert_and_pass_on(env)
end
#app.call(env)
end
def convert_and_pass_on(env)
tempfile = env['rack.input'].to_tempfile
fake_file = {
:filename => env['HTTP_X_FILE_NAME'],
:type => content_type(env['HTTP_X_FILE_NAME']),
:tempfile => tempfile
}
env['rack.request.form_input'] = env['rack.input']
env['rack.request.form_hash'] ||= {}
env['rack.request.query_hash'] ||= {}
env['rack.request.form_hash']['file'] = fake_file
env['rack.request.query_hash']['file'] = fake_file
if query_params = env['HTTP_X_QUERY_PARAMS']
require 'json'
params = JSON.parse(query_params)
env['rack.request.form_hash'].merge!(params)
env['rack.request.query_hash'].merge!(params)
end
end
def content_type(filename)
case type = (filename.to_s.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
when %r"jp(e|g|eg)" then "image/jpeg"
when %r"tiff?" then "image/tiff"
when %r"png", "gif", "bmp" then "image/#{type}"
when "txt" then "text/plain"
when %r"html?" then "text/html"
when "js" then "application/js"
when "csv", "xml", "css" then "text/#{type}"
else 'application/octet-stream'
end
end
end
end
Later, in application.rb:
config.middleware.use 'Rack::RawFileStubber'
Then in the controller:
def upload
#foo = modelWithPaperclip.create({ :img => params[:file] })
end
This works reliably, though it can be a slow process when uploading a lot of files simultaneously.
DISCLAIMER
This was implemented for a project with a single, known & trusted back-end user. It almost certainly has some serious performance implications for a high traffic Heroku app and I have not fire tested it for security. That said, it definitely works.
The method Ryan Bigg recommends is here:
https://github.com/rails3book/ticketee/commit/cd8b466e2ee86733e9b26c6c9015d4b811d88169
https://github.com/rails3book/ticketee/commit/982ddf6241a78a9e6547e16af29086627d9e72d2
The file-uploader recommendation by Daniel Mendel is really great. It's a seriously awesome user experience, like Gmail drag-and-drop uploads. Someone wrote a blog post about how to wire it up with a rails app using the rack-raw-upload middleware, if you're interested in an up-to-date middleware component.
http://pogodan.com/blog/2011/03/28/rails-html5-drag-drop-multi-file-upload
https://github.com/newbamboo/rack-raw-upload
http://marc-bowes.com/2011/08/17/drag-n-drop-upload.html
There's also another plugin that's been updated more recently which may be useful
jQuery-File-Upload
Rails setup instructions
Rails setup instructions for multiples
And another one (Included for completeness. I haven't investigated this one.)
PlUpload
plupload-rails3
These questions are highly related
Drag-and-drop file upload in Google Chrome/Chromium and Safari?
jQuery Upload Progress and AJAX file upload
I cover this in Rails 3 in Action's Chapter 8. I don't cover uploading to S3 or resizing images however.
Recommending you buy it based solely on it fixing this one problem may sound a little biased, but I can just about guarantee you that it'll answer other questions you have down the line. It has a Behaviour Driven Development approach as one of the main themes, introducing you to Rails features during the development of an application. This shows you not only how you can build an application, but also make it maintainable.
As for the resizing of images after they've been uploaded, Paperclip's got pretty good documentation on that. I'd recommend having a read and then asking another question on SO if you don't understand any of the options / methods.
And as for S3 uploading, you can do this:
has_attached_file :photo, :styles => { ... }, :storage => :s3
You'd need to configure Paperclip::Storage::S3 with your S3 details to set it up, and again Paperclip's got some pretty awesome documentation for this.
Good luck!

Problems with x_sendfile in Rails

I’m having some problems with John Guenin's x_sendfile (http://john.guen.in/past/2007/4/17/send_files_faster_with_xsendfile/).
When coding the download of a PDF file, I’m using the following code:
def send_the_file(filename)
xsendfile (“#{Rails.root}/doc/” + filename, :type => ‘application/pdf’)
end
but I only get 1 byte downloaded. This usually happens if the filename is not absolute (hence the #{Rails.root} being added. I’ve also checked that the file has the necessary permissions. This is failing both on localhost and my prod site.
Any ideas what I'm doing wrong?
TIA,
Urf
What version of Rails are you using? If you're on 2.1 or later, the X-Sendfile option is built into Rails' send_file method.
send_file 'filename', :x_sendfile => true
Otherwise, are you sure that mod_xsendfile has been installed and configured correctly?
You may want to make sure that your are actually using a web server that supports xsendfile. If you are dev mode you probably aren't and it may fail.
Try to set in apche httpd.conf file
XSendFileAllowAbove on

Resources