change routes URL from a random :id to :username in rails - ruby-on-rails

I want to change my routes.rb file in a way so that it changes my current urls
localhost:3000/amitian/1
to localhost:3000/username
I will provide my routes.rb file for reference
Rails.application.routes.draw do
devise_for :amitians
root 'home#index'
resources :amitians do
member do
get :following, :followers
end
end
resources :confessions do
member do
get 'like' , to: 'confessions#upvote'
get 'dislike' , to: 'confessions#downvote'
end
resources :confessioncomments
end
resources :relationships, only: [:create, :destroy]
end

As Gregg mentioned, you can change the name of the parameter using:
resources :amitians, param: :username
But you're essentially just renaming a variable. Whether you expect an id or a username is determined in the controller action amitians#show:
#amitian = Amitian.find(param[:id]) # Treat :id as id
#amitian = Amitian.find_by_username(param[:id]) # Treat :id as username
Now, if you want to specifically route to /:username rather than /amitians/:username, you'll have to override that resource route:
resources :amitians, except: [:show] do
member do
get :following, :followers
end
end
get '/:username', to: 'amitians#show'
However, I would recommend against that. Having a parameter directly off root will cause lots of confusion for you when users type in the incorrect url and get a user page instead of a 404 error. Or even worse, what if a user chooses the username 'login' or 'register'? Either your register page would be unreachable or else that user's page would be.
I should also point out that rails convenience methods such as resources, Amitian.find, url_for #amitian, link_to #amitian etc. all use the REST standard which uses numerical IDs.
If you want to use a username instead of IDs, you'll have to stop relying on these methods and change your controllers and views, in addition to your routes file.

In rails 4 you can do the following in the route
resources :amitians, param: :username

Related

Rails Routing with Enum User Roles

I created a users table and implemented Devise. I then added an enum role to the user table so that I can identify admins, guides, and members.
What I'd like to do is limit routes for certain resources based on the enum user type. I've tried:
resources :users.guide do
get 'guide/dashboard', :to => 'guides#dashboard'
end
and some variants, but with no success. The above gives me the error:
undefined method `guide' for :users:Symbol
I've done some poking around and can't seem to find a good response. I'm avoiding CanCanCan and Rolify as I'd like to keep things as basic as possible. Any ideas? Thanks!
Edited Per Below Suggestion
So I went and updated my routes as suggested below so that the file looks similar to this:
Rails.application.routes.draw do
devise_for :users
#open to public
root 'welcome#index'
resources :guides, only: [:show], param: :username
resources :itineraries, only: [:index] #, only: [:index, :show]
authenticate :user, ->(u) { u.guide? } do
resources :guides, only: [:edit, :destroy], param: :username
get 'guide/dashboard', :to => 'guides#dashboard'
end
resources :locations, only: [:new, :create, :edit, :update, :destroy, :index, :show]
end
For whatever reason, the guide profile show/edit works fine; the dashboard works fine; but the other things outside of the authenticate block (itineraries and locations resources) don't work, I get redirected to the Devise login page. They are outside (and in one case above) the authenticate block, not sure why this is occurring with some resources/routes and not others.
You can use devise helpers. With authenticate, it is possible to make resources and routes that will ask for authentication before they can be accessed. Replacing authenticate with authenticated causes the resource to simply be unavailable if the user is not authenticated. Both helpers take an optional scope and block to provide constraints on the model instance itself.
authenticate :user, ->(u) { u.guide? } do
get 'guide/dashboard', :to => 'guides#dashboard'
end
So your problem is limit routes for certain resources based on the enum user type. I suggest you should implement the authorization stuff instead of trying to create more resources/scopes in your route, it's correctness.

Rails multiple routes, same `as`

I have the following routes:
authenticated :user do
scope module: 'admin', path: ':publisher_id' do
get 'settings/general', to: 'publishers#index', as: 'publisher_settings'
post 'settings/general', to: 'publishers#create'
put 'settings/general', to: 'publishers#update'
end
end
However the first as: can only be used once. How can I make this apply to all these routes? I want all of them to work for the publisher_settings_path
You can also pass the as option to namespaces and scopes in Rails Routes.
# prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+
scope as: "sekret" do
resources :posts
end
Source: https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Routing/Mapper/Scoping.html
This should also work though i think
Rails.application.routes.draw do
resources :publishers, only: [:create, :update, :index], path: 'settings/general', as: 'publisher_settings'
end
Get's me
publisher_settings GET /settings/general(.:format) publishers#index
POST /settings/general(.:format) publishers#create
publisher_setting PATCH /settings/general/:id(.:format) publishers#update
PUT /settings/general/:id(.:format) publishers#update
publisher_settings_path already returns the path of of the three routes, e.g. /admin/settings/general. So you don't need to set as: multiple times.
And actually setting the same as: multiple times for multiple routes would not make sense, it has to return one value.

Ruby on Rails - Path variable without using resources route

I have a controller, let's just call it FruitsController, that grabs all of the fruit and sends it to the index view. In the view, I want to show links to the individual pages for those fruits. I'm using the format:
<% #fruits.each do |fruit| %>
<%= link_to fruit.name, fruit_path(fruit) %>
<% end %>
And this works great when I have the route resources :fruits, but I don't want routes for deleting, saving, and updating, so I don't want to use resources. But when I just do individual routes for showing all and individual fruits, I get the error fruit_path function is not defined, and when I use the function fruits_path it works but it just appends a period to the path like /fruits.1. How can I use the fruit_path function without using resources? Thanks.
There are a number of ways you could do this; in your config/routes.rb, any of the following should work:
resources :fruits, only: :show
resources :fruits, except: [:index, :edit, :destroy, :update] # etc
get 'fruits/:id', to: 'fruits#show', as: :fruit
scope controller: :fruits do
get 'fruits/:id' => :show, as: :fruit
end
You're not limited to just resources, you can customize and create your own routes, for example:
get '/:username/photos', to: 'users#show', as: 'collage'
to: means controller/action, in this case users is the controller and show is the action.
as: creates a path for you 'collage_path'
you can find good info regarding routing =>http://guides.rubyonrails.org/routing.html

how to fill twice id from routes rails

i try to fill twice id in url, but when i send params twice id just one id fill the url id.
My route :
namespace :admin do
resources :stores
get "/:id/new_items"=> 'stores#new_items', as: :store_new_items
post "/:id/create_items"=> 'stores#create_items', as: :store_create_items
get "/:id/show_items/:id"=> 'stores#show_items', as: :store_show_items
get "/:id/items/:id/new_items_sub" => 'stores#new_items_sub', as: :store_new_items_sub
post "/:id/items/:id/create_items_sub" => 'stores#create_items_sub', as: :store_create_items_sub
get "/:id/items/:id/show_items_sub/:id" => 'stores#show_items_sub', as: :store_show_items_sub
end
my view :
<%= link_to "add new items", admin_store_new_items_sub_path(#store.id, #items.id), :class=> "btn" %>
i hope my url like this :
http://localhost:3000/admin/#{store.id}/items/#{items.id}/new_items_sub
but i get same id like this :
http://localhost:3000/admin/#{store.id}/items/#{store.id}/new_items_sub
please tell me when i'm wrong? thanks
you have to create neseted routes for that .have a look at
http://guides.rubyonrails.org/routing.html#nested-resources
for example
resources :publishers do
resources :magazines do
resources :photos
end
end
will accept routes /publishers/1/magazines/2/photos/3
Your params should be unique, so you can't pass more than one different :id params. Instead. you can do something like:
get '/:store_id/show_items/:id', as: :store_show_items
and in view:
<%= link_to 'show items', store_show_items_path(#store.id, #item.id) %>
Also, you should read more about Resources and Nested Resources in Rails, there's probably no need to complicate your life by creating each route independently.
You could refactor this to use nested routes like this (you may have to change controller method names):
namespace :admin do
resources :stores do
resources :items, :only => [:new, :create, :show] do
resources :subs, :only => [:new, :create, :show]
end
end
end
This would give you a few url helpers like this: new_store_item_sub_path(#store.id, #item.id) for the new action and store_item_sub_path(#store.id, #item.id, #sub.id) for the show action.
Run rake routes to see what helpers and routes you have access to.
Have a look here to find out more about nested routes.
Your code can be DRYed up significantly. Hopefully this works; might need some tweaking:
namespace :admin do
resources :stores do
member do
get :new_items, as: :store_new_items
post :create_items, as: :store_create_items
end
get "show_items/:id"=> 'stores#show_items', as: :store_show_items
resources :items do
get :new_items_stub => 'stores#new_items_sub', as: :store_new_items_sub
post :create_items_stub => 'stores#create_items_sub', as: :store_create_items_sub
get "show_items_sub/:id" => 'stores#show_items_sub', as: :store_show_items_sub
end
end
end
Uses Member Routes (see 2.10) & Nested Resources
Nested Resources
The crux of your issue is that you're trying to pass the :id param twice
Fortunately, Rails has a solution to this, in the form of Nested Resources. These work by taking the "parent" id and prepending a singular prefix, such as :store_id, allowing you to use the :id param for another set of methods

How do I rename a resources routes?

How do I rename some of these routes...for example, below i want to use signup_path in my controllers instead of signup_sessions_path...
resources :sessions, only: [] do
collection do
post :signup, :as => :signup
post :login
delete :logout
end
end
Try not to nest the routes under resources :sessions but rather use the to: option like so:
post :signup, to: 'sessions#signup', as: :signup, on: :collection
Not too sure about your collection there but I'm sure you get the gist of it
Update
According to your comment, as of today, I don't know of any way to remove the nested route resource name from the path name of a nested resource route. In other words, whatever is nested is purposely to use the scope of the resource and therefore there are no options to revert that behaviour other than taking it out of the resource's block.

Resources