Redirect action not working - ruby-on-rails

I'm guessing this one's pretty easy to do, but I don't know how, so I figured I'd ask.
I have a delay job in my app that calls a roll_back method in my Order model if something hasn't happened within five minutes. That method both destroys the instance it's called on and, before doing so, does a bunch of related things to associated models. The method is working just great.
The trouble is, once the method is done running, most users will be on the show page for the Order that has just been deleted, and I was hoping to redirect users to a different page once the time ran out and that roll_back method was called.
So I originally just tried this in the rollback method, right after the "self.destroy" line.
redirect_to "/orders/new", :notice => "Blah"
Didn't work, though. And at any rate, some poking around on the internet informed me that actions like redirect should be in the controller instead.
So I instead went and put that line in the Orders Controller's destroy method, right after #order.destroy. I figured when the roll_back method called self.destroy, it'd then do the redirect -- but that didn't work either, I'm guessing because the model destroy doesn't go through the controller.
So I'm not really sure how to do this. All the delay job/rollback stuff is in my model, but I'm apparently not supposed / unable to redirect users from the model. Any idea how to do this?
Tried this again, with this in my method, in case it was a routing error (I know orders_path to exist).
def destroy
#order = Order.find(params[:id])
#order.destroy
redirect_to orders_path, notice: "blah"
end

As you've already discovered, you can't respond to no request (in other words, you need a request, if you are to respond). So, what do you do when your displayed information gets stale? StackOverflow uses WebSockets to auto update some pages, including this one. You may want to check out How do real time updates work? for more information. Another technique would be to use a javascript request to make a request to your app, to verify that the order is still valid. You could easily redirect if the result indicates that the order is no longer valid. Anyway, hope this helps - good luck!

Related

Rails redirect or render to show errors

When a user makes an invalid create request for a resource, I want to send them back to the form and show them an error. As far as I can tell, calling render "new" is the standard way to do it.
This seems like a bad idea to me, because the create request has taken the user to /resources, whereas my "new" form is otherwise at /resources/new. If I use render "new", the URL in the address bar will not reflect what the user sees. If they make a GET request there, they'll end up on a different page.
I thought I could solve this problem by using redirect_to new_[resource]_path, but if I do that I lose access to the form data and errors. Surely this is not an uncommon problem. Is there a better way to deal with it?
I have the same problem with edit/update and other form/submit action pairs.
TL;DR:
class ResourcesController < ApplicationController
def new
#resource = Resource.new(resource_params)
if resource_params.present?
#resource.validate
end
end
def create
#resource = Resource.new(resource_params)
if #resource.save
redirect_to #resource, notice: 'Resource has been created'
else
redirect_to new_resource_url(resource: resource_params)
end
end
private
def resource_params
params.fetch(:resource, {}).permit(...)
end
end
I asked this myself as well in my early beginner days, on why Rails scaffold generator generates the def create action to render :new if saving failed, instead of redirecting to the correct URL just something like above, which would confuse the users because their URL would have changed from /resources/new into /resources even though they would still see the exact same Resource form on the page, and therefore they would not be able to reload the page or copy this /resources URL (for example, if they want to share this URL to someone else), because should they share this /resources URL, the others would see a list of Resources on the page instead of what the original user would have expected to see from the copied URL: which is the form page.
After the user submits the form, the URL they see on the address bar should have changed into POST http://localhost:3000/resources instead of just simply http://localhost:3000/resources. The browser hides the HTTP Method being used, that's why this leads to their possible confusion that /resources seems to have been both sometimes: a form page, sometimes a list of resources page. However, speaking of UX, every time anyone enters something or pastes something in the URL address bar of the browser, it is always automatically implied to be doing a GET request. Therefore, it makes sense for the browser-developers to just simply hide the HTTP method (i.e. GET in particular) from the users as to not confuse them.
From my answer above, I only used something like this once before (because there was a certain action that demanded me not to change the URL from the referrer form page). However, I normally render :new instead of redirect_to new_resources_url, simply because:
a redirect_to new_resource_url(resource_params) would take twice as much time and data-transmitted than simply rendering :new. Why? Because you redirect (opening up a new request) with the exact same parameters anyway. Just imagine if your form page is soooo big, and has so many input fields.
and also that there's a limit to how long a URL can be, of which won't guarantee to work if you have a very big form with very long text fields. See this SO
Updated:
As you have said though, why not just POST /resources/new instead of POST /resources when the form is submitted, right? This will solve the redirect_to new_resource_url(resource_params) problem I've shown above, because the URL after form-submit would have been the same and you can just simply render :new, then. And I actually agree on that, and I've used something like this also before long time ago. The main reason I don't use this is that it is not inline with REST standards. That is: POST /resources/new means that you're creating a Resource object "inside" the resources/new location, which then means by REST, after submitting the form, I would and should be able to access this newly created resource by doing something like GET /resources/new/the_newly_created_record, except that... you can't.
But you can still use POST /resources/new, although I would not recommend it on a normal Rails application like yours, and however strictly discourage it on an API-based Rails application.
You may be overthinking this. How about get /resources the index page vs post /resources the create action? Same url in the address bar!
It's not a problem. Neither a technical problem or an aesthetic problem.
render "new" means: render this template, not: go to this route. And although templates often have the name of the corresponding action, it's not a requirement.

redirect and login or login and redirect?

Simple question. What is preferred and why, or does it simply not matter at all?
Sorcery gem example:
if #user.save
redirect_to index_path
auto_login(#user)
OR
if #user.save
auto_login(#user)
redirect_to index_path
Just a skipment of a place, one line doesn't maybe matter that much, but what is better, safer, why or it really like of no importance?
The order doesn't really matter much so long as you are providing valid arguments to your redirect_to call. redirect_to will throw exceptions if you don't provide valid arguments and therefore auto_login won't run, but you won't see errors from redirect_to unless you're doing something pretty wrong like passing in nil options or you've already called redirect_to in the method already.
I tend to prefer having redirect_to come last as it makes sense from a control flow point of view. It gives you a general sense of where Rails will take you after executing all of your code in your controller action and it will also potentially help stop you from calling redirect_to multiple times and causing a DoubleRenderError.
If you look at the redirect_to documentation and click on the "show source" link you can see it's basically only setting variables, so you can really call it wherever you want. redirect_to looks similar to a return statement you'd see in other languages, but it does not act like one, so just keep that in mind. It's just a method that is setting some state on your controller.
I'd log in first and redirect after.
The place you are redirecting to could depend on the logged in user. It might not in your case but it could in other cases, and it could later on in the development of your application.
For example, if the index route is changed so that it goes to a different location depending on whether a user is logged in or not, it matters a great deal whether you log in before or after redirecting. If you log in after redirecting then you'll redirect to the version of the route for users that aren't logged in.

Is the "Rails Way" for `update` fundamentally flawed?

I'm intentionally asking this question in an inflammatory way because I'm concerned that I'm missing something.
The Rails Way for dealing with an update to a model is the following:
class UsersController < ApplicationController
...
def update
#current_user = load_user
if #current_user.update_attributes params[:user]
redirect_to success_path
else
render :edit
end
end
end
This is all well and good except that you end up on an odd URL when the form submission is incorrect:
Editing User
You find yourself on the path:
users/:user_id/edit
After submitting edits that don't validate
i.e. you're going to need to fix the inputs in your form and resubmit:
users/:user_id
After submitting edits that do validate
success_path
Why the hell should you be on a different URL just because the form has errors?
The problem...
You're doing the same thing but you're now on a different URL. This is a bit odd.
In fact frankly it feels wrong. You're on a form which has not validated correctly and so has reloaded. You should still be on /users/:user_id/edit. If you'd done JS validation you would be.
Furthermore if you've got any "currently selected" logic going on in your navigation then you are in fact visually in the wrong place as the correct nav item is no longer highlighted - it looks like you're on the user profile page.
Why the hell should you be on a different URL just because the form
has errors?
Because when you first went to:
users/:user_id/edit
...you were requesting a GET.
Then you POSTed to:
users/:user_id
So by sending the form post, you have requested a different resource route, and have a different URL by definition.
The framework doesn't care what happened in the background while your request was processing - all it knows is it was a POST (which by convention is not necessarily idempotent as a GET is)
Actually it's not the "Rails Way", it's "REST Way". Wikipedia: Representational state transfer
If you follow the rules you get REST-compliant web-service for free. As I understand it the path "resource/id/edit" is specific to html documents. Web-service clients don't need form for editing.
So the guys were trying to be consistent. If you don't need web-service compatibility you can change the routes of course.

RAILS: I've got an action that calls itself, when the form of the action's view is being sent, now I want to rout out of that action

this time it's about Ruby On Rails.
I have a form in the view "progress.html.erb" and an action called "progress". When the form is sent, the action "progress" is called again.
This must be like that, because I'm asking the user several questions by an automatic system.
Then, when the questioning is finished and the user is done with one seminar of questions, I want to route out of "progress" to "finishing" (where session data is erased and a "happy wishes"-site is shown).
But this won't work because of that routing error. There must be a way, even just rendering won't work :(
The complete system is the following:
I have a box with different panels.
In these panels are cards with questions.
All card get asked to the user.
When all panels evaluate to empty, the user is done.
Please help me!
Yours,
Joern.
When you want to redirect, and then stop rendering, you need to not reach the end of control of the function. The way I do this (for redirecting when someone is somewhere they are not supposed to be, usually) is:
redirect_to(:finishing) and return
optionally, you can do this conditionally:
redirect_to(:finishing) and return if #survey.completed?
I'm not sure what routing error you're getting. Your question/problem isn't very clear. However, it sounds like you want to redirect_to(:finishing) at the end of #progress when the user is "done".

redirect_to doesn't work well for RESTful apps?

As a long-time Ruby and Rails user, it never struck me until today to really think about the get-and-redirect pattern in Rails. The typical example of this would be calling a create() action, and then redirecting the user to a show() action to display the newly-created item:
class JournalEntries
def index
#entries = JournalEntry.all
end
def create
#entry = JournalEntry.new( :name => "to-do list" )
#entry.save
redirect_to :action => "index"
end
end
However, this has the inherent disadvantage that you are doubling your network traffic. This both slows down your users' site experience, as well as increasing your bandwidth charges.
So why not just do this instead:
def create
#entry = JournalEntry.new( :name => "to-do list" )
#entry.save
index
Same output, and no extra overhead required. But in addition to this, there is an even more substantial problem: redirect_to can only redirect using GET. This causes major problems for RESTful apps that use four different HTTP methods.
In my case, I wanted a user to be able to call /journals/8 and retrieve the Journal with that ID. If it wasn't found, I wanted to create a new, empty Journal object. In either case, the Journal object would then be sent to the caller.
Note that the create() method in RESTful Rails is routed from "POST /players". But since redirect_to (and the underlying HTTP redirect) can only send GET requests, it actually redirects to "GET /players", which is the index() method. This behavior is clearly wrong.
The only solution I could think of was to simply call create() instead of redirect_to() as in my example above. It seems to work fine.
Any thoughts on why redirect_to is preferred to calling actions directly?
If they do a page refresh they don't get that annoying "Resend data?" popup
It's not just that the popup is annoying (and makes no sense to most users) -- if the user clicks "yes, re-do the POST", he'll end up creating another Journal Entry (or whatever).
Also, it's annoying for the URL to read /posts/create instead of /posts since the user cannot copy / re-use it.
The reason for it is as you point out. You redirect to a GET request, which is correct when it comes to REST (only do updates with POST/PUT, only get data with GET).
A redirect surely gives a little overhead with the redirect, but since no data is actually being sent between the browser and the server except for the POST data and the redirect (which is only sending the new url to the browser) I don't think that the issue of bandwith is of concern.
But on another point, you should not redirect to /journals (by calling redirect_to :index), you should redirect it to the newly created journal entry (by calling redirect_to #entry) which will work if you set up the routes correctly by, for instance map.resources :journals
Update:
I think, for creating the Journal when one doesn't exist, you should ask the user for more input. What is the reason for you to create the entry? The entry should have some text or some other input from the user, so I think from a REST (and rails) perspective you should actually redirect it to the new() method (with a GET request) where the user can input the additional information, that one will then POST the input and create the entry and after redirect to the newly created entry.
If you don't have any extra information that needs to put in, I'm not sure how to do it in a RESTful way, but I would probably have done it by putting the creation logic in a separate method that I would call from the create() and the show() method and then just continue with the show(), not redirecting at all, but also not calling the resource method.
I'm a Python/Django person, but the reasons for the redirect is language agnostic:
If they do a page refresh they don't get that annoying "Resend data?" popup.
This gives you a completely clean, RESTful URL for the page they are looking at. If you used POST it might not matter that much, but if GET was used for the update then you definitely want to get rid of any dangling params.

Resources