Rails routes: GET without param :id - ruby-on-rails

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
...
...

Related

Rails route alias for a nested route with specific id

I am trying to make some alias for my rails route like this 'events/8/event_participants/new' to /business-meet/registration,
My routes are written like this:
resources :events, only: [], shallow: true do
resources :event_participants, only: [:new, :create, :edit, :update] do
post :complete, on: :collection
member do
get :invite, :add_people, :accept_invitation, :invitation_success, :reserved
put :refer
end
end
end
I want the alias for the specific event id 8, I tried with redirect, but it actually redirect to the the route, so the whole route is visible in the browser, I want the to keep visible /business-meet/registration routes to my browser.
You can use a match method in your route.
match '/business-meet/registration', to: 'event_participants#new', via: :get, defaults: { event_id: 8 }

Rails custom and default routes

I'm trying to define custom routes to my controller and I need to use some of the default routes too. Is there any simple solution?
So far I've something like this
resources :users do
member do
get 'users/:id', to: 'users#show'
delete 'users/:id', to: 'users#destroy'
end
collection do
post 'users', to: 'users#create'
post 'users/login', to: 'users#login'
end
end
resources :users, :only => [:show, :destroy, :create, :login]
I don't need nor want the index route but with this settings it's still trying to route GET users/ to user_controller index method.
I know that there is probably some simple and obvious answer but I'm not able to find it.
Thank's in advance.
You got your routes wrong. The resources :users generates seven default routes which include the index route as well. You need to tweak the code to below
resources :users, :only => [:show, :destroy, :create] do
collection do
post 'login', to: 'users#login'
end
end
Note:
If you noticed, I've removed the custom routes for show,create and delete as they are generated by default.
Your first line defines the route to the index action. Define a resource once only. Read the routing guide.
resources :users, :except => [:index] do
collection do
post 'users/login', to: 'users#login'
end
end
Run rake routes from the command line in your project root folder to see all your route definitions.

How can I specify two actions mapped to the DELETE verb in a rails resource?

I have an api tokens controller based on Matteo Melanis blog post. I'd like to add two custom actions register and unregister to the controller, and so the route that looked like this
namespace :api do
namespace :v1 do
resources :tokens,:only => [:create, :destroy]
end
end
has now become this
namespace :api do
namespace :v1 do
resources :tokens do
put 'register', on: :member, as: :register
delete 'unregister', on: :member, as: :unregister
end
end
end
This is the only way I've found that doesn't let unregister suppress the CRUD destroy action, associated with the DELETE verb. I tried to do
resources :tokens, :only => [:create, :destroy, :register, :unregister] do
in the above code, as well as defining resources :tokens,:only => [:create, :destroy] in parallel to the block. Yet, I either get the undesirable all CRUD + custom actions, or one of the custom actions overriding a CRUD action.
In short, I'd like to end up with
register_api_v1_token PUT /api/v1/tokens/:id/register(.:format) api/v1/tokens#register
unregister_api_v1_token DELETE /api/v1/tokens/:id/unregister(.:format) api/v1/tokens#unregister
api_v1_tokens GET /api/v1/tokens(.:format) api/v1/tokens#index
POST /api/v1/tokens(.:format) api/v1/tokens#create
DELETE /api/v1/tokens/:id(.:format) api/v1/tokens#destroy
Is this possible, and if yes: how can I make it so?
This should work:
resources :tokens, only: [:create, :destroy] do
member do
put 'register'
delete 'unregister'
end
end

How to add extra parameter to resources in routes

I want member routes generating by resources to contain additional parameter.
Something like:
resources :users
with folowing routes:
users/:id/:another_param
users/:id/:another_param/edit
Any ideas ?
resources method doesn't allow you to do that. But you can do something similar using the path option and including extra parameters:
resources :users, path: "users/:another_param"
That will generate urls like this:
users/:another_param/:id
users/:another_param/:id/edit
In this case you will need to send :another_param value to routing helpers manually:
edit_user_path(#user, another_param: "another_value")
# => "/users/another_value/#{#user.id}/edit"
Passing :another_param value is not required if a default value has been set:
resources :users, path: "users/:another_param", defaults: {another_param: "default_value"}
edit_user_path(#user) # => "/users/default_value/#{#user.id}/edit"
Or you can even make the extra parameter not mandatory in the path:
resources :users, path: "users/(:another_param)"
edit_user_path(#user) # => "/users/#{#user.id}/edit"
edit_user_path(#user, another_param: "another_value")
# => "/users/another_value/#{#user.id}/edit"
# The same can be achieved by setting default value as empty string:
resources :users, path: "users/:another_param", defaults: {another_param: ""}
If you need extra parameters for particular actions, it can be done this way:
resources :users, only: [:index, :new, :create]
# adding extra parameter for member actions only
resources :users, path: "users/:another_param/", only: [:show, :edit, :update, :destroy]
you could do something more explicit like
get 'my_controller/my_action/:params_01/:params_02', :controller => 'my_controller', :action => 'my_action'
resources :users, path: 'user' do
collection do
get ':id/:some_param', action: :action_name
get ':id/:some_param/edit', action: :custom_edit
end
end

Abstracting rails route

I want to replace the normal /users/:id route that is created by the resources command, with a more abstract /profile route. It won't be possible to view other users profiles in my app, and therefor the current route is unnecessary specific.
I have tried to overwrite the route created by 'resources :users' with:
get '/profile', to: 'users#show'
and other variances and combinations, but can't seem to get it right. Either the controller can't find the user because of a missing id or it simply can't find the route.
Thanks for the help!
You can use this code in routes.rb file:
resources :users, :except => :show
collection do
get 'profile', :action => 'show'
end
end
It will generate url "/users/profile".
But, if u want to use only '/profile', then don't create route as collection inside users resources block.
resources :users, :except => :show
get 'profile' => "users#show", :as => :user_profile
It will redirect '/profile' to show action in users controller.
I suggest simply adding a users/me route pointing to the show action of your UsersController like so:
resources :users, only: [] do
collection do
get 'me', action: :show
end
end
You can also use the match keyword in routes.rb file.
match 'users/:id' => 'users#show', as: :user_profile, via: :get

Resources