微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ruby-on-rails – 为什么Ruby on Rails在编辑代码后虚假地提出“无法自动加载常量”?

自动加载(和重新加载)通常在我的 Ruby(2.3.0)Rails(5.0.1)项目中正常工作.但是,在开发模式下,我偶尔会看到如下错误

Unable to autoload constant Foo::Bar,expected /app/models/foo/bar.rb to define it

这是意外的,因为:

>第一个请求工作正常(它已经自动加载过一次).
>它仅在编辑代码并发送新请求后出现.
>并非总是如此.我无法弄清楚为什么有时它无法重新加载.
>文件(foo / bar.rb)确实定义了Foo :: Bar.

而且,foo / bar.rb的代码很简单:

module Foo
  class Bar < Customrecord
  end
end

简单的解决方法是重新启动服务器,然后再次发送请求(总是成功). FWIW,我正在使用zeus server.

我最好的猜测是没有重新加载某些东西,但我不确定如何进行调试.我似乎无法指出导致问题的任何具体行动.有时编辑代码会导致它发生,有时不会.

解决方法

您可以找到有关Rails自动加载行为的几个很好的解释. This answer提供了一个有用的解释,它从 this blog post at urbanautomaton.com开始抓取.

摘要

基本上,Rails首先使用Ruby的常量查找.如果失败,那么它有自己的查找,如果错过了文件

>它们不在自动加载路径中(如果config.autoload_paths =%W(#{config.root} / foo)未添加到config / application.rb,则Rails将找不到app / foo / bar.rb)
>不遵循目录名称/命名空间约定

它将在app / foo / bar.rb中找到Foo :: Bar,如果它被定义为:

module Foo
  class Bar
  end
end

但不是

module Foo
  module Bar
  end
end

>引用不同名称空间中的常量,而不在另一个自动加载的文件中指示该名称空间

引用博客文章描述我的意思:

At this point,we’ve only seen how a single constant name maps to a
single file name. But as we kNow,a constant reference in Ruby can
resolve to a number of different constant deFinitions,which vary
depending on the nesting in which the reference was made. How does
Rails handle this?

The answer is,“partially”. As Module#const_missing passes no nesting
information to the receiver,Rails does not kNow the nesting in which
the reference was made,and it must make an assumption. For any
reference to a constant Foo::Bar::Baz,it assumes the following:

06002

end

In other words,it assumes the maximum nesting possible for a given constant
reference. The example reference is therefore treated exactly the same
as the following:

Foo::Bar::Baz # Module.nesting => []

06003

While there’s
been a significant loss of information,Rails does have some extra
information it can use. It kNows that Ruby Failed to resolve this
particular constant reference using its regular lookup,meaning that
whatever constant it should refer to cannot be already loaded.

When Foo::Bar::Baz is referred to,then,Rails will attempt to load
the following constants in turn,until it finds one that is already
loaded:

  • Foo::Bar::Baz
  • Foo::Baz
  • Baz

As soon as an already-loaded constant Baz
is encountered,Rails kNows this cannot be the Baz it is looking for,
and the algorithm raises a NameError.

如果没有关于如何设置代码的详细信息,很难说出究竟是什么导致了您的问题,但希望了解Rails中自动加载的情况可能会有所帮助.我强烈建议您阅读上面链接的博文和答案.

编辑可能的解决方案:

确保将’app / models / foo’添加到config / application.rb中的autoload_load路径 – 或将’foo’移动到app / models / concern,认情况下应该存在.

通常添加显式require语句会有所帮助 – 特别是如果事情在开发中正确加载但在测试中没有.我最近通过添加一个需要所有嵌套文件文件解决了这个问题(我的目录结构没有镜像我的命名空间结构),然后在我的测试助手中需要该文件.

第二次编辑

包括与问题相关的信息,为什么常量只能成功加载一次.

the same blog post

If constants are loaded only when they’re first encountered at
runtime,then by necessity their load order depends on the individual
execution path. This can mean that the same constant reference
resolves to different constant deFinitions in two runs of the same
code. Worse still,the same constant reference twice in a row can give
different results.

Let’s go back to our last example. What happens if we call .print_qux
twice?

06004

NameError: uninitialized constant Foo::Bar::Qux This is disastrous!
First we’ve been given the wrong result,and then we’ve been
incorrectly told that the constant we referred to doesn’t exist. What
on earth led to this?

The first time,as before,is down to the loss of nesting information.
Rails can’t kNow that Foo::Qux isn’t what we’re after,so once it
realises that Foo::Bar::Qux does not exist,it happily loads it.

The second time,however,Foo::Qux is already loaded. So our reference
can’t have been to that constant,otherwise Ruby would have resolved
it,and autoloading would never have been invoked. So the lookup
terminates with a NameError,even though our reference Could (and
should) have resolved to the as-yet-unloaded ::Qux.

We can fix this by referring to ::Qux first,ensuring that it’s loaded
for Ruby to resolve the reference:

06005

A funny thing has happened here. In order to get correct behavIoUr,we
deliberately loaded the constant we needed before we used it (albeit
indirectly,by referring to it,rather than loading the file that
defined it).

But wait; isn’t this suspicIoUsly close to explicitly loading our
dependencies with require,the very thing autoloading was supposed to
save us from?

To be fair,we Could also have fixed the issue by fully qualifying all of our constant references,i.e. making sure that within .print_qux we referred to ::Qux and not the ambiguous Qux. But this still costs us our existing intuitions about Ruby’s behavIoUr. Moreover,without intimate kNowledge of the autoloading process,we would have been hard pressed to deduce that this was necessary.

原文地址:https://www.jb51.cc/ruby/267886.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐