Defining a custom route in a namespaced api in rails application - ruby-on-rails

I have created a namespaced api in my rails 5 project. I have the following in my config/routes.rb
Rails.application.routes.draw do
post 'api_user_token' => 'api_user_token#create'
namespace :api do
namespace :v1 do
resources :events
end
end
end
Events Controller
module Api::V1
class EventsController < ApiController
#Code here
end
end
API Controller
module Api::V1
class ApiController < ApplicationController
before_action :authenticate_api_user
respond_to :json
def register
end
end
end
I want the '/api/v1/register' route to go to the register method defined in my api controller. How can i define the route in this case ?
Thanks

namespace :api do
namespace :v1 do
resources :events
post :register, to: 'apis#register'
end
end
This will generate
api_v1_register POST /api/v1/register(.:format) api/v1/apis#register

Related

Unable to use devise_token_auth with active_admin

I'm trying to used devise_token_auth with active_admin. When running rails g active_admin:install User I get the following error.
error
usr/local/lib/ruby/gems/2.4.0/gems/actionpack-5.1.4/lib/action_dispatch/routing/route_set.rb:578:in
add_route': Invalid route name, already in use: 'new_user_session'
You may have defined two routes with the same name using the:as`
option, or you may be overriding a route already defined by a resource
with the same naming.
routes.rb
Rails.application.routes.draw do
devise_for :users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
mount_devise_token_auth_for 'User', at: 'auth'
scope module: 'api' do
namespace :v1 do
resources :users, only: [:index, :show]
end
end
get '/docs' => redirect('/swagger/dist/index.html?url=/apidocs/api-docs.json')
end
can you try a different approach by defining two controllers: one for api and the other for active_admin?
# app/controllers/api_controller.rb
# API routes extend from this controller
class ApiController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
end
# app/controllers/application_controller.rb
# leave this for ActiveAdmin, and any other non-api routes
class ApplicationController < ActionController::Base
end
Now inherit all api controllers from ApiController and ActiveAdmin ones from ApplicationController.
There is a known issue between ActiveAdmin and DeviseTokenAuth
I got it working by moving mount_devise_token_auth_for 'User', at: 'auth'
into the api scope. The answer was right , here.
Rails.application.routes.draw do
devise_for :users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
constraints subdomain: 'api'do
scope module: 'api' do
namespace :v1 do
resources :users, only: [:index, :show]
mount_devise_token_auth_for 'User', at: 'auth'
end
end
get '/docs' => redirect('/swagger/dist/index.html?url=/apidocs/api-docs.json')
end
end

Routing for sub ressources in rails

So this is in routes file :
resources :users do
resources :lamps
end
I want to be able to display a user's "lamps" with something like :
http://localhost:3000/users/2/lamps
and show all the existing lamps whichever user owns it using :
http://localhost:3000/lamps
both are very different as the first one is more of a management view and the later is more what a user browsing would see.
The thing is they both go to the index action of the lamp_controller
How can I manage this in a clean way without having to use if statements in my action/view?
You can use the module option to route the nested routes to a namespaced controller:
Rails.application.routes.draw do
resources :users do
resources :lamps, only: [:index], module: 'users'
end
resources :lamps, only: [:index]
end
Another way to do this for a group of resources is:
Rails.application.routes.draw do
resources :users do
scope module: 'users' do
resources :lamps, only: [:index]
resources :chairs
resources :rugs
end
end
resources :lamps, only: [:index]
end
This would let you handle the different contexts in separate controllers:
# app/controllers/lamps_controller.rb
class LampsController < ApplicationController
# GET /lamps
def index
#lamps = Lamp.all
end
end
# app/controllers/users/lamps_controller.rb
class Users::LampsController < ApplicationController
before_action :set_user
# GET /users/:user_id/lamps
def index
#lamps = #user.lamps
# renders /views/users/lamps/index.html.erb
end
private
def set_user
#user = User.find(params[:user_id])
end
end

Rails and Devise: Error with namespace

What I need is that when a user goes to '/admin', it redirects to '/admins/sign_in' for the user to sign in.
This is what I did:
# routes.rb
devise_for :admins
namespace :admin do
root to: 'users#index'
end
.
# controllers/admin/base_controller.rb
class Admin::BaseController < ApplicationController
before_action :authenticate_user!
layout 'admin'
end
.
# controllers/admin/users_controller.rb
class Admin::UsersController < Admin::BaseController
def index
end
end
The problem is that when I go to '/admin' it returns me an error:
NoMethodError in Admin::UsersController#index ..
undefined method `authenticate_user!' for Admin::UsersController
How can I make it work?
We have nearly the same setup as you, and it works perfectly:
I think the problem is that you're using devise_for :admins, and yet calling authenticate_user!; it should be authenticate_admin! (as per the Devise docs):
class Admin::BaseController < ApplicationController
before_action :authenticate_admin!
end

Unitialized Constant Rails Routing

I have a rails 4 app where I have a controller like:
app/controllers/api/v1/books_controller.rb:
module Api::V1
class BooksController < ApplicationController
...
end
end
and then my routes.rb:
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
resources :books
end
end
But I'm getting the error:
uninitialized constant BooksController
Try this:
module Api
module V1
class BooksController < ApplicationController
...
end
end
end
or this:
class Api::V1::BooksController < ApplicationController
...
end
Both works for me.
Also there is a good practice to have some API Controller in like:
module Api
class BaseController < ApplicationController
respond_to :json
end
end
in api folder. Or it can be in api version folder, with version namespace.

Nesting resources within a singular resource

Given the following routes:
resource :public_profile do
resources :posts
end
resource :private_profile do
resources :posts
end
How can I, in the PostsController, determine which singular resource I am nested within?
One way you could do this is by creating 2 more controllers that extend some main PostsController, and use
resource :public_profile do
resources :posts, controller: "PublicPostsController"
end
resource :private_profile do
resources :posts, controller: "PrivatePostsController"
end
You could even do this in a variety of ways. For example, maybe it makes sense to have
class ProfileController < ApplicationController; end
class PostsController < ApplicationController; end
class Private::ProfileController < ProfileController; end
class Private::PostsController < PostsController; end
class Public::ProfileController < ProfileController; end
class Public::PostsController < PostsController; end
with routing
resource :public_profile, controller: "Public::ProfileController" do
resources :posts, controller: "Public::PostsController"
end
resource :private_profile, controller: "Private::ProfileController" do
resources :posts, controller: "Private::PostsController"
end
Regardless of how you set this up, you can easily 'know' what resource you're nested within because you'll actually be running within a separate controller specific to that nesting and can thus have a perfect place for logic specific to that nesting. For general logic, you'd put that into the parent PostsController.
Another way you could do this is by adding a before_filter to PostsController like
before_filter :check_nesting
private
def check_nesting
#is_public_profile = params.include?(:public)
end
and have routing like
resource :public_profile, public: true do
resources :posts, controller: "PublicPostsController"
end
resource :private_profile, private: true do
resources :posts, controller: "PrivatePostsController"
end
I don't care for this approach though.
You can route them to different controllers ( by specifying it in the routes) , that are extended from the same "base" controller PostsController. In the extended controllers you
identify them:
EX:
resource :public_profile do
resources :posts, :controller => "public_profile_posts_controller"
end
resource :private_profile do
resources :posts, :controller => "private_profile_posts_controller"
end
and the Controllers
class PublicProfilePostsController < PostsController
before_filter :identify_controller
def identify_controller
#nested_resource_of = :public_profile
end
end
class PrivateProfilePostsController < PostsController
before_filter :identify_controller
def identify_controller
#nested_resource_of = :private_profile
end
end
and then you have access to the variable
#nested_resource_of
in the PostsController actions

Resources