money 4.0.1 → 4.0.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 +405 -391
- data/LICENSE +21 -21
- data/README.md +249 -243
- data/Rakefile +52 -49
- data/config/currency.json +2068 -2068
- data/config/currency_bc.json +41 -41
- data/lib/money.rb +28 -28
- data/lib/money/bank/base.rb +130 -131
- data/lib/money/bank/variable_exchange.rb +253 -252
- data/lib/money/core_extensions.rb +82 -82
- data/lib/money/currency.rb +280 -263
- data/lib/money/currency_loader.rb +21 -19
- data/lib/money/money.rb +411 -405
- data/lib/money/money/arithmetic.rb +257 -246
- data/lib/money/money/formatting.rb +251 -260
- data/lib/money/money/parsing.rb +350 -350
- data/money.gemspec +29 -29
- data/spec/bank/base_spec.rb +69 -72
- data/spec/bank/variable_exchange_spec.rb +233 -238
- data/spec/core_extensions_spec.rb +160 -158
- data/spec/currency_spec.rb +139 -120
- data/spec/money/arithmetic_spec.rb +482 -479
- data/spec/money/formatting_spec.rb +402 -383
- data/spec/money/parsing_spec.rb +210 -197
- data/spec/money_spec.rb +312 -292
- data/spec/spec_helper.rb +32 -28
- metadata +14 -14
@@ -1,260 +1,251 @@
|
|
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
|
-
# @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the
|
111
|
-
# currency should be separated by the specified character or '.'
|
112
|
-
#
|
113
|
-
# @example
|
114
|
-
# # If a string is specified, it's value is used.
|
115
|
-
# Money.new(100, "USD").format(:decimal_mark => ",") #=> "$1,00"
|
116
|
-
#
|
117
|
-
# # If the decimal_mark for a given currency isn't known, then it will default
|
118
|
-
# # to "." as decimal_mark.
|
119
|
-
# Money.new(100, "FOO").format #=> "$1.00"
|
120
|
-
#
|
121
|
-
# @option *rules [Boolean, String, nil] :thousands_separator (true) Whether
|
122
|
-
# the currency should be delimited by the specified character or ','
|
123
|
-
#
|
124
|
-
# @example
|
125
|
-
# # If false is specified, no thousands_separator is used.
|
126
|
-
# Money.new(100000, "USD").format(:thousands_separator => false) #=> "1000.00"
|
127
|
-
# Money.new(100000, "USD").format(:thousands_separator => nil) #=> "1000.00"
|
128
|
-
# Money.new(100000, "USD").format(:thousands_separator => "") #=> "1000.00"
|
129
|
-
#
|
130
|
-
# # If a string is specified, it's value is used.
|
131
|
-
# Money.new(100000, "USD").format(:thousands_separator => ".") #=> "$1.000.00"
|
132
|
-
#
|
133
|
-
# # If the thousands_separator for a given currency isn't known, then it will
|
134
|
-
# # default to "," as thousands_separator.
|
135
|
-
# Money.new(100000, "FOO").format #=> "$1,000.00"
|
136
|
-
#
|
137
|
-
# @option *rules [Boolean] :html (false) Whether the currency should be
|
138
|
-
# HTML-formatted. Only useful in combination with +:with_currency+.
|
139
|
-
#
|
140
|
-
# @example
|
141
|
-
# s = Money.ca_dollar(570).format(:html => true, :with_currency => true)
|
142
|
-
# s #=> "$5.70 <span class=\"currency\">CAD</span>"
|
143
|
-
def format(*rules)
|
144
|
-
# support for old format parameters
|
145
|
-
rules = normalize_formatting_rules(rules)
|
146
|
-
rules = localize_formatting_rules(rules)
|
147
|
-
|
148
|
-
if cents == 0
|
149
|
-
if rules[:display_free].respond_to?(:to_str)
|
150
|
-
return rules[:display_free]
|
151
|
-
elsif rules[:display_free]
|
152
|
-
return "free"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
symbol_value =
|
157
|
-
if rules.has_key?(:symbol)
|
158
|
-
if rules[:symbol] === true
|
159
|
-
symbol
|
160
|
-
elsif rules[:symbol]
|
161
|
-
rules[:symbol]
|
162
|
-
else
|
163
|
-
""
|
164
|
-
end
|
165
|
-
elsif rules[:html]
|
166
|
-
currency.html_entity
|
167
|
-
else
|
168
|
-
symbol
|
169
|
-
end
|
170
|
-
|
171
|
-
formatted =
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
end
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
#
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
if rules.
|
237
|
-
rules =
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
end
|
251
|
-
|
252
|
-
def localize_formatting_rules(rules)
|
253
|
-
if currency.iso_code == "JPY" && I18n.locale == :ja
|
254
|
-
rules[:symbol] = "円"
|
255
|
-
rules[:symbol_position] = :after
|
256
|
-
rules[:symbol_after_without_space] = true
|
257
|
-
end
|
258
|
-
rules
|
259
|
-
end
|
260
|
-
end
|
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
|
+
# @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the
|
111
|
+
# currency should be separated by the specified character or '.'
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# # If a string is specified, it's value is used.
|
115
|
+
# Money.new(100, "USD").format(:decimal_mark => ",") #=> "$1,00"
|
116
|
+
#
|
117
|
+
# # If the decimal_mark for a given currency isn't known, then it will default
|
118
|
+
# # to "." as decimal_mark.
|
119
|
+
# Money.new(100, "FOO").format #=> "$1.00"
|
120
|
+
#
|
121
|
+
# @option *rules [Boolean, String, nil] :thousands_separator (true) Whether
|
122
|
+
# the currency should be delimited by the specified character or ','
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# # If false is specified, no thousands_separator is used.
|
126
|
+
# Money.new(100000, "USD").format(:thousands_separator => false) #=> "1000.00"
|
127
|
+
# Money.new(100000, "USD").format(:thousands_separator => nil) #=> "1000.00"
|
128
|
+
# Money.new(100000, "USD").format(:thousands_separator => "") #=> "1000.00"
|
129
|
+
#
|
130
|
+
# # If a string is specified, it's value is used.
|
131
|
+
# Money.new(100000, "USD").format(:thousands_separator => ".") #=> "$1.000.00"
|
132
|
+
#
|
133
|
+
# # If the thousands_separator for a given currency isn't known, then it will
|
134
|
+
# # default to "," as thousands_separator.
|
135
|
+
# Money.new(100000, "FOO").format #=> "$1,000.00"
|
136
|
+
#
|
137
|
+
# @option *rules [Boolean] :html (false) Whether the currency should be
|
138
|
+
# HTML-formatted. Only useful in combination with +:with_currency+.
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# s = Money.ca_dollar(570).format(:html => true, :with_currency => true)
|
142
|
+
# s #=> "$5.70 <span class=\"currency\">CAD</span>"
|
143
|
+
def format(*rules)
|
144
|
+
# support for old format parameters
|
145
|
+
rules = normalize_formatting_rules(rules)
|
146
|
+
rules = localize_formatting_rules(rules)
|
147
|
+
|
148
|
+
if cents == 0
|
149
|
+
if rules[:display_free].respond_to?(:to_str)
|
150
|
+
return rules[:display_free]
|
151
|
+
elsif rules[:display_free]
|
152
|
+
return "free"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
symbol_value =
|
157
|
+
if rules.has_key?(:symbol)
|
158
|
+
if rules[:symbol] === true
|
159
|
+
symbol
|
160
|
+
elsif rules[:symbol]
|
161
|
+
rules[:symbol]
|
162
|
+
else
|
163
|
+
""
|
164
|
+
end
|
165
|
+
elsif rules[:html]
|
166
|
+
currency.html_entity
|
167
|
+
else
|
168
|
+
symbol
|
169
|
+
end
|
170
|
+
|
171
|
+
formatted = rules[:no_cents] ? "#{self.to_s.to_i}" : self.to_s
|
172
|
+
|
173
|
+
if rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0
|
174
|
+
formatted = "#{self.to_s.to_i}"
|
175
|
+
end
|
176
|
+
|
177
|
+
symbol_position =
|
178
|
+
if rules.has_key?(:symbol_position)
|
179
|
+
rules[:symbol_position]
|
180
|
+
elsif currency.symbol_first?
|
181
|
+
:before
|
182
|
+
else
|
183
|
+
:after
|
184
|
+
end
|
185
|
+
|
186
|
+
if symbol_value && !symbol_value.empty?
|
187
|
+
formatted = if symbol_position == :before
|
188
|
+
"#{symbol_value}#{formatted}"
|
189
|
+
else
|
190
|
+
symbol_space = rules[:symbol_after_without_space] ? "" : " "
|
191
|
+
"#{formatted}#{symbol_space}#{symbol_value}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if rules.has_key?(:decimal_mark) and rules[:decimal_mark] and
|
196
|
+
rules[:decimal_mark] != decimal_mark
|
197
|
+
formatted.sub!(decimal_mark, rules[:decimal_mark])
|
198
|
+
end
|
199
|
+
|
200
|
+
thousands_separator_value = thousands_separator
|
201
|
+
# Determine thousands_separator
|
202
|
+
if rules.has_key?(:thousands_separator)
|
203
|
+
thousands_separator_value = rules[:thousands_separator] || ''
|
204
|
+
end
|
205
|
+
|
206
|
+
# Apply thousands_separator
|
207
|
+
formatted.gsub!(/(\d)(?=(?:\d{3})+(?:[^\d]|$))/, "\\1#{thousands_separator_value}")
|
208
|
+
|
209
|
+
if rules[:with_currency]
|
210
|
+
formatted << " "
|
211
|
+
formatted << '<span class="currency">' if rules[:html]
|
212
|
+
formatted << currency.to_s
|
213
|
+
formatted << '</span>' if rules[:html]
|
214
|
+
end
|
215
|
+
formatted
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
# Cleans up formatting rules.
|
222
|
+
#
|
223
|
+
# @param [Hash]
|
224
|
+
#
|
225
|
+
# @return [Hash]
|
226
|
+
def normalize_formatting_rules(rules)
|
227
|
+
if rules.size == 0
|
228
|
+
rules = {}
|
229
|
+
elsif rules.size == 1
|
230
|
+
rules = rules.pop
|
231
|
+
rules = { rules => true } if rules.is_a?(Symbol)
|
232
|
+
end
|
233
|
+
if not rules.include?(:decimal_mark) and rules.include?(:separator)
|
234
|
+
rules[:decimal_mark] = rules[:separator]
|
235
|
+
end
|
236
|
+
if not rules.include?(:thousands_separator) and rules.include?(:delimiter)
|
237
|
+
rules[:thousands_separator] = rules[:delimiter]
|
238
|
+
end
|
239
|
+
rules
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def localize_formatting_rules(rules)
|
244
|
+
if currency.iso_code == "JPY" && I18n.locale == :ja
|
245
|
+
rules[:symbol] = "円"
|
246
|
+
rules[:symbol_position] = :after
|
247
|
+
rules[:symbol_after_without_space] = true
|
248
|
+
end
|
249
|
+
rules
|
250
|
+
end
|
251
|
+
end
|