Rails 3 render plaintext page using view - ruby-on-rails

I have a Linux configuration file stored in my database in Rails, and I'd like to be able to download the configuration through a web request
My goal on the Linux side is to curl/wget the webpage, compare it to the current configuration, and then hup the server. Easy enough to do in a script.
In normal circumstances on Rails, you could do
render :text => #config_file
However, I need to do some formatting of the data first to apply the static headers, etc. This isn't a one-liner, so I need to be able to render a view.
I have the following set in my controller, but I still get a minimal set of HTML tags in the document
render(:content_type => 'text/plain', :layout => false);
I've done something similar in .Net before, so it printed out a text file with \n interpreted. How do I get that in Rails?

Normally, this is done with
# config/initializers/mime_types.rb
# ...
# Mime::Type.register "text/plain", :plaintext
# No changes needed as rails comes preconfigured with the text/plain mime type
# app/controllers/my_controller.rb
class MyController < ApplicationController
def my_action
respond_to do |format|
format.text
end
end
end
and a view file
# app/views/my_controller/my_action.text.erb
...
About the minimal HTML you find in the DOM: Are you seeing this from within some kind of in-browser inspector, like the ones included in google chrome or safari? If so then don't worry, this is added by the browser in order to display your text/plain document inline. If you look at the source of the delivered document (ctrl-u) no HTML should show up.

Related

PDF from ERB template with PDFKit and Rails

In my Rails application I'd like to have a special route to download a custom PDF.
This PDF should be generated via PDFKit from an ERB template in my application. Instead of describing what I'd like to achieve, I better paste some non-executable but commented code:
class MyController < ApplicationController
def download_my_list_as_pdf
# The template uses the instance variables below
#user_id = params[:user_id]
#items = ['first_item', 'second_item']
# This line describes what I'd like to do but behaves not like I want ;)
# Render the ERB template and save the rendered html in a variable
# I'd also use another layout
rendered_html = render :download_my_list_as_pdf
kit = PDFKit.new(rendered_html, page_size: 'A4')
kit.to_pdf
pdf_file_path = "#{Rails.root}/public/my_list.pdf"
kit.to_file(pdf_file_path)
send_file pdf_file_path, type: 'application/pdf'
# This is the message I'd like to show at the end
# But using 'render' more than once is not allowed
render plain: 'Download complete'
end
end
I wasn't able to find an answer to this problem yet, any help would be greatly appreciated!
render_to_string(*args, &block)
Raw rendering of a template to a string.
It is similar to render, except that it does not set the response_body
and it should be guaranteed to always return a string.
render does not return a string it sets the response_body of the response.
class MyController < ApplicationController
def download_my_list_as_pdf
# The template uses the instance variables below
#user_id = params[:user_id]
#items = ['first_item', 'second_item']
# This line describes what I'd like to do but behaves not like I want ;)
# Render the ERB template and save the rendered html in a variable
# I'd also use another layout
rendered_html = render_string(:download_my_list_as_pdf)
kit = PDFKit.new(rendered_html, page_size: 'A4')
kit.to_pdf
pdf_file_path = "#{Rails.root}/public/my_list.pdf"
kit.to_file(pdf_file_path)
send_file pdf_file_path, type: 'application/pdf'
end
end
However if you are sending a file you cannot send text or html as well. This is not a limitation of Rails but rather how HTTP works. One request - one response.
Usually javascript is used to create notifications surrounding file downloads. But consider first if its really needed as its pretty annoying to users as the browser usually tells you that you downloaded a file anyways.

How does respond_to and respond_with work in rails?

When there is
def some_action
respond_to do |format|
format.html {}
format.js {}
format.json { respond_with #objects}
end
end
It seems like html line and the js line automatically serve up/call the file matching the action's name. And the html and the js are serve up one or the other, not both. Is this correct?
The json gets called if you have an ajax call in your js that got called, and it requests data, and these need data to respond with, right? Do I need it to respond to json and to js, or just one?
If you don't to respond_to, and omit all the types, does it by default respond to html and to js?
When I do respond_to in the controller, rather than a respond_to block within each action, does using respond_with #objects apply to any argument (:thml, :js, :xml, :json, etc)?
Alternate syntax:
class TheController < ApplicationController
respond_to :html, :js, :json, only: [:some_action, :other_action]
def some_action
respond_with #objects
end
end
How does the alternate syntax work?
If you use the alternate syntax, can you not respond differently to different types of requests? Do you have to do a respond_to block isntead of the alternate syntax if you want to respond differently? How do each of these cases address graceful degradation to html?
respond_with
For a given controller action, respond_with generates an appropriate response based on the mime-type requested by the client.
This basically means your controller will send the appropriate data on a request basis - for example, if you did the following:
#app/controllers/articles_controller.rb
Class ArticlesController < ApplicationController
def show
#article = Article.find params[:id]
respond_with #article
end
end
This would basically respond with the data from #article each time you send a request. If the request is in the json mime-type, it will come back as a JSON object; if it's an HTML request, it will come back with the HTML object on the show view
--
respond_to
Basically allows you to tailor the specific responses to different mime-types. If you send a JS request, you can manage the JS response etc
respond_to blocks inside the controller actions are very cumbersome, and only really meant for specific changes / edits to the response itself.
A much simpler way to handle the respond_to is to declare it at the top of the controller file, essentially telling Rails that every action will use the options defined in that method:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
respond_to :js, :json, :html #-> the same as using respond_to block for each action
end
note that in Rails 4 respond_with feature has been extracted to the gem 'responders' (https://github.com/plataformatec/responders).
The cases of when you need/don't need each format.*whatever* line.
Normally, you don't need any of it. Rails by default looks for an html file (aka template) matching the action name under the view folder matching the controller's name.
I'm not sure when/why the json and html are sometimes paired together(like in the scaffold-generated code). Perhaps the json line is for turbolinks (please confirm/correct this). But I do know you use a respond_to block with various types of format lines when you want each type to behave differently (e.g. serve up 10 results at a time through js, but more results through html).
The js format is needed when you use remote: true in a form or link. This is because Using this remote: true disables the html template from being served up, and instead looks for a js file matching the action's name, and executes/renders that file. You don't actually need the respond to json line if you're doing things in js only.
Bonus hint: if your js files have js.erb, you can access the instance variables (how about local variables? please confirm/correct this) that you set in your action. This kind of makes sense because your *.js.erb file is technically a view. Views can access its corresponding actions' variables (hmm what about when vies get rendered from another controller?). So if you already have access to your action's variables in your js file, this can eliminate the need to make ajax calls or json calls in many situations.
I'm actually not sure when you need the json line when also using remote: true / javascript. explicit jQuery.ajax() method calls that want json data might warrant the use of the respond to json line.

Rails: Easiest way to provide file downloads?

G'day guys, currently have almost finished writing a rails application that allows a CSV download of the database. This is generated when the index is first viewed.
Is there an easy way to insert a link to a helper that returns a CSV document? That is to say is it easy to insert links to helpers? This would make a lot of problems I've been having much easier
If you sticked to the general conventions, then you registered a mime-type for csv and return the csv file content via your #index action. So your link helper would be like this:
link_to 'export as csv', posts_path(:format => :csv)
If, in exchange, your file is generated WHEN index is first view but NOT BY Rails, you may want to avoid standart render and call send_data or send_file instead (check the api for them).
# in your controller:
def index
# your suff here
#csv_path = find_or_generate_csv_file
send_data #csv_path, :type=>"text/csv", :disposition=>'attachment'
end
protected
def find_or_generate_csv_file
#your file generation logic
end

Custom formats in Ruby on Rails

I'm creating a website in Ruby on Rails, where users can login using RESTful Authentication. Someone can get a specific user using html, xml and json, just like scaffolding. But I want to add one more format: vCard (e.g. /users/1.vcard). This has a specific format, but how do I define my own formats? Using views, or must I use another way? Thanks
In your /config/initializers/mime_types.rb file, add a new registration for your format. It should look something like this:
Mime::Type.register "text/x-vcard", :vcard #The :vcard is the important part
After that (you'll have to restart your app to pick up the change), you can respond to the symbol like any other format:
# then in your controller action
def show
respond_to do |format|
format.html # render html
format.vcard { #render vcard }
end
end
Adding from comments (thanks nanda):
In your views folder, then, you would put the vCard template into a show.vcard.erb file (for example).

question about creating a link to download

I'm new to Rails and working on a project where after the user logs in
they can click on a link to download an exe file ( say for example the
file is at http://test.com/test.exe). I want to keep the link to the exe
file hidden. What are the best ways to implement this step.
I was thinking about using the redirect to url but I have been getting
an error saying that I cannot use two redirect's in a function.
Also, I was thinking of using NET:HTTP to use http request but I have no
idea how to implement this.
Thanks!!
You should read the file in your code and output it as-is to the client.
Send before it the relevant headers:
Content-type: application/octet-stream
Content-Disposition: inline; filename=[file name you want the user to see]
Content-length: [file size]
Why doing two redirects in same place is bad, it is like trying to steer a car in two directions at once...
It might be better to use
Content-Type: application/force-download
instead of
Content-type: application/octet-stream
Seems like no one has mentioned X-Sendfile.
File Downloads Done Right
I tried the below code, just an example exe file but the code looks like is reading the whole file which is over 500 MB. I want it to just give me the option to save the exe file Thanks!
def download
send_data(open('http://mirrors.gigenet.com/ubuntu/intrepid/ubuntu-8.10-desktop-i386.iso').read,
:filename => 'ubuntu-8.10-desktop-i386.iso' ,
:type => 'application/force-download',
:disposition => 'attachment')
end
To proxy the file through your controller, you can use open-uri.
In environment.rb
require 'open-uri'
In your controller
def download
send_data(open(params[:url]).read,
:filename => 'somefilename',
:disposition => 'attachment'
)
end
EDIT: If you don't want to proxy the file, you have to redirect_to(url). If you're getting the message about redirect called twice, then keep in mind that you cannot both render and redirect from the same action. If you need to load a view, and then start a download, use two actions:
def present_download
#download = Download.find(params[:id])
# implicitly calls render :action => :present_download
end
def download_file
#download = Download.find(params[:id])
respond_to do |format|
format.html { redirect_to(#download.url) }
end
end
And in your view (present_download.html.erb):
<html>
<head>
<meta http-equiv="refresh" content="1;url=<%= url_for :action => :download_file -%>" />
</head>
<body>
Your download will automatically start…
…

Resources