I am using ActiveModelSerializers
gem with my Rails API project.
I created a JobsController
inside of app/controllers/v1/jobs_controller.rb
because of API versioning.
I also created a JobSerializer
inside of app/serializers/v1/job_serializer.rb
also because of API versioning.
When I try to access V1::JobSerializer
inside of the controller like this:
def today_jobs
todays_jobs = Job.where(created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).all.order('created_at DESC').page(params[:page] ? params[:page].to_i : 1).per(10)
render json: {objects: ActiveModel::Serializer::CollectionSerializer.new(todays_jobs, each_serializer: V1::JobSerializer), meta: pagination_meta(todays_jobs)}
end
Don't mind the pagination, this part is important:
objects: ActiveModel::Serializer::CollectionSerializer.new(todays_jobs, each_serializer: V1::JobSerializer)
When I try to return this it says uncaught throw :no_serializer
because I think it doesn't know what V1::JobSerializer
is.
Just to make sure:
jobs_controller.rb
is defined like this:
class V1::JobsController < ApplicationController
end
and job_serializer.rb
is defined like this:
class V1::JobSerializer < ActiveModel::Serializer
end
What should I do to be able to access V1::JobSerializer
inside of my jobs controller?
The scope resolution operator ::
should never be used when declaring nested classes / modules. Always use explicit nesting:
# Bad:
class V1::JobsController < ApplicationController
puts JobSerializer.inspect # missing constant error
end
class V1::JobSerializer < ActiveModel::Serializer
end
# Good:
module V1
class JobsController < ApplicationController
puts JobSerializer.inspect # resolves to V1::JobSerializer
end
end
module V1
class JobSerializer < ActiveModel::Serializer
end
end
Why? Because when you use the scope resolution operator the module nesting is resolved to the place of definition. And this can lead to extremely suprising constant lookup:
A = "I'm in the main scope"
module B
A = "I'm in B"
D = "Hello"
end
class B::C
puts A # outputs "I'm in the main scope"
puts D # Missing constant error
end
When you use explicit nesting you actually reopen the module/class and set the correct module nesting so that constants are resolved in the same module:
module B
class C
puts A # outputs "I'm in B"
puts D # outputs "Hello"
end
end