money-joshm1 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +475 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +52 -0
- data/config/currency_backwards_compatible.json +128 -0
- data/config/currency_iso.json +2297 -0
- data/config/currency_non_iso.json +30 -0
- data/lib/money.rb +6 -0
- data/lib/money/bank/base.rb +130 -0
- data/lib/money/bank/variable_exchange.rb +252 -0
- data/lib/money/core_extensions.rb +82 -0
- data/lib/money/currency.rb +355 -0
- data/lib/money/currency/heuristics.rb +149 -0
- data/lib/money/currency/loader.rb +22 -0
- data/lib/money/money.rb +536 -0
- data/lib/money/money/arithmetic.rb +288 -0
- data/lib/money/money/formatting.rb +315 -0
- data/lib/money/money/parsing.rb +371 -0
- data/money.gemspec +29 -0
- data/spec/bank/base_spec.rb +77 -0
- data/spec/bank/variable_exchange_spec.rb +233 -0
- data/spec/core_extensions_spec.rb +160 -0
- data/spec/currency/heuristics_spec.rb +84 -0
- data/spec/currency_spec.rb +183 -0
- data/spec/money/arithmetic_spec.rb +598 -0
- data/spec/money/formatting_spec.rb +466 -0
- data/spec/money/parsing_spec.rb +309 -0
- data/spec/money_spec.rb +497 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/default_currency_helper.rb +13 -0
- metadata +145 -0
@@ -0,0 +1,288 @@
|
|
1
|
+
class Money
|
2
|
+
module Arithmetic
|
3
|
+
|
4
|
+
# Returns a money object with changed polarity.
|
5
|
+
#
|
6
|
+
# @return [Money]
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# - Money.new(100) #=> #<Money @fractional=-100>
|
10
|
+
def -@
|
11
|
+
Money.new(-fractional, currency)
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Checks whether two money objects have the same currency and the same
|
16
|
+
# amount. Checks against money objects with a different currency and checks
|
17
|
+
# against objects that do not respond to #to_money will always return false.
|
18
|
+
#
|
19
|
+
# @param [Money] other_money Value to compare with.
|
20
|
+
#
|
21
|
+
# @return [Boolean]
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# Money.new(100) == Money.new(101) #=> false
|
25
|
+
# Money.new(100) == Money.new(100) #=> true
|
26
|
+
def ==(other_money)
|
27
|
+
if other_money.respond_to?(:to_money)
|
28
|
+
other_money = other_money.to_money
|
29
|
+
fractional == other_money.fractional && self.currency == other_money.currency
|
30
|
+
else
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Synonymous with +#==+.
|
36
|
+
#
|
37
|
+
# @param [Money] other_money Value to compare with.
|
38
|
+
#
|
39
|
+
# @return [Money]
|
40
|
+
#
|
41
|
+
# @see #==
|
42
|
+
def eql?(other_money)
|
43
|
+
self == other_money
|
44
|
+
end
|
45
|
+
|
46
|
+
def <=>(other_money)
|
47
|
+
if other_money.respond_to?(:to_money)
|
48
|
+
other_money = other_money.to_money
|
49
|
+
if fractional == 0 || other_money.fractional == 0 || currency == other_money.currency
|
50
|
+
fractional <=> other_money.fractional
|
51
|
+
else
|
52
|
+
fractional <=> other_money.exchange_to(currency).fractional
|
53
|
+
end
|
54
|
+
else
|
55
|
+
raise ArgumentError, "Comparison of #{self.class} with #{other_money.inspect} failed"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Test if the amount is positive. Returns +true+ if the money amount is
|
60
|
+
# greater than 0, +false+ otherwise.
|
61
|
+
#
|
62
|
+
# @return [Boolean]
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# Money.new(1).positive? #=> true
|
66
|
+
# Money.new(0).positive? #=> false
|
67
|
+
# Money.new(-1).positive? #=> false
|
68
|
+
def positive?
|
69
|
+
fractional > 0
|
70
|
+
end
|
71
|
+
|
72
|
+
# Test if the amount is negative. Returns +true+ if the money amount is
|
73
|
+
# less than 0, +false+ otherwise.
|
74
|
+
#
|
75
|
+
# @return [Boolean]
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# Money.new(-1).negative? #=> true
|
79
|
+
# Money.new(0).negative? #=> false
|
80
|
+
# Money.new(1).negative? #=> false
|
81
|
+
def negative?
|
82
|
+
fractional < 0
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns a new Money object containing the sum of the two operands' monetary
|
86
|
+
# values. If +other_money+ has a different currency then its monetary value
|
87
|
+
# is automatically exchanged to this object's currency using +exchange_to+.
|
88
|
+
#
|
89
|
+
# @param [Money] other_money Other +Money+ object to add.
|
90
|
+
#
|
91
|
+
# @return [Money]
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# Money.new(100) + Money.new(100) #=> #<Money @fractional=200>
|
95
|
+
def +(other_money)
|
96
|
+
if currency == other_money.currency
|
97
|
+
Money.new(fractional + other_money.fractional, other_money.currency)
|
98
|
+
else
|
99
|
+
Money.new(fractional + other_money.exchange_to(currency).fractional, currency)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns a new Money object containing the difference between the two
|
104
|
+
# operands' monetary values. If +other_money+ has a different currency then
|
105
|
+
# its monetary value is automatically exchanged to this object's currency
|
106
|
+
# using +exchange_to+.
|
107
|
+
#
|
108
|
+
# @param [Money] other_money Other +Money+ object to subtract.
|
109
|
+
#
|
110
|
+
# @return [Money]
|
111
|
+
#
|
112
|
+
# @example
|
113
|
+
# Money.new(100) - Money.new(99) #=> #<Money @fractional=1>
|
114
|
+
def -(other_money)
|
115
|
+
if currency == other_money.currency
|
116
|
+
Money.new(fractional - other_money.fractional, other_money.currency)
|
117
|
+
else
|
118
|
+
Money.new(fractional - other_money.exchange_to(currency).fractional, currency)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Multiplies the monetary value with the given number and returns a new
|
123
|
+
# +Money+ object with this monetary value and the same currency.
|
124
|
+
#
|
125
|
+
# Note that you can't multiply a Money object by an other +Money+ object.
|
126
|
+
#
|
127
|
+
# @param [Numeric] value Number to multiply by.
|
128
|
+
#
|
129
|
+
# @return [Money] The resulting money.
|
130
|
+
#
|
131
|
+
# @raise [ArgumentError] If +value+ is a Money instance.
|
132
|
+
#
|
133
|
+
# @example
|
134
|
+
# Money.new(100) * 2 #=> #<Money @fractional=200>
|
135
|
+
#
|
136
|
+
def *(value)
|
137
|
+
if value.is_a?(Money)
|
138
|
+
raise ArgumentError, "Can't multiply a Money by a Money"
|
139
|
+
else
|
140
|
+
Money.new(fractional * value, currency)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Divides the monetary value with the given number and returns a new +Money+
|
145
|
+
# object with this monetary value and the same currency.
|
146
|
+
# Can also divide by another +Money+ object to get a ratio.
|
147
|
+
#
|
148
|
+
# +Money/Numeric+ returns +Money+. +Money/Money+ returns +Float+.
|
149
|
+
#
|
150
|
+
# @param [Money, Numeric] value Number to divide by.
|
151
|
+
#
|
152
|
+
# @return [Money] The resulting money if you divide Money by a number.
|
153
|
+
# @return [Float] The resulting number if you divide Money by a Money.
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
# Money.new(100) / 10 #=> #<Money @fractional=10>
|
157
|
+
# Money.new(100) / Money.new(10) #=> 10.0
|
158
|
+
#
|
159
|
+
def /(value)
|
160
|
+
if value.is_a?(Money)
|
161
|
+
if currency == value.currency
|
162
|
+
(fractional / BigDecimal.new(value.fractional.to_s)).to_f
|
163
|
+
else
|
164
|
+
(fractional / BigDecimal(value.exchange_to(currency).fractional.to_s)).to_f
|
165
|
+
end
|
166
|
+
else
|
167
|
+
Money.new(fractional / value, currency)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Synonym for +#/+.
|
172
|
+
#
|
173
|
+
# @param [Money, Numeric] value Number to divide by.
|
174
|
+
#
|
175
|
+
# @return [Money] The resulting money if you divide Money by a number.
|
176
|
+
# @return [Float] The resulting number if you divide Money by a Money.
|
177
|
+
#
|
178
|
+
# @see #/
|
179
|
+
#
|
180
|
+
def div(value)
|
181
|
+
self / value
|
182
|
+
end
|
183
|
+
|
184
|
+
# Divide money by money or fixnum and return array containing quotient and
|
185
|
+
# modulus.
|
186
|
+
#
|
187
|
+
# @param [Money, Fixnum] val Number to divmod by.
|
188
|
+
#
|
189
|
+
# @return [Array<Money,Money>,Array<Fixnum,Money>]
|
190
|
+
#
|
191
|
+
# @example
|
192
|
+
# Money.new(100).divmod(9) #=> [#<Money @fractional=11>, #<Money @fractional=1>]
|
193
|
+
# Money.new(100).divmod(Money.new(9)) #=> [11, #<Money @fractional=1>]
|
194
|
+
def divmod(val)
|
195
|
+
if val.is_a?(Money)
|
196
|
+
a = self.fractional
|
197
|
+
b = self.currency == val.currency ? val.fractional : val.exchange_to(self.currency).cents
|
198
|
+
q, m = a.divmod(b)
|
199
|
+
return [q, Money.new(m, self.currency)]
|
200
|
+
else
|
201
|
+
if self.class.infinite_precision
|
202
|
+
q, m = self.fractional.divmod(BigDecimal(val.to_s))
|
203
|
+
return [Money.new(q, self.currency), Money.new(m, self.currency)]
|
204
|
+
else
|
205
|
+
return [self.div(val), Money.new(self.fractional.modulo(val), self.currency)]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Equivalent to +self.divmod(val)[1]+
|
211
|
+
#
|
212
|
+
# @param [Money, Fixnum] val Number take modulo with.
|
213
|
+
#
|
214
|
+
# @return [Money]
|
215
|
+
#
|
216
|
+
# @example
|
217
|
+
# Money.new(100).modulo(9) #=> #<Money @fractional=1>
|
218
|
+
# Money.new(100).modulo(Money.new(9)) #=> #<Money @fractional=1>
|
219
|
+
def modulo(val)
|
220
|
+
self.divmod(val)[1]
|
221
|
+
end
|
222
|
+
|
223
|
+
# Synonym for +#modulo+.
|
224
|
+
#
|
225
|
+
# @param [Money, Fixnum] val Number take modulo with.
|
226
|
+
#
|
227
|
+
# @return [Money]
|
228
|
+
#
|
229
|
+
# @see #modulo
|
230
|
+
def %(val)
|
231
|
+
self.modulo(val)
|
232
|
+
end
|
233
|
+
|
234
|
+
# If different signs +self.modulo(val) - val+ otherwise +self.modulo(val)+
|
235
|
+
#
|
236
|
+
# @param [Money, Fixnum] val Number to rake remainder with.
|
237
|
+
#
|
238
|
+
# @return [Money]
|
239
|
+
#
|
240
|
+
# @example
|
241
|
+
# Money.new(100).remainder(9) #=> #<Money @fractional=1>
|
242
|
+
def remainder(val)
|
243
|
+
a, b = self, val
|
244
|
+
b = b.exchange_to(a.currency) if b.is_a?(Money) and a.currency != b.currency
|
245
|
+
|
246
|
+
a_sign, b_sign = :pos, :pos
|
247
|
+
a_sign = :neg if a.fractional < 0
|
248
|
+
b_sign = :neg if (b.is_a?(Money) and b.fractional < 0) or (b < 0)
|
249
|
+
|
250
|
+
return a.modulo(b) if a_sign == b_sign
|
251
|
+
a.modulo(b) - (b.is_a?(Money) ? b : Money.new(b, a.currency))
|
252
|
+
end
|
253
|
+
|
254
|
+
# Return absolute value of self as a new Money object.
|
255
|
+
#
|
256
|
+
# @return [Money]
|
257
|
+
#
|
258
|
+
# @example
|
259
|
+
# Money.new(-100).abs #=> #<Money @fractional=100>
|
260
|
+
def abs
|
261
|
+
Money.new(self.fractional.abs, self.currency)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Test if the money amount is zero.
|
265
|
+
#
|
266
|
+
# @return [Boolean]
|
267
|
+
#
|
268
|
+
# @example
|
269
|
+
# Money.new(100).zero? #=> false
|
270
|
+
# Money.new(0).zero? #=> true
|
271
|
+
def zero?
|
272
|
+
fractional == 0
|
273
|
+
end
|
274
|
+
|
275
|
+
# Test if the money amount is non-zero. Returns this money object if it is
|
276
|
+
# non-zero, or nil otherwise, like +Numeric#nonzero?+.
|
277
|
+
#
|
278
|
+
# @return [Money, nil]
|
279
|
+
#
|
280
|
+
# @example
|
281
|
+
# Money.new(100).nonzero? #=> #<Money @fractional=100>
|
282
|
+
# Money.new(0).nonzero? #=> nil
|
283
|
+
def nonzero?
|
284
|
+
fractional != 0 ? self : nil
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,315 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
class Money
|
3
|
+
module Formatting
|
4
|
+
|
5
|
+
if Object.const_defined?("I18n")
|
6
|
+
def thousands_separator
|
7
|
+
if self.class.use_i18n
|
8
|
+
I18n.t(
|
9
|
+
:"number.currency.format.delimiter",
|
10
|
+
:default => I18n.t(
|
11
|
+
:"number.format.delimiter",
|
12
|
+
:default => (currency.thousands_separator || ",")
|
13
|
+
)
|
14
|
+
)
|
15
|
+
else
|
16
|
+
currency.thousands_separator || ","
|
17
|
+
end
|
18
|
+
end
|
19
|
+
else
|
20
|
+
def thousands_separator
|
21
|
+
currency.thousands_separator || ","
|
22
|
+
end
|
23
|
+
end
|
24
|
+
alias :delimiter :thousands_separator
|
25
|
+
|
26
|
+
|
27
|
+
if Object.const_defined?("I18n")
|
28
|
+
def decimal_mark
|
29
|
+
if self.class.use_i18n
|
30
|
+
I18n.t(
|
31
|
+
:"number.currency.format.separator",
|
32
|
+
:default => I18n.t(
|
33
|
+
:"number.format.separator",
|
34
|
+
:default => (currency.decimal_mark || ".")
|
35
|
+
)
|
36
|
+
)
|
37
|
+
else
|
38
|
+
currency.decimal_mark || "."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
def decimal_mark
|
43
|
+
currency.decimal_mark || "."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
alias :separator :decimal_mark
|
47
|
+
|
48
|
+
# Creates a formatted price string according to several rules.
|
49
|
+
#
|
50
|
+
# @param [Hash] rules The options used to format the string.
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
#
|
54
|
+
# @option *rules [Boolean, String] :display_free (false) Whether a zero
|
55
|
+
# amount of money should be formatted of "free" or as the supplied string.
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# Money.us_dollar(0).format(:display_free => true) #=> "free"
|
59
|
+
# Money.us_dollar(0).format(:display_free => "gratis") #=> "gratis"
|
60
|
+
# Money.us_dollar(0).format #=> "$0.00"
|
61
|
+
#
|
62
|
+
# @option *rules [Boolean] :with_currency (false) Whether the currency name
|
63
|
+
# should be appended to the result string.
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# Money.ca_dollar(100).format => "$1.00"
|
67
|
+
# Money.ca_dollar(100).format(:with_currency => true) #=> "$1.00 CAD"
|
68
|
+
# Money.us_dollar(85).format(:with_currency => true) #=> "$0.85 USD"
|
69
|
+
#
|
70
|
+
# @option *rules [Boolean] :no_cents (false) Whether cents should be omitted.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# Money.ca_dollar(100).format(:no_cents => true) #=> "$1"
|
74
|
+
# Money.ca_dollar(599).format(:no_cents => true) #=> "$5"
|
75
|
+
#
|
76
|
+
# @option *rules [Boolean] :no_cents_if_whole (false) Whether cents should be
|
77
|
+
# omitted if the cent value is zero
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# Money.ca_dollar(10000).format(:no_cents_if_whole => true) #=> "$100"
|
81
|
+
# Money.ca_dollar(10034).format(:no_cents_if_whole => true) #=> "$100.34"
|
82
|
+
#
|
83
|
+
# @option *rules [Boolean, String, nil] :symbol (true) Whether a money symbol
|
84
|
+
# should be prepended to the result string. The default is true. This method
|
85
|
+
# attempts to pick a symbol that's suitable for the given currency.
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# Money.new(100, "USD") #=> "$1.00"
|
89
|
+
# Money.new(100, "GBP") #=> "£1.00"
|
90
|
+
# Money.new(100, "EUR") #=> "€1.00"
|
91
|
+
#
|
92
|
+
# # Same thing.
|
93
|
+
# Money.new(100, "USD").format(:symbol => true) #=> "$1.00"
|
94
|
+
# Money.new(100, "GBP").format(:symbol => true) #=> "£1.00"
|
95
|
+
# Money.new(100, "EUR").format(:symbol => true) #=> "€1.00"
|
96
|
+
#
|
97
|
+
# # You can specify a false expression or an empty string to disable
|
98
|
+
# # prepending a money symbol.§
|
99
|
+
# Money.new(100, "USD").format(:symbol => false) #=> "1.00"
|
100
|
+
# Money.new(100, "GBP").format(:symbol => nil) #=> "1.00"
|
101
|
+
# Money.new(100, "EUR").format(:symbol => "") #=> "1.00"
|
102
|
+
#
|
103
|
+
# # If the symbol for the given currency isn't known, then it will default
|
104
|
+
# # to "¤" as symbol.
|
105
|
+
# Money.new(100, "AWG").format(:symbol => true) #=> "¤1.00"
|
106
|
+
#
|
107
|
+
# # You can specify a string as value to enforce using a particular symbol.
|
108
|
+
# Money.new(100, "AWG").format(:symbol => "ƒ") #=> "ƒ1.00"
|
109
|
+
#
|
110
|
+
# # You can specify a indian currency format
|
111
|
+
# Money.new(10000000, "INR").format(:south_asian_number_formatting => true) #=> "1,00,000.00"
|
112
|
+
# Money.new(10000000).format(:south_asian_number_formatting => true) #=> "$1,00,000.00"
|
113
|
+
#
|
114
|
+
# @option *rules [Boolean, nil] :symbol_before_without_space (true) Whether
|
115
|
+
# a space between the money symbol and the amount should be inserted when
|
116
|
+
# +:symbol_position+ is +:before+. The default is true (meaning no space). Ignored
|
117
|
+
# if +:symbol+ is false or +:symbol_position+ is not +:before+.
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
# # Default is to not insert a space.
|
121
|
+
# Money.new(100, "USD").format #=> "$1.00"
|
122
|
+
#
|
123
|
+
# # Same thing.
|
124
|
+
# Money.new(100, "USD").format(:symbol_before_without_space => true) #=> "$1.00"
|
125
|
+
#
|
126
|
+
# # If set to false, will insert a space.
|
127
|
+
# Money.new(100, "USD").format(:symbol_before_without_space => false) #=> "$ 1.00"
|
128
|
+
#
|
129
|
+
# @option *rules [Boolean, nil] :symbol_after_without_space (false) Whether
|
130
|
+
# a space between the the amount and the money symbol should be inserted when
|
131
|
+
# +:symbol_position+ is +:after+. The default is false (meaning space). Ignored
|
132
|
+
# if +:symbol+ is false or +:symbol_position+ is not +:after+.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# # Default is to insert a space.
|
136
|
+
# Money.new(100, "USD").format(:symbol_position => :after) #=> "1.00 $"
|
137
|
+
#
|
138
|
+
# # If set to true, will not insert a space.
|
139
|
+
# Money.new(100, "USD").format(:symbol_position => :after, :symbol_after_without_space => true) #=> "1.00$"
|
140
|
+
#
|
141
|
+
# @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the
|
142
|
+
# currency should be separated by the specified character or '.'
|
143
|
+
#
|
144
|
+
# @example
|
145
|
+
# # If a string is specified, it's value is used.
|
146
|
+
# Money.new(100, "USD").format(:decimal_mark => ",") #=> "$1,00"
|
147
|
+
#
|
148
|
+
# # If the decimal_mark for a given currency isn't known, then it will default
|
149
|
+
# # to "." as decimal_mark.
|
150
|
+
# Money.new(100, "FOO").format #=> "$1.00"
|
151
|
+
#
|
152
|
+
# @option *rules [Boolean, String, nil] :thousands_separator (true) Whether
|
153
|
+
# the currency should be delimited by the specified character or ','
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
# # If false is specified, no thousands_separator is used.
|
157
|
+
# Money.new(100000, "USD").format(:thousands_separator => false) #=> "1000.00"
|
158
|
+
# Money.new(100000, "USD").format(:thousands_separator => nil) #=> "1000.00"
|
159
|
+
# Money.new(100000, "USD").format(:thousands_separator => "") #=> "1000.00"
|
160
|
+
#
|
161
|
+
# # If a string is specified, it's value is used.
|
162
|
+
# Money.new(100000, "USD").format(:thousands_separator => ".") #=> "$1.000.00"
|
163
|
+
#
|
164
|
+
# # If the thousands_separator for a given currency isn't known, then it will
|
165
|
+
# # default to "," as thousands_separator.
|
166
|
+
# Money.new(100000, "FOO").format #=> "$1,000.00"
|
167
|
+
#
|
168
|
+
# @option *rules [Boolean] :html (false) Whether the currency should be
|
169
|
+
# HTML-formatted. Only useful in combination with +:with_currency+.
|
170
|
+
#
|
171
|
+
# @example
|
172
|
+
# s = Money.ca_dollar(570).format(:html => true, :with_currency => true)
|
173
|
+
# s #=> "$5.70 <span class=\"currency\">CAD</span>"
|
174
|
+
#
|
175
|
+
# @option *rules [Boolean] :sign_before_symbol (false) Whether the sign should be
|
176
|
+
# before the currency symbol.
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# # You can specify to display the sign before the symbol for negative numbers
|
180
|
+
# Money.new(-100, "GBP").format(:sign_before_symbol => true) #=> "-£1.00"
|
181
|
+
# Money.new(-100, "GBP").format(:sign_before_symbol => false) #=> "£-1.00"
|
182
|
+
# Money.new(-100, "GBP").format #=> "£-1.00"
|
183
|
+
def format(*rules)
|
184
|
+
# support for old format parameters
|
185
|
+
rules = normalize_formatting_rules(rules)
|
186
|
+
rules = localize_formatting_rules(rules)
|
187
|
+
|
188
|
+
if fractional == 0
|
189
|
+
if rules[:display_free].respond_to?(:to_str)
|
190
|
+
return rules[:display_free]
|
191
|
+
elsif rules[:display_free]
|
192
|
+
return "free"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
symbol_value =
|
197
|
+
if rules.has_key?(:symbol)
|
198
|
+
if rules[:symbol] === true
|
199
|
+
symbol
|
200
|
+
elsif rules[:symbol]
|
201
|
+
rules[:symbol]
|
202
|
+
else
|
203
|
+
""
|
204
|
+
end
|
205
|
+
elsif rules[:html]
|
206
|
+
currency.html_entity == '' ? currency.symbol : currency.html_entity
|
207
|
+
else
|
208
|
+
symbol
|
209
|
+
end
|
210
|
+
|
211
|
+
formatted = rules[:no_cents] ? "#{self.to_s.to_i}" : self.to_s
|
212
|
+
|
213
|
+
if rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0
|
214
|
+
formatted = "#{self.to_s.to_i}"
|
215
|
+
end
|
216
|
+
|
217
|
+
thousands_separator_value = thousands_separator
|
218
|
+
# Determine thousands_separator
|
219
|
+
if rules.has_key?(:thousands_separator)
|
220
|
+
thousands_separator_value = rules[:thousands_separator] || ''
|
221
|
+
end
|
222
|
+
|
223
|
+
# Apply thousands_separator
|
224
|
+
formatted.gsub!(regexp_format(formatted, rules, decimal_mark, symbol_value),
|
225
|
+
"\\1#{thousands_separator_value}")
|
226
|
+
|
227
|
+
symbol_position =
|
228
|
+
if rules.has_key?(:symbol_position)
|
229
|
+
rules[:symbol_position]
|
230
|
+
elsif currency.symbol_first?
|
231
|
+
:before
|
232
|
+
else
|
233
|
+
:after
|
234
|
+
end
|
235
|
+
|
236
|
+
sign = ""
|
237
|
+
if rules[:sign_before_symbol] == true && self.negative?
|
238
|
+
formatted.tr!("-", "")
|
239
|
+
sign = "-"
|
240
|
+
end
|
241
|
+
|
242
|
+
if symbol_value && !symbol_value.empty?
|
243
|
+
symbol_value = "<span class=\"currency_symbol\">#{symbol_value}</span>" if rules[:html_wrap_symbol]
|
244
|
+
|
245
|
+
formatted = if symbol_position == :before
|
246
|
+
symbol_space = rules[:symbol_before_without_space] === false ? " " : ""
|
247
|
+
"#{sign}#{symbol_value}#{symbol_space}#{formatted}"
|
248
|
+
else
|
249
|
+
symbol_space = rules[:symbol_after_without_space] ? "" : " "
|
250
|
+
"#{sign}#{formatted}#{symbol_space}#{symbol_value}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
if rules.has_key?(:decimal_mark) && rules[:decimal_mark] &&
|
255
|
+
rules[:decimal_mark] != decimal_mark
|
256
|
+
formatted.sub!(decimal_mark, rules[:decimal_mark])
|
257
|
+
end
|
258
|
+
|
259
|
+
if rules[:with_currency]
|
260
|
+
formatted << " "
|
261
|
+
formatted << '<span class="currency">' if rules[:html]
|
262
|
+
formatted << currency.to_s
|
263
|
+
formatted << '</span>' if rules[:html]
|
264
|
+
end
|
265
|
+
formatted
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
# Cleans up formatting rules.
|
272
|
+
#
|
273
|
+
# @param [Hash] rules
|
274
|
+
#
|
275
|
+
# @return [Hash]
|
276
|
+
def normalize_formatting_rules(rules)
|
277
|
+
if rules.size == 0
|
278
|
+
rules = {}
|
279
|
+
elsif rules.size == 1
|
280
|
+
rules = rules.pop
|
281
|
+
rules = { rules => true } if rules.is_a?(Symbol)
|
282
|
+
end
|
283
|
+
if !rules.include?(:decimal_mark) && rules.include?(:separator)
|
284
|
+
rules[:decimal_mark] = rules[:separator]
|
285
|
+
end
|
286
|
+
if !rules.include?(:thousands_separator) && rules.include?(:delimiter)
|
287
|
+
rules[:thousands_separator] = rules[:delimiter]
|
288
|
+
end
|
289
|
+
rules
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def regexp_format(formatted, rules, decimal_mark, symbol_value)
|
294
|
+
regexp_decimal = Regexp.escape(decimal_mark)
|
295
|
+
if rules[:south_asian_number_formatting]
|
296
|
+
/(\d+?)(?=(\d\d)+(\d)(?:\.))/
|
297
|
+
else
|
298
|
+
# Symbols may contain decimal marks (E.g "դր.")
|
299
|
+
if formatted.sub(symbol_value, "") =~ /#{regexp_decimal}/
|
300
|
+
/(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
|
301
|
+
else
|
302
|
+
/(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def localize_formatting_rules(rules)
|
308
|
+
if currency.iso_code == "JPY" && I18n.locale == :ja
|
309
|
+
rules[:symbol] = "円"
|
310
|
+
rules[:symbol_position] = :after
|
311
|
+
rules[:symbol_after_without_space] = true
|
312
|
+
end
|
313
|
+
rules
|
314
|
+
end
|
315
|
+
end
|