# encoding: UTF-8 class Money module Formatting if Object.const_defined?("I18n") def thousands_separator if self.class.use_i18n I18n.t( :"number.currency.format.delimiter", :default => I18n.t( :"number.format.delimiter", :default => (currency.thousands_separator || ",") ) ) else currency.thousands_separator || "," end end else def thousands_separator currency.thousands_separator || "," end end alias :delimiter :thousands_separator if Object.const_defined?("I18n") def decimal_mark if self.class.use_i18n I18n.t( :"number.currency.format.separator", :default => I18n.t( :"number.format.separator", :default => (currency.decimal_mark || ".") ) ) else currency.decimal_mark || "." end end else def decimal_mark currency.decimal_mark || "." end end alias :separator :decimal_mark # Creates a formatted price string according to several rules. # # @param [Hash] rules The options used to format the string. # # @return [String] # # @option *rules [Boolean, String] :display_free (false) Whether a zero # amount of money should be formatted of "free" or as the supplied string. # # @example # Money.us_dollar(0).format(:display_free => true) #=> "free" # Money.us_dollar(0).format(:display_free => "gratis") #=> "gratis" # Money.us_dollar(0).format #=> "$0.00" # # @option *rules [Boolean] :with_currency (false) Whether the currency name # should be appended to the result string. # # @example # Money.ca_dollar(100).format #=> "$1.00" # Money.ca_dollar(100).format(:with_currency => true) #=> "$1.00 CAD" # Money.us_dollar(85).format(:with_currency => true) #=> "$0.85 USD" # # @option *rules [Boolean] :rounded_infinite_precision (false) Whether the # amount of money should be rounded when using infinite_precision # # @example # Money.us_dollar(100.1).format #=> "$1.001" # Money.us_dollar(100.1).format(:rounded_infinite_precision => true) #=> "$1" # Money.us_dollar(100.9).format(:rounded_infinite_precision => true) #=> "$1.01" # # @option *rules [Boolean] :no_cents (false) Whether cents should be omitted. # # @example # Money.ca_dollar(100).format(:no_cents => true) #=> "$1" # Money.ca_dollar(599).format(:no_cents => true) #=> "$5" # # @option *rules [Boolean] :no_cents_if_whole (false) Whether cents should be # omitted if the cent value is zero # # @example # Money.ca_dollar(10000).format(:no_cents_if_whole => true) #=> "$100" # Money.ca_dollar(10034).format(:no_cents_if_whole => true) #=> "$100.34" # # @option *rules [Boolean, String, nil] :symbol (true) Whether a money symbol # should be prepended to the result string. The default is true. This method # attempts to pick a symbol that's suitable for the given currency. # # @example # Money.new(100, "USD") #=> "$1.00" # Money.new(100, "GBP") #=> "£1.00" # Money.new(100, "EUR") #=> "€1.00" # # # Same thing. # Money.new(100, "USD").format(:symbol => true) #=> "$1.00" # Money.new(100, "GBP").format(:symbol => true) #=> "£1.00" # Money.new(100, "EUR").format(:symbol => true) #=> "€1.00" # # # You can specify a false expression or an empty string to disable # # prepending a money symbol.§ # Money.new(100, "USD").format(:symbol => false) #=> "1.00" # Money.new(100, "GBP").format(:symbol => nil) #=> "1.00" # Money.new(100, "EUR").format(:symbol => "") #=> "1.00" # # # If the symbol for the given currency isn't known, then it will default # # to "¤" as symbol. # Money.new(100, "AWG").format(:symbol => true) #=> "¤1.00" # # # You can specify a string as value to enforce using a particular symbol. # Money.new(100, "AWG").format(:symbol => "ƒ") #=> "ƒ1.00" # # # You can specify a indian currency format # Money.new(10000000, "INR").format(:south_asian_number_formatting => true) #=> "1,00,000.00" # Money.new(10000000).format(:south_asian_number_formatting => true) #=> "$1,00,000.00" # # @option *rules [Boolean, nil] :symbol_before_without_space (true) Whether # a space between the money symbol and the amount should be inserted when # +:symbol_position+ is +:before+. The default is true (meaning no space). Ignored # if +:symbol+ is false or +:symbol_position+ is not +:before+. # # @example # # Default is to not insert a space. # Money.new(100, "USD").format #=> "$1.00" # # # Same thing. # Money.new(100, "USD").format(:symbol_before_without_space => true) #=> "$1.00" # # # If set to false, will insert a space. # Money.new(100, "USD").format(:symbol_before_without_space => false) #=> "$ 1.00" # # @option *rules [Boolean, nil] :symbol_after_without_space (false) Whether # a space between the the amount and the money symbol should be inserted when # +:symbol_position+ is +:after+. The default is false (meaning space). Ignored # if +:symbol+ is false or +:symbol_position+ is not +:after+. # # @example # # Default is to insert a space. # Money.new(100, "USD").format(:symbol_position => :after) #=> "1.00 $" # # # If set to true, will not insert a space. # Money.new(100, "USD").format(:symbol_position => :after, :symbol_after_without_space => true) #=> "1.00$" # # @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the # currency should be separated by the specified character or '.' # # @example # # If a string is specified, it's value is used. # Money.new(100, "USD").format(:decimal_mark => ",") #=> "$1,00" # # # If the decimal_mark for a given currency isn't known, then it will default # # to "." as decimal_mark. # Money.new(100, "FOO").format #=> "$1.00" # # @option *rules [Boolean, String, nil] :thousands_separator (true) Whether # the currency should be delimited by the specified character or ',' # # @example # # If false is specified, no thousands_separator is used. # Money.new(100000, "USD").format(:thousands_separator => false) #=> "1000.00" # Money.new(100000, "USD").format(:thousands_separator => nil) #=> "1000.00" # Money.new(100000, "USD").format(:thousands_separator => "") #=> "1000.00" # # # If a string is specified, it's value is used. # Money.new(100000, "USD").format(:thousands_separator => ".") #=> "$1.000.00" # # # If the thousands_separator for a given currency isn't known, then it will # # default to "," as thousands_separator. # Money.new(100000, "FOO").format #=> "$1,000.00" # # @option *rules [Boolean] :html (false) Whether the currency should be # HTML-formatted. Only useful in combination with +:with_currency+. # # @example # s = Money.ca_dollar(570).format(:html => true, :with_currency => true) # s #=> "$5.70 CAD" # # @option *rules [Boolean] :sign_before_symbol (false) Whether the sign should be # before the currency symbol. # # @example # # You can specify to display the sign before the symbol for negative numbers # Money.new(-100, "GBP").format(:sign_before_symbol => true) #=> "-£1.00" # Money.new(-100, "GBP").format(:sign_before_symbol => false) #=> "£-1.00" # Money.new(-100, "GBP").format #=> "£-1.00" # # @option *rules [Boolean] :sign_positive (false) Whether positive numbers should be # signed, too. # # @example # # You can specify to display the sign with positive numbers # Money.new(100, "GBP").format(:sign_positive => true, :sign_before_symbol => true) #=> "+£1.00" # Money.new(100, "GBP").format(:sign_positive => true, :sign_before_symbol => false) #=> "£+1.00" # Money.new(100, "GBP").format(:sign_positive => false, :sign_before_symbol => true) #=> "£1.00" # Money.new(100, "GBP").format(:sign_positive => false, :sign_before_symbol => false) #=> "£1.00" # Money.new(100, "GBP").format #=> "£+1.00" def format(*rules) # support for old format parameters rules = normalize_formatting_rules(rules) rules = localize_formatting_rules(rules) if fractional == 0 if rules[:display_free].respond_to?(:to_str) return rules[:display_free] elsif rules[:display_free] return "free" end end symbol_value = if rules.has_key?(:symbol) if rules[:symbol] === true symbol elsif rules[:symbol] rules[:symbol] else "" end elsif rules[:html] currency.html_entity == '' ? currency.symbol : currency.html_entity else symbol end formatted = self.abs.to_s if rules[:rounded_infinite_precision] formatted = ((BigDecimal(formatted) * currency.subunit_to_unit).round / BigDecimal(currency.subunit_to_unit.to_s)).to_s("F") formatted.gsub!(/\..*/) do |decimal_part| if decimal_part == '.0' '' else decimal_part << '0' while decimal_part.length < (currency.decimal_places + 1) decimal_part end end end sign = self.negative? ? '-' : '' if rules[:no_cents] || (rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0) formatted = "#{formatted.to_i}" end thousands_separator_value = thousands_separator # Determine thousands_separator if rules.has_key?(:thousands_separator) thousands_separator_value = rules[:thousands_separator] || '' end # Apply thousands_separator formatted.gsub!(regexp_format(formatted, rules, decimal_mark, symbol_value), "\\1#{thousands_separator_value}") symbol_position = if rules.has_key?(:symbol_position) rules[:symbol_position] elsif currency.symbol_first? :before else :after end if rules[:sign_positive] == true && self.positive? sign = '+' end if rules[:sign_before_symbol] == true sign_before = sign sign = '' end if symbol_value && !symbol_value.empty? symbol_value = "#{symbol_value}" if rules[:html_wrap_symbol] formatted = if symbol_position == :before symbol_space = rules[:symbol_before_without_space] === false ? " " : "" "#{sign_before}#{symbol_value}#{symbol_space}#{sign}#{formatted}" else symbol_space = rules[:symbol_after_without_space] ? "" : " " "#{sign_before}#{formatted}#{symbol_space}#{sign}#{symbol_value}" end end if rules.has_key?(:decimal_mark) && rules[:decimal_mark] && rules[:decimal_mark] != decimal_mark formatted.sub!(decimal_mark, rules[:decimal_mark]) end if rules[:with_currency] formatted << " " formatted << '' if rules[:html] formatted << currency.to_s formatted << '' if rules[:html] end formatted end private # Cleans up formatting rules. # # @param [Hash] rules # # @return [Hash] def normalize_formatting_rules(rules) if rules.size == 0 rules = {} elsif rules.size == 1 rules = rules.pop rules = { rules => true } if rules.is_a?(Symbol) end if !rules.include?(:decimal_mark) && rules.include?(:separator) rules[:decimal_mark] = rules[:separator] end if !rules.include?(:thousands_separator) && rules.include?(:delimiter) rules[:thousands_separator] = rules[:delimiter] end rules end end def regexp_format(formatted, rules, decimal_mark, symbol_value) regexp_decimal = Regexp.escape(decimal_mark) if rules[:south_asian_number_formatting] /(\d+?)(?=(\d\d)+(\d)(?:\.))/ else # Symbols may contain decimal marks (E.g "դր.") if formatted.sub(symbol_value.to_s, "") =~ /#{regexp_decimal}/ /(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/ else /(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/ end end end def localize_formatting_rules(rules) if currency.iso_code == "JPY" && I18n.locale == :ja rules[:symbol] = "円" unless rules[:symbol] == false rules[:symbol_position] = :after rules[:symbol_after_without_space] = true end rules end end