文件路径没有被加载,尽管文件路径只是打印出来时显示在文件列表数组中

时间:2014-11-14 15:55:05

标签: ruby require

我有一个奇怪的行为问题,涉及以下代码:

ignore_list = %w(boot boot_as_service required test)
info_file = ''
file_list = []
Dir.chdir(__dir__){
  Dir.glob('**/*.rb') do | file|
    t = file.split('/').last.split('.')
    f_name = t.first
    f_type = t.last
    # puts [f_name,f_type].inspect
    next unless f_type == 'rb'
    next if ignore_list.include?(f_name)
    info_file = file.gsub('/','\\') and next if f_name == 'info'
    file_list << file.gsub('/','\\')
  end
}
file_list.unshift(info_file)

然后我依次要求file_list中的每个文件:

file_list.each do |file|
  puts "loading #{File.basename(file)}"
  require  __dir__.gsub('/','\\')  +  "\\#{file.gsub('lib\\','')}"
end

当我运行代码并打印出数组时,我得到:

["gg-web-opsboard\\info.rb", "gg-web-opsboard\\data\\data.rb", "gg-web-opsboard\\helpers\\helpers.rb", "gg-web-opsboard\\main.rb", "gg-web-opsboard\\routing\\a
ctions.rb", "test.rb"]
像我期望的那样,当我将我的应用程序作为gem安装,并尝试运行启动它的命令时,我得到了这个: <class:GG_Web_OpsBoard>': uninitialized constant Sinatra::GG_Web_OpsBoard::Actions (NameError)

并且加载的文件是:

Starting Sinatra app on 0.0.0.0:3000
booting...
loading info.rb
loading data.rb
loading helpers.rb
loading main.rb

所以我的问题是,actions.rb被忽略的原因是什么?

2 个答案:

答案 0 :(得分:1)

您可以采取一些措施来简化代码。

从您的代码开始:

ignore_list = %w(boot boot_as_service required test)
info_file = ''
file_list = []
Dir.chdir(__dir__){
  Dir.glob('**/*.rb') do | file|
    t = file.split('/').last.split('.')
    f_name = t.first
    f_type = t.last
    # puts [f_name,f_type].inspect
    next unless f_type == 'rb'
    next if ignore_list.include?(f_name)
    info_file = file.gsub('/','\\') and next if f_name == 'info'
    file_list << file.gsub('/','\\')
  end
}
file_list.unshift(info_file)

首先,Ruby并不关心Windows上的文件名是否使用反斜杠或转义斜杠。这来自the IO documentation

  如果可能,Ruby将在不同的操作系统约定之间转换路径名。例如,在Windows系统上,文件名为&#34; /gumby/ruby/test.rb"将打开为#34; \ gumby \ ruby​​ \ test.rb&#34;。

换句话说,省去转换为路径中反斜杠的麻烦,让Ruby处理它。这减少了行

info_file = file.gsub('/','\\') and next if f_name == 'info'
file_list << file.gsub('/','\\')

info_file = file and next if f_name == 'info'
file_list << file

这些行中的第一行可以更具可读性。将and next与尾随if条件一起使用是尝试将代码缩减为单行,但这不应成为目标,因为它不会提高性能或可维护性。相反,我会使用:

if f_name == 'info'
  info_file = file
  next 
end

该行

next unless f_type == 'rb'

可以完全删除,因为

Dir.glob('**/*.rb')

只返回.rb作为其扩展名的文件。

让我们看一下你用

做什么
ignore_list = %w(boot boot_as_service required test)

它是一个数组,您想使用include?检查文件是否在该列表中。每次检查时,必须走这个数组,这可能会变得很昂贵,无论是数组的大小增加还是被检查的文件数量增加。相反,使用Set,它也有include?方法,但比数组快得多,因为Set基于哈希,所以它是随机访问,而不是顺序访问。另外,因为ignore_list表现得像一个常数,我会把它作为一个:

include 'set'
IGNORE_LIST = %w(boot boot_as_service required test).to_set

看着

t = file.split('/').last.split('.')
f_name = t.first
f_type = t.last
next if IGNORE_LIST.include?(f_name)
当你只是试图从文件中获取基本名称和扩展名时,它会非常复杂。我这样做:

f_type = File.extname(file)
f_name = File.basename(file, f_type)
next if IGNORE_LIST.include?(f_name)

前两行是这样做的:

f_type = File.extname(file)           # => ".rb"
f_name = File.basename(file, f_type)  # => "file"

f_type现在有一个领先.并不重要,因为我们已经知道测试f_type == 'rb'并非必要。相反,basename可以使用它来删除extname返回的扩展名,保留文件名减去扩展名。

这使代码看起来像:

include 'set'
IGNORE_LIST = %w(boot boot_as_service required test).to_set

info_file = ''
file_list = []

Dir.chdir(__dir__) do

  Dir.glob('**/*.rb') do | file|

    f_type = File.extname(file)
    f_name = File.basename(file, f_type)
    next if IGNORE_LIST.include?(f_name)

    if f_name == 'info'
      info_file = file
      next 
    end

    file_list << file

  end
end

file_list.unshift(info_file)

这是一个相当合理的复杂性降低,并且我认为代码更易读,更易于维护。但是,有一些关于Ruby的东西以及它如何让我们像处理有用的集合一样对待数组。考虑一下:

IGNORE_LIST = %w(boot boot_as_service required test).map{ |s| "#{ s }.rb" }

files = %w[a.rb boot.rb boot_as_service.rb q.rb required.rb s.rb test.rb]

如果filesDir.glob('**/*.rb')的输出,并且IGNORE_LIST透明地包含了扩展名,那么我们可以轻松找到目录中存在哪些不被忽略:

files - IGNORE_LIST # => ["a.rb", "q.rb", "s.rb"]

知道了,代码简化为:

IGNORE_LIST = %w(boot boot_as_service required test info).map{ |s| "#{ s }.rb" }

file_list = []

Dir.chdir(__dir__) do
  file_list = Dir.glob('**/*.rb') - IGNORE_LIST
end

file_list << 'info.rb'

唯一的问题是名称Dir.glob返回路径,因此必须适应。创建一个哈希,其中键是文件的基本名称会有所帮助。 group_by让事情变得简单:

files = %w[/foo/a.rb /foo/b.rb /bar/a.rb /bar/b.rb]
files_by_basenames = files.group_by{ |f| File.basename(f) }
files_by_basenames # => {"a.rb"=>["/foo/a.rb", "/bar/a.rb"], "b.rb"=>["/foo/b.rb", "/bar/b.rb"]}

我们可以轻松抓住钥匙:

files_by_basenames.keys # => ["a.rb", "b.rb"]

这使我们回到可以测试块中所需文件的位置,因为所有期望的完整路径名文件都作为数组分组在值中。这里有一些代码要考虑:

files = %w[/foo/a.rb /foo/b.rb /foo/boot.rb /bar/a.rb /bar/b.rb /bar/test.rb]
files_by_basenames = files.group_by{ |f| File.basename(f) }
files_by_basenames # => {"a.rb"=>["/foo/a.rb", "/bar/a.rb"], "b.rb"=>["/foo/b.rb", "/bar/b.rb"], "boot.rb"=>["/foo/boot.rb"], "test.rb"=>["/bar/test.rb"]}

files_by_basenames.keys # => ["a.rb", "b.rb", "boot.rb", "test.rb"]

good_files = files_by_basenames.keys - %w[boot.rb test.rb] # => ["a.rb", "b.rb"]

files_by_basenames.values_at(*good_files) # => [["/foo/a.rb", "/bar/a.rb"], ["/foo/b.rb", "/bar/b.rb"]]

files_by_basenames.values_at(*good_files).flatten # => ["/foo/a.rb", "/bar/a.rb", "/foo/b.rb", "/bar/b.rb"]

将代码重新组合在一起:

IGNORE_LIST = %w(boot boot_as_service required test info).map{ |s| "#{ s }.rb" }

file_list = []

Dir.chdir(__dir__) do
  files_by_basenames = Dir.glob('**/*.rb').group_by{ |f| File.basename(f) }
  good_filenames = files_by_basenames.keys - IGNORE_LIST
  file_list = files_by_basenames.values_at(*good_filenames).flatten
end

file_list << 'info.rb'

因为我不知道您的层次结构会看到哪些路径,所以很难说出在&#39; info.rb&#39前面应该有哪些路径名称;,但即便如此,也可以通过以下方式快速找到:

IGNORE_LIST = %w(boot boot_as_service required test).map{ |s| "#{ s }.rb" }

file_list = []

Dir.chdir(__dir__) do
  files_by_basenames = Dir.glob('**/*.rb').group_by{ |f| File.basename(f) }
  good_filenames = files_by_basenames.keys - IGNORE_LIST
  info_files = files_by_basenames.delete('info.rb')
  file_list = files_by_basenames.values_at(*good_filenames).flatten
end

file_list += info_files

&#34;信息&#34;已从IGNORE_LIST移除,因此我们可以将其捕获到info_files,然后在事后添加它们。

那是未经测试的,但看起来是正确的。

答案 1 :(得分:0)

我刚刚意识到问题是什么,它根本没有跳过actions.rb,它只是没有达到它,我只需要确保最后加载main.rb即可。 我可以通过将info_files更改为哈希然后执行以下操作来完成此操作:

file_list.unshift(ordered_files[:info]) << ordered_files[:main]