Skip to content

Instantly share code, notes, and snippets.

@sgonyea
Created May 27, 2011 19:33
Show Gist options
  • Save sgonyea/995965 to your computer and use it in GitHub Desktop.
Save sgonyea/995965 to your computer and use it in GitHub Desktop.
Currencify
require 'active_support/core_ext/array/extract_options'
# takes a number and options hash and outputs a string in any currency format
# @param [Numeric, #to_f] number The float-able value, to be currencified
# @see Numeric#currencify
def Currencify(number, *opts)
number.to_f.currencify(number, *opts)
end
class Numeric
CURRENCIFY_OPTS = {
:currency_symbol => "$",
:scale_delimiter => ",",
:decimal_delimiter => ".",
:precision => 2,
:trailing_currency => false,
:show_currency_symbol => true,
:show_decimal => true
}
CURRENCIFY_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
# takes a number and options hash and outputs a string in any currency format
# @param [Hash] opts Optional parameters, to adjust the output format
# @options opts [String] :currency_symbol The Symbol to use for currency. Default '$'
# @options opts [String] :scale_delimiter The default delimiter, between numeric scales. Default ','
# @options opts [String] :decimal_delimiter The symbol to use for the decimal place. Default '.'
# @options opts [Integer] :precision The symbol to use for the decimal place. Default '.'
# @options opts [true, false] :trailing_currency Whether or not the currency should be appended. Default false.
# @options opts [true, false] :show_currency_symbol Whether or not to show the currency symbol. Default true.
# @options opts [true, false] :show_decimal Whether or not to show the decimal (or to floor the output). Default is true.
# @return [String] The given numeric value, stringified and structured with common currency conventions
def currencify(*opts)
opts = CURRENCIFY_OPTS.merge(opts.extract_options!)
float_str = "%.#{opts[:precision]}f" % self.to_f.round(opts[:precision])
integer, decimal = float_str.split('.')
delimit_currency!(integer, opts[:scale_delimiter])
if opts[:show_decimal]
decimal.insert(0, opts[:decimal_delimiter])
else
decimal.clear
end
currency = integer << decimal
if opts[:show_currency_symbol]
if opts[:trailing_currency]
currency << opts[:currency_symbol]
else
currency.insert(0, opts[:currency_symbol])
end
end
return(currency)
end
private
def delimit_currency!(numeric_str, delimiter)
numeric_str.gsub!(CURRENCIFY_REGEX, "\\1#{delimiter}")
return(numeric_str)
end
end
require Bundler.root.join('lib/core_ext/currencify')
describe Numeric do
describe '#currencify' do
context 'Transforming number-ish values (responding to #to_f) into currency' do
it 'should transform strings with numbers' do
str = '1923.21'
Currencify(str).should == '$1,923.21'
end
it 'should transform floating-point values' do
float = 926.18
float.currencify.should == '$926.18'
Currencify(float).should == '$926.18'
end
it 'should transform integer values' do
int = 3243563452
int.currencify.should == '$3,243,563,452.00'
Currencify(int).should == '$3,243,563,452.00'
end
it 'should handle nil' do
Currencify(nil).should == '$0.00'
end
end
context 'Rounding floating-point values with more than 2 decimal positions' do
it 'should round decimals appropriately' do
1923.234.currencify.should == '$1,923.23'
1923.236.currencify.should == '$1,923.24'
end
end
context 'Optional arguments' do
context ':currency_symbol' do
it 'should take any string' do
int = 3243563452
int.currencify(:currency_symbol => 'BANANA').should == 'BANANA3,243,563,452.00'
end
end
context ':delimiter' do
it 'should satisfy even the Germans' do
int = 3243563452
int.currencify(:scale_delimiter => '.').should == '$3.243.563.452.00'
end
end
context ':precision' do
it 'should take an integer' do
int = 3243563452
int.currencify(:precision => 3).should == '$3,243,563,452.000'
end
end
context ':decimal_delimiter' do
it 'should take any string' do
int = 3243563452
int.currencify(:decimal_delimiter => '!').should == '$3,243,563,452!00'
end
end
context ':trailing_currency' do
it 'should take any string' do
int = 3243563452
int.currencify(:trailing_currency => true).should == '3,243,563,452.00$'
end
end
context ':show_currency_symbol' do
it 'should know when it\'s not welcome here' do
int = 3243563452
int.currencify(:show_currency_symbol => false).should == '3,243,563,452.00'
end
end
context ':show_decimal' do
it 'was stupid anyway' do
int = 3243563452
int.currencify(:show_decimal => false).should == '$3,243,563,452'
end
end
it 'Should satisfy even the Germans' do
float = 3243563452.24
currency = float.currencify :scale_delimiter => '.', :currency_symbol => ' DEM', :decimal_delimiter => ',', :trailing_currency => true
currency.should == '3.243.563.452,24 DEM' # R€GR€T
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment