diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..c4cff67 --- /dev/null +++ b/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'minitest', '~> 5.4' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..6bdfd6c --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,13 @@ +GEM + remote: https://rubygems.org/ + specs: + minitest (5.14.0) + +PLATFORMS + ruby + +DEPENDENCIES + minitest (~> 5.4) + +BUNDLED WITH + 2.1.4 diff --git a/lib/bottles.rb b/lib/bottles.rb new file mode 100644 index 0000000..d1078fd --- /dev/null +++ b/lib/bottles.rb @@ -0,0 +1,90 @@ +class CountdownSong + attr_reader :verse_template, :max, :min + + def initialize(verse_template:, max: 999999, min: 0) + @verse_template = verse_template + @max, @min = max, min + end + + def song + verses(max, min) + end + + def verses(upper, lower) + upper.downto(lower).collect {|i| verse(i)}.join("\n") + end + + def verse(number) + verse_template.lyrics(number) + end +end + + +class BottleVerse + def self.lyrics(number) + new(BottleNumber.new(number)).lyrics + end + + attr_reader :bottle_number + + def initialize(bottle_number) + @bottle_number = bottle_number + end + + def lyrics + "#{bottle_number} of beer on the wall, ".capitalize + + "#{bottle_number} of beer.\n" + + "#{bottle_number.action}, " + + "#{bottle_number.successor} of beer on the wall.\n" + end +end + + +class BottleNumber + attr_reader :number + def initialize(number) + @number = number + end + + def to_s + "#{quantity} #{container}" + end + + def quantity + BOTTLE_NUMBER_DEFAULTS[number]&.[](:quantity) || number.to_s + end + + def container + BOTTLE_NUMBER_DEFAULTS[number]&.[](:container) || "bottles" + end + + def action + BOTTLE_NUMBER_DEFAULTS[number]&.[](:action) || "Take #{pronoun} down and pass it around" + end + + def pronoun + BOTTLE_NUMBER_DEFAULTS[number]&.[](:pronoun) || "one" + end + + def successor + BOTTLE_NUMBER_DEFAULTS[number]&.[](:successor) ? BottleNumber.new(BOTTLE_NUMBER_DEFAULTS[number]&.[](:successor)) : BottleNumber.new(number - 1) + end +end + +BOTTLE_NUMBER_DEFAULTS = { + 0 => { + quantity: "no more", + action: "Go to the store and buy some more", + successor: 99 + }, + 1 => { + container: "bottle", + pronoun: "it" + }, + 6 => { + quantity: "1", + container: "six-pack" + } +} + +pp BOTTLE_NUMBER_DEFAULTS[9]&.[](:quantity) \ No newline at end of file diff --git a/test/bottles_test.rb b/test/bottles_test.rb new file mode 100644 index 0000000..0f0e240 --- /dev/null +++ b/test/bottles_test.rb @@ -0,0 +1,139 @@ +gem 'minitest', '~> 5.4' +require 'minitest/autorun' +require 'minitest/pride' +require_relative '../lib/bottles' + +module VerseRoleTest + def test_plays_verse_role + assert_respond_to @role_player, :lyrics + end +end + +class VerseFake + def self.lyrics(number) + "This is verse #{number}.\n" + end +end + +class VerseFakeTest < Minitest::Test + include VerseRoleTest + + def setup + @role_player = VerseFake + end +end + + +class BottleVerseTest < Minitest::Test + include VerseRoleTest + + def setup + @role_player = BottleVerse + end + + def test_verse_general_rule_upper_bound + expected = + "99 bottles of beer on the wall, " + + "99 bottles of beer.\n" + + "Take one down and pass it around, " + + "98 bottles of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(99) + end + + def test_verse_general_rule_lower_bound + expected = + "3 bottles of beer on the wall, " + + "3 bottles of beer.\n" + + "Take one down and pass it around, " + + "2 bottles of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(3) + end + + def test_verse_7 + expected = + "7 bottles of beer on the wall, " + + "7 bottles of beer.\n" + + "Take one down and pass it around, " + + "1 six-pack of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(7) + end + + def test_verse_6 + expected = + "1 six-pack of beer on the wall, " + + "1 six-pack of beer.\n" + + "Take one down and pass it around, " + + "5 bottles of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(6) + end + + def test_verse_2 + expected = + "2 bottles of beer on the wall, " + + "2 bottles of beer.\n" + + "Take one down and pass it around, " + + "1 bottle of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(2) + end + + def test_verse_1 + expected = + "1 bottle of beer on the wall, " + + "1 bottle of beer.\n" + + "Take it down and pass it around, " + + "no more bottles of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(1) + end + + def test_verse_0 + expected = + "No more bottles of beer on the wall, " + + "no more bottles of beer.\n" + + "Go to the store and buy some more, " + + "99 bottles of beer on the wall.\n" + assert_equal expected, BottleVerse.lyrics(0) + end +end + + +class CountdownSongTest < Minitest::Test + def test_verse + expected = "This is verse 500.\n" + assert_equal( + expected, + CountdownSong.new(verse_template: VerseFake) + .verse(500)) + end + + def test_verses + expected = + "This is verse 99.\n" + + "\n" + + "This is verse 98.\n" + + "\n" + + "This is verse 97.\n" + assert_equal( + expected, + CountdownSong.new(verse_template: VerseFake) + .verses(99, 97)) + end + + def test_song + expected = + "This is verse 47.\n" + + "\n" + + "This is verse 46.\n" + + "\n" + + "This is verse 45.\n" + + "\n" + + "This is verse 44.\n" + + "\n" + + "This is verse 43.\n" + assert_equal( + expected, + CountdownSong.new(verse_template: VerseFake, + max: 47, + min: 43) + .song) + end +end