I am using this 3rd party controller:
class LibController
def update
# 29 lines of code
respond_with resource
end
end
I want to do something other than the respond_with at the end. But I don't want to just copy/paste all 29 lines into MyController.update. Unfortunately I can't figure out a way to render or redirect anywhere else:
class MyController < LibController
def update
super
redirect_to somewhere_else
end
end
I get a DoubleRenderError: Render and/or redirect were called multiple times in this action. I assume this is because respond_with calls render immediately. Is there a way to block/prevent that?
Thanks!
I think you are doing a twice redirection.
Try to remove one redirection on your update method.
Check sample code below that shows equivalent response when using respond_with.
def create
#user = User.new(params[:user])
flash[:notice] = 'User was successfully created.' if #user.save
respond_with(#user)
end
Which is exactly the same as:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = 'User was successfully created.'
format.html { redirect_to(#user) }
format.xml { render xml: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.xml { render xml: #user.errors, status: :unprocessable_entity }
end
end
end
Related
I'm using Devise and Pundit.
To create a new profile page, the user has to be authorized to do so.
This has been working fine since I first implemented it, but today it just started acting up with an error message:
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
Here's my code from my Application controller:
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
...
private
def user_not_authorized(exception)
flash[:alert] = "Sorry, you are not authorized to perform this action."
redirect_to(request.referrer || root_path)
end
Here's my ProfilePage controller:
def new
#profile_page = ProfilePage.new
authorize #profile_page
end
def create
#profile_page = ProfilePage.new(profile_page_params)
respond_to do |format|
if #profile_page.save
format.html { redirect_to #profile_page, notice: 'Profile page was successfully created.' }
format.json { render :show, status: :created, location: #profile_page }
else
format.html { render :new }
format.json { render json: #profile_page.errors, status: :unprocessable_entity }
end
end
authorize #profile_page
end
Someone suggested that I add this line of code below flash[:alert]:
self.response_body = nil
But now my user is redirected to the 'new profile' page again, rather than the successful profile page. It also tells the user that they are not authorized to complete this action, despite the fact that it HAS authorized them to do so.
In the create action you have to put the authorization logic before saving the record:
You have to move
authorize #profile_page
at the top of your create action, after initializing the #profile_page, like so:
def create
#profile_page = ProfilePage.new(profile_page_params)
authorize #profile_page
respond_to do |format|
if #profile_page.save
format.html { redirect_to #profile_page, notice: 'Profile page was successfully created.' }
format.json { render :show, status: :created, location: #profile_page }
else
format.html { render :new }
format.json { render json: #profile_page.errors, status: :unprocessable_entity }
end
end
end
I have a rails controller where every action has the same respond_to block for every action, eg:
def some_action
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
Is there a way that I can set this as the default response for all actions? I know that I can use
respond_to :html, :js
at the top of the controller, but can this be used to set the specific responses for each format?
Going though respond_with and respond_to documentation and source code. You can either
Use respond_to
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = 'User was successfully created.'
format.html { redirect_to(#user) }
format.xml { render xml: #user }
else
format.html { render action: "new" }
format.xml { render xml: #user }
end
end
end
OR respond_with
respond_to :html, :xml
def create
#user = User.new(params[:user])
flash[:notice] = 'User was successfully created.' if #user.save
respond_with(#user)
end
A work around is to create your own custom respond method, or manually check for the mime type as follows:
****NOTE: this is a really bad practice, I recommend sticking to conventions.
def some_action
render json: {"a" => "s"} if request.format.json?
render :some_action if request.format.html?
end
If you want all actions to respond exactly the same, move the respond_to block into a method.
def some_action
# do things
respond
end
def another_action
# do more things
respond
end
def special_action
# do special things
respond
end
private
def respond
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
This will DRY up your controller, which I assume was the question.
I understand that I cannot POST on an HTML redirect, but my situation requires that I redirect to create action after authenticating user. I would like to know how to bypass this restriction:
In particular, I would like to allow an user to fill out a post without logging in using Omniauth. I save the post to session[:post] using an AJAX call. Then, the user can login using omniauth and persist the post.
I have a PostsController with create action that handle initial ajax call, and also handle html request after authenticating user:
def create
#post = Post.new(params[:post])
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
Then, in my controller that handles callback from Facebook, I have:
class ServicesController < ApplicationController
def create
... authentication logic here ...
sign_in(:user, service.user)
redirect_to :controller => "posts", :action =>"create"
end
method_alias: :facebook, :create
However, this doesn't work, because I can't redirect to a "create" action. How can I accomplish this task?
In the code you posted, you never read the content of the session. I think it can work if you change your code with this :
Change initialization of #post:
#post = Post.new(params[:post]) || session[:post] # Find object in session if not
And add after post.save :
session.delete :post # clean session after successful creation
New full method:
def create
#post = Post.new(params[:post]) || session[:post] # Find object in session if not in params
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
session.delete :post # clean session after successful creation
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
You can create a method on a Post model and call it both from PostsController and ServicesController to save the post (though in this case it's quite trivial: new, then save, so you achieve nothing in terms of DRY, may be some encapsulation benefits). Or create a common mixin containing the create_post method with all the logic. Then and include it into SessionsController and PostsController and call it from 'create'.
In the mixin module:
def create_post(allow_json=false)
#post = Post.new(params[:post])
respond_to do |format|
format.html {
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render "posts/new"
end
}
if allow_json
... your post-saving & json-processing logic ...
end
end
end
In PostsController:
def create
create_post(true)
end
In SessionsController:
def create
... authentication logic here ...
sign_in(:user, service.user)
create_post(false)
end
I didn't compile and try, so I only hope it works. In general, I must say there's something basically wrong, so I'd look for other architectural solutions to achieve the same results, but as a quick-and-dirty approach it should probably work.
I found a hack to avoid this issue: I let #create handling AJAX call and write to session, then create another action to persist it into my database after user get authenticated:
class PostsController < ApplicationController
def create
#post = Post.new(params[:post])
respond_to do |format|
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
def persist
#post = session[:post]
if #post.save
session.delete :post
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
end
Then in my routes.rb, I have:
resources :posts do
collection do
get 'persist'
end
end
Finally, in my ServicesController:
sign_in(:user, service.user)
redirect_to persist_posts_path
I'm using Ryan Bates' Rails Cast on Wicked Wizard Forms to create a multi-step form. I don't have a current_user method defined (not using an authentication gem) - so, I'm trying to pass the user.id parameter during the redirect_to - unfortunately, I can't seem to get it to work. Any help is appreciated!
My user controller create method
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to controller: 'user_steps', id: 'user.id' }
#format.html { redirect_to #user, notice: 'User was successfully created.' }#
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
The user_steps controller that to which I am redirecting:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :gender, :items, :brands, :final
def show
render_wizard
end
end
You should pass it through as a param, ideally, which the redirect_to method will do for you if you use a proper route path.
Example:
redirect_to(user_steps_path(#user))
In your case, if you don't have a named route, you might do this:
redirect_to(controller: 'user_steps', id: #user.to_param)
In URLs it's advisable to use the to_param method. id is used for database queries.
What you're passing in is literally 'user.id' as a parameter. It will not be evaluated.
What's the difference between respond_to and respond_with ?
What do they do?
Can anyone post example with the screenshot of output?
Thanks.
There is a pretty complete answer here. Essentially respond_with does the same thing as respond_to but makes your code a bit cleaner. It is only available in rails 3 I think
Both respond_to and respond_with does the same work, but respond_with tends to make code a bit simple,
Here in this example,
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
The same code using respond_with ,
def create
#task = Task.new(task_params)
flash[:notice] = "Task was successfully created." if #task.save
respond_with(#task)
end
also you need to mention the formats in your controller as:
respond_to :html,:json,:xml
When we pass #taskto respond_with, it will actually check if the object is valid? first. If the object is not valid, then it will call render :new when in a create or render :edit when in an update.
If the object is valid, it will automatically redirect to the show action for that object.
Maybe you would rather redirect to the index after successful creation. You can override the redirect by adding the :location option to respond_with:
def create
#task = Task.new(task_params)
flash[:notice] = #task.save ? "Your task was created." : "Task failed to save."
respond_with #task, location: task_path
end
For more information visit this Blog