为什么我得到"未初始化的常数"引用类时出错?

时间:2014-06-24 15:51:01

标签: ruby-on-rails ruby

以下是我的数据结构的样子

Controller
   API
      V1
         Controller1.rb
         Controller2.rb
         Serializers
            Model1Serializer.rb
            Model2Serializer.rb

我试图访问控制器中的序列化程序

这是我的控制器

class API::V1::Controller1 < ApplicationController

  require_relative 'model1_serializer'

   def doStuff
      render json:MyData, each_serializer:Model1Serializer
   end

end

这是我的序列化程序

class API::V1::Serializers::Model1Serializer < ActiveModel::Serializer
   # Code here for serializing
end

我收到以下错误消息。为什么认为Model1Serializer属于Controller 1?

未初始化的常量API :: V1 :: Controller1 :: Model1Serializer

如果在我的控制器中我将Model1Serializer更改为API::V1::Serializers::Model1Serializer然后它可以正常工作,除非我不想依赖我的命名空间中的V1,这样如果我决定移动代码到V2我不会改变代码指向V2。处理此问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

  

&#34;为什么认为Model1Serializer在Controller 1之下:

严格来说,事实并非如此。查找常量时,Ruby会检查多个名称空间(如果可用),但错误消息只提到最内层名称空间。

  

为什么在引用类时会出现“未初始化的常量”错误?

要回答这个问题,了解Ruby如何查找常量非常重要。假设您有此命名空间层次结构:

module API
  module V1
    class Controller1
    end
  end
end

如果您从Model1Serializer访问Controller1,Ruby会检查以下嵌套命名空间:

  1. API::VI::Controller1::Model1Serializer
  2. API::VI::Model1Serializer
  3. API::Model1Serializer
  4. ::Model1Serializer
  5. Model1Serializer中定义的API::VI::Serializers::Model1Serializer未包含在此列表中。这就是Ruby无法找到它的原因。

    要解决此问题,您应该更改有问题的行以包含子模块:

    render json: MyData, each_serializer: Serializers::Model1Serializer
    

    但很可能它仍然无法工作,因为你正在使用&#34;快捷命名空间&#34;,即API::V1而不是module API; module V1; ...; end; end这会阻止Ruby搜索父命名空间,因为它们未添加到嵌套模块列表中。

    换句话说,Ruby只检查以下命名空间:

    1. API::VI::Controller1::Serializers::Model1Serializer
    2. ::Serializers::Model1Serializer
    3. 您可以通过在所需的代码位置调用Module.nesting来访问模块层次结构。

      免责声明:Ruby常量查找比此处介绍的要多得多。

答案 1 :(得分:1)

您的控制器为API::V1::Controller1,但您的序列化程序为API::V1::Serializers::Model1Serializer

doStuff方法中,您尝试查找Model1Serializer - 但在命名空间中无法从此处查看该类。

尝试使用API::V1::Serializers::Model1Serializer代替。


更新: OP编辑了他的问题:

  

如果在我的控制器中我将Model1Serializer更改为API :: V1 :: Serializers :: Model1Serializer然后它可以工作,除了我不想依赖于y命名空间中的Vq,这样如果我决定移动到V2的代码我不会最终改变代码指向V2。处理此问题的最佳方法是什么?

快速方法是在API模块中定义Current = V1,然后在从控制器引用模型时引用API::Current::...

更好的方法是仔细考虑为什么需要在同一个应用程序中同时提供多个API,以及这是否真的是对它进行版本化的正确方法。 (但这可能超出了单个SO答案的范围,而且过于依赖于您的具体应用。)