All --
After years of grumbling I've finally seen the light about test driven
development, and have finally realized just how phenomenally powerful
it can be. As a celebration of my new found love for this technique,
I'm presenting this month's hangman in TDD form. First the tests,
then the code (with a few of those annoying underscores fig-leafing
about).
(Full disclosure: although I was a good doober and wrote the tests
first, I wound up adjusting a few of them slightly to make them pass
after I had the code working. I've noted the changes in these tests
in comments. All three are in the philosophy section).
--------------
hangman7.spec-------------------------------------------------
require 'hangman7.rb'
class My_funky_class
attr_reader :weight,:tags
def initialize(weight,*tags,&action)
@weight = weight
@tags = tags
@action = action
end
def tag(t)
@tags ||= t
end
def do_it
@action.call(@weight,@tags)
end
end
describe "Chauncy," do
describe "when unable to see the internet" do
it "should raise an error" do
Ping.stub!(:pingecho).with('
google.com',10,80).and_return
false
lambda { Chauncy.new(Integer,7) }.should
raise_error(IOError)
end
end
describe "when instantiating instances" do
it "should be able to act like any other class" do
Chauncy.new(Integer,7).should == 7
Chauncy.new(String,'bob').should == 'bob'
Chauncy.new(Integer,'18').should_not == '18'
Chauncy.new(Integer,'18').should == 18
Chauncy.new(String,42).should_not == 42
Chauncy.new(String,42).should == '42'
Chauncy.new(Array,:a,'b',"c").should == [:a,'b',"c"]
mfo = Chauncy.new(My_funky_class,1234,:this,:is,:funky)
{ |w,t| {:weight => w, :tags => t } }
mfo.weight.should == 1234
mfo.do_it[:weight].should == 1234
mfo.tags.should == [:this,:is,:funky]
mfo.do_it[:tags].should == [:this,:is,:funky]
end
end
describe "when counting in foreign languages" do
it "should know French" do
Chauncy.languages.should be_include("French")
end
it "should know Spanish" do
Chauncy.languages.should be_include("Spanish")
end
it "should know Latin" do
Chauncy.languages.should be_include("Latin")
end
it "should know English" do
# Hey, to most of the world's population it's a foreign
language!
Chauncy.languages.should be_include("English")
end
it "should know how to count to ten in French" do
Chauncy.languages["French"].express(1..10).should ==
['un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit',
'neuf', 'dix']
end
it "should know how to count to ten in Spanish" do
Chauncy.languages["Spanish"].express(1..10).should ==
['uno', 'dos', 'tres', 'quatro', 'cinco', 'seis', 'siete', 'ocho',
'neuve', 'diez']
end
it "should know how to count to ten in Latin" do
Chauncy.languages["Latin"].express(1..10).should ==
['unus
','duo
','tres','quattor','quinque','sex','septem','octo','novem','decem']
end
it "should know how to count to ten in English" do
Chauncy.languages["English"].express(1..10).should ==
['one','two','three','four','five','six','seven','eight','nine','ten']
end
it "should default to English if no language is specified" do
Chauncy.language.should == "English"
Chauncy.express(1..10).should ==
['one','two','three','four','five','six','seven','eight','nine','ten']
end
end
describe "when doing math" do
it "should be able to do basic operations in any combination
of languages (including idioms)" do
Chauncy.evaluate("quatro + deux").should == "six"
Chauncy.languages["Latin"].evaluate("quatro divided by
deux").should == "duo"
Chauncy.evaluate("half a score").should == "ten"
end
it "should raise an exception on ambiguities" do
# Could be a date or (7 in French) divided by (3 in
idiomatic English).
lambda { Chauncy.evaluate("third of sept") }.should
raise_error(ArgumentError)
end
it "should be able to differentiate functions from their
verbal descriptions" do
f = 'the height of his shoulder plus the length of his
arm times the sine of t over the rate of rotation',
Chauncy.differentiate(f,'t').should == '(v1/v2)*cos(t/v2)
where v1 is the length of his arm, v2 is the rate of rotation'
end
it "should be able to integrate functions from their verbal
descriptions" do
f = 'the height of his shoulder plus the length of his
arm times the sine of t over the rate of rotation',
Chauncy.integrate(f,'t').should == '(v1*t)-((v2*v3)*cos(t/
v3) where v1 is the height of his shoulders, v2 is the length of his
arm, v3 is the rate of rotation'
end
end
describe "when doing philosophy / metamath / wordgames" do
it "should be able to answer the old standards" do
# match rather than equality because the wording isn't
consistent.
Chauncy.query("Which came first, the chicken or the
egg?").should =~ /(the )?egg/i
# chomp in case it comes back with a trailing "\n"
Chauncy.query("Why did the chicken cross the
road?").chomp.should == 'To get to the other side.'
end
it "should raise a NotImplementedError if asked an
undecidable question" do
lambda { Chauncy.query("How can you tell if a context
free grammar is ambiguous?") }.should raise_error(NotImplementedError)
#
# Note that with the improved query algorithm the
original test query,
#
# lambda { Chauncy.query("Does P = NP? ") }.should
raise_error(NotImplementedError")
#
# now appears to hang rather than raising an error. This
may or may not be a bug.
#
end
end
end
--------------------------------------------------------------------------------------------------------------------
The results of running these tests:
--------------------------------------------------------------------------------------------------------------------
Chauncy, when unable to see the internet
- should raise an error
Chauncy, when instantiating instances
- should be able to act like any other class
Chauncy, when counting in foreign languages
- should know French
- should know Spanish
- should know Latin
- should know English
- should know how to count to ten in French
- should know how to count to ten in Spanish
- should know how to count to ten in Latin
- should know how to count to ten in English
- should default to English if no language is specified
Chauncy, when doing math
- should be able to do basic operations in any combination of
languages (including idioms)
- should raise an exception on ambiguities
- should be able to differentiate functions from their verbal
descriptions
- should be able to integrate functions from their verbal descriptions
Chauncy, when doing philosophy / metamath / wordgames
- should be able to answer the old standards
- should raise a NotImplementedError if asked an undecidable question
Finished in 0.005178 seconds
17 examples, 0 failures
--------------------------------------------------------------------------------------------------------------------
One of the great things about doing it this way is that it gets rid of
all the hand-wavy description of what the code does, so we can focus
on how in the heck it does it. The actual code is rather short
(though it does load a few libraries):
----------------------------------------------------------------------------------------------------------------------
require 'ping'
require 'net/http'
require 'strscan'
require 'observer'
X = (0..19).partition { |i| i = i+10; (2...i).any? { |j| i % j == 0 } }
Y = __________u___l_______.split(//)
Z = X.collect { |a| Y.values_at(*a).join }
class << Chauncy =
Class.new.new
Languages = [/[{=>}]/=>{}]
W = %w{
google.com www.wolframalpha.com twitter.com }
[::Proc] << [W,X,Y,Z]
class ::Proc
Z.each { |q| __f__________(q) { self } }
end
Z.each { |q| __f__________(q) { self } }
end
-----------------------------------------------------------------------------------------------------------------
Enjoy!
-- Markus