I have a download button in my view form, which use an "index" action in the controller
def index
respond_to do |format|
format.html { #some_variables = some_variables.page(params[:page]) }
format.csv {
render csv: some_variables, headers: SomeVariable.csv_headers
}
end
end
And some def self.to_csv method in my model SomeVariable, in which I generate the CSV file.
Is there anyway to check if the download was OK and for example set a flag.
if download went OK
do something
else
raise some error
end
I thought about "begin rescue" in a index action, but if there is any other "smarter" implementation sharing it would be more than appreciated.
send_file can be a good alternative to what you're planning to do.
You can find more information here How to download file with send_file? and at the API Dock
I figured out a simple solution, I opted for an after_action.
after_action is run only if the action before succeeds, and in this case I'm trying to do something if the download was executed i.e if the csv render get a 202.
So the if the action index is executed after that I run a method for example updating the downloaded time.
class SomeVariableController < ApplicationController
after_action :update_downloaded_time, only: :index, if: -> {current_user.admin?}
def index
respond_to do |format|
format.html { #some_variables = some_variables.page(params[:page]) }
format.csv {
render csv: some_variables, headers: SomeVariable.csv_headers
}
end
end
def update_downloaded_time
#some_varibale.update_all(downloaded_at: Time.now)
end
Related
In my action I wish to only respond with processing if it was called from an AJAX request. How do I check?
I want to do something like this:
def action
#model = Model.find(params[:id])
respond_to do |format|
if (wasAJAXRequest()) #How do I do this?
format.html #action.html.erb
else
format.html {redirect_to root_url}
end
end
You can check for a header[X-Requested-With] to see if it is an AJAX request. Here is a good article on how to do it.
Here is an example:
if request.xhr?
# respond to Ajax request
else
# respond to normal request
end
If you're using :remote => true in your links or forms, you'd do:
respond_to do |format|
format.js { #Do some stuff }
You can also check before the respond_to block by calling request.xhr?.
Update:
As of Rails 6.1.0, xhr?() does actually (finally) return a boolean value.
https://github.com/rails/rails/commit/0196551e6039ca864d1eee1e01819fcae12c1dc9#diff-60b77e427ea7ba142faa477fac10b8d0134cede4e35a3b1953c425200fadf1acL267-L269
Original Answer:
The docs say that request.xhr?
Returns true if the “X-Requested-With” header contains “XMLHttpRequest”....
But BEWARE that
request.xhr?
returns numeric or nil values not BOOLEAN values as the docs say, in accordance with =~.
irb(main):004:0> /hay/ =~ 'haystack'
=> 0
irb(main):006:0> /stack/ =~ 'haystack'
=> 3
irb(main):005:0> /asfd/ =~ 'haystack'
=> nil
It's based on this:
# File actionpack/lib/action_dispatch/http/request.rb, line 220
def xml_http_request?
#env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/
end
so
env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/ => 0
The docs:
http://apidock.com/rails/v4.2.1/ActionDispatch/Request/xml_http_request%3F
I like using before_action filters. They are especially nice when you need the same filter/authorization for multiple actions.
class MyController < AuthController
before_action :require_xhr_request, only: [:action, :action_2]
def action
#model = Model.find(params[:id])
end
def action_2
# load resource(s)
end
private
def require_xhr_request
redirect_to(root_url) unless request.xhr?
end
end
request.xhr?
if this return 0 then it means its an ajax request, else it will return nil.
if you are using :remote => true in links,form, then your response would come in js form instead of HTML, JSON, etc.
def action
#model = Model.find(params[:id])
respond_to do |format|
format.js
if (wasAJAXRequest()) #How do I do this?
format.html #action.html.erb
else
format.html {redirect_to root_url}
end
end
end
you also need to create, action appropriate js file, in your case action name is action, so file name would be - action.js.erb. also shift our action code inside action.js.erb file
I have a controller action that responds to the same root in two formats - html and json. But the code that runs for the html response is completely different than the one for the json response..
Now I have something like
def index
result_html = ...
result_json = ...
respond_to |format|
format.html
format.json { result = result_json.limit(10) }
end
end
and I would like to have it like
def index.html
result_html ...
end
and
def index.json
result_json ...
end
What would be the best way to organize it?
May be something like this will work for you.
def index
respond_to |format|
format.html { index_html}
format.json { index_json }
end
end
def index_html
...
end
def index_json
...
end
You can test for the format with request.format.symbol then when :json call your json action or when :html call your html action.
In my controller, I have this:
class TestController < ApplicationController
respond_to :html, :json
# code...
def create
#test = current_user.tests.create(params[:test])
respond_with #test, location: tests_url
end
# code...
end
That's cool, when I create the test, it redirects to test#index (as expected), but, if I press F5, the browser asks me to resubmit the form.
If I remove the location statement from respond_with it works just fine, but doesn't go to URL I want.
How could I fix this?
Thanks in advance.
EDIT
I change my method to
#test = current_user.tests.new(params[:transaction])
respond_with(#test) do |format|
format.html { redirect_to "/tests/" } if #test.save
end
And it works.. but, It's kinda weird I had to use a String instead of tests_url.
EDIT 2
See this complete example code amd the
Bug report.
EDIT 3
I'm unable to reproduce it with Ruby 1.9.3-p327, only in -p385.
You could just use the redirect_to method, like this:
def create
#test = current_user.tests.create(params[:test])
respond_with #test do |format|
format.html { redirect_to tests_path }
end
end
Then, if the client asks for a json response, the default to_json behavior will be triggered, but if the response format desired is html, then a redirect will be sent.
What is the advantage of using respond_to in Rails instead of a case statement?
I have several instance variables that I want to set same way for some formats, but not for HTML. This does not seem to work:
respond_to do |format|
format.html do
# ...
end
format.any(:csv, :xml) do
# common stuff
end
format.csv do
# ...
end
format.xml do
# ...
end
end
I think I'll end up using a couple of case request.format and not using respond_to at all:
case request.format
when 'html'
# ...
when 'csv', 'xml'
# common stuff
end
# more common stuff
case request.format
when 'html'
# render
when 'csv'
# custom render csv
when 'xml'
# render xml with a template
end
So I wonder what is a good use case for respond_to, where case request.format wouldn't look better?
respond_to is not just a way to find out what type of response the client expected but also a way to tell rails what type of response you are willing to provide.
For example a simple scenario where we have this controller:
class SimpleController < ApplicationController
def index
respond_to :html, :json
end
end
A client sends a request expecting xml response
curl -H "Accept: application/xml" \
-H "Content-Type: application/xml" \
-X GET "host:port/simple/index"
Rails will response with 406
Completed 406 Not Acceptable in 0ms (ActiveRecord: 0.0ms)
However, if you simply filter request.format using case like in your example the client will receive a 500 error because rails cannot find a corresponding template to the request format.
Of course you can also call respond_to on class level as well as specify respond format in routes.rb
Dive into rails source code and api documentation if you want to get more in-depth explanation to this.
First of all, the reason the respond_to block does not work is because you are using format.any(:csv, :xml) in conjunction with format.csv. You can only use one of these.
This should be a clear indication that you are trying to do too much in your controller. For example, if you are doing common stuff for csv and xml responses, then maybe you should create a class in your lib directory:
# lib/foo_serializer.rb
class FooSerializer
def initialize(bar)
#bar = bar
end
def do_stuff
#bar.to_s
end
end
And then call one of its method for each type of response:
respond_to do |format|
format.html do
# ...
end
format.csv do
FooSerializer.new(data).do_stuff
# ...
end
format.xml do
FooSerializer.new(data).do_stuff
# ...
end
end
The respond_to syntax:
Is part of the standard Rails Way™
Takes care of rendering the correct file
Not only looks better but is also more maintainable
You should try to stick with it.
The request.format field can be useful where the respond_to block is not appropriate.
For example, I'm using the sunspot gem for searching, and my controller looks like this:
def index
case request.format
when 'xlsx'
per_page: Person.count
when 'html'
per_page: 30
end
#search = Person.search do
with :name, params[:name]
fulltext params[:search_term]
paginate page: params[:page], per_page: per_page
end
#people = #search.results
respond_to do |format|
format.xlsx {send_file #people.to_xlsx, filename: 'people.xlsx'}
format.html
end
end
I've been working with Rails for a while, and I've always seen respond_to in this form:
respond_to do |format|
format.js
format.html # index.html.erb
format.xml { render :xml => #posts }
end
I don't know where you would've seen something with the nested do's.
EDIT:
For pagination, see
https://github.com/mislav/will_paginate
EDIT 2:
format.html { render :html => Post.paginate(:page => params[:page]) }
or something like that.
I have a particular Rails controller method that returns some JSON when I do an javascript ajax request in the front-end.
However, I want to prevent users from directly typing in the url, which displays the JSON that the method returns. I also want to still be able to perform my ajax requests. How can I go about doing this simply? Thanks!!
Just a thought... You could do something custom in your respond_to block for html requests.
respond_to do |format|
format.html { ... } # give them a 404 response?
format.js { render :json => #obj }
end
Or maybe your html.erb template with that name could just show some kind of access denied message. Then you'd just have this:
respond_to do |format|
format.html
format.js { render :json => #obj }
end
You could wrap your action with
if request.xhr?
...
end
The respond_to filter in rails3 controllers is pretty sweet.
YourJsonController < ApplicationController
respond_to :json
def index
#non-json requests will receive a 406 error
end
end
In your routes.rb, you can add :via => :post so your URL accepts only POST requests. See "HTTP Verb Constraints" at http://guides.rubyonrails.org/routing.html