Challenge 9: Paint Calculator

Challenge 9 simulates the math you would need to do in order to buy paint for the inside of your house. The idea being that you can’t buy one third of a can of paint, so you need to figure out how many cans are required, rounded up to the nearest unit. The book suggests that 1 gallon of paint covers 350 square feet, and the basic program requirements are that the user input length and width, and then returns the number of gallons needed to cover the area. 

In looking back on how I solved this one, it was pretty straightforward, but I did take the opportunity to have some fun with the output statement. I required the active_support/inflector which has a .pluralize(integer) method, where you can pass in a number, and it will determine how to pluralize a string, given the string’s quantity. In this case, I used “gallon”.pluralize(gallons) where gallons is the variable for how many cans of paint I’d need. The result is if you only need 1 gallon, the pluralize method will keep the word “gallon” singular. Any other number, and it will add an “s”.

Another thing I noticed about my solution is that it could use some refactoring, and I could benefit from having a system where you could choose the shape of the room. I started by removing the getInteger(prompt) definition, and requiring the useful_methods.rb file. Then I started working on how to make it calculate the area of a variety of shapes.

I’m a little proud of this part, because my first impulse was to make a big method that does everything, and then call it. I was able to take a breath, and remember that I should try to keep the methods as specific as possible, since I’d rather have a program consisting of a bunch of robust methods that each do one thing well, than have one really big method that does a bunch of things okay. It’s easier to troubleshoot and test, and it makes the code more legible. 

The new method I wrote is a calculateArea(shape) method, which does exactly what it says. I was able to reuse another method from the Helpful Methods file: chooseOne(prompt, optionsArray) that asks the user their prompt, and then lets the user choose one of the elements from the options array. I set it up to give a list of choices of shapes, and the user can select the shape of the room they want to calculate:

shape = chooseOne(“What shape is the room you’d like to paint?”,[“rectangle”,”circle”,”triangle”,”L shape”])

area = calculateArea(shape)

Then, I pass the shape variable as an argument for the calculateArea(shape) method, it asks the dimensions of the specified shape, and returns the area of that shape. Let’s see what goes into the calculateArea(shape) method:

def calculateArea(shape)

   if shape == “rectangle”

       length = getInteger(“length”)

       width = getInteger(“width”)

       area = length * width

   elsif shape == “circle”

       radius = getInteger(“radius”)

       area = Math::PI * radius ** 2

   elsif shape == “triangle”

       base = getInteger(“base”)

       height = getInteger(“height”)

       area = 0.5 * base * height

   elsif shape == “L shape”

       longest_sideA = getInteger(“longest side”)

       longest_sideB = getInteger(“next longest side”)

       cutaway_sideA = getInteger(“vertical stem inside the L bend”)

       cutaway_sideB = getInteger(“horizontal side inside the L bend”)

       area = longest_sideA * longest_sideB – cutaway_sideA * cutaway_sideB

   end

end

It uses the getInteger(prompt) method to find the relevant dimensions of the shape, and spits out the answer.

I had to change the chooseOne() method just a bit, because the capital “L” was giving me trouble, of all things. Luckily that revealed some other brittle parts of the chooseOne() method: I wrote it in a context where strings were expected as options, but if it’s going to be a useful general method, it ought to handle at least both strings and numbers, so I removed the downcase, and added an error message that reminds people that their selection is case sensitive. The problem with that was that the gets method automatically saves input as a string, so I couldn’t pass in numbers, because they’re not the same class. What I wound up doing was checking to see if the selection was a number, and setting that to either true or false. Then, I modified the raise conditional to check to see if choice as both a string and an integer were in there. Ultimately, when it works, it returns the kind of object that was selected. It’s a bit confusing, but here it is: 

def chooseOne(prompt,optionsArray)

   begin

       puts prompt

       choice = gets.chomp

       is_numeral = choice.to_i.to_s == choice ? true : false

       raise if (optionsArray.include? choice) == false && ( (optionsArray.include? choice.to_i) == false || is_numeral == false )

   rescue

       puts “\nPlease enter* one of the options:”

       optionsArray.each { |option| puts option }

       puts “\n**** Entries are case sensitive; make sure you enter your option exactly as indicated ***”

       retry

   end

   return is_numeral == true ? choice.to_i : choice

end

And now, it is able to give choices that are numbers, as well as strings! Anyway, that’s day 9 of 57!

Published by corbettbw

I am a Ruby developer in Phoenix, AZ. I'm interested in the intersection of technology and social justice, love weird science facts, and my dog, Coco; a cute black lab/pit bull mix, who won't stop eating rocks.

Leave a comment