loopylou loopylou - 3 months ago 8
Ruby Question

What's wrong with my 'Bottles of beer on the wall' loop in Ruby

I'm a beginner and I've been stuck on the below for a while; I can't figure out where I'm going wrong.

I am trying to write a program that prints out the 'Bottles of Beer' song, taking in a number and translating it to the equivalent English word, in each iteration of the song.
When I try to run the whole program I get the error message:

in `english_words': undefined method `[]' for nil:NilClass (NoMethodError)
from 11_classes.rb:83:in `block in print_song'
from 11_classes.rb:78:in `downto'
from 11_classes.rb:78:in `print_song'
from 11_classes.rb:116:in `<main>'


But when I test it in irb, the song prints out fine.

Please could someone help explain why this doesn't work when I try to create a new object? I know it's pretty messy and probably quite a long-winded way of doing it but thought trying to do it my own way with what I've learnt so far would be a better way to learn for now.

Thank you!

class BeerSong

attr_accessor :bottles

def initialize(bottles)
bottles = 0 if bottles < 0
bottles = 99 if bottles > 99
@bottles = bottles
end

@single_nums = {
19 => "Nineteen",
18 => "Eighteen",
17 => "Seventeen",
16 => "Sixteen",
15 => "Fifteen",
14 => "Fourteen",
13 => "Thirteen",
12 => "Twelve",
11 => "Eleven",
10 => "Ten",
9 => "nine",
8 => "eight",
7 => "seven",
6 => "six",
5 => "five",
4 => "four",
3 => "three",
2 => "two",
1 => "one",
0 => "Zero"
}

@big_nums = {
9 => "Ninety",
8 => "Eighty",
7 => "Seventy",
6 => "Sixty",
5 => "Fifty",
4 => "Fourty",
3 => "Thirty",
2 => "Twenty"
}


def print_song
@bottles.downto 1 do |n|
if @bottles.zero?
String.new
else
puts """
#{english_words(n)} #{bottle(n)} of beer on the wall,
#{english_words(n)} #{bottle(n)} of beer,
Take one down, pass it around,
#{english_words(n-1)} #{bottle(n+1)} of beer on the wall.
"""
end
end
end

def english_words(bottles)
if bottles <= 19
@single_nums[bottles].capitalize
elsif bottles % 10 == 0
split_number = bottles.to_s.split('').map(&:to_i)
@big_nums[split_number[0]]
else
split_number = bottles.to_s.split('').map(&:to_i)
"#{@big_nums[split_number[0]]}-#{@single_nums[split_number[1]]}"
end
end

def bottle(n)
if n == 1
'bottle'
else
'bottles'
end
end
end

Answer

Instance variables @single_nums and @big_nums are defined in terms of an instance and should be set up in initialize.

Move @single_nums = {... and @big_nums = {... into initialize and it should work.

Or you could make them constants: SINGLE_NUMS = {..., BIG_NUMS = {... and leave them where they are.