Okay, this one was fun. It had the feel of an actual, genuine application. The book asks the programmer to build an app that converts currency, and provides the equation to do so. That equation is:
amount_to = (amount_from * rate_from) / rate_to
I was confused about the need for the rate_to part of the equation, but I figured it out, and I’ll explain that in a sec.
The first level of this challenge assumed the user would convert from Euros to USD. It would prompt for the amount of money in euros, and then prompt for the exchange rate, then print out the new amount in USD. Simple enough. The tricky part at this point was figuring out how to make sure that the fractions of a penny were rounded to the nearest cent. I accomplished this with the %#.2f formatting, which lets you insert that series of symbols in the place of the variable, and then after the string, you use the % [varable1,variable2] syntax, and it renders the string as though you were using standard string interpolation. The final line looks something like this:
puts “%#.2f #{currency1}(s) is equal to %#.2f #{currency2}(s)” % [amount_from,amount_to]
Where currency1 is the name of the currency, rather than the numerical value (US Dollars, Mexican Pesos, etc).
That was the first part of the challenge, and the second was relatively easy as well: build a dictionary of currencies and the countries they’re from, so you can prompt for the name of the currency, or the country, and do the same conversion. That’s where the question of the rate_to divisor gets answered. When you’re converting from a currency, the conversion rate from Euros to Dollars is 1.12, meaning you multiply your starting amount with the conversion, and you get the amount in USD. The top half of the equation is how you convert from any currency into USD. That begs the question: how do you convert one currency that isn’t USD into another currency that also isn’t USD? That’s where that rate_to becomes useful. Because each currency isn’t pegged to any other currency or standard (generally speaking, there might be exceptions or something), in order to convert from one currency to another, it helps to have a reference frame. This reference frame is arbitrary, and for my purposes, I chose to use the American Dollar.
Here’s how the math actually works: to convert from any currency to any other currency, you find a list of conversion rates from currencies to the USD, you convert your first currency into USD, then you divide the result by the conversion rate to go from USD to your second currency. Here’s an example with made-up numbers:
1 Euro = 2 USD
1 Peso = 0.5 USD
Value in Pesos = (1 Euro * 2 USD per Euro) / 0.5 USD per Peso
Value in Pesos = 2 USD / 0.5 USD per Peso
Value in Pesos = 4 Pesos = 1 Euro
The next part of the challenge was to use an API to get up-to-date, real-world conversion rates for all these currencies. My friend found a simple JSON API that spits out a list of conversions, and using the JSON and net/http gems for Ruby, I was able to read the JS object and parse the ‘usd’ response as a hash object containing all the conversion rates. That looked a little something like this:
require ‘json’
require ‘net/http’
url = ‘https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/usd.json’
uri = URI(url)
response = Net::HTTP.get(uri)
$rates = JSON.parse(response)[‘usd’]
def convertCurrency(amount_from,currency1,currency2)
amount_to = (amount_from * $rates[currency2]) / $rates[currency1]
end
Eagle eyed readers might notice that there’s something fishy about this convertCurrency() method: if currency1 is what you’re converting from, and currency2 is what you’re converting to then why are the rates in reverse order?
I was wondering this myself, since I’d originally put them where they’d logically go; the error was easy to catch, and the solution was easy to figure out. In looking a little closer at the values for the rates, it turns out that the API wasn’t showing the amount of USD per currency. It was showing the amount of currency per USD. In my example above, I show 0.5 Dollars per Peso, but this API would show that as 2 Pesos per Dollar.
Anyway, after fixing that bug, and revamping the code to include the new Useful_Methods file, it consistently works, and I managed to get the length of the file down considerably:
require ‘../useful_methods.rb’
require ‘json’
require ‘net/http’
url = ‘https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/usd.json’
uri = URI(url)
response = Net::HTTP.get(uri)
rates = JSON.parse(response)[‘usd’]
def convertCurrency(amount_from,rates,currency1,currency2)
amount_to = (amount_from * rates[currency2]) / rates[currency1]
end
amount_from = getFloat(“Please enter the amount of currency to convert with no symbols ($10 = 10)”)
currency1 = chooseOne(“Please enter the letter abbreviation of the country you wish to convert FROM”,rates.keys)
currency2 = chooseOne(“Please enter the letter abbreviation of the country you wish to convert TO”,rates.keys)
amount_to = convertCurrency(amount_from,rates,currency1,currency2)
puts “%#.2f #{currency1}(s) is equal to %#.2f #{currency2}(s)” % [amount_from,amount_to]
The result being:
And that is day 11 of 57!