I have a subfolder api in my controllers folder.
So when I call POST "/api/auth" I would like the program to get there using rails conventions.
I.E. I don't want to write the route for each call, but to use rails "patent" that makes rails go to CRUD actions understanding the PUT, POST, GET by itself.
So in my routes.rb I have:
namespace :api do
resources :debts, :auth
end
But when I POST (or GET) with localhost:3000/api/auth I get:
ActionController::RoutingError (uninitialized constant Api::AuthController)
What am I missing?
Please note that I also need to have many controllers inside the subfolder. Is there a short match for all?
You have to put your controller in a sub folder too en then do e.g.
# /app/controllers/api/debts_controller.rb
module Api
class DebtsController < ApiController
end
end
# /app/controllers/api/auth_controller.rb
module Api
class AuthController < ApiController
end
end
Then in base controller folder:
# /app/controllers/api_controller.rb
class ApiController < ApplicationController
end
You need to namespace the class also in order to get this working.
The following 2 ways can be used:
module Api
class AuthController < ApplicationController
# Your controller code here
end
end
class Api::AuthController < ApplicationController
# Your controller code here
end
If you have some code that needs to be run for every controller inside the Api namespace, you can make an Api base controller, but it's not necessary.
Related
I'm having trouble starting to build my own admin section. I get this error when trying to view example.com/admin:
TypeError in Admin::AdminController#dashboard
"superclass mismatch for class AdminController"
My admin controller is in app/controllers/admin/admin_controller.rb
Here is my routes.rb:
Rails.application.routes.draw do
namespace :admin do
root :to => "admin#dashboard"
resources :posts
end
Here is my AdminController:
class AdminController < ApplicationController
def dashboard
print "Dashboard"
end
end
My plan is to have example.com/admin go to the admin dashboard. To edit/create posts: /admin/posts.
You already have a Admin::AdminController class defined elsewhere. Which inherits from different class other then ApplicationController
If you have not created a second Admin::AdminController class yourself, it is likely one of your Gems or plugins already defines it.
If you are using active_admin or rails_admin gem , maybe it does have class with the above name AdminController
You can cross check by replacing the AdminController with some other name maybe AdminController2
Basically I've developed an app which has two namespaces: admin, api and the public one (simply resources :users for instance). All works fine, however I'm repeating myself quite a bit as some of the controllers in api for instance could easily be used in admin.
How can I DRY my code in this case keeping the namespaces?
Thanks!
There are a couple ways I can think of doing it:
(NOT RECOMMENDED) - Send the urls to the same controller in your routes.rb file.
Shared namespace that your controllers inherit from
For example you could have:
# controllers/shared/users_controller.rb
class Shared::UsersController < ApplicationController
def index
#users = User.all
end
end
# controllers/api/users_controller.rb
class Api::UsersController < Shared::UsersController
end
# controllers/admin/users_controller.rb
class Admin::UsersController < Shared::UsersController
end
The above would allow you to share your index action across the relevant controllers. Your routes file in this case would look like this:
# config/routes.rb
namespace :api do
resources :users
end
namespace :admin do
resources :users
end
That's definitely a lot of code to share one action, but the value multiplies as the number of shared actions does, and, most importantly, your code is located in one spot.
Given the following controller structure:
# application_controller.rb
class ApplicationController < ActiveController::Base; end
# pages_controller.rb
class PagesController < ApplicationController; end
# admin/application_controller.rb
module Admin
class ApplicationController < ::ApplicationController; end
end
# admin/pages_controller.rb
module Admin
class PagesController < ApplicationController; end
end
One would expect Admin::PagesController to inherit from Admin::ApplicationController and it does. But I have noticed that sometimes it inherits from ::ApplicationController.
So I decided not to risk it and changed declaration of all controllers in /admin to specifically target Admin::ApplicationController
# admin/pages_controller.rb
module Admin
class PagesController < Admin::ApplicationController; end
end
Okay that works, but from what I know it was correct in the first place. Why Rails inherits from a wrong controller sometimes?
Admin::PagesController sometimes inherits from ApplicationController instead of Admin::ApplicationController despite both being in the same module Admin
The problem here is rails' development mode code loading: in general code is loaded when you try to do something with a constant (eg subclass from it) and that constant doesn't exist. This results in const_missing being called and rails uses it this to try to load the class (for a detailed description see the guide).
If neither ApplicationController nor Admin::ApplicationController exist then when you access your admin pages controller ruby will hit that const_missing and try to load admin/application_controller.rb
However if ApplicationController is already loaded then ruby won't fire const_missing since it perfectly legal for a class in the admin module to inherit from something at the toplevel.
The solution as you say is to make explicit what you are inheriting from. Personally in my own apps I use Admin::BaseController as the base class.
Another option is to use require_dependency to point Rails to the correct file:
# admin/application_controller.rb
require_dependency 'admin/application_controller'
module Admin
class PagesController < ApplicationController
end
end
I have trouble creating a module for my controller, and getting my routes to point to that module within the controller.
Getting this error:
Routing Error
uninitialized constant Api::Fb
So, this is how my routes are set up:
namespace :api do
namespace :fb do
post :login
resources :my_lists do
resources :my_wishes
end
end
end
In my fb_controller i want to include modules that will give me paths like this:
/api/fb/my_lists
This is some of my fb_controller:
class Api::FbController < ApplicationController
skip_before_filter :authenticate_user!, :only => [:login]
include MyLists # <-- This is where i want to include the /my_lists
# namespace(currently not working, and gives me error
# mentioned above)
def login
#loads of logic
end
end
The MyLists.rb file(where i define a module) is in the same directory as the fb_controller.rb.
How can i get the namespacing to point to my module inside of the fb_controller, like /api/fb/my_lists ?
The namespace you have set up is looking for a controller class that looks like this
class Api::Fb::MyListsController
If you want to have a route that looks like /api/fb/my_lists but you want to still use the FbController instead of having a MyListsController you need to set up your routes to look like this
namespace :api do
scope "/fb" do
resources :my_lists, :controller => 'fb'
end
end
In my opinion, instead of including a module MyLists in your FbController seems kind of awkward.
What I would probably do is have a module FB with a generic FbController then have MyListsController < FbController. Anyway, this is beyond the scope of your question.
The above should answer for your needs.
EDIT
From your comments, and my assumptions on what you're trying to do this is a small example:
config/routes.rb
namespace :api do
scope "/fb" do
post "login" => "fb#login"
# some fb controller specific routes
resources :my_lists
end
end
api/fb/fb_controller.rb
class Api::FbController < ApiController
# some facebook specific logic like authorization and such.
def login
end
end
api/fb/my_lists_controller.rb
class Api::MyListsController < Api::FbController
def create
# Here the controller should gather the parameters and call the model's create
end
end
Now, if all you want to create a MyList Object then you could just do the logic directly to the model. If, on the other hand, you want to handle some more logic you'd want to put that logic in a Service Object that handles the creation of a MyList and its associated Wishes or your MyList model. I would probably go for the Service Object though. Do note, the service object should be a class and not a module.
In your example, Fb isn't a namespace, it's a controller. The namespace call is forcing your app to look for a Fb module that doesn't exist. Try setting up your routes like this:
namespace :api do
resource :fb do
post :login
resources :my_lists do
resources :my_wishes
end
end
end
You can optionally define a new base controller for the API namespace:
# app/controllers/api/base_controller.rb
class Api::BaseController < ApplicationController
end
If you do so, your other controllers can inherit from this:
# app/controllers/api/fb_controller.rb
class Api::FbController < Api::BaseController
end
Running rake routes should give you an idea of how your other controllers are laid out. Just a warning - it's generally not recommended to have resources nested more than 1 deep (you're going to end up with complex paths like edit_api_fb_my_list_my_wish_path). If you can architect this in a simpler way, you'll probably have an easier time of this.
I'm building a Rails engine called Engrave.
I have the engine mounted like so:
# Routes.rb of the host app
mount Engrave::Engine => "/engrave", :as => "engrave_engine"
Within this engine I have a controller called "PostsController". When I navigate to this controller to view a post like so: /engrave/posts/1 I get this error:
undefined local variable or method `new_user_session_path'
The PostsController in the engine is inheriting from the engine controller, which is inheriting from the application controller, like so:
module Engrave
class PostsController < ApplicationController
...
end
class Engrave::ApplicationController < ApplicationController
end
The new_user_session_path is being defined by devise, which I have setup like:
devise_for :users
The call to new_user_session_path is in the layouts/application.html.erb template file in the host app
I cannot figure out why this route helper isn't available in this context. What am I doing wrong?
Use
main_app.new_user_session_path
that should work
I've had success doing the following in the main app's application_helper.rb:
module ApplicationHelper
# Can search for named routes directly in the main app, omitting
# the "main_app." prefix
def method_missing method, *args, &block
if main_app_url_helper?(method)
main_app.send(method, *args)
else
super
end
end
def respond_to?(method)
main_app_url_helper?(method) or super
end
private
def main_app_url_helper?(method)
(method.to_s.end_with?('_path') or method.to_s.end_with?('_url')) and
main_app.respond_to?(method)
end
end
I've used this in mountable engines, so you do not have to sacrifice those features.
Working off of this response, I include all helpers found in application_helpers.rb by stating helper "manager/application" inside the controller (if 'manager' is the current namespace of your mountable engine. Just use 'application' if you are calling this from a standard application).