>上下文
我通过以下方式自动要求目录结构中的所有文件
# base.rb
dir = File.dirname(__FILE__)
path = File.join(dir, '**', '*.rb')
Dir[path].each { |file| require File.expand_path(file, __FILE__) }
并通过单独文件中的 require
语句调用此代码段,api.rb
.
问题
这个代码片段包括它自己(base.rb
(以及api.rb
。
问题
有没有一种"干净"的方式来执行这种类型的自动要求,同时动态避免包含调用自动要求器的文件(即 api.rb
(?
请记住,当您多次require
由特定路径标识的文件时,对require
的每个后续调用都将返回false
,并且不会重新评估该文件。因此,如果你require
其他一切的base.rb
本身是require
d,进一步证明require
它不应该导致重新评估。
让我们用一个例子来演示它。创建一个包含 3 个文件的lib
目录。
# lib/a.rb
require 'base'
puts :a
# lib/b.rb
require 'base'
puts :b
# lib/base.rb
$counter ||= 0
puts "Evaluated base.rb #{$counter += 1} times"
dir = File.dirname(__FILE__)
path = File.join(dir, '**', '*.rb')
Dir[path].each { |file| require File.expand_path file }
直接执行lib/base.rb
。 base.rb
将被评估两次:首先,当它直接执行时;其次,当它被a.rb
require
时。请注意,当需要时,不会对其进行评估 b.rb
.
$ ruby -I lib lib/base.rb
Evaluated base.rb 1 times
Evaluated base.rb 2 times
a
b
与require
比较。现在,base.rb
仅评估一次,因为尝试在 a.rb
和 b.rb
中require
它之前,使用命令行-r
开关将文件require
d。
$ ruby -I lib -r base -e 'puts :ok'
Evaluated base.rb 1 times
a
b
ok
Kernel.caller以字符串数组的形式返回当前执行堆栈。你可以避免调用 require 在你找到的文件名,你可以在该数组中找到。多个文件相同的基本名称会绊倒这一点。我看不出有什么办法可以更精确祖先文件列表。
$ head *.rb
==> A.rb <==
require 'base'
puts :A
==> B.rb <==
require 'base'
puts :B
==> CA.rb <==
require 'base'
puts :CA
==> base.rb <==
dir = File.dirname(__FILE__)
path = File.join(dir, '**', '*.rb')
required = caller.map { |frame| /^(.+):d+:in `require'$/.match(frame) and File.basename $1 }.compact
Dir[path].each { |file| required.include?(File.basename file) or require File.expand_path file }
$ ruby A.rb
B
CA
A
$ ruby B.rb
A
CA
B
$ ruby CA.rb
A
B
CA
$ ruby base.rb
B
CA
A
$