I have the following router:
scope ':name' do # Category :name
get :animators, controller: 'categories'
get :creators, controller: 'categories'
resources :items, only: [:show]
end
it generates the following URLs:
http://localhost:3000/birthday/ # index page
http://localhost:3000/birthday/item/123 # resource show page
However what I would like to do is make my second URL look like this
http://localhost:3000/birthday/animator/123 # resource show page
On my Item model Animator is a :type
If I do it with inner scope
scope ':type' do
resources :items, only: [:show]
end
I will get
http://localhost:3000/birthday/animator/item/123
But I would like to get rid of item, plus it makes me indicate an additional parameter when using link_to in the view, which is not good.
scope ':name' do
get :animators, controller: 'categories'
get :creators, controller: 'categories'
resources :items, only: [:show]
get '/animator/:id' => 'animator#show'
end
resources :items, only: [:show] will only catch routes like /birthday/items/1, /birthday/items/2 etc. While get '/animator/:id' => 'animator#show will be able to catch the routes like you have mentioned in your question:
http://localhost:3000/birthday/animator/123
I've added
get ':type/:slug', to: 'items#show', as: :item
to the scope block. It doesn't look pretty IMO, but I will try to fix that eventually.
Related
I have the following route:
resources :trees, shallow: true, controller: 'base', param: :ed_node_id, only: %i[index show] do
resources :courses, shallow: true, controller: 'base', param: :ed_node_id, only: %i[index show]
end
Which gives me:
api_v1_tree_courses GET /api/v1/trees/:tree_ed_node_id/courses(.:format)
api_v1_course GET /api/v1/courses/:ed_node_id(.:format)
api_v1_trees GET /api/v1/trees(.:format)
api_v1_tree GET /api/v1/trees/:ed_node_id(.:format)
What i’m trying to avoid is on that first route, the param not being prefixed by :tree. Is there a way in nested routes to force the param not to have a parent route prefix or possibly using a regex to give the desired route?
Unfortunately Rails just generates the param key for nested resources by combining the singular form of the name with the param option:
module ActionDispatch
module Routing
module Mapper
class Resource
...
def nested_param
:"#{singular}_#{param}"
end
end
end
end
end
There is no option to actually alter the entire nested param - it would make a good feature request though.
The alternative is to use scope:
resources :trees, only: :index
scope '/trees/:ed_node_id' do
resources :courses,
only: :index,
as: :tree_courses
end
# shallow nesting won't work so we have to define this separately.
resources :courses, only: [:show]
im currently trying to use the resource but one problem im having , that when i do the following
resource :orders
the route /orders dosent route to OrdersController#index rather it points to the show action of the controller, how can i fix this issue ?
becuase of this problem im having to do this which i feel is kinda hack and not good
get '/orders', to: 'orders#index'
get '/orders/:id', to: 'orders#show'
this is my routes.rb file
Rails.application.routes.draw do
get '/carts', to: 'carts#index'
get '/payments', to: 'payments#index'
post '/payments', to: 'payments#add_credits'
get '/orders', to: 'orders#index'
get '/orders/:id', to: 'orders#show'
resources :users do
resource :orders, only: %i[show create index]
resource :carts, only: %i[create destroy], path: 'cart', as: 'cart'
end
resource :sessions, only: [] do
post 'login', action: :create
post 'logout', action: :destroy
get 'login', action: :new
end
resources :products
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
thanks for your answer :)
Don't use
resource :orders
use
resources :orders
You would only use resource when the item orders is a single entity in your application... which is to say you're using the plural to refer to that one item.
Move the resource for orders outside of the Users routes.
Just and FYI, you can have both the full resource outside of Users and then those restricted routes inside Users, but I'm not sure what the goal is here so it is up to you to decide that.
Rails.application.routes.draw do
get '/carts', to: 'carts#index'
get '/payments', to: 'payments#index'
post '/payments', to: 'payments#add_credits'
resources :orders
resources :users do
resource :orders, only: %i[show create index] <-- not sure if this remains here
resource :carts, only: %i[create destroy], path: 'cart', as: 'cart'
end
...
The two particular routes im having issues with are admin/inspections and admin/activities. When I first save routes.rb, whichever route I load first works but the other will not, it gives me error: "Unitialized constant Admin::Towers"
I have the following routes setup.
namespace :admin do
#...etc...
resources :inspections, only: [:index,:show], controller: 'towers/inspections'
resources :activities, only: [:index], controller: 'towers/activities'
end
As you've namespaced your resources, your controllers should reside within app/controllers/admin/* and have a name, i.e. for inspections: class Admin::InspectionsController
I'm guessing you would like to have:
scope '/admin' do
resources :inspections, only: [:index,:show], controller: 'towers/inspections'
resources :activities, only: [:index], controller: 'towers/activities'
end
How to rename index path only?
routes.rb
resources :tasks, :except => [:create] do
collection do
..............
end
member do
.............
end
end
instead of /tasks in URL I need /trigger or even /tasks/trigger will do, im on rails3.
I tried
1.
collection do
'/', to: 'tasks#trigger'
end
and
2.
resources :tasks, :except => [:create] do
get '/task', to: 'task#trigger', as: task_index
end
both throw up errors.
any ideas?
For URL like '/trigger' add to routes.rb:
get 'trigger' => 'tasks#index'
For URL like '/tasks/trigger' modify routes.rb:
resources :tasks, :except => [:create] do
collection do
get 'trigger' => 'tasks#index'
end
end
If your index method in task's controller named as 'trigger, 'tasks#index' is replaced by 'tasks#trigger'
If you want to make your own index route then you need to do two things
1- you need to disable index path created by resources
2- Define your own path whatever you want.
As per your requirement you want to make index route as '/trigger'.
Following is the solution.
get 'trigger' => 'tasks#index'
resources :tasks, :except => [:create, :index] do
collection do
..............
end
member do
.............
end
end
Just restraint index route from being generated in your resources call:
resources :tasks, :except => [:create, :index]
And then do your own custom route. For '/trigger':
get 'trigger', to: 'task#trigger', as: 'trigger
I'm developing a REST API based on rails. To use this API, you MUST be logged in. Regarding that, I'd like to create a method me in my user controller that will return a JSON of the logged in user infos.
So, I don't need an :id to be passed in the URL. I just want to call http://example.com/api/users/me
So I tried this:
namespace :api, defaults: { format: 'json' } do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :tokens, :only => [:create, :destroy]
resources :users, :only => [:index, :update] do
# I tried this
match 'me', :via => :get
# => api_user_me GET /api/users/:user_id/me(.:format) api/v1/users#me {:format=>"json"}
# Then I tried this
member do
get 'me'
end
# => me_api_user GET /api/users/:id/me(.:format) api/v1/users#me {:format=>"json"}
end
end
end
As you can see, my route waits for an id, but I'd like to get something like devise has. Something based on current_user id. Example below:
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
In this example you can edit the current user password without passing the id as a param.
I could use a collection instead of a member, but that's a dirty bypass.
The way to go is to use singular resources:
So, instead of resources use resource:
Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like /profile to always show the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action [...]
So, in your case:
resource :user do
get :me, on: :member
end
# => me_api_user GET /api/users/me(.:format) api/v1/users#me {:format=>"json"}
Resource routes are designed to work this way. If you want something different, design it yourself, like this.
match 'users/me' => 'users#me', :via => :get
Put it outside of your resources :users block
You can use
resources :users, only: [:index, :update] do
get :me, on: :collection
end
or
resources :users, only: [:index, :update] do
collection do
get :me
end
end
"A member route will require an ID, because it acts on a member. A collection route doesn't because it acts on a collection of objects. Preview is an example of a member route, because it acts on (and displays) a single object. Search is an example of a collection route, because it acts on (and displays) a collection of objects." (from here)
Maybe I am missing something, but why don't you use:
get 'me', on: :collection
resources :users, only: [:index, :update] do
collection do
get :me, action: 'show'
end
end
specifying the action is optional. you can skip action here and name your controller action as me.
This gives same result as Arjan's in simpler way
get 'users/me', to: 'users#me'
When you create a route nested within a resource, you can mention, whether it is member action or a collection action.
namespace :api, defaults: { format: 'json' } do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :tokens, :only => [:create, :destroy]
resources :users, :only => [:index, :update] do
# I tried this
match 'me', :via => :get, :collection => true
...
...