I have my directory under app/controllers set up as such: api/v1/sessions_controller.rb I then have a BaseController: api/v1/base_controller.rb
I then set up each class to look as such:
module Api
module V1
class BaseController < ApplicationController
respond_to :json
before_action :default_json
protected
def default_json
request.format = :json if params[:format].nil?
end
def auth_only!
render :json: {}, status: 401 unless current_user
end
end
end
end
And then Sessions:
module Api
module V1
class SessionsController < BaseController
def create
user = User.authenticate(params[:user_name], params[:password])
if sign_in(user)
set_up_cookie(user)
render json: create_session_data, status: 201
else
invalid_user_crendentials
end
end
end
end
end
The tests are set up the same way, for example the sessions test is:
require 'spec_helper'
describe Api::V1::SessionsController do
before(:each) do
#user = FactoryGirl.create(:user)
end
context "Create a session" do
it "should NOT create a session" do
post :create
response.response_code.should == 401
end
end
end
The Error:
'<module:V1>': uninitialized constant Api::V1::BaseController (NameError)
Try formatting your naming slightly differently.
class Api::V1::BaseController < ApplicationController
# code
end
and
class Api::V1::SessionsController < BaseController
# might need to name this Api::V1::BaseController
end
Related
Doing API for my first Rails project.
I have base class ApiController for all the APIs:
module Api
class ApiController < ::ApplicationController
respond_to :json
skip_before_action :verify_authenticity_token
def index
#collection = resource_class.all
render json: #collection.as_json(as_json_collection)
end
private
def resource_class
raise NotImplementedError
end
def as_json_collection
{}
end
end
end
And I have child class UsersController:
module Api
class UsersController < ApiController
private
def resource_class
User
end
def resource_params
params.require(:user).permit(:name, :email)
end
end
end
My routes:
namespace :api do
resources :users
end
Then I'm going to my_app/api/users I have error:
The action 'index' could not be found for Api::UsersController
But then I changing UsersController writing it's own index class, everything works fine and I'm having all my Users in JSON format.
I've alrady tried to comment all private marks in both classes, but that doesn't help.
I don't want to write an API for every entity in my project and I'd like to avoid this problem in future.
I got it to work with this:
module Api
class ApiController < ::ApplicationController
def index
respond_to do |format|
format.json { render json: '"abc"' }
end
end
end
end
module Api
class UsersController < ApiController
end
end
The URL was http://localhost:3000/api/users.json
So for you I suggest:
module Api
class ApiController < ::ApplicationController
def index
respond_to do |format|
format.json do
#collection = resource_class.all
render json: #collection.as_json(as_json_collection)
end
end
end
end
end
module Api
class UsersController < ApiController
def resource_class
User
end
def resource_params
params.require(:user).permit(:name, :email)
end
end
end
Its supposed to be like this:
class Api::ApiController < ApplicationController
and do not forget to remove extra end, end of the file!
#sample
- api(folder)
-- api_controller.rb (Api::ApiController < ApplicationController)
-- users_controller.rb (Api::UsersController < Api::ApiController)
application_controller.rb
You need to read this my friend:
rails routes
When you do this:
namespace :api do
resources :users
end
rails creates CRUD routes automatically which means that my_app/api/users will translate to: .../users#index.
Do this to see your routes created by rails:
rails routes and for specific word (e.g. user): rails routes | grep user
Seeing is believing ;)
I am still a beginner concerning ruby on rails and I am trying to create a simple API but I am facing this error :"uninitialized constant Api"
ideas_controller.rb
module Api
module V1
class ItemsController < BaseController
def index
#ideas = Idea.all
render json: #ideas
end
end
end
end
routes.db
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :ideas
end
end
end
application_controller.rb
class ApplicationController < ActionController
protect_from_forgery with: :null_session
end
base_controller.rb
module Api
module V1
class BaseController < ApplicationController
respond_to :json
end
end
The project's directories:
The server's error:
I have also tried this approach and changed the project's structure to :
Also, I have enabled :
config.eager_load = true
After that I got the following error:
`block in load_missing_constant': uninitialized constant Api::V1::BaseController (NameError)
If you're in the development environment, eager loading is turned off by default, which can be fixed by turning on eager load (change to config.eager_load = true in config/development.rb). Eager loading will allow the whole Rails app to be loaded when the server starts (which is slightly slower), but will fix your problem since that file will be loaded.
This error because module Api is not defined before, I guess if you made like this it gonna work. from an answer here
module Api
module V1
class IdeasController < ApplicationController
def index
#ideas = Idea.all
render json: #ideas
end
end
end
end
another solution could be like this:
module Api
module V1
end
end
class Api::V1::IdeasController < ApplicationController
def index
#ideas = Idea.all
render json: #ideas
end
end
hope it helps.
Try this in your ideas controller
module Api
module V1
class IdeasController < Api::V1::BaseController
def index
#ideas = Idea.all
render json: #ideas
end
end
end
end
And define your base controller in the following way
class Api::V1::BaseController < ApplicationController
respond_to :json
end
To have a cleaner code I want to split my controller in some concerns.
In my routes.rb how to redirect to concern without redefine the methods of concern index show destroy create ...
class SomeController
include SomeConcern
def index
end
end
module SomeConcern
def index
end
end
Sorry for my bad english.
Lets say we have a CarsController and AirplanesController that have the typical create and new actions.
class AirplanesController < ApplicationController
def new
#airplane = Airplane.new
end
def create
#airplane = Airplane.new(create_params)
if #airplane.save
redirect_to #airplane
else
render :new
end
end
# ...
end
class CarsController < ApplicationController
def new
#car = Car.new
end
def create
#car = Car.new(create_params)
if #car.save
redirect_to #car
else
render :new
end
end
# ...
end
To dry this up we can extract the shared code to a module:
module Createable
extend ActiveSupport::Concern
included do
attr_accessor :resource
alias_attribute :self.controller_name.to_sym, :resource
end
def new
#resource = resource_class.new
yield #resource if block_given?
end
def create
#resource = resource_class.new(create_params)
if #resource.save
yield #resource if block_given?
redirect_to #resource
else
render :new
end
end
private
def create_params
raise "not implemented controller!"
end
def resource_class
#resource_class ||= self.controller_name.classify.constantize
end
end
We can then apply it to the controller classes by:
class CarsController < ApplicationController
include Createable
def create_params
params.require(:car)
.permit(:model) # ...
end
end
class AirplanesController < ApplicationController
include Createable
def create_params
params.require(:airplane)
.permit(:model) # ...
end
end
But a very important point here is that you are not routing to the module. The module is providing methods to the controller class.
You have to always map to your controller. Concerns are modules where you can put shared logic (it makes sense only in case you need 2 absolutely similar methods in 2 different controllers).
I think, that such code should work:
class SomeController
include SomeConcern
end
module SomeConcern
def index
end
end
Isn't it?
But concerns mostly used to move out some private helper methods from controller, rather actions as we do in this code piece
My posts_controller_Test.rb file:
# test/controllers/posts_controller_test.rb
require 'test_helper'
class PostControllerTest < ActionController::TestCase
def setup
#post = Post.new
end
test 'should get index' do
get :index
assert_response :success
assert_not_nil assigns(:posts)
end
end
My post_controller File
class PostsController < ActionController::Base
before_action :authenticate_user!
def index
#post = current_user.posts.paginate(page: params[:page], per_page: 5)
respond_to do |format|
format.html
format.json { render json: #post }
end
end
def new
#post = Post.new
end
def create
#post = current_user.posts.build(post_param)
if #post.save
redirect_to action: 'index'
else
render 'new'
end
end
Why my test cases are failing? Is it because I have authenticate_user! condition? I also have .yml file and tried to test with it but after initializing with .yml data I am getting RuntimeError: #controller is nil: make sure you set it in your test's setup method.
yml file
one:
data 2
value: 3
user_id: 1
name: 'test'
.yml has everything what i have required in
params.require(:post).permit(:data, :value, :name) and obvious `user` for foreign key reference
Edit -1
After the suggestion of inheriting with ApplicationController
Got this new error:
NameError: uninitialized constant ApplicationController::Base
app/controllers/posts_controller.rb:3:in `<top (required)>'
app/controllers/posts_controller.rb:3:in `<top (required)>'
this is my line 3:
class PostsController < ApplicationController::Base
Edit-2
class PostsController < ApplicationController
got this:
NoMethodError: undefined method `authenticate!' for nil:NilClass
The error is because you've inherited your controller from the wrong class. Inherit from ApplicationController instead:
class PostsController < ApplicationController
...
end
I'm following a tutorial on how to create a REST Api using the rails-api gem.
In the tutorial, most of the API logic is encapsulated in the base class file app/controllers/api/v1/api_base_controller.rb
module Api
module V1
class ApiBaseController < ApplicationController
protect_from_forgery with: :null_session
before_action :set_resource, only: [:destroy, :show, :update]
respond_to :json
private
#Returns the resource from the created instance variable
##return [Object]
def get_resource
instance_variable_get("##{resource_name}")
end
#Returns the allowed parameters for searching
# Override this method in each API controller
# to permit additional parameters to search on
# #return [Hash]
def query_params
{}
end
#Returns the allowed parameters for pagination
# #return [Hash]
def page_params
params.permit(:page, :page_size)
end
# The resource class based on the controller
# #return [Class]
def resource_class
#resource_class ||= resource_name.classify.constantize
end
# The singular name for the resource class based on the controller
# #return [String]
def resource_name
#resource_name ||= self.controller_name.singularize
end
#Only allow a trusted parameter "white list" through.
# If a single resource is loaded for #create or #update,
# then the controller for the resource must implement
# the method "#{resource_name}_params" to limit permitted
# parameters for the individual model.
def resource_params
#resource_params ||= self.send("#{resource_name}_params")
end
#Use callbacks to share common setup or constraints between actions.
def set_resource(resource = nil)
resource ||= resource_class.find(params[:id])
instance_variable_set("##{resource_name}", resource)
end
#POST /api/v1/{plural_resource_name}
def create
set_resource(resource_class.new(resource_params))
if get_resource.save
render :show, status: :created
else
render json: get_resource.errors, status: :unprocessable_entity
end
end
#DELETE /api/v1/{plural_resource_name}/:id
def destroy
get_resource.destroy
head :no_content
end
#GET /api/v1/{plural_resource_name}
def index
plural_resource_name = "##{resource_name.pluralize}"
resources = resource_class.where(query_params).page(page_params[:page]).per(page_params[:page_size])
instance_variable_set(plural_resource_name, resources)
respond_with instance_variable_get(plural_resource_name)
end
#GET /api/v1/{plural_resource_name}/1
def show
respond_with get_resource
end
#PATCH/PUT /api/{plural_resource_name}/1
def update
if get_resource.update(resource_params)
render :show
else
render json: get_resource.errors, status: :unprocessable_entity
end
end
end
end
end
The model controllers inherit from this ApiBaseController
one of the model controllers (albums_controllers.rb) looks like this:
module Api
module V1
class AlbumsController < Api::V1::ApiBaseController
private
def album_params
params.require(:album).permit(:title)
end
def query_params
params.permit(:title, :artist_id)
end
end
end
end
I also have this in routes.rb
Rails.application.routes.draw do
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
resources :albums, :artists
end
end
end
when I do http://localhost:3000/api/v1/artists on the browser, i get this error:
`The action 'index' could not be found for Api::V1::ArtistsController`
I have also noted that the ApplicationController generated by the rails-api gem inherits from ActionController::API and not ActionController::Base but i'm not sure whether this is the problem.
private methods are not available to subclasses. If you REALLY want to hide all of those methods but make it available to subclasses, use protected instead.