I'm working on a Rails application and I'm versioning the API.
Following RailsCast #350 I have this:
routes.rb
namespace :v1 do
#resources for version 1
end
namespace :v2 do
#resources for version 2
end
I use active_model_serializer and I have app/serializers/v1/ and .../v2/ with:
(for /v1)
module V1
class ResourceSerializer < ActiveModel::Serializer
attributes :id
end
end
(for /v2)
module V2
class ResourceSerializer < ActiveModel::Serializer
attributes :id, :data
end
end
But Rails doesn't call my custom serializer.
module V1
class ResourcesController < ApplicationController
def show
#resource = Resource.find(params[:id])
render json: #resource
end
end
end
OUTPUT for .../v1/resources/1
{"id":1,"name":"...","city":"...","created_at":"...","updated_at":"2..."}
instead of
{"id":1}
If I put render json: #resources, serializer: ResourceSerializer it retrieves undefined method 'read_attribute_for_serialization'
Any help would be appreciated. Thanks!
EDIT: Namespaces are valid!
I got this problem also, I have tried many solutions, but didn't work for me
the only solution that works is calling the serializer class directly:
render json: V1::ResourceSerializer.new(#resource)
If your problem only "undefined method 'read_attribute_for_serialization'", include ActiveModel::Serialization into your ActiveModel sub class
module V1
class ResourceSerializer < ActiveModel::Serializer
include ActiveModel::Serialization
attributes :id
end
end
I finally got a solution using each_serializer: V1::UserSerializer for collections and serializer: V2::UserSerializer for normal objects.
Thanks to all.
Related
I have created API in rails 5.
I am getting error superclass mismatch for class UsersController.
My controller:
module Api
module V1
class UsersController < ApplicationController
def index
users = User.Order('created_at DESC')
render json: {status:"SUCCESS", message:"Load Users", data:users}, status: :ok
end
def create
user = User.new(user_params)
end
def user_params
params.permit(:firstname, :lastname, :email, :age, :id)
end
end
end
end
My routes:
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :users
end
end
end
Here is my folder structure:
I got below error in console:
Actually, I have just started in ruby on rails. I tried to figure out problem but couldn't find it.
You need to reference ApplicationController from the Main module (the "global" namespace in Ruby):
module Api
module V1
class UsersController < ::ApplicationController
:: tells Ruby to resolve the constant from main rather than the current module nesting which is Api::V1.
You also need to ensure that ApplicationController inherits from ActionController::API.
See:
Everything you ever wanted to know about constant lookup in Ruby
I'm trying to get my app to return in lowercase camelcase for eventual JSON API formatting.
I've installed gem 'active_model_serializers' and created a new initializer with the following code in it:
ActiveModelSerializers.config.adapter = :json_api
ActiveModelSerializers.config.key_transform = :camel_lower
Then I have a small API that returns json, as all of the best internet applications do:
class Api::V1::UsersController < API::V1::BaseController
def sky
#user = User.find_by_id(params[:user_id])
if #user
obj = {
sky: {
sectors: #user.sectors,
slots: #user.slots
}
}
render json: obj
else
raise "Unable to get Sky"
end
end
More on the API controller inheritance pattern: class API::V1::BaseController < ActionController::Base
The Problem
In my API response, things are still snake cased and I see this error in the console [active_model_serializers] Rendered ActiveModel::Serializer::Null but my research has led me to a dead end as to what to do.
Any suggestions would be very welcome. Thanks!
The problem is you're not calling an active record serializer in your controller, so those config settings aren't being picked up.
Solution:
Create a UserSerializer in "app/serializers/user_serializer.rb" that should look something like this:
class UserSerializer < ActiveModel::Serializer
attributes :id
has_many :sectors
has_many :slots
end
as well as similarly structured SectorSerializer and a SlotSerializer with all of the attributes you want from each (Here are the getting started docs and the general syntax docs for active record serializers)
Then in your controller:
class Api::V1::UsersController < API::V1::BaseController
def sky
#user = User.includes(:sectors, :slots).find_by_id(params[:user_id])
if #user
render json: #user
else
raise "Unable to get Sky"
end
end
end
Which will eager load :slots and :sectors with includes then calls your UserSerializer using your camel_case config options.
In your controller put respond_to :json
class Api::V1::UsersController < API::V1::BaseController
respond_to :json
and in the action put same that you have
def sky
...
render json: obj
...
end
and define in base controller
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
From this pull request (*) it looks like you should be able to configure key_format = :lower_camel in your ActiveModel::Serializers config.
(*) https://github.com/rails-api/active_model_serializers/pull/534
i think it helps you. in my case i use gem 'active_model_serializers', '~> 0.10.5' which depends on case_transform (>= 0.2)
and in rails console i can do
CaseTransform.camel_lower(initially_serialized_output)
=> {:name=>"My Company", :jsonThings=>{:rating=>8, :aKey=>{:aSubkey=>{:anotherKey=>"value"}}}}
my research was by steps:
https://github.com/rails-api/active_model_serializers/pull/1993 => https://github.com/NullVoxPopuli/case_transform-rust-extensions
did you find this?
I have api with version system.
My controller
module Api;module V1
class PlansController < ApplicationController
def show
#plan = Plan.find(params[:id])
render json: #plan
end
end
end;end
I have folder serializers/api/v1 where i have plan_serializer.rb
module Api;module V1
class PlanSerializer < ActiveModel::Serializer
attributes :name, :amount, :days
end
end;end
But its not serializing json response automatically.
Please tell me what wrong am I doing ?
I also tried adding
class ApplicationController < ActionController::API
include ActionController::Serialization
but still its not working.
If I am doing
render json: #plan, serializer: V1::PlanSerializer
then it is working but I want it to work without adding serializer in every render.
Please tell me solution.
It may work if you override render.
class ApplicationController < ActionController::API
include ActionController::Serialization
DEFAULT_SERIALIZER= V1::PlanSerializer
def render options, &block
options[:serializer]= DEFAULT_SERIALIZER unless options[:serializer]
super options, &block
end
end
I have below serializer /serializers/api/club_serializer.rb:
class Api::ClubSerializer < ActiveModel::Serializer
cached
attributes :id, :name, :created_at
end
And below method under /controllers/api/clubs_controller.rb
module Api
class ClubsController < BaseController
include ActionController::ImplicitRender
include ActionController::MimeResponds
# GET /clubs
def index
#clubs = Club.all
render json: #clubs, serializer: ClubSerializer
end
This doesn't seem to be working properly as I remove name it still shows name with all fields.
How do I change it so it works?
The way you do this now:
render json: #clubs, namespace: Api
See: https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/rendering.md#namespace
why not:
render json: #clubs, each_serializer: ::Api::ClubSerializer
Please find here is my controller and json file
//controller file
module Api
module V1
class CouponsController < ApplicationController
respond_to :json
def show
#coupon = Coupon.find(params[:id])
render "/coupons/show.json.jbuilder"
end
end
end
end
//show.json.jbuilder
json.extract! #coupon, :id, :category
Maybe you need to rewrite the head in controller like this:
module Api::V1::CouponsController < ApplicationController
Because your current write assumes that you have Api::V1::ApplicationController. And Api::V1::CouponsController is inherited from it, not from ApplicationController.