How to display a friendly name in URL in rails - ruby-on-rails

This is my new resource in routes file :
resources :staticpage do
collection do
get :aboutus
get :snacks
get :contactus
end
end
this generates my urls localhost:3000/staticpage/aboutus localhost:3000/staticpage/snacks/ localhost:3000/staticpage/contactus
Instead of this complex url i want to have localhost:3000/aboutus , localhost:3000/snacks/ localhost:3000/contactus in the URL.
using gem can change my action name, but i want to alter "controllername/action" to a simple string.
What modification should be done in the routes path declaration ?

You can simply write matchers for each route like this:
get 'aboutus', to: 'staticpage#aboutus'
get 'snacks', to: 'staticpage#snacks'
get 'contactus', to: 'staticpage#contactus'
Note that you don't need the resources or collection block if you do this.

As per the routes documentation, you will need to define the routes manually (like David Underwood's answer). However, there is an even better way to do it:
#config/routes.rb
static = %w(aboutus snacks contactus)
static.each do |page|
get page.to_sym, to: "staticpage#show", page: page
end
#app/controllers/staticpages_controller.rb
def show
page = params[:page]
render "staticpages#{page}"
end

Related

Rails 6 - change params for root_path in URL to SEO friendly URL

I know how to change routes when I'm calling some controller which name is in URL, but do not have idea what do with this:
Will_paginate:
http://localhost:3000/?page=4
and I want
http://localhost:3000/page/4
Route:
root to: 'pages#home_logged_in'
I'm not sure if will_paginate has the functionality to change the way it structures the links so you may need to calculate and display the page links manually in your views.
In the routes, you can setup a standard non-paginated route to handle the initial request and then a paginated route to handle individual pages.
get '/pages/:page', to: 'pages#index' # will pass params[:page]
root to: 'pages#index'
Then in the pages#index you just check for and apply the page param if it exists:
def index
page = params[:page] || 1
#items_being_paginated = ItemModel.paginate(page)
end
I've not used the will_paginate gem personally so the gem specific code is taken from their wiki.
Depending on what you're using this for -- if its just paginating items, for example -- the above should work for you. If you're trying to get SEO quality links for individual items you can using dynamic routing, like this:
get '*path', to: 'pages#show', as: 'pages_show'
or parameterized
get ':category/:sub_category', to: 'pages#index', as: 'sub_pages_index'
get ':category/:sub_category/pages/:page', to: 'pages#index', as: 'sub_paginated_pages_index'
get ':category/:sub_category/:item', to: 'pages#show', as: 'pages_show'
In the controller you just parse the route with the first, or use the params with the second.
This part of the Rails Routing Guide might interest you if you want more info.

route is redirecting to wrong path

This is what my routes currently look like:
which gives
On my homepage I have a create vacancy button
<%= link_to "plaats", new_employer_vacancy_path(:employer_id)%>
Which should be linked to the line from the first image
get '/employers/:employer_id/vacancies/new', to: 'vacancies#new', as: 'new_employer_vacancy'
In the vacancies_controller#new - create I have:
def new
#vacancy = Vacancy.new
#employervacancy = Employervacancy.new
end
def create
#vacancy = Vacancy.create(vacancy_params)
createEmployervacancy
redirect_to employer_vacancy_path(current_employer, #vacancy)
end
def createEmployervacancy
#employer = current_employer
Employervacancy.create(vacancy_id: #vacancy.id, employer_id: #employer.id)
end
But whenever I click the button I get redirected to some other method in my vacancies_controller that is totally irrelevant.
How is this even possible? Don't I clearly define that when that path is clicked he should go to vacancies#new? and not to vacancies#show_specific_employer_vacancies?
EDIT
After following the answers I am indeed being linked to the correct route.
First, it gave me this error.
After trying to pass the current_employer.id instead of #employer like suggested I got following error:
For your routes, you'd better to change into nested route for easily maintaining routes.
Remove these codes:
get '/employers/:employer_id/vacancies/:id', to:"vacancies#show_specific_employer_vacancies", as: "employer_vacancy"
get '/employers/:employer_id/vacancies/edit/:id' ...
get '/employers/:employer_id/vacancies/index' ...
get '/employers/:employer_id/vacancies/new' ...
path '/employers/:employer_id/vacancies/:id' ...
change into:
resources :employers do
resources :vacancies
end
Try to use basic routes here because you use standard simple form url. For example:
<%= simple_form_for(#employee, #vacancy) %>
The simple_form_for will generate url well if you use nested routes above.
Finally, in your link you have to add #employer_id
<%= link_to "plaats", new_employer_vacancy_path(:employer_id => #employer_id)%>
I hope this help you
Your router cannot tell the difference between your employer_vacancy and new_emplyer_vacancy routes because the :id parameter accepts anything. Because of this, when you point your browser to "/employers/5/vacancies/new", the route is taking your employer_vacancy route and assigning {:employer_id => 5, :id => "new"} instead of going to your new_employer_vacancy route (because routes are first-come-first-serve).
To correct this, add a constraint to your first route to ensure that only numbers (and not the string "new") is accepted into the employer_vacancy route:
get '/employers/:employer_id/vacancies/:id',
to: 'vacancies#show_specific_emplyer_vacancies',
as: 'employer_vacancy',
constraints: { id: /\d+/ } # <- This line
As Wes Foster said rails router is trying to find a first match.
It means that given a path /employers/999/vacancies/new your router looks through the routes and when it sees get '/employers/:employer_id/vacancies/:id he thinks that this route matches. So :employer_id is 999 and :id is new.
I'd suggest to put the route with :id at the end of employers routes:
...
get '/employers/:employer_id/vacancies/new'
...
get '/employers/:employer_id/vacancies/:id'
Btw this is better than adding a constraint because:
It is easier
It doesn't pollute routes file
Later you may want to change ids to be hashed or alphabetic and then you'd have to change the constraint

Including attributes in custom Rails routes

I hope the title is not to misleading, as I don't know a better title for the problem I'm working on:
I have a doctor which belongs to location and specialty. I'd like to route to show action of the doc controller like this:
/dentist/berlin/7
I defined my routes like this:
get ':specialty/:location/:id', to: 'docs#show'
And in my views create the following url to link to the show action of the doc controller:
<%= link_to doc.name, "#{doc.specialty.name}/#{doc.location.name}/#{doc.id}" %>
Is this a good solution to the problem? If not, is there a cleaner way to construct urls like this possibly using resources? What the heck is the name for a this problem?
Thank your very much for your help in advance.
For references, you should have a look at this page (especially the end of section 2.6)
If it is only for a single route, it's okay as you did. But then if you want to have more than one route (like /dentist/berlin/7, /dentist/berlin/7/make_appointment, etc.) you might want to structure a bit more your routes so as to take advantage of rails resources.
For example, instead of
get ':specialty/:location/:id', to: 'doctors#show'
get ':specialty/:location/:id/appointment', to: 'doctors#new_appointment'
post ':specialty/:location/:id/appointment', to: 'doctors#post_appointment'
You could have something like this (the code is almost equivalent, see explanation below)
resources :doctors, path: '/:specialty/:location', only: [:show] do
member do
get 'new_appointment'
post 'create_appointment'
end
end
Explanation
resources will generate the RESTful routes (index, show, edit, new, create, destroy) for the specified controller (doctors_controller I assume)
The 'only' means you don't want to add all the RESTful routes, just the ones specified
Then you want to add member actions, ie. actions that can be executed on a particular item of the collection. You can chose different syntaxes
resources :doctors do
member do
# Everything here will have the prefix /:id so the action applies to a particular item
end
end
# OR
resources :doctors do
get 'new_appointement', on: :member
end
By default, the controller action is the same as the path name you give, but you can also override it
member do
get 'appointment', action: 'new_appointment'
post 'appointment', action: 'post_appointment'
end
Rails has some wonderful helpers when it comes to routing !
The correct approach is to give your route a name, like this:
get ':specialty/:location/:id', to: 'docs#show', as: 'docs_show'
Then you can use it like this:
<%= link_to doc.name, docs_show_path(doc.specialty.name, doc.location.name, doc.id) %>
Note 1:
Rails appends _path at the end of the route names you define.
Note 2:
You can see all the available named routes by executing rake routes.

Rails: in controller, a new view is always rendered by `show`?

I want to implement a search function in the controller, which contains "show, new, create, etc..."
I added in route.rb:
get 'apps/search' => 'apps#search'
and in apps_controller.rb:
def show
#app_info = App.find(params[:id])
end
def search
# get parameter and do search function
end
but each time when i request the /apps/search?xxx=xxx then it will be rendered by show... and then search?xxx=xxx is the parameter for method show...
should I rather create a new controller for search? Or is it possible to implement search as my requirements?
Your routes are incorrectly prioritized - somewhere else in your routes file (before the get 'apps/search' line) you have resources :apps, which defines a route that matches the same regex as apps/search.
Routes match from top to bottom, so if you check the output of rake routes, you'll see that your request to apps/search is actually matching apps/:id - which is the show route of your apps resource.
Either move the apps/search route above the resources :apps declaration, or alternatively declare your search route as part of the apps resource, eg.
resources :apps do
get :search, on: :collection
end
(this will define apps/search in the way you want).
For more information on routing: http://guides.rubyonrails.org/routing.html
I think you should edit route.rb as the following:
get 'apps/search' => 'apps#show'
The Rails' way to "say" search is a new route to the apps controller is using collection. So, for example, supposing you already have a resources :apps, you can do:
resources :apps do
get 'search', on: :collection #or you can use another HTTP verb instead of get
end
And that would give you:
search_apps GET /apps/search(.:format) apps#search

Removing controller name from Rails URL route

This is my first Rails project, I am trying to piece things together slowly.
When I'm trying to view the page I generated using rails g controller <controller> <page>, I find myself going to 0.0.0.0:3000/controller/page.html, How can I configure it so that my route file globally allows viewing the page via the page name, rather than controller/page, if no such way exists, then how can I route controller/page.html to /page.html
I've looked around, and haven't really found any explanation, maybe I'm looking in the wrong places?
In config/routes.rb:
get '/page' => 'controller#action'
If your controller is:
class UsersController < ApplicationController
def something
end
end
Then config/routes.rb would be:
get '/page' => 'users#something'
For static pages you could want to use public folder though, everything you put there is directly accessible, for example public/qqqqqq.html would be accessed in localhost:3000/qqqqqq.html
We've just achieved this by using the path argument in resources method:
#config/routes.rb
resources :controller, path: ""
For you specifically, you'll want to make something like this:
#config/routes.rb
resources :static_pages, path: "", only: [:index]
get :page
get :other_page
end
#app/controllers/your_controller.rb
def page
end
def other_page
end
This will give you routes without the controller name. You'll have to define this at the end of your routes (so other paths come first)
Obviously this will form part of a wider routes file, so if it doesn't work straight up, we can refactor!
It sounds like this is a static page, so you can do as juanpastas says, or another option is to create a folder under your app/views directory to hold these pages. Maybe something like
app/views/static_pages/the_page.html.erb
Then in your config/routes.rb you can add:
match '/your_page_name', to: 'static_pages#the_page', via: :get

Resources