在 Sinatra 中要求规范文件的更简单方法

Easier way to require spec files in Sinatra

我有一个 spec_helper 看起来像这样:

require 'pry'
require 'helpers/data_helper.rb'
require 'distributer.rb'
require 'house_distributer.rb'
require 'accounting_service.rb'
require 'mixer_worker.rb'
require 'mixer.rb'
require 'transaction_service.rb'

ENV['RACK_ENV'] = 'test'

RSpec.configure do |config|
  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.warnings = true

  config.order = :random
end

和如下所示的文件夹结构:

.
├── Gemfile
├── Gemfile.lock
├── README.md
├── app.rb
├── config.ru
├── lib
│   ├── accounting_service.rb
│   ├── distributer.rb
│   ├── house_distributer.rb
│   ├── mixer.rb
│   ├── mixer_worker.rb
│   └── transaction_service.rb
├── public
│   ├── css
│   │   └── add_coins.css
│   ├── images
│   │   └── bitcoin_dawg.jpg
│   └── javascripts
│       └── add_coins.js
├── spec
│   ├── helpers
│   │   └── data_helper.rb
│   ├── lib
│   │   ├── accounting_service_spec.rb
│   │   └── transaction_service_spec.rb
│   └── spec_helper.rb
└── views
    └── add_coins.erb

这不起作用:

Dir["lib/*.rb"].each {|file| require file }

[1] pry(main)> Dir["lib/*.rb"]
=> ["lib/house_distributer.rb", "lib/distributer.rb", "lib/mixer.rb", "lib/accounting_service.rb", "lib/mixer_worker.rb", "lib/transaction_service.rb"]

我收到此错误消息:

/Users/jwan/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- lib/house_distributer.rb (LoadError)
    from /Users/jwan/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'

我该怎么做才能使这更容易?

另请注意,distributer.rb 必须在 house_distributer.rb 之前加载,因为:

class HouseDistributer < Distributer
end

找不到文件是因为您使用 "lib/*.rb" 而不是 ./"lib/*.rb"

为确保以正确的顺序加载依赖项,您可以这样做:

  1. 将 HouseDistributor 移动到 lib/distributor/house_distributor.rb

  2. 需要这样的文件:

    Dir['./lib/**/*.rb']
    .sort_by { |path| path.count("/") }
    .each { |path| require path }
    

    这使用 **/*.rb 进行递归搜索,并在要求

  3. 之前按“/”的计数(它们的深度)对文件进行排序

请注意,如果您正在执行递归要求,请记住您实际上确实需要所有这些文件。例如,如果您正在使用 ActiveRecord 并且有一个 schema.rb 文件,您可能不想要求它。

max pleaner 回答的一些解释...

当你写:

require 'lib/house_distributer.rb'

ruby 在分配给 $LOAD_PATH 环境变量的目录中查找文件。 $LOAD_PATH 是一个字符串数组,其中每个字符串都是一个目录路径。 $LOAD_PATH 数组中的目录按顺序搜索,第一个匹配的获胜。

如果 $LOAD_PATH 包含名为:

的目录
'/Users/7stud/ruby_programs'

那么上面的require语句会查找绝对路径为:

的文件
'/Users/7stud/ruby_programs/lib/house_distributer.rb'

您可以像这样检查 $LOAD_PATH 中有哪些目录:

$ puts $LOAD_PATH

这是我得到的:

/Users/7stud/.rvm/gems/ruby-2.4.0@global/gems/did_you_mean-1.1.0/lib
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0/x86_64-darwin14
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/vendor_ruby/2.4.0
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/vendor_ruby/2.4.0/x86_64-darwin14
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/vendor_ruby
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0/x86_64-darwin14

显然,您应用的文件不在那些目录中。

另一方面,如果您 require 一个路径以 /. 开头的文件——例如:

require './lib/house_distributer.rb'

然后 ruby 跳过 $LOAD_PATH,在这种情况下 ruby 查找相对于 当前工作目录 的文件。但是请注意,当前工作目录 可能不是包含带有 require 语句的文件的目录。例如,如果你从不同的目录执行你的 sinatra 程序,比如从包含 require 语句的文件向上两级,那么向上两级目录将是 当前工作目录 , ruby 将查找相对于上两级目录的所需文件。

输入require_relativerequire_relative 将查找相对于当前文件路径的文件——而不是当前工作目录。

因此,您可能永远不应该将 require 与相对路径一起使用,而应使用 require_relative.

请注意,您也可以随时以编程方式将路径添加到 $LOAD_PATH:

$LOAD_PATH << '/Users/7stud/ruby_programs'

如果该目录中有一个名为 dog.rb is 的文件,我可以这样要求它:

require 'dog'  #extension is unnecessary

回复评论:

最简单的做法是:

$LOAD_PATH << "/Users/jwan/Desktop/programming/interview_questions/gemini/‌​jobcoin_mixer/"

但是在sinatra中,settings.root是你的app目录的路径,所以:

$LOAD_PATH.unshift setttings.root 

这样您就可以将您的应用程序移动到另一个目录而无需更改任何内容。

或者,您可以从 Dir[] 返回的每个路径的前面删除 lib/

require 'pathname'

paths = [
  "lib/house_distributer.rb", 
  "lib/distributer.rb", 
  "lib/mixer.rb", 
  "lib/accounting_service.rb", 
]

new_paths = paths.map do |path|
  pn = Pathname.new path
  pn.relative_path_from(pn.parent).to_s
end

p new_paths

--output:--
["house_distributer.rb", "distributer.rb", "mixer.rb", "accounting_service.rb"]

其他 2 个答案在技术上是正确的,并且解决了 目标 但没有解决 目标。换句话说 - 你为什么要这样做? 例如,为什么这样做:

Also side note, distributer.rb has to be loaded before house_distributer.rb because of this:

class HouseDistributer < Distributer
end

不是这个?

require_relative "distributer.rb"
class HouseDistributer < Distributer
end

并且在测试中(咳嗽)抱歉,规格

# spec/house_distributer_spec.rb
require 'spec_helper'
require_relative "../lib/house_distributer.rb"

# spec/transaction_service_spec.rb
require 'spec_helper'
require_relative "../lib/transaction_service.rb"

并且由于 transaction_service.rb 似乎需要 lib/house_distributer.rbHouseDistributer

# lib/transaction_service.rb
require_relative "house_distributer.rb"

经验法则

如果一个文件需要另一个 运行 然后 require (或 require_relative)它在需要它的文件中。 然后你得到:

  • 一种仅 运行 需要的自然沙盒(也许 Distributer 工作完美,而 HouseDistributer 由于猴子补丁而出现错误 - 你怎么知道你是否 require 文件实际上不需要?)
  • 无需在别处处理需求。
  • 不需要(或更少需要)知道要求的顺序。
  • 不需要 fiddle 加载路径(自从引入 require_relative 以来,这一直是一个可疑的需求)。
  • 更改加载路径然后使用 require 可以破坏沙箱并使规范正常工作,因为加载 gem 您已在系统上安装但未定义为依赖项gem规格

我要补充一点,也使用捆绑器的沙箱,以避免进一步的错误并让它处理您的加载路径,例如

bundle install --binstubs --path=vendor(或 Mac 上的 vendor.noindex)然后:

bin/rspec 以及您需要的任何命令行参数。