Rails render "action" as custom url - ruby-on-rails

In my routes.rb I have the following:
get "contact" => "inquiries#new"
So that when I go to /contact in the browswer, it calls the InquiriesController's new action.
Now when I try to call render "new" in the create action inside InquiriesController:
def create
…
render "new"
end
The resulting url in the browser is /inquiries.
Is there a way besides calling redirect_to to render "new" but have the url as /contact in the browser?

Short answer is No. And here's why:
render is different from redirect_to. When you write redirect_to :action, you are initiating an entirely new browser request. The rails stack is hit, again routes are searched for and the corresponding action is executed. Its exactly the same as entering the url in address bar and pressing enter.
On the other hand, when you use render, you are telling which view to use for the current request. As such, the address in the address bar will generally be of the action in which you are calling render. That's because you put an address and then you tell rails to display a different page in that same request.
In a nutshell, while redirect_to begins an entirely new request cycle, render simply replaces the default view with what you choose in the same request cycle.
So if you want the address bar to change, you will have to initiate a new request to the address you want. It can be by manually entering the address, clicking a link to that address or redirecting to it from rails.
Hope this helps.

The solution is to use custom routes, if you use Restful routing, you can simply add this line to your routes.rb :
resources :inquiries, path: "contact", as: :inquiries, only: [:create]
here you tell rails to change url by default from inquiries to contact when the name of the action is create
if you want other action to match an url which is begin with contact, just add the name of the action to "only", for example : only: [:create, :update ...]
if you want all your actions in that controller (inquiries) to be customized to "contact" just remove only like this :
resources :inquiries, path: "contact", as: :inquiries
and all your routes for inquiries controller will be change from /inquiries to /contact
for more details about how to customizing restful routes please check this link

When using render :new in the create action, it will use the same URL that the form posted to.
Therefore, if you would like to set up both an inquires you can set up your routes like:
get '/contact', 'inquiries#new', as: 'contact'
post '/contact', 'inquiries#create'
You can also use the resources method as medBo references, but I just prefer plain old get and post when I'm doing custom things. Also, these routes can co-exist with your exiting inquiries routes without any ill affects.
Then with those routes sets, you can create your contact from by writing:
<%= form_tag contacts_url do %>
...
<% end %>
The important step here, is that we set up the form to post to `/contact' instead of posting to '/inquiries'.

I think you first need to understand difference between redirect_to & render
For /contact url
change
render "new"
to
redirect_to "/contact"

Related

Rails route pointing to wrong place

In attempts to make my application more clean I have decided to create an imports controller and a views folder along with that instead of creating an import.html.erb view and an import method in my users controller.
My goal is to make the url: http://10.0.0.7/accounts/1/users/import
However, as one might think, this is directing to the show page and is thinking that import is the users id. How can I create the route so that it does not think that the word import is actually the users id?
Parameters shown in error page: {"account_id"=>"1", "id"=>"import"}
In my routes file I have this route which takes care of the
resources :accounts do
resources :users do collection { post :import, :controller => "imports", :action => "users" } end
end
I have also tried this route.
resources :accounts do
post :import, controller: 'imports', action: 'users'
end
There's a few issues I see here.
First, #accounts is nil. This is probably because it's in a partial, and you need to pass in variables into the partial.
<%= render partial: 'admin_sidebar', accounts: #accounts %>
And then use the variable accounts in the partial. Also, why is users in the URL? Your basic routing should have done this just fine.
I'd suggest a route like:
post 'account_users/:id', to: 'imports#users', as 'account_users'
This says - if a post request to URL /account_users/# send to imports controller and use the users method.

Rendering 'new' leads to a different url

When handling validation errors in a User form on my rails project, I have the instruction render 'new' if the user is not valid.
However, when this occurs, the url in the search bar changes.
Originally, it's https://localhost:3000/signup but when the user submit the forms and the render 'new' occurs, the URL becomes https://localhost:3000/users
Why is that?
Here's my routes.rb
Rails.application.routes.draw do
# Resources
resources :users, only: [ :new, :create ]
# Application root
root 'static_pages#home'
# Static pages
get '/help', to: 'static_pages#help'
get '/contact', to: 'static_pages#contact'
# Users
get '/signup', to: 'users#new'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Thanks,
In the Rails REST conventions you use different URI's to display the form to create a resource and for the forms action attribute which you POST to.
Lets say you have:
resources :pets
This gives us:
GET /pets/new => pets#new
POST /pets => pets#create
We then have a typical form:
# posts to /pets
<%= form_for(#pet) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
And a run of the mill controller:
class PetsController < ApplicationController
def new
#pet = Pet.new
end
def create
#pet = Pet.new(pet_params)
if #pet.save
redirect_to #pet
else
render :new
end
end
# ...
end
So when the user visits /pets/new and fills in the form the browser url should read /pets if the form is invalid. This is the intended behavior - posting to a different URL avoids a myriad of cache and history related issues.
From a restful standpoint its also correct - you're no longer viewing the new action - you're viewing the results of attempting to create a resource. The former is idempotent - the latter is not.
You need to recognize that the line render :new is short for render template: 'pets/new'. It does not redirect and it does not call the new action in the controller. It simply renders the template and returns it as the response to the current request.
The reason this is happening is because of the way Rails handles routes. Most likely, the form on your signup page has something like:
<?= form_for #user do |f| ?>
If you look at the generated HTML you'll notice that the form is POSTing to the url '/users'. So when the signup form is submitted, the app is going to redirect you to /users.
This is the normal behavior in Rails. (see Rails Guides)
If you want the URL to look like /signup you can add a named route for it:
# Users
get '/signup', to: 'lead_magnets#new'
post '/signup', to: 'lead_magnets#create'
Then in your signup form you'll need to explicitly reference the new signup path:
<?= form_for #user, url: signup_path do |f| ?>
The generated HTML should look like
<form class="new_user" id="new_user" action="/signup" method="post">
You need to add
post '/signup', to: 'users#new'
Did you try to include recognize path?
Rails.application.routes.recognize_path
The reason for this is because when you submit your form, you're submitting it to some action from your new view. The action that the form submits to has a path, and you can see what the path is by typing:
rake routes
in your console and searching for the create action. When your validation fails in the create action, you call render 'new'.
When you render a template, the url of the page being rendered will be the url / path of the controller action that rendered the template.
In your case, whatever action you have that calls render 'new' will be the one determing the url once your template is rendered.

How does a rails resource know to use the show view?

I'm looking at this tutorial: https://www.railstutorial.org/book/sign_up
We get on to rails resources.
config/routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
resources :users
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
end
end
app/views/users/show.html.erb
<%= #user.name %>, <%= #user.email %>
There is also this handy table of telling us how ruby will handle the various requests to the Users resource.
I have two questions here.
How does RoR know when accessing the /users, /users/1 etc urls, what to actually use the index, show methods.
More importantly, - when the show method is called, how it does it know the give the show.html.erb view to browser? What if I wanted to return a different view?
1. Rails knows which methods to use based on the HTTP request type (GET, POST, PUT, or DELETE) and the endpoint. So when you hit the '/users' endpoint with a GET request, it will use the index method. When you hit the '/users/:id' endpoint with a GET request, it will use the show method.
2. The show.html.erb view is used because the name matches the show method. To use a different view, just use match in the routes file like so:
match "users" => "users#show"
The example above would match the '/users' route to the '/user/:id' route.
The mechanism that is responsible for dispatching actions in response to requests is router. You define all the routes in config/routes.rb file as you said. Each line in this files define the request in clear way. resources is a shorthand to define a set of routes - all of them listed here.
There are two thins that distinct requests - the URL and the HTTP request type (GET, POST, PUT, DELETE). Based on these two things you can clearly direct the request to proper controller. Take a look on this one:
get 'help' => 'static_pages#help'
This means: If you get a request of type get directed to url of value /help dispatch the request to controller static_pages and its action help. What happens in help action is a matter of code.
This is also an answer to your second question, how does Rails know what template should it render. Assume that we still work on above example of help action. If this action has no explicit render call, Rails will use its convention over configuration and search for a template that will be in directory called as the controller and in file name as the action name. Therefore, it will render the file that is in app/views/static_pages/help.html.erb. On the other hand, the developer can call render with explicit other file name, e.g.: render "products/show".
If you want to find more please take a look at these two Rails guides:
Layouts and rendering
Rails Routing
rails is said to have convention over configuaration. this is one of the strongest feature available in Rails.in another way. Smart and less code. Here when u say method name as show. rails by default will look for show.html.erb. if you want to explicitly render some other view. You can do this in your controller methods
render :new_view_name
Regarding the routes.
resources :users
This is very helpful when we have Rest calls ,crud operations.it creates 7 urls for crud. And how it differentiate is depending on http data type.

How to change a rails controller default router from index to other?

As we know, if we have a UsersController, when we get /users it default to /users/index. If I want to map /users to /users/show, what can I do? (This can apply to any other controllers, not just users.)
You can modified the file Global.asax.
There is a method named 'RegisterRoutes',change the 'Index' in 'action=Index' to 'show'
The only difference between URLs for the index action and the show action is the show action requires the identity of the resource. The default Rails URL for index on the users resource is /users, and the default show URL looks like /users/12, where the 12 identifies the resource. This identification part isn't optional, so you can't usefully map /users to the show action in a RESTful scheme.
For example if you want to have a profile page at localhost:3000/username you can add match '/:id' => 'users#show', :as => :user to routes.rb and <%= link_to "Profile", #user %> to your view. With a little fiddling in the controller to grab your profile info via the current user's username, this will link to localhost:3000/username.

custom action in rails 3

I'm trying to make a simple link that will toggle my "status" attribute in my model from "pending" to "active". For example, when I first create a user, I set the status to "pending". Then when I show the list of users, I add a button that should change that user's status to "active". I tried this via a custom action (is this a good approach?) but I'm having trouble with the auto-generated named route.
in my user index.html.haml:
button_to "Manually Activate", activate_user_path
in routes.rb:
resources :users do
get :activate, :on => :member
in users_controller.rb:
def activate
#user = User.find(params[:id])
#user.update_attribute(:status, 'Active')
redirect_to #user
end
this seems to work when I go to say, /users/1/activate, as the status will update. However, the /users page doesn't show and gives me error:
ActionController::RoutingError in Users#index
No route matches {:action=>"activate", :controller=>"users"}
ie, it is having a problem with the activate_user_path I specified in my view. (However if I use another named-routes-style path that I haven't specified in my routes.rb to test it out, I get
NameError in Users#index
undefined local variable or method `blahblah_user_url' for #<#<Class:0x00000102bd5d50>:0x00000102bb9588>
so it seems that it knows it's in the routes.rb but something else is wrong? I'm really new to rails and would appreciate the help!
thanks!
Your link should look like this:
button_to "Manually Activate", activate_user_path(#user)
You need to add what user you want to activate.
A number of problems, I can see.
Firstly you should NOT update the database using a GET request.
Secondly button_to will provide you with an inplace form which when clicked will POST to your app.
Thirdly, the way you have your routes setup, you need to provide the user in the path (you've tested it by forming the url in the browser already).
run
rake routes
on the command prompt to see how your routes look and the name you can use to generate those routes.
I suspect you need to use
button_to "Manually Activate", activate_user_path(user)
(user or #user or whatever is the user object). In your button_to call and change the "get" to "post" in the routes file.
resources :users do
member do
post :activate
end
end

Resources