money 5.0.0 → 5.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,9 +6,9 @@ class Money
6
6
  # @return [Money]
7
7
  #
8
8
  # @example
9
- # - Money.new(100) #=> #<Money @cents=-100>
9
+ # - Money.new(100) #=> #<Money @fractional=-100>
10
10
  def -@
11
- Money.new(-cents, currency)
11
+ Money.new(-fractional, currency)
12
12
  end
13
13
 
14
14
 
@@ -26,7 +26,7 @@ class Money
26
26
  def ==(other_money)
27
27
  if other_money.respond_to?(:to_money)
28
28
  other_money = other_money.to_money
29
- cents == other_money.cents && self.currency == other_money.currency
29
+ fractional == other_money.fractional && self.currency == other_money.currency
30
30
  else
31
31
  false
32
32
  end
@@ -47,9 +47,9 @@ class Money
47
47
  if other_money.respond_to?(:to_money)
48
48
  other_money = other_money.to_money
49
49
  if self.currency == other_money.currency
50
- cents <=> other_money.cents
50
+ fractional <=> other_money.fractional
51
51
  else
52
- cents <=> other_money.exchange_to(currency).cents
52
+ fractional <=> other_money.exchange_to(currency).fractional
53
53
  end
54
54
  else
55
55
  raise ArgumentError, "Comparison of #{self.class} with #{other_money.inspect} failed"
@@ -66,7 +66,7 @@ class Money
66
66
  # Money.new(0).positive? #=> false
67
67
  # Money.new(-1).positive? #=> false
68
68
  def positive?
69
- cents > 0
69
+ fractional > 0
70
70
  end
71
71
 
72
72
  # Test if the amount is negative. Returns +true+ if the money amount is
@@ -79,7 +79,7 @@ class Money
79
79
  # Money.new(0).negative? #=> false
80
80
  # Money.new(1).negative? #=> false
81
81
  def negative?
82
- cents < 0
82
+ fractional < 0
83
83
  end
84
84
 
85
85
  # Returns a new Money object containing the sum of the two operands' monetary
@@ -91,12 +91,12 @@ class Money
91
91
  # @return [Money]
92
92
  #
93
93
  # @example
94
- # Money.new(100) + Money.new(100) #=> #<Money @cents=200>
94
+ # Money.new(100) + Money.new(100) #=> #<Money @fractional=200>
95
95
  def +(other_money)
96
96
  if currency == other_money.currency
97
- Money.new(cents + other_money.cents, other_money.currency)
97
+ Money.new(fractional + other_money.fractional, other_money.currency)
98
98
  else
99
- Money.new(cents + other_money.exchange_to(currency).cents, currency)
99
+ Money.new(fractional + other_money.exchange_to(currency).fractional, currency)
100
100
  end
101
101
  end
102
102
 
@@ -110,12 +110,12 @@ class Money
110
110
  # @return [Money]
111
111
  #
112
112
  # @example
113
- # Money.new(100) - Money.new(99) #=> #<Money @cents=1>
113
+ # Money.new(100) - Money.new(99) #=> #<Money @fractional=1>
114
114
  def -(other_money)
115
115
  if currency == other_money.currency
116
- Money.new(cents - other_money.cents, other_money.currency)
116
+ Money.new(fractional - other_money.fractional, other_money.currency)
117
117
  else
118
- Money.new(cents - other_money.exchange_to(currency).cents, currency)
118
+ Money.new(fractional - other_money.exchange_to(currency).fractional, currency)
119
119
  end
120
120
  end
121
121
 
@@ -131,13 +131,13 @@ class Money
131
131
  # @raise [ArgumentError] If +value+ is a Money instance.
132
132
  #
133
133
  # @example
134
- # Money.new(100) * 2 #=> #<Money @cents=200>
134
+ # Money.new(100) * 2 #=> #<Money @fractional=200>
135
135
  #
136
136
  def *(value)
137
137
  if value.is_a?(Money)
138
138
  raise ArgumentError, "Can't multiply a Money by a Money"
139
139
  else
140
- Money.new(cents * value, currency)
140
+ Money.new(fractional * value, currency)
141
141
  end
142
142
  end
143
143
 
@@ -153,18 +153,18 @@ class Money
153
153
  # @return [Float] The resulting number if you divide Money by a Money.
154
154
  #
155
155
  # @example
156
- # Money.new(100) / 10 #=> #<Money @cents=10>
156
+ # Money.new(100) / 10 #=> #<Money @fractional=10>
157
157
  # Money.new(100) / Money.new(10) #=> 10.0
158
158
  #
159
159
  def /(value)
160
160
  if value.is_a?(Money)
161
161
  if currency == value.currency
162
- (cents / BigDecimal.new(value.cents.to_s)).to_f
162
+ (fractional / BigDecimal.new(value.fractional.to_s)).to_f
163
163
  else
164
- (cents / BigDecimal(value.exchange_to(currency).cents.to_s)).to_f
164
+ (fractional / BigDecimal(value.exchange_to(currency).fractional.to_s)).to_f
165
165
  end
166
166
  else
167
- Money.new(cents / value, currency)
167
+ Money.new(fractional / value, currency)
168
168
  end
169
169
  end
170
170
 
@@ -189,16 +189,21 @@ class Money
189
189
  # @return [Array<Money,Money>,Array<Fixnum,Money>]
190
190
  #
191
191
  # @example
192
- # Money.new(100).divmod(9) #=> [#<Money @cents=11>, #<Money @cents=1>]
193
- # Money.new(100).divmod(Money.new(9)) #=> [11, #<Money @cents=1>]
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
194
  def divmod(val)
195
195
  if val.is_a?(Money)
196
- a = self.cents
197
- b = self.currency == val.currency ? val.cents : val.exchange_to(self.currency).cents
196
+ a = self.fractional
197
+ b = self.currency == val.currency ? val.fractional : val.exchange_to(self.currency).cents
198
198
  q, m = a.divmod(b)
199
199
  return [q, Money.new(m, self.currency)]
200
200
  else
201
- return [self.div(val), Money.new(self.cents.modulo(val), self.currency)]
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
202
207
  end
203
208
  end
204
209
 
@@ -209,8 +214,8 @@ class Money
209
214
  # @return [Money]
210
215
  #
211
216
  # @example
212
- # Money.new(100).modulo(9) #=> #<Money @cents=1>
213
- # Money.new(100).modulo(Money.new(9)) #=> #<Money @cents=1>
217
+ # Money.new(100).modulo(9) #=> #<Money @fractional=1>
218
+ # Money.new(100).modulo(Money.new(9)) #=> #<Money @fractional=1>
214
219
  def modulo(val)
215
220
  self.divmod(val)[1]
216
221
  end
@@ -233,14 +238,14 @@ class Money
233
238
  # @return [Money]
234
239
  #
235
240
  # @example
236
- # Money.new(100).remainder(9) #=> #<Money @cents=1>
241
+ # Money.new(100).remainder(9) #=> #<Money @fractional=1>
237
242
  def remainder(val)
238
243
  a, b = self, val
239
244
  b = b.exchange_to(a.currency) if b.is_a?(Money) and a.currency != b.currency
240
245
 
241
246
  a_sign, b_sign = :pos, :pos
242
- a_sign = :neg if a.cents < 0
243
- b_sign = :neg if (b.is_a?(Money) and b.cents < 0) or (b < 0)
247
+ a_sign = :neg if a.fractional < 0
248
+ b_sign = :neg if (b.is_a?(Money) and b.fractional < 0) or (b < 0)
244
249
 
245
250
  return a.modulo(b) if a_sign == b_sign
246
251
  a.modulo(b) - (b.is_a?(Money) ? b : Money.new(b, a.currency))
@@ -251,9 +256,9 @@ class Money
251
256
  # @return [Money]
252
257
  #
253
258
  # @example
254
- # Money.new(-100).abs #=> #<Money @cents=100>
259
+ # Money.new(-100).abs #=> #<Money @fractional=100>
255
260
  def abs
256
- Money.new(self.cents.abs, self.currency)
261
+ Money.new(self.fractional.abs, self.currency)
257
262
  end
258
263
 
259
264
  # Test if the money amount is zero.
@@ -264,7 +269,7 @@ class Money
264
269
  # Money.new(100).zero? #=> false
265
270
  # Money.new(0).zero? #=> true
266
271
  def zero?
267
- cents == 0
272
+ fractional == 0
268
273
  end
269
274
 
270
275
  # Test if the money amount is non-zero. Returns this money object if it is
@@ -273,10 +278,10 @@ class Money
273
278
  # @return [Money, nil]
274
279
  #
275
280
  # @example
276
- # Money.new(100).nonzero? #=> #<Money @cents=100>
281
+ # Money.new(100).nonzero? #=> #<Money @fractional=100>
277
282
  # Money.new(0).nonzero? #=> nil
278
283
  def nonzero?
279
- cents != 0 ? self : nil
284
+ fractional != 0 ? self : nil
280
285
  end
281
286
 
282
287
  end
@@ -73,7 +73,7 @@ class Money
73
73
  # Money.ca_dollar(100).format(:no_cents => true) #=> "$1"
74
74
  # Money.ca_dollar(599).format(:no_cents => true) #=> "$5"
75
75
  #
76
- # @option *rules [Boolean] :no_cents_if_whole (false) Whether cents should be
76
+ # @option *rules [Boolean] :no_cents_if_whole (false) Whether cents should be
77
77
  # omitted if the cent value is zero
78
78
  #
79
79
  # @example
@@ -107,6 +107,10 @@ class Money
107
107
  # # You can specify a string as value to enforce using a particular symbol.
108
108
  # Money.new(100, "AWG").format(:symbol => "ƒ") #=> "ƒ1.00"
109
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
+ #
110
114
  # @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the
111
115
  # currency should be separated by the specified character or '.'
112
116
  #
@@ -145,7 +149,7 @@ class Money
145
149
  rules = normalize_formatting_rules(rules)
146
150
  rules = localize_formatting_rules(rules)
147
151
 
148
- if cents == 0
152
+ if fractional == 0
149
153
  if rules[:display_free].respond_to?(:to_str)
150
154
  return rules[:display_free]
151
155
  elsif rules[:display_free]
@@ -184,7 +188,7 @@ class Money
184
188
  end
185
189
 
186
190
  if symbol_value && !symbol_value.empty?
187
- formatted = if symbol_position == :before
191
+ formatted = if symbol_position == :before
188
192
  "#{symbol_value}#{formatted}"
189
193
  else
190
194
  symbol_space = rules[:symbol_after_without_space] ? "" : " "
@@ -192,7 +196,7 @@ class Money
192
196
  end
193
197
  end
194
198
 
195
- if rules.has_key?(:decimal_mark) and rules[:decimal_mark] and
199
+ if rules.has_key?(:decimal_mark) && rules[:decimal_mark] &&
196
200
  rules[:decimal_mark] != decimal_mark
197
201
  formatted.sub!(decimal_mark, rules[:decimal_mark])
198
202
  end
@@ -204,13 +208,7 @@ class Money
204
208
  end
205
209
 
206
210
  # Apply thousands_separator
207
- regexp_decimal = Regexp.escape(decimal_mark)
208
- regexp_format = if formatted =~ /#{regexp_decimal}/
209
- /(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
210
- else
211
- /(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
212
- end
213
- formatted.gsub!(regexp_format, "\\1#{thousands_separator_value}")
211
+ formatted.gsub!(regexp_format(formatted, rules, decimal_mark), "\\1#{thousands_separator_value}")
214
212
 
215
213
  if rules[:with_currency]
216
214
  formatted << " "
@@ -236,16 +234,29 @@ class Money
236
234
  rules = rules.pop
237
235
  rules = { rules => true } if rules.is_a?(Symbol)
238
236
  end
239
- if not rules.include?(:decimal_mark) and rules.include?(:separator)
237
+ if !rules.include?(:decimal_mark) && rules.include?(:separator)
240
238
  rules[:decimal_mark] = rules[:separator]
241
239
  end
242
- if not rules.include?(:thousands_separator) and rules.include?(:delimiter)
240
+ if !rules.include?(:thousands_separator) && rules.include?(:delimiter)
243
241
  rules[:thousands_separator] = rules[:delimiter]
244
242
  end
245
243
  rules
246
244
  end
247
245
  end
248
246
 
247
+ def regexp_format(formatted, rules, decimal_mark)
248
+ regexp_decimal = Regexp.escape(decimal_mark)
249
+ if rules[:south_asian_number_formatting]
250
+ /(\d+?)(?=(\d\d)+(\d)(?:\.))/
251
+ else
252
+ if formatted =~ /#{regexp_decimal}/
253
+ /(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
254
+ else
255
+ /(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
256
+ end
257
+ end
258
+ end
259
+
249
260
  def localize_formatting_rules(rules)
250
261
  if currency.iso_code == "JPY" && I18n.locale == :ja
251
262
  rules[:symbol] = "円"
@@ -21,12 +21,12 @@ class Money
21
21
  # the +input+ string.
22
22
  #
23
23
  # @example
24
- # '100'.to_money #=> #<Money @cents=10000>
25
- # '100.37'.to_money #=> #<Money @cents=10037>
26
- # '100 USD'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
27
- # 'USD 100'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
28
- # '$100 USD'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
29
- # 'hello 2000 world'.to_money #=> #<Money @cents=200000 @currency=#<Money::Currency id: usd>>
24
+ # '100'.to_money #=> #<Money @fractional=10000>
25
+ # '100.37'.to_money #=> #<Money @fractional=10037>
26
+ # '100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
27
+ # 'USD 100'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
28
+ # '$100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
29
+ # 'hello 2000 world'.to_money #=> #<Money @fractional=200000 @currency=#<Money::Currency id: usd>>
30
30
  #
31
31
  # @example Mismatching currencies
32
32
  # 'USD 2000'.to_money("EUR") #=> ArgumentError
@@ -64,12 +64,12 @@ class Money
64
64
  end
65
65
  currency = Money::Currency.wrap(currency)
66
66
 
67
- cents = extract_cents(i, currency)
68
- new(cents, currency)
67
+ fractional = extract_cents(i, currency)
68
+ new(fractional, currency)
69
69
  end
70
70
 
71
71
  # Converts a String into a Money object treating the +value+
72
- # as dollars and converting them to the corresponding cents value,
72
+ # as amount and converting to fractional unit,
73
73
  # according to +currency+ subunit property,
74
74
  # before instantiating the Money object.
75
75
  #
@@ -84,13 +84,13 @@ class Money
84
84
  #
85
85
  # @example
86
86
  # Money.from_string("100")
87
- # #=> #<Money @cents=10000 @currency="USD">
87
+ # #=> #<Money @fractional=10000 @currency="USD">
88
88
  # Money.from_string("100", "USD")
89
- # #=> #<Money @cents=10000 @currency="USD">
89
+ # #=> #<Money @fractional=10000 @currency="USD">
90
90
  # Money.from_string("100", "EUR")
91
- # #=> #<Money @cents=10000 @currency="EUR">
91
+ # #=> #<Money @fractional=10000 @currency="EUR">
92
92
  # Money.from_string("100", "BHD")
93
- # #=> #<Money @cents=100 @currency="BHD">
93
+ # #=> #<Money @fractional=100 @currency="BHD">
94
94
  #
95
95
  # @see String#to_money
96
96
  # @see Money.parse
@@ -100,7 +100,7 @@ class Money
100
100
  end
101
101
 
102
102
  # Converts a Fixnum into a Money object treating the +value+
103
- # as dollars and converting them to the corresponding cents value,
103
+ # as amount and to corresponding fractional unit,
104
104
  # according to +currency+ subunit property,
105
105
  # before instantiating the Money object.
106
106
  #
@@ -111,13 +111,13 @@ class Money
111
111
  #
112
112
  # @example
113
113
  # Money.from_fixnum(100)
114
- # #=> #<Money @cents=10000 @currency="USD">
114
+ # #=> #<Money @fractional=10000 @currency="USD">
115
115
  # Money.from_fixnum(100, "USD")
116
- # #=> #<Money @cents=10000 @currency="USD">
116
+ # #=> #<Money @fractional=10000 @currency="USD">
117
117
  # Money.from_fixnum(100, "EUR")
118
- # #=> #<Money @cents=10000 @currency="EUR">
118
+ # #=> #<Money @fractional=10000 @currency="EUR">
119
119
  # Money.from_fixnum(100, "BHD")
120
- # #=> #<Money @cents=100 @currency="BHD">
120
+ # #=> #<Money @fractional=100 @currency="BHD">
121
121
  #
122
122
  # @see Fixnum#to_money
123
123
  # @see Money.from_numeric
@@ -129,7 +129,7 @@ class Money
129
129
  end
130
130
 
131
131
  # Converts a Float into a Money object treating the +value+
132
- # as dollars and converting them to the corresponding cents value,
132
+ # as dollars and to corresponding fractional unit,
133
133
  # according to +currency+ subunit property,
134
134
  # before instantiating the Money object.
135
135
  #
@@ -143,13 +143,13 @@ class Money
143
143
  #
144
144
  # @example
145
145
  # Money.from_float(100.0)
146
- # #=> #<Money @cents=10000 @currency="USD">
146
+ # #=> #<Money @fractional=10000 @currency="USD">
147
147
  # Money.from_float(100.0, "USD")
148
- # #=> #<Money @cents=10000 @currency="USD">
148
+ # #=> #<Money @fractional=10000 @currency="USD">
149
149
  # Money.from_float(100.0, "EUR")
150
- # #=> #<Money @cents=10000 @currency="EUR">
150
+ # #=> #<Money @fractional=10000 @currency="EUR">
151
151
  # Money.from_float(100.0, "BHD")
152
- # #=> #<Money @cents=100 @currency="BHD">
152
+ # #=> #<Money @fractional=100 @currency="BHD">
153
153
  #
154
154
  # @see Float#to_money
155
155
  # @see Money.from_numeric
@@ -159,7 +159,7 @@ class Money
159
159
  end
160
160
 
161
161
  # Converts a BigDecimal into a Money object treating the +value+
162
- # as dollars and converting them to the corresponding cents value,
162
+ # as dollars and converting to corresponding fractional unit,
163
163
  # according to +currency+ subunit property,
164
164
  # before instantiating the Money object.
165
165
  #
@@ -170,13 +170,13 @@ class Money
170
170
  #
171
171
  # @example
172
172
  # Money.from_bigdecimal(BigDecimal.new("100")
173
- # #=> #<Money @cents=10000 @currency="USD">
173
+ # #=> #<Money @fractional=10000 @currency="USD">
174
174
  # Money.from_bigdecimal(BigDecimal.new("100", "USD")
175
- # #=> #<Money @cents=10000 @currency="USD">
175
+ # #=> #<Money @fractional=10000 @currency="USD">
176
176
  # Money.from_bigdecimal(BigDecimal.new("100", "EUR")
177
- # #=> #<Money @cents=10000 @currency="EUR">
177
+ # #=> #<Money @fractional=10000 @currency="EUR">
178
178
  # Money.from_bigdecimal(BigDecimal.new("100", "BHD")
179
- # #=> #<Money @cents=100 @currency="BHD">
179
+ # #=> #<Money @fractional=100 @currency="BHD">
180
180
  #
181
181
  # @see BigDecimal#to_money
182
182
  # @see Money.from_numeric
@@ -188,7 +188,7 @@ class Money
188
188
  end
189
189
 
190
190
  # Converts a Numeric value into a Money object treating the +value+
191
- # as dollars and converting them to the corresponding cents value,
191
+ # as dollars and converting to corresponding fractional unit,
192
192
  # according to +currency+ subunit property,
193
193
  # before instantiating the Money object.
194
194
  #
@@ -209,9 +209,9 @@ class Money
209
209
  #
210
210
  # @example
211
211
  # Money.from_numeric(100)
212
- # #=> #<Money @cents=10000 @currency="USD">
212
+ # #=> #<Money @fractional=10000 @currency="USD">
213
213
  # Money.from_numeric(100.00)
214
- # #=> #<Money @cents=10000 @currency="USD">
214
+ # #=> #<Money @fractional=10000 @currency="USD">
215
215
  # Money.from_numeric("100")
216
216
  # #=> ArgumentError
217
217
  #
@@ -258,10 +258,10 @@ class Money
258
258
  #if the number ends with punctuation, just throw it out. If it means decimal,
259
259
  #it won't hurt anything. If it means a literal period or comma, this will
260
260
  #save it from being mis-interpreted as a decimal.
261
- num.chop! if num.match /[\.|,]$/
261
+ num.chop! if num.match(/[\.|,]$/)
262
262
 
263
263
  # gather all decimal_marks within the result number
264
- used_decimal_marks = num.scan /[^\d]/
264
+ used_decimal_marks = num.scan(/[^\d]/)
265
265
 
266
266
  # determine the number of unique decimal_marks within the number
267
267
  #