In namespaced api (Api::V1) I can't access serializers - ruby-on-rails

Api::V1 is the namespacing of my schedules#index resource. But the controller won't access the serializer.
module Api
module V1
class ApiController < ApplicationController
include ActionController::Serialization
include Concerns::ErrorHandler
before_action do
namespace_for_serializer = Api::V1
end
end
end
end
module Api
module V1
class SchedulesController < ApiController
def index
#schedules = current_user.schedules
render json: #schedules
end
end
end
end
module Api
module V1
class ScheduleSerializer < ActiveModel::Serializer
attributes :id
end
end
end
I've tried everything. Declare the serializer directly vs each_serializer: ScheduleSerializer which just returns an "uninitialized constant" error.
What am I missing?

Related

Use the same `before_action` filter in multiple controllers

In a Rails app, I have a before_action filter that sends a webhook message. Since I have a few controllers that I want the before_action to act on, is there a good way to make it a module and prepend it?
My current logic is:
# first_controller.rb:
before_action :do_something
def do_something
#same logic
end
# second_controller.rb:
before_action :do_something
def do_something
#same logic
end
# third_controller.rb:
before_action :do_something
def do_something
#same logic
end
If your controllers inherit from ApplicationController, You can do the following:
class ApplicationController < ActionController::Base
def do_something
#same logic
end
end
class FirstController < ApplicationController
before_action :do_something
end
class SecondController < ApplicationController
before_action :do_something
end
class ThirdController < ApplicationController
before_action :do_something
end
Or you can make your own parent controller, eg. DoSomethingController
class DoSomethingController < ApplicationController
before_action do_something
def do_something
#same logic
end
end
class FirstController < DoSomethingController
end
class SecondController < DoSomethingController
end
class ThirdController < DoSomethingController
end
Or you can use #code_aks's answer https://stackoverflow.com/a/59846330/8554172 to make a module and include it.
Yes, it is good to use DRY here. If your controllers do have same parent class you can place that method in there. If not it is good practice to move this method to the module and include it with reusing.
You can try below code write the methods in a controller concern. For example:
# app/controllers/concerns/example_concern.rb
module ExampleConcern
extend ActiveSupport::Concern
protected
def before_filter_1
puts "from first before_filter_method"
end
def before_filter_2
puts "from second before_filter_method"
end
end
Now in the controller, include the module in the concern and call the methods using before_action as required. For example:
# app/controllers/examples_controller.rb
class ExamplesController < ApplicationController
include ExampleConcern
before_action :before_filter_1, only: [:action_a, :action_b, :action_c]
before_action :before_filter_2, only: [:action_d, :action_e]
def action_a
end
def action_b
end
def action_c
end
def action_d
end
def action_e
end
end
Hope this will help you. :)

Create Pundit Policies to API controller methods

How to create Policies for API-Controller's using Pundit gem?
Api controller path: /app/controllers/api/posts_controller.rb
#posts_controller.rb
class Api::PostsController < ApplicationController
def create
......
end
def update
......
end
def delete
......
end
end
I have Controller for the same and the corresponding Model
controller path: /controllers/posts_controller.rb
#posts_controller.rb
class PostsController < ApplicationController
def create
......
end
def update
......
end
def delete
......
end
end
I have created the policies for posts controller. How to create the same for API's Controller
Pundit is resource-based, not controller-based. When you call authorize and pass it a resource, Pundit cares about the action name and the resource type, but it does not care about the controller name.
Regardless of whether you call from the Api::PostsController:
# /app/controllers/api/posts_controller.rb
class Api::PostsController < ApplicationController
def create
#post = Post.find(params[:post_id])
authorize #post
end
end
or from your original PostsController:
# /app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.find(params[:post_id])
authorize #post
end
end
So long as #post is a member of the Post class, you can call authorize #post from the controller of a parent or child or a completely unrelated controller, it doesn't matter. In all cases Pundit will go and look for a method called create? within app/policies/post_policy:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def create?
user.present?
end
end

Share some before_filters between controllers

I have two parent controllers, one for an API, one for normal html pages.
class ApplicationController < ActionController::Base
...
end
class ApiController < ActionController::Metal
include AbstractController::Callbacks
...
end
I want to share some before_filter on both controllers. I tried something like this:
class ApplicationController < ActionController::Base
include MyFilters
end
class ApiController < ActionController::Metal
include MyFilters
end
module MyFilters
before_filter :filter1
before_filter :filter2
def filter1
end
def filter2
end
...
end
And also this:
module MyFilters
def self.included(klass)
klass.before_filter :filter1
klass.before_filter :filter2
end
def filter1
end
def filter2
end
...
end
But in both cases I receive:
undefined method `before_filter' for MyFilters:Module
What is the correct way to implement this?
You could use a concern for that:
# app/controllers/concerns/my_filters.rb
module MyFilters
extend ActiveSupport::Concern
included do
before_filter :filter1
before_filter :filter2
end
def filter1
end
def filter1
end
end
Use them through a standard include in your controllers:
class ApplicationController < ActionController::Base
include MyFilters
end
class ApiController < ActionController::Metal
include MyFilters
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.

Namespacing issue in rails

I have a class A in module M like below
Module M
class A
def method1
# how to instantiate a model having same name as A
#like A.first
end
end
end
In my models I have a class A
class A < ActiveRecord::Base
end
You can access the global scope using the :: operator, e.g:
Module M
class A
def method1
::A.first
end
end
end

Resources