Thursday, August 6, 2009

Converting Numbers to Currency (USD, Dollars, Money) in Ruby or Leaving It Alone If a String

We have something providing us with strings that may either be a number that should be converted into a $0.00 formatted currency string in Ruby, but sometimes the values might be nil, blank, or some other string- in that case we want to leave it alone. (Note: We're not using Rails. We're using Rack and Sinatra.)

Here is the working solution with tests. The currencify method is by Tad Thorley and found in this post:

# if is a non-decimal string, leave it alone. but if it is decimal, convert to money format
def self.money(input)
  begin
    # check if a number (integer or float)
    if input && !(input.to_s.empty?) && input.to_f
      input = currencify(input)              
    end
  rescue Exception => e
    # Note: you may choose to log this instead (we do)
    p "Assuming '#{input}' is not a number, so won't format as money. Error was #{e} #{e.backtrace}"
  end
  input
end

# solution from Tad Thorley: http://codesnippets.joyent.com/tag/ruby/2#post1812
# takes a number and options hash and outputs a string in any currency format
def self.currencify(number, options={})
  # :currency_before => false puts the currency symbol after the number
  # default format: $12,345,678.90
  options = {:currency_symbol => "$", :delimiter => ",", :decimal_symbol => ".", :currency_before => true}.merge(options)

  # split integer and fractional parts 
  int, frac = ("%.2f" % number).split('.')
  # insert the delimiters
  int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")

  if options[:currency_before]
    options[:currency_symbol] + int + options[:decimal_symbol] + frac
  else
    int + options[:decimal_symbol] + frac + options[:currency_symbol]
  end
end

If you wanted you could add "options" as an argument to money and pass it through to currencify. I've left it half-and-half for this example.

Tests:

def test_money
  result = Our::Utility::Methods.money(nil)
  assert result == nil, "money(nil) should be nil. result was '#{result}'"
  result = Our::Utility::Methods.money('')
  assert result == '', "money('') should be ''. result was '#{result}'"
  result = Our::Utility::Methods.money('mystring')
  assert result == 'mystring', "money('mystring') should be 'mystring'. result was '#{result}'"
  result = Our::Utility::Methods.money('0')
  assert result == '$0.00', "money('0') should be '$0.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('.001')
  assert result == '$0.00', "money('.001') should be '$0.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('.01')
  assert result == '$0.01', "money('.01') should be '$0.01'. result was '#{result}'"
  result = Our::Utility::Methods.money('.10')
  assert result == '$0.10', "money('.10') should be '$0.10'. result was '#{result}'"
  result = Our::Utility::Methods.money('1.0')
  assert result == '$1.00', "money('1.0') should be '$1.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('1.00')
  assert result == '$1.00', "money('1.00') should be '$1.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('1.000')
  assert result == '$1.00', "money('1.000') should be '$1.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('1.001')
  assert result == '$1.00', "money('1.001') should be '$1.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('10')
  assert result == '$10.00', "money('10') should be '$10.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('100')
  assert result == '$100.00', "money('100') should be '$100.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('1000')
  assert result == '$1,000.00', "money('1000') should be '$1,000.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('10000')
  assert result == '$10,000.00', "money('10000') should be '$10,000.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('100000')
  assert result == '$100,000.00', "money('100000') should be '$100,000.00'. result was '#{result}'"
  result = Our::Utility::Methods.money('10.1')
  assert result == '$10.10', "money('10.1') should be '$10.10'. result was '#{result}'"
  result = Our::Utility::Methods.money('100.1')
  assert result == '$100.10', "money('100.1') should be '$100.10'. result was '#{result}'"
  result = Our::Utility::Methods.money('1000.1')
  assert result == '$1,000.10', "money('1000.1') should be '$1,000.10'. result was '#{result}'"
  result = Our::Utility::Methods.money(0)
  assert result == '$0.00', "money(0) should be '$0.00'. result was '#{result}'"
  result = Our::Utility::Methods.money(1000.2)
  assert result == '$1,000.20', "money(1000.2) should be '$1,000.10'. result was '#{result}'"
end

2 comments:

Phae said...

Awesome.

phoenyx said...

I'm glad you found it helpful.