Wicked pdf only for certain routes - ruby-on-rails

I have the route /document/:email/:filename, :email => /.*/, :filename => /.*/ that simply takes the filename, searches for it on the storage and returns it. However, after I started using wicked_pdf for other component, the param[:filename] in my controller stopped recognizing the .pdf extension. So before wicked_pdf the route /document/somemail#mail.com/myfile.pdf generated the param param[:filename] == 'myfile.pdf' on my controller, but after I integrated wicked_pdf the param is without the file extension i.e. param[:filename] == 'myfile' how can avoid such behavior?
I don't want wicked_pdf to handle all the pdf files request of my application, only for a specific route/controller

I believe this is because in the Wicked PDF Railtie, it registers the extension like this:
if Mime::Type.lookup_by_extension(:pdf).nil?
Mime::Type.register('application/pdf', :pdf)
end
So before, :filename was just a route parameter, but now that Rails knows there's a matching extension, it seems that it is treating it as a filename that can be followed by an extension, which should be available as params[:format].
You should be able to get the full filename by referring to it as
filename = [params[:filename], params[:format].compact.join('.')
Or unregister the Mime extension like this (maybe in the wicked_pdf initializer):
Mime::Type.unregister(:pdf)
There may also be a way to modify your route globbing to include the extension as part of the filename, but other StackOverflow threads related to that topic seem that it may not be possible to do that without trouble with filenames that also contain periods in them:
How can I get a rails route to keep the extension as part of the id?
ruby on rails - routes.rb - match file extension when multiple periods exist in filename

So I discovered that the pdf extension is removed in the middleware, but I also discovered that the middleware can get some conditions so we can tell when to process the file with wicked_pdf and when not to. See https://github.com/mileszs/wicked_pdf/blob/3e0e2e7bd131365769d230c23ea17b4e52d2702f/lib/wicked_pdf/middleware.rb#L65
Then on my application.rb I just put that condition:
wickedpdfFormat = /wickedpdf_/
config.middleware.use(WickedPdf::Middleware, {}, {:only => [wickedpdfFormat]})
And now I just have to include the wickedpdf_ prefix only for the files I want to be handled by wicked_pdf

Related

Allow users to download a file

I've read through a number of StackOverflow threads and tutorials, and I haven't found a good, simple explanation for how to allow a user to download a file from your site.
All I want to do is add a link to one of my views which downloads a file when clicked.
I'd like to understand:
Where do I store the downloadable file in my file system? public?
Is there anything special about linking to the file in your view, or it's just a link_to?
What needs to happen in routes? It's just a get for that controller#action?
What needs to happen in the controller? In rails documentation I've read that you need to "be careful to sanitize the path parameter if it is coming from a web page," but I'm not sure exactly what that means.
Thanks!
In simple scenario, you don't need controller to download file. Just save file to public folder. Public folder is default folder for static resources there are stored compiled js, css, images files, robot.txt and so on.
If you have file monthly-report.doc. Put it to public/reports/monthly-report.doc.
In view link_to 'Donwload Report', '/reports/monthly-report.doc'
There are basically two cases:
1. The file is public and should be downloadable by anyone.
Place it in the /public directory. Remember that this is the web root - so if you have a file that lives at /public/foo/bar.baz you would link to the file with <%= link_to 'A file', '/foo/bar.baz' %>.
No routes or controllers are need since we are just serving a static file from the servers public directory.
2. The file needs access control
In this example we are dynamically servering files stored in /downloads.
# routes.rb
resources :downloads, only: [:show]
class DownloadsController < ApplicationController
# do your authentication logic here
# GET /downloads/:id
# #example
# GET /downloads/foo.bar would download a file stored at
# /downloads/foo.bar
# #raise [ActiveRecord::RecordNotFound] if the file does not exist.
# This causes a 404 page to be rendered.
def show
fn = Rails.root.join('downloads', params[:id])
raise ActiveRecord::RecordNotFound and return unless file.exists?(fn)
send_file(fn)
end
end
By using Rails to serve the download we can apply whatever access control rules we want.
The link_to is the same as any other link.
If you want to store it in public then do this in whatever controller action you want.
send_file File.join(Rails.root, 'public', 'file.extension')
You could create a downloads controller and specify that in the index then simply link_to 'Download', download_index_path or such.
If you're trying to send a file name that a user inputs, you have to sanitize it. If it's "hard coded" like the above example, then you're fine.

Mount Sinatra app inside a rails app and sharing layout

I would like to mount a sinatra application in my rails app.
But I would like this one to share the same layout.
The iframe could work but do you have any other idea ?
Thanks
You basically need to do two things:
You need to tell the Rails router that a certain URL path is to be handled by another Rack app (in your case a Sinata app). This can be done by adding this to your routes.rb:
match "/sinatra" => MySinatraApp, :anchor => false
Having done that, you can create your app like so:
class MySinatraApp < Sinatra::Base
get "/" do
"Hello Sinatra World"
end
end
The second step now is to tell your Sinatra app to use the rails layout which by default lives in app/views/layouts/application.html.erb for Rails 3.1. by default, Sinatra uses ./views/layout.ext (with ext being the extension of your chosen template system). So you basically, have to tell Sinatra to
use another directory to find views and layouts instead of the default ./views
use another template file as the default layout.
Both can be achieved by setting the following in your sinatra app:
set :views, "/path/to/your/railsapp/views"
set :erb, layout => :"layout/application" # or whatever rendering engine you chose
to share the same layout, you can point sinatra to the folder where the layout is in your rails app:
(taken from here: http://www.sinatrarb.com/configuration.html)
:views - view template directory A string specifying the directory
where view templates are located. By default, this is assumed to be a
directory named “views” within the application’s root directory (see
the :root setting). The best way to specify an alternative directory
name within the root of the application is to use a deferred value
that references the :root setting:
set :views, Proc.new { File.join(root, "templates") }
From your Rails app you can build a method which you can call from the action where the sinatra app should be included in the view.
(given you want to use the index action for this)
def index
#sinatra_content = get_sinatra
end
# use #sinatra_content in your views for rendering
def get_sinatra
sinatra_ip = 127.0.0.1;
sinatra_port = 4567;
#start a request here
RestClient.get 'http://#{sinatra_ip}:{sinatra_port}/', {:params => {:id => 50, 'foo' => 'bar'}}
end
see how rest-client works here: https://github.com/archiloque/rest-client and don't forget to include the gem in your rails app.
To use links in your sinatra app you should decide if sinatra should handle this (point to sinatra app (with port) or build links in your sinatra app which are handled by your rails app)
I think that using the append_view_path in your rails application will work a little bit better. Just append the Sinatra views to your Rails app and it will look there after looking in app/views.
The Crafting Rails Applications book by José Valim has a lot of documentation on that topic (rendering views from other sources), you may want to look at that.
Also, this Railscasts can help: http://railscasts.com/episodes/222-rack-in-rails-3

Most appropriate way to generate directory of files from directory of template files with Rails and ERB?

My goal is to generate a directory of static html, javascript, and image files within my Rails (3) app, driven by ERB templates. For example, as a developer I might want to generate/update these files:
#{Rails.root}/public/products/baseball.html
#{Rails.root}/public/products/football.js
..from the following template files:
#{Rails.root}/product_templates/baseball.html.erb
#{Rails.root}/product_templates/football.js.erb
Ideally the templates would have access to my app's Rails environment (including URL helpers, view helpers, partials, etc.).
What's the latest and greatest way to accomplish this?
I experimented with a custom Rails generator, but found that I needed to write custom logic for skipping non-ERB files, substituting file names, etc. There must be a better way.
I'm not sure what you are trying to do exactly, that may help provide better answers, but here is some useful information:
You can call into erb directly, some information on that is here, which have probably already been doing:
http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
For the list of template files an easy Dir.glob should be able to help find the specific files easily and loop through them:
http://ruby-doc.org/core/classes/Dir.html#M000629
The tricky part I wouldn't know how to advise you on is getting access to the helpers and other things Rails provides. The helpers that you write are just modules, so you could mix those in, something similar might be possible with the built-in rails helpers.
This is interesting and related but doesn't directly answer your question, since its uses the Liquid templating engine instead of ERB, but otherwise, it does some of the static site generation you are talking about:
https://github.com/mojombo/jekyll
This is how I accomplished something similar. It accepts source and destination directories, wipes out the destination, then processes the source directory, either ERB-processing files and placing them in the destination or simply copying them (in the case of on-ERB files). It would need to be modified to handle recursively processing a directory.
I invoke it from a rake task like so:
DirectoryGenerator.new.generate(Rails.root.join('src'), Rails.root.join('public', 'dest'))
class DirectoryGenerator
include Rails.application.routes.url_helpers
include ActionView::Helpers::TagHelper
default_url_options[:host] = 'www.example.com'
def generate(source, destination)
FileUtils.rmtree(destination)
FileUtils.mkdir_p(destination)
Dir.glob(File.join(source, '*')).each do |path|
pathname = Pathname.new(path)
if pathname.extname == '.erb'
File.open(destination.join(pathname.basename.sub(/\.erb$/, '')), 'w') do |file|
file.puts(ERB.new(File.read(path)).result(binding))
end
else
FileUtils.cp(pathname, File.join(destination, pathname.basename))
end
end
end
end
Have you looked into Rails templates?
http://m.onkey.org/rails-templates for instance..
Not sure what you are getting at exactly.. are you trying to generate client sites by providing a few parameters.. that the end goal?

rails 3 - paperclip

With paperclip, how can you get paperclip to use full urls like http://mysite.com/image/asdasd.png
versus /image/asdad.png
thanks
Once you configure the :url interpolation string to your satisfaction, you can link to attachments with the full URL using something like:
def attachment_path(attachment)
attachment.url
end
def attachment_url(attachment)
"#{root_url}#{attachment.url.gsub(/^\//, '')}"
end
Assuming you want the behavior to work like the _url helpers on the controller/view level, its a little complicated as paperclip functions without the benefit of knowing the host from the request. An easy way to get around this is to define the constant HOST in the config/environments/ENV.rb and then passing the url parameter to has_attachment like
:url => "http://#{HOST}/:path"
or whatever your url rules are.
You can also side step this issue by using S3, which is kind of a life saver
If you are uploading files to amazon S3 then s3.url gives the full image path. But in case of local file storage, you can set :url option also

How Rails be configured to access a media resource that is not in its conventional directory location?

Let's say I have an image that does not reside in the normal location:
{appname}/public/images/unconventional.gif
But instead here:
{appname}/unconventional.gif
I understand this is a complete violation of Rails conventions, is immoral and you should never do this under any circumstances and, furthermore, why would I even suggest such a foolish thing?
Ok, now that we have that out of the way, assuming I am on Windows and therefore symbolic links are out of the question, how is it possible to set this up?
Rails does not serve these images, it lets the web server do that. You had best change the configuration of your web server to handle this scenario. If you use Apache, for example, it would fairly easy to set up with mod_rewrite.
Making Rails serve these images will be ugly, but it is possible if you provide a route in your routes.rb that matches /public/images/unconventional.gif, and if the file itself does not exist. For example:
map.connect "public/images/unconventional.gif",
:controller => "static_image_controller",
:action => "serve"
And then create a controller StaticImageController:
class StaticImageController < ApplicationController
def serve
image = File.read(File.join(Rails.root, "unconventional.gif"))
send_data image, :type => "image/gif", :disposition => "inline"
end
end
Warning: if you use the above concept, note that if you use input from the URL to decide which file to serve (with params[:file], for example), you need to thoroughly sanitize the input, because you are risking exposing your entire file system to the outside world.

Resources