1

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?

crodev
  • 1,323
  • 6
  • 22
  • 39
  • Possible dup of https://stackoverflow.com/questions/37053962/activemodelserializercollectionserializernoserializererror-in-active-model – McFadden May 28 '20 at 20:32
  • Try supplying the namespace option instead, https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/rendering.md#namespace – Kevin Etore May 28 '20 at 20:34
  • Surprisingly it worked by only removing `V1::` from the class naming in `job_serializer.rb` and adding `module V1`. Any idea why that would work? – crodev May 28 '20 at 20:44

1 Answers1

4

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
max
  • 96,212
  • 14
  • 104
  • 165