Render to named path - ruby-on-rails

I have a rails project with the following route:
get 'login', to: 'user_sessions#new', as: :login
In my UserSessionsController I have
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
# Do all the happy stuff
else
format.html { render :action => 'new' }
format.xml { render xml: #user_session.errors, status: :unprocessable_entity }
end
end
end
That's working ok, except that when the user enters incorrect parameters the route is via /user_sessions instead of /login, which is untidy (and means my test assertions are confusing).
Obviously I could just redirect_to login_path, but then my #user_session.errors don't seem to be available so by page doesn't show what was wrong.
How do I redirect back to /login and still have the errors show?
Edit:
It looks as if Rails makes this difficult because it's something I shouldn't try to do. The RESTful path isn't really something the user cares about so I shouldn't be using it as part of my UI testing. Instead, I am looking at the actual content of the rendered page, which the user does care about. Thanks all.

You can add
post 'login', to: 'user_sessions#create', as: :post_login
and change the form action accordingly.

This is happening because when you get validation errors in your form then you are on create action and not new action. Your create action simply render your new actions template with errors, it doesn't send a request to server and hence your url remains same so to fix it you can simply change the route for your create action to this:
post 'login', to: 'user_sessions#create', as: :login
Update:
You'll just have to change your route for create action and then make changes in your form, something like this:
<%= from_for #resource, url: login_path do |f| %>
// form fields
<% end %>
If you'll inspect your form you'll see that its method is POST so when you'll submit it, your form will send a POST request and when you hit /login in your browsers address bar it'll send a GET request so in first case you'll go to create action and in second one you'll go to new action

Related

Rails routing prob: on failing "create", re-renders the form (as it should) but not at the URL in my routes

RESTful resource, default type routes. Creating an event is supposed to work as follows:
def create
#event = current_user.events.build(params[:event])
if #event.save
redirect_to #event, :flash => { :success => "Event created!" }
else
render :action => "new" # new_event_path
end
end
When invalid data is entered, it does render the "new" view/form again, but it renders this view at the "localhost:3000/events" URL, where the "index" action/view should be on.
My event routes seem like they ought to be pretty predictable:
resources :events
I just updated to Capybara 2, began using DatabaseCleaner, and set transactional_fixtures to false in preparation for testing some JS-enabled functionality but can't think of any other way I might have stuffed this up.
Is there some simple thing I'm missing that could cause a weird routing muck up like this?
Ideas, anyone, on where to start troubleshooting it?
This is the correct behavior. What is happening is that it is using the POST method for that URL when issuing the create action. Using a GET at the URL would be the index action. Also note that rendering a different template does not change the URL (that would require a redirect).
Check out section 2.2 in the Rails Routing documentation:
http://guides.rubyonrails.org/routing.html
When you create event from submitting form you using post method to /events route. And when data becomes invalid rails render events/new for your /events(POST) request at /events address.
But you can
redirect_to action: "new", event: #event.attributes
and add to new action
#item = Item.new(params[:item].except('protected attributes','created_at', 'updated_at', 'id'))

where does the url routes get resolved when you call render in rails controller actions

Is there any method that i should look at in rails3.2 source code so as to know where the navigation or the url part of the render call get resolved?
The reason is, i have a small app in which url is of the form
www.example.com/bob/edit
the above route as it suggests renders the edit form.EDIT: i was able to get to this route by modifying response on the link_to helper.
def update
#when validation passes
redirect_to #user
#when validation fails
respond_to do |format|
format.html {render :action => "edit"}
end
end
Now the problem is when a validation error occurs on submission to update action of users_controller,
the url becomes
www.example.com/users/bob/edit
config/routes.rb
get "users/new", to: => "users#new"
resources :users
as you can see there's nothing interesting happening in routes,
in models/user.rb
def to_param
"#{name}"
end
in views/edit.html.erb
form_for(#user) do |f|
end
Observation: here when the form is rendered afresh, form 'action' points to "users/bob" but when the form is re-rendered 'cos of validation error, form action mysteriosly changes to "users/" which is weired and if i remove the to_param in user.rb model it works fine
Though its not such a big deal, i was thinking where, if i needed to override the url that is generated on render call, to change?????
Any suggestions and pointers to explore are wecome....
I'm not sure how you're getting the URLs you're getting, but a general answer to your question would be it doesn't. The URL you see after sending a request is the URL the request was sent to (or redirected to), not that of the page you came from, nor that of the template you render in the end. In your case, I'm guessing the problem is that you created a custom URL for the edit page, but not for update, and your form_for(#user) is sending the request to your update URL (probably PUT "/users/bob").
To fix this, the first thing is to create your custom update route. Maybe something like:
put ":id/update", to: => "users#update"
And then have your form_for use that URL:
form_for(#user, :url => "#{#user.to_param}/update")

render :new not going to the right place after a validation

I have a 'new' form that gets validated in a post model. When the validator kicks in, it renders incorrectly.
The new post page path is at '/posts/new'
On validation, the new post page path is at '/posts' .. I need it to go back to '/posts/new'.
This is my controller:
def create
#post = current_user.posts.build(params[:post])
if #post.save
redirect_to public_post_page_path(#post.public_url)
else
render :action => :new
end
end
I have a feeling it might have to do with my form. So here is the formtastic first line:
<%= semantic_form_for [:student, post], :html => {:id => "post_form"} do |form| %>
This is the correct behavior from rails.
In the create action it simply renders the "new" view file. As such the url will be /posts but the view will correctly display the form. There is nothing wrong with this behavior; and in general rails convention is good form. Also the built in rails errors work if you just render new; however if you redirect they won't display.
If you really feel like you want to go back to that url you need to use:
redirect_to
instead of render.
If validation fails, user should see the form with the errors and stay at /posts/new. That's what you want, right?
There's a simple way to achieve this.
Set remote: true on the form to prevent the url from advancing. Handle ajax:success to replace the form on the page with the newly rendered one.
$('form[data-remote=true]').on 'ajax:success', (e, data, status, xhr) ->
if isHTML(data)
thisForm = "form[id=#{#getAttribute('id')}]"
$(thisForm).replaceWith $(data).find(thisForm)
isHtml() function is from this question.

Retaining a URL on a failed form submission in Ruby on Rails

I'm currently working my way through various Rails tutorials, but all seem to recommend that a failed form submission should use a render to output the form with the errors. For example, the create method looks like this:
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = "Welcome to the Sample App"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
This has the desired effect, but means that if I have a new user form at http://localhost:3000/users/new and an error occurs, I get the same form with errors at http://localhost:3000/users
Is there a way to make sure I go to http://localhost:3000/users/new instead?
I thought about using a redirect instead of render 'new', but this would cause problems with displaying the form errors. Someone on the Rails IRC directed me to a way of doing clientside validations, but that seems like it dodges the issue.
I had a similar problem with my user signup form. Since I do not use RESTful routes there, I was able to solve it with the following routes:
resources :users, :except => [:new, :create] do
...
end
get "/signup" => "users#new"
post "/signup" => "users#create"
and in the new form:
form_for #user, :url => signup_path do ...
Try redirect_to :action => 'new' instead of just rendering, this will retain the url, but the information about errors will be lost (unless you put it in flash).
The other option is to submit the form to the URL that is used for displaying it. At the beginning of the action you check whether there is some data you need to process. If the processing goes fine, you redirect_to somewhere else, otherwise you do nothing - the form gets displayed, together with all the errors. This, I suppose, is not very Railsy, but it should work as you want.
Sorry for being late (a mere 6 years) to the party but somehow this seems to be an issue with Turbolinks.
See these issues here and here.

How do I maintain the same controller & action in a page URL upon re-rendering an action in Rails?

I am using AuthLogic to authenticate users in my rails app. That part is set up and workign properly.
I have the following route defined:
map.login '/account/login', :controller => :user_sessions, :action => :new
Calling rake routes returns what I expect:
login /account/login {:controller=>"user_sessions", :action=>"new"}
When someone submits a login, it calls UserSessionsController.create:
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Login successful!"
redirect_back_or_default account_url
else
render :action => :new
end
end
If #user_session.save fails, the appropriate error messages appear on the screen. However, the browser URL also changes to "http://localhost:3000/user_session" instead of staying on "http://localhost:3000/account/login".
I assume the problem is what I am feeding to the render method. What should I be feeding it?
This is actually the intended behavior for this process. In a standard scaffolded RESTful controller, a validation error in the create and update actions will simply render the original template without redirecting. This results in what you are seeing – the new template will be displayed with the create action's URL in the URL bar. The reason for this is that in order to display information to the user about what errors occurred, the view must have access to the invalid model object, which is #user_session in your case.
You can use redirect_to instead of render if you want to force a redirect to the original URL, but this will cause you to lose information about the errors. You would need to manually persist the errors in the session, which would be messy. My advice is not to worry about the fact that the URL doesn't match that of the original as this is pretty standard in all Rails apps.
Just adding solution for Rails 4 (based on Shaun's answer here):
Add new route to routes file:
post '/carts/new' => 'carts#create', as: :create_post
Add url: create_post_path to form tag
Done.
After further digging, I found the solution in another StackOverflow question: Use custom route upon model validation failure
I simply modified my routes to add a new one for posing to '/account/login':
map.login '/account/login', :controller => :user_sessions, :action => :new, :conditions => {:method => :get}
map.login_post '/account/login', :controller => :user_sessions, :action => :create, :conditions => {:method => :post}
Then, I updated my view to utilize the new route:
<% form_for #user_session, :url => login_post_path do |f| %>
This works perfectly. A failed login gives the appropriate error messages and maintains the '/account/login' URL.

Resources