Rails class loading skips namespaced class when another class of same name in root namespace is loaded

时间:2022-01-26 22:52:27

I have two namespaces, each with its own controller and presenter classes: Member::DocumentsController Member::DocumentPresenter Guest::DocumentsController Guest::DocumentPresenter

我有两个名称空间,每个名称空间都有自己的控制器和演示者类:Member :: DocumentsController Member :: DocumentPresenter Guest :: DocumentsController Guest :: DocumentPresenter

Both presenters inherit from ::DocumentPresenter.

两位演示者都继承自:: DocumentPresenter。

Controllers access their respective presenters without namespace specified, e.g.:

控制器在没有指定名称空间的情况下访问各自的演示者,例如:

class Guest::DocumentsController < ActionController::Base
    def show
        DocumentPresenter.new(find_document)
    end
end

This usually calls presenter within same namespace. However sometimes in development environment I see base ::DocumentPresenter is being used.

这通常会在同一名称空间内调用presenter。但有时在开发环境中我看到base :: DocumentPresenter正在使用中。

I suspect the cause is that base ::DocumentPresenter is already loaded, so Rails class auto-loading doesn't bother to look further. Is this likely the case? Can it happen in production environment too?

我怀疑原因是base :: DocumentPresenter已经加载了,所以Rails类自动加载并不需要进一步查看。这可能是这种情况吗?它也可以在生产环境中发生吗?

I can think of two solutions:

我可以想到两个解决方案:

  • rename base class to DocumentPresenterBase
  • 将基类重命名为DocumentPresenterBase

  • explicitly require appropriate presenter files in controller files
  • 在控制器文件中明确要求适当的presenter文件

Is there a better solution?

有更好的解决方案吗?

2 个解决方案

#1


3  

You are correct in your assumptions - If you do not specify namespace, Ruby starts from current namespace and works its way up to find the class, and because the namespaced class is not autoloaded yet, the ::DocumentPresenter is found and autoloader does not trigger.

你的假设是正确的 - 如果你没有指定命名空间,Ruby从当前命名空间开始并一直向上找到类,并且因为命名空间类还没有自动加载,所以找到:: DocumentPresenter并且自动加载器不会触发。

As a solution I would recommend renaming ::DocumentPresenter to DocumentPresenterBase, because this protects you from bugs when you forget namespacing or explicit requiring somewhere.

作为一种解决方案,我建议将:: DocumentPresenter重命名为DocumentPresenterBase,因为当您忘记命名空间或明确要求某处时,这可以保护您免受错误的影响。

The second option to consider would actually be using specific namespaced classnames all over the place, but this suffers from bugs when you accidentally forget to namespace some call.

要考虑的第二个选项实际上是在整个地方使用特定的命名空间类名,但是当你不小心忘记命名空间某些调用时,这会遇到错误。

class Guest::DocumentsController < ActionController::Base
  def show
    Guest::DocumentPresenter.new(find_document)
  end
end 

Third option would be your second - explicitly require all the classes in initializer beforehand. I have done this with Rails API which receives embedded models in JSON and Rails tends to namespace them when the actual models are not loaded yet.

第三个选项将是您的第二个选项 - 事先明确要求初始化程序中的所有类。我用Rails API完成了这个操作,它接收JSON中的嵌入式模型,并且当实际模型尚未加载时,Rails倾向于命名它们。

Option 3.5 You could probably trick autoloader to do the heavy lifting (though, this might seem more like a hack):

选项3.5您可能会欺骗自动加载器来完成繁重的工作(但是,这可能看起来更像是一个黑客):

class Guest::DocumentsController < ActionController::Base

  # trigger autoload
  Guest::DocumentPresenter

  def show
    # This should refer Guest::DocumentPresenter
    DocumentPresenter.new(find_document)
  end

  def show
    # As will this
    DocumentPresenter.new(find_document)
  end
end 

Still the cleanest would be to rename the base class.

仍然最干净的是重命名基类。

#2


1  

I think in 3 solutions if you want to mantein the name, one is your second solution.

我想在3个解决方案中如果你想要mantein这个名字,那么你的第二个解决方案就是。

1) explicitly require appropriate presenter files in controller files

1)在控制器文件中明确要求适当的presenter文件

2) Execute the full environment class path, like:

2)执行完整的环境类路径,如:

class Guest::DocumentsController < ActionController::Base
    def show
        Guest::DocumentPresenter.new(find_document)
    end
end

3) Create a file on initialize directory and execute require manually (the worst options :S)

3)在initialize目录上创建一个文件并手动执行require(最差的选项:S)

#1


3  

You are correct in your assumptions - If you do not specify namespace, Ruby starts from current namespace and works its way up to find the class, and because the namespaced class is not autoloaded yet, the ::DocumentPresenter is found and autoloader does not trigger.

你的假设是正确的 - 如果你没有指定命名空间,Ruby从当前命名空间开始并一直向上找到类,并且因为命名空间类还没有自动加载,所以找到:: DocumentPresenter并且自动加载器不会触发。

As a solution I would recommend renaming ::DocumentPresenter to DocumentPresenterBase, because this protects you from bugs when you forget namespacing or explicit requiring somewhere.

作为一种解决方案,我建议将:: DocumentPresenter重命名为DocumentPresenterBase,因为当您忘记命名空间或明确要求某处时,这可以保护您免受错误的影响。

The second option to consider would actually be using specific namespaced classnames all over the place, but this suffers from bugs when you accidentally forget to namespace some call.

要考虑的第二个选项实际上是在整个地方使用特定的命名空间类名,但是当你不小心忘记命名空间某些调用时,这会遇到错误。

class Guest::DocumentsController < ActionController::Base
  def show
    Guest::DocumentPresenter.new(find_document)
  end
end 

Third option would be your second - explicitly require all the classes in initializer beforehand. I have done this with Rails API which receives embedded models in JSON and Rails tends to namespace them when the actual models are not loaded yet.

第三个选项将是您的第二个选项 - 事先明确要求初始化程序中的所有类。我用Rails API完成了这个操作,它接收JSON中的嵌入式模型,并且当实际模型尚未加载时,Rails倾向于命名它们。

Option 3.5 You could probably trick autoloader to do the heavy lifting (though, this might seem more like a hack):

选项3.5您可能会欺骗自动加载器来完成繁重的工作(但是,这可能看起来更像是一个黑客):

class Guest::DocumentsController < ActionController::Base

  # trigger autoload
  Guest::DocumentPresenter

  def show
    # This should refer Guest::DocumentPresenter
    DocumentPresenter.new(find_document)
  end

  def show
    # As will this
    DocumentPresenter.new(find_document)
  end
end 

Still the cleanest would be to rename the base class.

仍然最干净的是重命名基类。

#2


1  

I think in 3 solutions if you want to mantein the name, one is your second solution.

我想在3个解决方案中如果你想要mantein这个名字,那么你的第二个解决方案就是。

1) explicitly require appropriate presenter files in controller files

1)在控制器文件中明确要求适当的presenter文件

2) Execute the full environment class path, like:

2)执行完整的环境类路径,如:

class Guest::DocumentsController < ActionController::Base
    def show
        Guest::DocumentPresenter.new(find_document)
    end
end

3) Create a file on initialize directory and execute require manually (the worst options :S)

3)在initialize目录上创建一个文件并手动执行require(最差的选项:S)