Created
May 27, 2011 19:33
-
-
Save sgonyea/995965 to your computer and use it in GitHub Desktop.
Currencify
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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