redirect_to # syntax meaning - ruby-on-rails

What does the code
redirect_to #user
when used in a controller method, mean in Rails?
Where exactly does it redirect to? Does it redirect to a Users controller (If so, which method of the controller), or does it not go through the controller and instead go directly to a view?

Basically it looks up a bunch of stuff about how your resource routes work
think of it like this
send("#{#user.class.name.underscore.downcase}_path", #user)
this is probably not the exact code but it should help you visualize whats actually going on.
The controller always runs, in this case it would be the show action of your users controller unless you have some funky router options.
rake routes explains how the routes are laid out in this case show is
get /users/:id => users#show :as => "user_path"
the substitution from your incoming model works like this
a regex is created from your route
:id being a param
would match to inbound_object to the path function which is #user
:id is replaced with #user.id

From the redirect_to docs:
Redirects the browser to the target specified in options. This parameter can take one of three forms:
...
Record - The URL will be generated by calling url_for with the options, which will reference a named URL for that record.
And from the url_for docs:
Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will trigger the named
route for that record. The lookup will happen on the name of the
class. So passing a Workshop object will attempt to use the
workshop_path route. If you have a nested route, such as
admin_workshop_path you’ll have to call that explicitly (it’s
impossible for url_for to guess that route).

This is the equivalent of writing
redirect_to user_path(#user)
More Rails magic at work here, for better or worse.

it calls the show function of users like this
redirect_to user_path(#user)
important point : if we see the routes then :id is passed but here object is getting passed
which get converts into the id of the user by
def to_param
self.id
end
which is available in model and we can overwrite it to
so basically to conversion take place first
redirect_to #user => redirect_to user_path(#user)
then
#user => #user.id

Related

why does redirecting with an instance variable take you to a show view(Log-in)

How does the following piece of code redirect_to #userin my create action know how to redirect to my show view?
I don't get how it would possibly know this. I know this is a newb question but I just can't understand why #user works. It would make sense if it was something like redirect 'show' but why is #user used?
Here is my controller code:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
redirect_to #user
is the short hand for:
redirect_to user_path(#user)
You can either use the long-hand or the short-hand. They will both work the same way.
by convention, redirect_to #user maps to:
GET /users/:id(.:format) users#show
That's how it knows to redirect you to the show page.
The show action is the only one (to my knowledge) that has a short-cut.
If you do rake routes, you'll see the complete map:
user_path GET /users/:id(.:format) users#show
If you run rake routes, you will able to see this route which is referring to users#show
GET /users/:id users#show
In your case redirect_to #user which is equivalent of
redirect_to :action => 'show', :id => #user which matches the /users/:id convention
OR
It can also written as redirect_to user_path(#user) which also returns /users/:id
So,the controller just ends up redirecting to the show page.
These Guides will help you understand in detail.
Hope it helps!
Infact in syntax
redirect_to #user
#user variable here gives you the id of that user and if you see your routes (using rake routes) you would see a route users/:id which exactly maps to users#show. So that why it goes to show method or users controller.
You can change this functionality if you want to. There are two approaches to it, a good one and a bad one.
First approach is:
Change you routes which maps users/:id to some other action of your controller
Second bad approach is to redirect to some other path within show action of users controller.
Hope this explanation is enough for your understanding
When you redirect_to an object in Rails (which is what you're doing), the framework has a lot of built-in functionality to take care of things for you
One of these in-built features is the ability to detect & redirect to the object itself
Objects
When you pass #user, you are passing a singular object, which Rails takes as meaning that you want to go to the show view (you want to view the object, right?)
You need to remember that Ruby / Rails is Object Orientated. You're calling them #instance variables, but really they are objects. Those objects contain a lot more data than your standard data - they contain information from the model & db
This means that if you build your #instance variable in the conventional Rails way (using Model data), you're going to get an object
--
Login
It seems to me that you're redirecting to the #user object. However, I would imagine this will be covnered by your authentication system, hence why you're receiving the login page
To fix this, you should try redirecting to specific paths, like this:
redirect_to users_path
Everyone has pointed out that controller interprets
redirect_to #user or redirect_to user_path(#user)
as
GET /users/:id(.:format) users#show
Question arises how Rails comes to know that out of so many routes it needs to go on to users#show
As you can see in above image that 3 paths are associated with
user_path & it requires id which you have passed using #user
So now controller ask which HTTP method GET/PATCH/PUT/DELETE
Since we have not specified any method it will automatically consider GET method & result is
users#show

Rails form action and routes

I have a controller action that requires parameters, and has to be accessed both through get and post.
with get it looks like:
get 'cart/add:id,:qty' => 'cart#add', as: :addToCart
the controller action is:
def add
product = Product.find(params[:id])
if ::AddToCart.new(#cart, product, params[:qty]).execute
flash[:succes] = "Product(s) added."
else
flash[:failure] = "Product cannot be added."
end
redirect_to request.referer
end
how should the post route look like if i would like it to use the same action.
PS: i use the get version as a link_to when only one product is added, the post route is needed because the quantity will be unknown
You should make this strictly a post route. If you want, you can default the quantity to one when it isn't provided. Or, use ajax to post to the route with a quantity of one when they click the "add one" link. That way it can look and seem to behave like a regular link_to sort of link, but you'll still have the benefits of only updating data via POST and leaving all your actual GET requests idempotent.

Rails: Route to custom controller action

I have a really hard time understanding routes and I hope someone can help me.
Here's my custom controller
class SettingsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = current_user
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
and I have the file settings/edit.html.erb and my link
<li><%= link_to('Settings', edit_settings_path) %></li>
The route
get "settings/edit"
doesn't work for this, because then I get
undefined local variable or method `edit_settings_path' for #<#<Class:0x00000001814ad8>:0x00000002b40a80>
What route do I have to give this? I can't figure it out. If I put "/settings/edit" instead of a path it messes up as soon as I'm on a other resource page because the resource name is put BEFORE settings/edit
Thx
Following should do:
get 'settings/edit' => 'settings#edit', :as => :edit_settings
# you can change put to post as you see fit
put 'settings/edit' => 'settings#update'
If you use /settings/edit directly in link, you shouldn't have problem with other resource name being prepended in path. However, without the leading slash, i.e. settings/edit it might have that issue.
Reason why edit_settings_path is not working might be because you didn't declare a named route. You have to use :as option to define by which method you will be generating this path/url.
If you want to explicitly define the route, you would use something like
get 'settings/edit' => 'settings#edit', :as => edit_settings
This statement defines that when a GET request is received for setting/edit, call the SettingsController#edit method, and that views can reference this link using 'edit_settings_path'.
Take some time to read the Rails guide on routing. It explains routing better than any other reference out there.
Also keep in mind the rake routes task, that lists the details of all the routes defined in your application.

Rails: create from two pages

I'm very new to Rails, so maybe I'm just missing the "Rails way" of doing this, but I have a model, call it Post. I can create a post from the canonical posts/new page but also from another page, say home/index.
In home/index i have a form_for #post (slightly different from the one in posts/new, but say that i can use a partial). The problem is that in the PostController.create I cannot pass the newly created #post object back to home/index (in case of errors) because:
if I don't specify a page to render, it will automatically render posts/new
i don't know the calling page in order to redirect it to the right calling page (posts/new or home/index)
even if i knew it (hacking the request referrer or using redirect_to :back), redirect_to doesn't pass objects back, so that #post is empty when called from home/index
Any help? thanks
EDIT
Maybe a possible solution would be to get the calling controller / action from the request and render it back. Any way to do this?
In theory, you could achieve what you're trying to do by checking the referer:
def create
#post = Post.new
if #post.update_attributes(params[:post])
# redirect as appropriate
else
render :action => case request.referer
when new_post_path then "posts/new"
when "/" then "home/index" # assuming that home/index is the root of the site
end
end
end
To get the referrer page, you can make a hidden field with the name redirect. You can use it in the controller.
redirect_to params[:redirect] || posts_path
Have you tried that you pass the post's id in the query string to the home/index
eg: /home/index?post_id=42
You can find out who called your page by looking at
request.referrer
I don't know if this is the "rails way" but here's my solution.
You can add a route for
match home/index/(:id) => "home#index"
and redirect to this after creating the Post. Then in your Home controllers index action just do a
#Post = Post.find(params[:index]) if params[:index]
Your view should display the post if #Post exists
I like this approach because it keeps all the logic where it should be. Routing logic in the controller and view logic in the views.

Rails redirect_to post method?

redirect_to :controller=>'groups',:action=>'invite'
but I got error because redirect_to send GET method I want to change this method to 'POST' there is no :method option in redirect_to what will I do ? Can I do this without redirect_to.
Edit:
I have this in groups/invite.html.erb
<%= link_to "Send invite", group_members_path(:group_member=>{:user_id=>friendship.friend.id, :group_id=>#group.id,:sender_id=>current_user.id,:status=>"requested"}), :method => :post %>
This link call create action in group_members controller,and after create action performed I want to show groups/invite.html.erb with group_id(I mean after click 'send invite' group_members will be created and then the current page will be shown) like this:
redirect_to :controller=>'groups',:action=>'invite',:group_id=>#group_member.group_id
After redirect_to request this with GET method, it calls show action in group and take invite as id and give this error
Couldn't find Group with ID=invite
My invite action in group
def invite
#friendships = current_user.friendships.find(:all,:conditions=>"status='accepted'")
#requested_friendships=current_user.requested_friendships.find(:all,:conditions=>"status='accepted'")
#group=Group.find(params[:group_id])
end
The solution is I have to redirect this with POST method but I couldn't find a way.
Ugly solution: I solved this problem which I don't prefer. I still wait if you have solution in fair way.
My solution is add route for invite to get rid of 'Couldn't find Group with ID=invite' error.
in routes.rb
map.connect "/invite",:controller=>'groups',:action=>'invite'
in create action
redirect_to "/invite?group_id=#{#group_member.group_id}"
I call this solution in may language 'amele yontemi' in english 'manual worker method' (I think).
The answer is that you cannot do a POST using a redirect_to.
This is because what redirect_to does is just send an HTTP 30x redirect header to the browser which in turn GETs the destination URL, and browsers do only GETs on redirects
It sounds like you are getting tripped up by how Rails routing works. This code:
redirect_to :controller=>'groups',:action=>'invite',:group_id=>#group_member.group_id
creates a URL that looks something like /groups/invite?group_id=1.
Without the mapping in your routes.rb, the Rails router maps this to the show action, not invite. The invite part of the URL is mapped to params[:id] and when it tries to find that record in the database, it fails and you get the message you found.
If you are using RESTful routes, you already have a map.resources line that looks like this:
map.resources :groups
You need to add a custom action for invite:
map.resources :groups, :member => { :invite => :get }
Then change your reference to params[:group_id] in the #invite method to use just params[:id].
I found a semi-workaround that I needed to make this happen in Rails 3. I made a route that would call the method in that controller that requires a post call. A line in "route.rb", such as:
match '/create', :to => "content#create"
It's probably ugly but desperate times call for desperate measures. Just thought I'd share.
The idea is to make a 'redirect' while under the hood you generate a form with method :post.
I was facing the same problem and extracted the solution into the gem repost, so it is doing all that work for you, so no need to create a separate view with the form, just use the provided by gem function redirect_post() on your controller.
class MyController < ActionController::Base
...
def some_action
redirect_post('url', params: {}, options: {})
end
...
end
Should be available on rubygems.

Resources