我正在用ruby创建一个简单的二十一点程序。我这样做是为了研究我的TDD技能,因此我使用Rspec进行测试覆盖。为了保持工作更清洁,我的文件结构是一个名为blackjack的主文件夹。其中包括lib和spec文件夹。目前在lib中有一个deck.rb和card.rb文件。然后,我为每个都有追溯规格文件夹。当我尝试在irb中加载程序时,我遇到了这样的问题。如果我想测试两个文件夹之间分割的方法,我有点困惑,我应该怎么做呢。当单独加载卡组和卡文件时,我会收到以下错误。
2.1.1 :001 > require "./deck.rb"
=> true
2.1.1 :002 > cards = Deck.build_cards
NameError: uninitialized constant Deck::Card
from /Users/em/ruby/blackjack/lib/deck.rb:7:in `block (2 levels) in build_cards'
from /Users/em/ruby/blackjack/lib/deck.rb:6:in `each'
from /Users/em/ruby/blackjack/lib/deck.rb:6:in `block in build_cards'
from /Users/em/ruby/blackjack/lib/deck.rb:5:in `each'
from /Users/em/ruby/blackjack/lib/deck.rb:5:in `build_cards'
from (irb):2
from /Users/em/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>'
2.1.1 :001 > require './card'
=> true
2.1.1 :002 > cards = Deck.build_cards
NameError: uninitialized constant Deck
from (irb):2
from /Users/em/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>'
如果有人可以帮我澄清一些事情,我将不胜感激。我已经包含了下面的其余代码。
card_spec.rb
require 'card'
require 'deck'
describe Card do
it "should accept a suit and value when building" do
card = Card.new(:clubs, 10)
expect(card.suit).to eq (:clubs)
expect(card.value).to eq(10)
end
it "should have a value of 10 for facecards" do
facecards = ["J", "Q", "K"]
facecards.each do |facecard|
card = Card.new(:hearts, facecard)
expect(card.value).to eq(10)
end
end
it "should have a value of 4 for the 4-clubs" do
card = Card.new(:clubs, 4)
expect(card.value).to eq(4)
end
it "should return 11 for Ace" do
card = Card.new(:diamonds, "A")
expect(card.value).to eq(11)
end
it "should be formatted nicely" do
card = Card.new(:diamonds, "A")
expect(card.to_s).to eq("A-diamonds")
end
end
deck_spec.rb
require 'card'
require 'deck'
describe Deck do
it 'should build 52 cards' do
expect(Deck.build_cards.length).to eq(52)
end
end
card.rb
require 'deck'
class Card
attr_reader :suit, :value
def initialize(suit, value)
@suit = suit
@value = value
#the value here is what the card should return-facevalue
end
def value
return 10 if ["J", "Q", "K"].include?(@value)
return 11 if @value == "A"
return @value
end
def to_s
"A-diamonds"
"#{@value}-#{suit}"
end
end
deck.rb
class Deck
def self.build_cards
cards = []
[:clubs, :diamonds, :spades, :hearts].each do |suit|
(2..10).each do |number|
cards << Card.new(suit, number)
end
["J", "Q", "K", "A"].each do |facecard|
cards << Card.new(suit, facecard)
end
end
cards
end
#going to occur on the class as it is a class method and will execute once
#The idea of a deck needs to be able to build a deck
end
答案 0 :(得分:3)
您需要将require "card"
添加到deck.rb
,因为您在Card
中引用了.build_cards
。您还需要将lib
添加到加载路径中,因为在Ruby中不再为您完成。您可以在命令行上执行此操作:
irb -Ilib
ruby -Ilib
请注意,rspec
会自动为您执行此操作,这就是您的规范有效的原因。
如果需要(例如,如果您在bin
中有一个脚本要加载lib
中的文件),您也可以在代码中执行此操作。
# Assuming this code is in bin
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
加载路径说明
-I
标志将目录添加到$LOAD_PATH
全局变量。您可以直接在程序中检查:
> ruby -e 'puts $LOAD_PATH'
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0/x86_64-darwin13.0
> ruby -Itesting -e 'puts $LOAD_PATH'
/Users/xavier/Code/ex/ruby/testing
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0/x86_64-darwin13.0
注意第二次调用如何包含额外的testing
目录。如果您使用IRB进行了类似的测试,您会发现如果-I
lib
目录不在$LOAD_PATH
,则无法找到您需要的文件。
Ruby使用此$LOAD_PATH
变量来确定在需要时查看的位置。在伪代码中:
def require(filename)
$LOAD_PATH.each do |dir|
path = File.join(dir, filename)
if File.exist?(path)
load(path)
return
end
end
end
它实际上比那更复杂(和更有效),但这是它的要点。如果你对我talked about it some at RubyConf 2013感兴趣。
其他相关要点并未直接回答您的问题:
您还应该从require "deck"
删除card.rb
,因为该文件中的任何内容都不需要了解Deck
。如果你有警告,这会警告你“循环要求”,如果你有两个文件相互加载会发生这种情况。这通常是一种不好的做法。 (要明确的是:这不是你在这里做的,因为card.rb
您的规格应该只需要正在测试的特定内容。在这种情况下,deck.rb
应该需要卡片,card_spec.rb
应该需要卡片。他们不需要两者都需要。
IRB要求的deck_spec.rb
是多余的,不是必需的。