ruby:如何正确要求(以避免循环依赖)
今天我遇到了一个奇怪的问题: 在模块上出现“缺少方法”错误,但该方法在那里并且需要定义该模块的文件。经过一番搜索后,我发现了一个循环依赖,其中两个文件相互需要,现在我假设 ruby 默默地中止了循环需要。
编辑开始:示例
文件'a.rb':
require './b.rb'
module A
def self.do_something
puts 'doing..'
end
end
文件'b.rb':
require './a.rb'
module B
def self.calling
::A.do_something
end
end
B.calling
执行b.rb给出b.rb:5:in'calling':未初始化的常量A(NameError)
。这两个文件都必须存在需求,因为它们打算从命令行单独运行(我省略了该代码以保持简短)。 所以 B.calling 必须存在。一种可能的解决方案是将需求包装在 if __FILE__ == $0
中,但这似乎不是正确的方法。
编辑 End
以避免这些难以发现的错误(顺便说一句,如果 require 抛出异常不是更好吗?),是否有一些关于如何构建项目以及在哪里需要什么的指南/规则?例如,如果我
module MainModule
module SubModule
module SubSubModule
end
end
end
应该在哪里需要子模块?全部在主中,还是只有子在主中,子子在子中?
任何帮助都会非常好。
摘要
forforfs 的回答和评论中讨论了为什么会发生这种情况的解释。
到目前为止,最佳实践(正如lain所指出或暗示的那样)似乎如下(如果我错了,请纠正我):
- 将顶部命名空间中的每个模块或类放在以模块/类命名的文件中。在我的示例中,这将是 1 个名为“main_module.rb”的文件。 如果有子模块或子类,则创建一个以模块/类命名的目录(在我的示例中为目录“main_module”,并将子类/子模块的文件放入其中(在示例 1 中名为“sub_module.rb”的文件)对命名空间的每个级别都
- 需要逐步重复此操作(在示例中,
MainModule
需要SubModule
,而 .Submodule
将需要SubSubModule
) - 将运行代码中的“运行”代码与“定义”代码分开,需要一次顶级模块/类,因此因为 2.现在您的所有库功能都应该可用,并且您可以运行任何定义的方法,
感谢所有回答/评论的人,这对我帮助很大!
today i was facing a strange problem:
got a 'missing method' error on a module, but the method was there and the file where the module was defined was required. After some searching i found a circular dependency, where 2 files required each other, and now i assume ruby silently aborts circular requires.
Edit Begin: Example
File 'a.rb':
require './b.rb'
module A
def self.do_something
puts 'doing..'
end
end
File 'b.rb':
require './a.rb'
module B
def self.calling
::A.do_something
end
end
B.calling
Executing b.rb gives b.rb:5:in 'calling': uninitialized constant A (NameError)
. The requires have to be there for both files as they are intended to be run on their own from command line (i ommitted that code to keep it short).
So the B.calling has to be there. One possible solution is to wrap the requires in if __FILE__ == $0
, but that does not seem the right way to go.
Edit End
to avoid these hard-to-find errors (wouldn't it be nicer if the require threw an exception, by the way?), are there some guidelines/rules on how to structure a project and where to require what? For example, if i have
module MainModule
module SubModule
module SubSubModule
end
end
end
where should i require the submodules? all in the main, or only the sub in the main and the subsub in the sub?
any help would be very nice.
Summary
An explanation why this happens is discussed in forforfs answer and comments.
So far best practice (as pointed out or hinted to by lain) seems to be the following (please correct me if i'm wrong):
- put every module or class in the top namespace in a file named after the module/class. in my example this would be 1 file named 'main_module.rb.'
if there are submodules or subclasses, create a directory named after the module/class (in my example a directory 'main_module', and put the files for the subclasses/submodules in there (in the example 1 file named 'sub_module.rb'). repeat this for every level of your namespace. - require step-by-step (in the example, the
MainModule
would require theSubModule
, and theSubmodule
would require theSubSubModule
) - separate 'running' code from 'defining' code. in the running code require once your top-level module/class, so because of 2. all your library functionality should now be available, and you can run any defined methods.
thanks to everyone who answered/commented, it helped me a lot!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不久前在 Ruby 邮件列表上询问过这个问题后,当我的库中曾经有一个文件只是为了需要一些东西时,我更改了这两条规则:
如果一个文件需要来自同一库中另一个文件的代码,我在需要代码的文件中使用
require_relative
。如果文件需要来自不同库的代码,我会在需要代码的文件中使用
require
。据我了解,Ruby 按照要求的顺序进行请求,因此循环依赖关系并不重要。
(Ruby v1.9.2)
回答关于显示循环依赖问题的示例的评论:
实际上,该示例的问题不在于 require 是循环的,而是在之前调用了
B.calling
要求已完成。如果你从 b.rb 中删除 B.calling ,它就可以正常工作。例如,在 irb 中,代码文件中没有 B.calling,但随后运行:After asking about this on the Ruby mailing list a while back, when I used to have a file in my libraries just for requiring things, I changed to these two rules:
If a file needs code from another in the same library, I use
require_relative
in the file that needs the code.If a file needs code from a different library, I use
require
in the file that needs the code.As far as I understand it, Ruby requires in the order it is asked to, and so it doesn't matter about circular dependencies.
(Ruby v1.9.2)
In answer to the comment about the example showing circular dependency problems:
actually, the problem with the example isn't that the requires are circular, but that
B.calling
is called before the requires have completed. If you remove the B.calling from b.rb it works fine. For example, in irb without B.calling in the code file but run afterwards:希望您已经了解一些基本知识:
Ruby 是解释型的,而不是编译型的,因此您无法执行解释器未见过的任何代码。
require
只是将文件中的代码插入到程序的该位置,换句话说,程序顶部的require
将在 < code>require 在底部。(注意:编辑以考虑 require 语句行为)
所以如果你要这样做:
ruby a.rb
这是 ruby 解释器将看到并执行的内容:如果您先运行 b,
ruby b.rb
,解释器将看到:希望这解释了其他人给了你很好的答案,如果你想一想,为什么很难回答你最后一个关于在哪里放置 require 语句的问题。使用 Ruby,您需要的是文件而不是模块,因此您将需求放在代码中的位置取决于文件的组织方式。
如果您绝对需要能够定义模块并以随机顺序执行方法,那么您可以实现类似的方法来收集对尚不存在的模块的调用,然后在它们出现时调用它们。
请务必先定义 Delay 模块,然后使用
Delay.exec(:B, :calling, any_other_args)
,而不是调用B.calling
。因此,如果在“延迟”模块之后有以下内容:结果:
最后一步是将代码分解为文件。一种方法可能是拥有三个文件,
只要您确保
require 'delay'
是模块文件的第一行(a.rb 和 b.rb),并且 Delay 包含在模块文件的末尾模块,一切应该可以工作。最后注意:仅当您无法将定义代码与模块执行调用分离时,此实现才有意义。
A couple of basic things that you hopefully already know:
Ruby is interpreted, not compiled, so you can't execute any code that hasn't been seen by the interpreter.
require
just inserts the code from the file into that point of the program, in other words, arequire
at the top of the program will be interpreted before arequire
at the bottom.(Note: Edited to account for require statements behavior)
So if you were to do:
ruby a.rb
this is what the ruby interpreter would see and execute:If instead you ran b first,
ruby b.rb
, the interpreter would see:Hopefully this explains the good answers the others have given you, and if you think about it, why it's hard to answer your last question about where to put require statements. With Ruby, you're requiring files not modules, so where you put the require in your code, depends on how your files are organized.
If you absolutely need to be able to have modules defined and methods execute in random order, then you could implement something like this to collect calls on modules that don't yet exist, and then call them when they pop into being.
Be sure to define the Delay module first and then rather than calling
B.calling
you would useDelay.exec(:B, :calling, any_other_args)
. So if you have this after the Delay module:Results in:
Final step is to break the code up into files. One approach could be to have three files
As long as you make sure
require 'delay'
is the first line of the module files (a.rb and b.rb) and Delay included at the end of the module, things should work.Final Note: This implementation only makes sense if you cannot decouple your definition code from the module execution calls.