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.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## master
4
+
5
+ - Renamed Money#cents to Money#fractional. Money#cents can still be used as a synonym and equivalent of Money#fractional.
6
+ - Added Money.new_with_amount and Money#amount. Money.new_with_dollars and Money#dollars remain as synonyms.
7
+ - Calling Bank::Base.instance doesn't make Bank::VariableExchange.instance
8
+ return Bank::Base.instance anymore (semaperepelitsa)
9
+ - Update Turkmenistan manat from TMM to TMT currency (GH-181). [Thanks @Exoth]
10
+ - Moved ZWD Zimbabwean dollars to currency_bc.json, also added there ZWL, ZWN, and ZWR Zimbabwean dollars (GH-184).
11
+ - Switch to multi_json gem (GH-170)
12
+ - Fix "warning: ambiguous first argument..." (GH-166)
13
+ - Update dependencies to latest and greatest (GH-172)
14
+ - TravisBot is now watching Pull Request!!! (GH-171)
15
+ - Minor code cleaning
16
+
3
17
  ## 5.0.0
4
18
  - Minor bugfix - incorrect use of character range resulted in
5
19
  botched results for Money::Parsing#extract_cents (GH-162)
data/README.md CHANGED
@@ -1,13 +1,10 @@
1
1
  # RubyMoney - Money
2
2
 
3
3
  [![Build Status](http://travis-ci.org/RubyMoney/money.png)](http://travis-ci.org/RubyMoney/money)
4
- [![Dependency Status](https://gemnasium.com/RubyMoney/money.png)](https://gemnasium.com/RubyMoney/money)
5
4
 
6
5
  ## Contributing
7
6
 
8
- When contributing, please make sure to update the CHANGELOG and AUTHORS files
9
- when you submit your pull request. Upon merging of your first pull request,
10
- you will be given commit access to the repository.
7
+ See the [Contribution Guidelines](https://github.com/RubyMoney/money/blob/master/CONTRIBUTING.md)
11
8
 
12
9
  ## Introduction
13
10
 
@@ -33,9 +30,12 @@ This library aids one in handling money and different currencies.
33
30
  - [API Documentation](http://rubydoc.info/gems/money/frames)
34
31
  - [Git Repository](http://github.com/RubyMoney/money)
35
32
 
36
- ### Note
33
+ ### Notes
37
34
 
38
- Your app must use UTF-8 to function with this library. There are a number of non-ASCII currency attributes.
35
+ - Your app must use UTF-8 to function with this library. There are a
36
+ number of non-ASCII currency attributes.
37
+ - This app requires Ruby 1.9 and JSON. If you're using JRuby < 1.7.0
38
+ you'll need to add `gem "json"` to your Gemfile or similar.
39
39
 
40
40
  ## Downloading
41
41
 
@@ -73,9 +73,9 @@ Money.new(1000, "USD") * 5 == Money.new(5000, "USD")
73
73
 
74
74
  # Assumptive Currencies
75
75
  Money.assume_from_symbol = true
76
- Money.new("$100") == Money.new(10000, "USD")
77
- Money.new("€100") == Money.new(10000, "EUR")
78
- Money.new("£100") == Money.new(10000, "GBP")
76
+ Money.parse("$100") == Money.new(10000, "USD")
77
+ Money.parse("€100") == Money.new(10000, "EUR")
78
+ Money.parse("£100") == Money.new(10000, "GBP")
79
79
 
80
80
  # Currency conversions
81
81
  some_code_to_setup_exchange_rates
@@ -227,40 +227,10 @@ implementations.
227
227
  - [nordea](https://github.com/k33l0r/nordea)
228
228
  - [nbrb_currency](https://github.com/slbug/nbrb_currency)
229
229
  - [money-open-exchange-rates](https://github.com/spk/money-open-exchange-rates)
230
- - [money-historical-bank](https://github.com/coutud/money-historical-bank)
230
+ - [money-historical-bank](https://github.com/atwam/money-historical-bank)
231
231
 
232
232
  ## Ruby on Rails
233
233
 
234
- To integrate money in a rails application use [money-rails](http://github.com/RubyMoney/money-rails)
235
- gem or follow the instructions below.
234
+ To integrate money in a Rails application use [money-rails](http://github.com/RubyMoney/money-rails).
236
235
 
237
- Use the `composed_of` helper to let Active Record deal with embedding the money
238
- object in your models. The following example requires 2 columns:
239
-
240
- ``` ruby
241
- :price_cents, :integer, :default => 0, :null => false
242
- :currency, :string
243
- ```
244
-
245
- Then in your model file:
246
-
247
- ``` ruby
248
- composed_of :price,
249
- :class_name => "Money",
250
- :mapping => [%w(price_cents cents), %w(currency currency_as_string)],
251
- :constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) },
252
- :converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
253
- ```
254
-
255
- For Money 2.2.x and previous versions, simply use the following `composed_of`
256
- definition:
257
-
258
- ``` ruby
259
- composed_of :price,
260
- :class_name => "Money",
261
- :mapping => [%w(price_cents cents), %w(currency currency)],
262
- :constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) }
263
- ```
264
-
265
- Note the difference in the currency column mapping (currency_as_string vs. currency) - this matters! For further details read the full discussion
266
- [here](http://github.com/RubyMoney/money/issues/4#comment_224880).
236
+ For depreceated methods of integrating with Rails, check [the wiki](https://github.com/RubyMoney/money/wiki).
data/config/currency.json CHANGED
@@ -1592,10 +1592,10 @@
1592
1592
  "symbol": "kr",
1593
1593
  "subunit": "Öre",
1594
1594
  "subunit_to_unit": 100,
1595
- "symbol_first": true,
1595
+ "symbol_first": false,
1596
1596
  "html_entity": "",
1597
- "decimal_mark": ".",
1598
- "thousands_separator": ",",
1597
+ "decimal_mark": ",",
1598
+ "thousands_separator": ".",
1599
1599
  "iso_numeric": "752"
1600
1600
  },
1601
1601
  "sgd": {
@@ -1754,7 +1754,7 @@
1754
1754
  "thousands_separator": ",",
1755
1755
  "iso_numeric": "972"
1756
1756
  },
1757
- "tmm": {
1757
+ "tmt": {
1758
1758
  "priority": 100,
1759
1759
  "iso_code": "TMM",
1760
1760
  "name": "Turkmenistani Manat",
@@ -1765,7 +1765,7 @@
1765
1765
  "html_entity": "",
1766
1766
  "decimal_mark": ".",
1767
1767
  "thousands_separator": ",",
1768
- "iso_numeric": "795"
1768
+ "iso_numeric": "934"
1769
1769
  },
1770
1770
  "tnd": {
1771
1771
  "priority": 100,
@@ -2052,18 +2052,5 @@
2052
2052
  "decimal_mark": ".",
2053
2053
  "thousands_separator": ",",
2054
2054
  "iso_numeric": "894"
2055
- },
2056
- "zwd": {
2057
- "priority": 100,
2058
- "iso_code": "ZWD",
2059
- "name": "Zimbabwean Dollar",
2060
- "symbol": "$",
2061
- "subunit": "Cent",
2062
- "subunit_to_unit": 100,
2063
- "symbol_first": true,
2064
- "html_entity": "$",
2065
- "decimal_mark": ".",
2066
- "thousands_separator": ",",
2067
- "iso_numeric": "716"
2068
2055
  }
2069
- }
2056
+ }
@@ -12,6 +12,32 @@
12
12
  "thousands_separator": ",",
13
13
  "iso_numeric": "233"
14
14
  },
15
+ "ghc": {
16
+ "priority": 100,
17
+ "iso_code": "GHS",
18
+ "name": "Ghanaian Cedi",
19
+ "symbol": "₵",
20
+ "subunit": "Pesewa",
21
+ "subunit_to_unit": 100,
22
+ "symbol_first": true,
23
+ "html_entity": "&#x20B5;",
24
+ "decimal_mark": ".",
25
+ "thousands_separator": ",",
26
+ "iso_numeric": "288"
27
+ },
28
+ "tmm": {
29
+ "priority": 100,
30
+ "iso_code": "TMM",
31
+ "name": "Turkmenistani Manat",
32
+ "symbol": "m",
33
+ "subunit": "Tennesi",
34
+ "subunit_to_unit": 100,
35
+ "symbol_first": false,
36
+ "html_entity": "",
37
+ "decimal_mark": ".",
38
+ "thousands_separator": ",",
39
+ "iso_numeric": "795"
40
+ },
15
41
  "yen": {
16
42
  "priority": 100,
17
43
  "iso_code": "JPY",
@@ -25,17 +51,56 @@
25
51
  "thousands_separator": ",",
26
52
  "iso_numeric": ""
27
53
  },
28
- "ghc": {
54
+ "zwd": {
29
55
  "priority": 100,
30
- "iso_code": "GHS",
31
- "name": "Ghanaian Cedi",
32
- "symbol": "",
33
- "subunit": "Pesewa",
56
+ "iso_code": "ZWD",
57
+ "name": "Zimbabwean Dollar",
58
+ "symbol": "$",
59
+ "subunit": "Cent",
34
60
  "subunit_to_unit": 100,
35
61
  "symbol_first": true,
36
- "html_entity": "&#x20B5;",
62
+ "html_entity": "$",
37
63
  "decimal_mark": ".",
38
64
  "thousands_separator": ",",
39
- "iso_numeric": "288"
65
+ "iso_numeric": "716"
66
+ },
67
+ "zwl": {
68
+ "priority": 100,
69
+ "iso_code": "ZWL",
70
+ "name": "Zimbabwean Dollar",
71
+ "symbol": "$",
72
+ "subunit": "Cent",
73
+ "subunit_to_unit": 100,
74
+ "symbol_first": true,
75
+ "html_entity": "$",
76
+ "decimal_mark": ".",
77
+ "thousands_separator": ",",
78
+ "iso_numeric": "932"
79
+ },
80
+ "zwn": {
81
+ "priority": 100,
82
+ "iso_code": "ZWN",
83
+ "name": "Zimbabwean Dollar",
84
+ "symbol": "$",
85
+ "subunit": "Cent",
86
+ "subunit_to_unit": 100,
87
+ "symbol_first": true,
88
+ "html_entity": "$",
89
+ "decimal_mark": ".",
90
+ "thousands_separator": ",",
91
+ "iso_numeric": "942"
92
+ },
93
+ "zwr": {
94
+ "priority": 100,
95
+ "iso_code": "ZWR",
96
+ "name": "Zimbabwean Dollar",
97
+ "symbol": "$",
98
+ "subunit": "Cent",
99
+ "subunit_to_unit": 100,
100
+ "symbol_first": true,
101
+ "html_entity": "$",
102
+ "decimal_mark": ".",
103
+ "thousands_separator": ",",
104
+ "iso_numeric": "935"
40
105
  }
41
106
  }
data/lib/money.rb CHANGED
@@ -1,28 +1,7 @@
1
- # Copyright (c) 2005 Tobias Luetke
2
- # Copyright (c) 2008 Phusion
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
-
23
- require 'bigdecimal'
24
- require 'i18n' rescue LoadError
25
- require 'money/currency_loader'
26
- require 'money/currency'
27
- require 'money/money'
28
- require 'money/core_extensions'
1
+ require "bigdecimal"
2
+ require "bigdecimal/util"
3
+ require "i18n" rescue LoadError
4
+ require "money/currency_loader"
5
+ require "money/currency"
6
+ require "money/money"
7
+ require "money/core_extensions"
@@ -45,7 +45,7 @@ class Money
45
45
  #
46
46
  # @return [Money::Bank::Base]
47
47
  def self.instance
48
- @@singleton ||= self.new
48
+ @singleton ||= self.new
49
49
  end
50
50
 
51
51
  # The rounding method to use when exchanging rates.
@@ -1,7 +1,6 @@
1
1
  require 'money/bank/base'
2
-
3
- autoload :JSON, 'json'
4
- autoload :YAML, 'yaml'
2
+ require 'json'
3
+ require 'yaml'
5
4
 
6
5
  class Money
7
6
  module Bank
@@ -25,10 +24,10 @@ class Money
25
24
  # c2 = 100_00.to_money("CAD")
26
25
  #
27
26
  # # Exchange 100 USD to CAD:
28
- # bank.exchange_with(c1, "CAD") #=> #<Money @cents=1245150>
27
+ # bank.exchange_with(c1, "CAD") #=> #<Money @fractional=1245150>
29
28
  #
30
29
  # # Exchange 100 CAD to USD:
31
- # bank.exchange_with(c2, "USD") #=> #<Money @cents=803115>
30
+ # bank.exchange_with(c2, "USD") #=> #<Money @fractional=803115>
32
31
  class VariableExchange < Base
33
32
 
34
33
  attr_reader :rates
@@ -81,10 +80,10 @@ class Money
81
80
  # c2 = 100_00.to_money("CAD")
82
81
  #
83
82
  # # Exchange 100 USD to CAD:
84
- # bank.exchange_with(c1, "CAD") #=> #<Money @cents=1245150>
83
+ # bank.exchange_with(c1, "CAD") #=> #<Money @fractional=1245150>
85
84
  #
86
85
  # # Exchange 100 CAD to USD:
87
- # bank.exchange_with(c2, "USD") #=> #<Money @cents=803115>
86
+ # bank.exchange_with(c2, "USD") #=> #<Money @fractional=803115>
88
87
  def exchange_with(from, to_currency)
89
88
  return from if same_currency?(from.currency, to_currency)
90
89
 
@@ -94,9 +93,9 @@ class Money
94
93
  end
95
94
  _to_currency_ = Currency.wrap(to_currency)
96
95
 
97
- cents = BigDecimal.new(from.cents.to_s) / (BigDecimal.new(from.currency.subunit_to_unit.to_s) / BigDecimal.new(_to_currency_.subunit_to_unit.to_s))
96
+ fractional = BigDecimal.new(from.fractional.to_s) / (BigDecimal.new(from.currency.subunit_to_unit.to_s) / BigDecimal.new(_to_currency_.subunit_to_unit.to_s))
98
97
 
99
- ex = cents * BigDecimal.new(rate.to_s)
98
+ ex = fractional * BigDecimal.new(rate.to_s)
100
99
  ex = ex.to_f
101
100
  ex = if block_given?
102
101
  yield ex
@@ -9,9 +9,9 @@ class Numeric
9
9
  # @return [Money]
10
10
  #
11
11
  # @example
12
- # 100.to_money #=> #<Money @cents=10000>
13
- # 100.37.to_money #=> #<Money @cents=10037>
14
- # BigDecimal.new('100').to_money #=> #<Money @cents=10000>
12
+ # 100.to_money #=> #<Money @fractional=10000>
13
+ # 100.37.to_money #=> #<Money @fractional=10037>
14
+ # BigDecimal.new('100').to_money #=> #<Money @fractional=10000>
15
15
  #
16
16
  # @see Money.from_numeric
17
17
  #
@@ -33,12 +33,12 @@ class String
33
33
  # @return [Money]
34
34
  #
35
35
  # @example
36
- # '100'.to_money #=> #<Money @cents=10000>
37
- # '100.37'.to_money #=> #<Money @cents=10037>
38
- # '100 USD'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
39
- # 'USD 100'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
40
- # '$100 USD'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
41
- # 'hello 2000 world'.to_money #=> #<Money @cents=200000 @currency=#<Money::Currency id: usd>>
36
+ # '100'.to_money #=> #<Money @fractional=10000>
37
+ # '100.37'.to_money #=> #<Money @fractional=10037>
38
+ # '100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
39
+ # 'USD 100'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
40
+ # '$100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
41
+ # 'hello 2000 world'.to_money #=> #<Money @fractional=200000 @currency=#<Money::Currency id: usd>>
42
42
  #
43
43
  # @see Money.from_string
44
44
  #
@@ -144,7 +144,7 @@ class Money
144
144
  #
145
145
  # @return [String]
146
146
  attr_reader :thousands_separator
147
- alias :delimiter :thousands_separator
147
+ alias :delimiter :thousands_separator
148
148
 
149
149
  # Should the currency symbol precede the amount, or should it come after?
150
150
  #
data/lib/money/money.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # encoding: utf-8
2
- require 'money/bank/variable_exchange'
3
- require 'money/money/arithmetic'
4
- require 'money/money/parsing'
5
- require 'money/money/formatting'
2
+ require "money/bank/variable_exchange"
3
+ require "money/money/arithmetic"
4
+ require "money/money/parsing"
5
+ require "money/money/formatting"
6
6
 
7
7
  # Represents an amount of money in a given currency.
8
8
  class Money
@@ -11,10 +11,23 @@ class Money
11
11
  include Formatting
12
12
  include Parsing
13
13
 
14
- # The value of the money in cents.
14
+ # Convenience method for fractional part of the amount. Synonym of #fractional
15
15
  #
16
16
  # @return [Integer]
17
- attr_reader :cents
17
+ def cents
18
+ fractional
19
+ end
20
+
21
+ # The value of the amount represented in the fractional unit of the currency. Example: USD, 1 dollar (amount) == 100 cents (fractional unit).
22
+ #
23
+ # @return [Integer]
24
+ def fractional
25
+ if self.class.infinite_precision
26
+ @fractional
27
+ else
28
+ @fractional.round(0, self.class.rounding_mode).to_i
29
+ end
30
+ end
18
31
 
19
32
  # The currency the money is in.
20
33
  #
@@ -52,6 +65,21 @@ class Money
52
65
  #
53
66
  # @return [true,false]
54
67
  attr_accessor :assume_from_symbol
68
+
69
+ # Use this to enable infinite precision cents
70
+ #
71
+ # @return [true,false]
72
+ attr_accessor :infinite_precision
73
+
74
+ # Use this to specify the rounding mode
75
+ #
76
+ # @return [BigDecimal::ROUND_MODE]
77
+ attr_accessor :rounding_mode
78
+
79
+ # Use this to specify precision for converting Rational to BigDecimal
80
+ #
81
+ # @return [Integer]
82
+ attr_accessor :conversion_precision
55
83
  end
56
84
 
57
85
  # Set the default bank for creating new +Money+ objects.
@@ -66,6 +94,15 @@ class Money
66
94
  # Default to not using currency symbol assumptions when parsing
67
95
  self.assume_from_symbol = false
68
96
 
97
+ # Default to not using infinite precision cents
98
+ self.infinite_precision = false
99
+
100
+ # Default to bankers rounding
101
+ self.rounding_mode = BigDecimal::ROUND_HALF_EVEN
102
+
103
+ # Default the conversion of Rationals precision to 16
104
+ self.conversion_precision = 16
105
+
69
106
  # Create a new money object with value 0.
70
107
  #
71
108
  # @param [Currency, String, Symbol] currency The currency to use.
@@ -73,7 +110,7 @@ class Money
73
110
  # @return [Money]
74
111
  #
75
112
  # @example
76
- # Money.empty #=> #<Money @cents=0>
113
+ # Money.empty #=> #<Money @fractional=0>
77
114
  def self.empty(currency = default_currency)
78
115
  Money.new(0, currency)
79
116
  end
@@ -122,37 +159,43 @@ class Money
122
159
  Money.new(cents, "EUR")
123
160
  end
124
161
 
125
- # Creates a new Money object of +amount+ value in dollars,
162
+ # Creates a new Money object of +amount+ value ,
126
163
  # with given +currency+.
127
164
  #
128
- # The amount value is expressed in +dollars+
129
- # where the +dollar+ is the main monetary unit,
165
+ # The amount value is expressed in the main monetary unit,
130
166
  # opposite to the subunit-based representation
131
167
  # used internally by this library called +cents+.
132
168
  #
133
- # @param [Numeric] amount The money amount, in dollars.
169
+ # @param [Numeric] amount The money amount, in main monetary unit.
134
170
  # @param [Currency, String, Symbol] currency The currency format.
135
171
  # @param [Money::Bank::*] bank The exchange bank to use.
136
172
  #
137
173
  # @return [Money]
138
174
  #
139
175
  # @example
140
- # Money.new_with_dollars(100)
141
- # #=> #<Money @cents=10000 @currency="USD">
142
- # Money.new_with_dollars(100, "USD")
143
- # #=> #<Money @cents=10000 @currency="USD">
144
- # Money.new_with_dollars(100, "EUR")
145
- # #=> #<Money @cents=10000 @currency="EUR">
176
+ # Money.new_with_amount(100)
177
+ # #=> #<Money @fractional=10000 @currency="USD">
178
+ # Money.new_with_amount(100, "USD")
179
+ # #=> #<Money @fractional=10000 @currency="USD">
180
+ # Money.new_with_amount(100, "EUR")
181
+ # #=> #<Money @fractional=10000 @currency="EUR">
146
182
  #
147
183
  # @see Money.new
148
184
  #
149
- def self.new_with_dollars(amount, currency = Money.default_currency, bank = Money.default_bank)
185
+ def self.new_with_amount(amount, currency = Money.default_currency, bank = Money.default_bank)
150
186
  money = from_numeric(amount, currency)
151
187
  # Hack! You can't change a bank
152
188
  money.instance_variable_set("@bank", bank)
153
189
  money
154
190
  end
155
191
 
192
+ # Synonym of #new_with_amount
193
+ #
194
+ # @see Money.new_with_amount
195
+ def self.new_with_dollars(*args)
196
+ self.new_with_amount(*args)
197
+ end
198
+
156
199
  # Adds a new exchange rate to the default bank and return the rate.
157
200
  #
158
201
  # @param [Currency, String, Symbol] from_currency Currency to exchange from.
@@ -168,13 +211,13 @@ class Money
168
211
  end
169
212
 
170
213
 
171
- # Creates a new Money object of +cents+ value in cents,
172
- # with given +currency+.
214
+ # Creates a new Money object of value given in the
215
+ # +fractional unit+ of the given +currency+.
173
216
  #
174
217
  # Alternatively you can use the convenience
175
218
  # methods like {Money.ca_dollar} and {Money.us_dollar}.
176
219
  #
177
- # @param [Integer] cents The money amount, in cents.
220
+ # @param [Integer] The value given in the fractional unit.
178
221
  # @param [Currency, String, Symbol] currency The currency format.
179
222
  # @param [Money::Bank::*] bank The exchange bank to use.
180
223
  #
@@ -182,22 +225,31 @@ class Money
182
225
  #
183
226
  # @example
184
227
  # Money.new(100)
185
- # #=> #<Money @cents=100 @currency="USD">
228
+ # #=> #<Money @fractional=100 @currency="USD">
186
229
  # Money.new(100, "USD")
187
- # #=> #<Money @cents=100 @currency="USD">
230
+ # #=> #<Money @fractional=100 @currency="USD">
188
231
  # Money.new(100, "EUR")
189
- # #=> #<Money @cents=100 @currency="EUR">
232
+ # #=> #<Money @fractional=100 @currency="EUR">
190
233
  #
191
234
  # @see Money.new_with_dollars
192
235
  #
193
- def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
194
- @cents = cents.round.to_i
236
+ def initialize(fractional, currency = Money.default_currency, bank = Money.default_bank)
237
+ @fractional = if fractional.is_a?(Rational)
238
+ fractional.to_d(self.class.conversion_precision)
239
+ elsif fractional.respond_to?(:to_d)
240
+ fractional.to_d
241
+ else
242
+ BigDecimal.new(fractional.to_s)
243
+ end
195
244
  @currency = Currency.wrap(currency)
196
- @bank = bank
245
+ @bank = bank
197
246
  end
198
247
 
248
+ # Assuming using a currency using dollars:
199
249
  # Returns the value of the money in dollars,
200
- # instead of in cents.
250
+ # instead of in the fractional unit cents.
251
+ #
252
+ # Synonym of #amount
201
253
  #
202
254
  # @return [Float]
203
255
  #
@@ -205,10 +257,26 @@ class Money
205
257
  # Money.new(100).dollars # => 1.0
206
258
  # Money.new_with_dollars(1).dollar # => 1.0
207
259
  #
260
+ # @see #amount
208
261
  # @see #to_f
209
262
  # @see #cents
210
263
  #
211
264
  def dollars
265
+ amount
266
+ end
267
+
268
+ # Returns the numerical value of the money
269
+ #
270
+ # @return [Float]
271
+ #
272
+ # @example
273
+ # Money.new(100).amount # => 1.0
274
+ # Money.new_with_amount(1).amount # => 1.0
275
+ #
276
+ # @see #to_f
277
+ # @see #fractional
278
+ #
279
+ def amount
212
280
  to_f
213
281
  end
214
282
 
@@ -234,7 +302,7 @@ class Money
234
302
  @currency = Currency.wrap(val)
235
303
  end
236
304
 
237
- # Returns a Fixnum hash value based on the +cents+ and +currency+ attributes
305
+ # Returns a Fixnum hash value based on the +fractional+ and +currency+ attributes
238
306
  # in order to use functions like & (intersection), group_by, etc.
239
307
  #
240
308
  # @return [Fixnum]
@@ -242,7 +310,7 @@ class Money
242
310
  # @example
243
311
  # Money.new(100).hash #=> 908351
244
312
  def hash
245
- [cents.hash, currency.hash].hash
313
+ [fractional.hash, currency.hash].hash
246
314
  end
247
315
 
248
316
  # Uses +Currency#symbol+. If +nil+ is returned, defaults to "¤".
@@ -259,7 +327,7 @@ class Money
259
327
  #
260
328
  # @return [String]
261
329
  def inspect
262
- "#<Money cents:#{cents} currency:#{currency}>"
330
+ "#<Money fractional:#{fractional} currency:#{currency}>"
263
331
  end
264
332
 
265
333
  # Returns the amount of money as a string.
@@ -269,14 +337,41 @@ class Money
269
337
  # @example
270
338
  # Money.ca_dollar(100).to_s #=> "1.00"
271
339
  def to_s
272
- unit, subunit = cents.abs.divmod(currency.subunit_to_unit).map{|o| o.to_s}
273
- if currency.decimal_places == 0
274
- return "-#{unit}" if cents < 0
275
- return unit
340
+ unit, subunit = fractional().abs.divmod(currency.subunit_to_unit)
341
+
342
+ unit_str = ""
343
+ subunit_str = ""
344
+ fraction_str = ""
345
+
346
+ if self.class.infinite_precision
347
+ subunit, fraction = subunit.divmod(BigDecimal("1"))
348
+
349
+ unit_str = unit.to_i.to_s
350
+ subunit_str = subunit.to_i.to_s
351
+ fraction_str = fraction.to_s("F")[2..-1] # want fractional part "0.xxx"
352
+
353
+ fraction_str = "" if fraction_str =~ /^0+$/
354
+ else
355
+ unit_str, subunit_str = unit.to_s, subunit.to_s
356
+ end
357
+
358
+ absolute_str = if currency.decimal_places == 0
359
+ if fraction_str == ""
360
+ unit_str
361
+ else
362
+ "#{unit_str}#{decimal_mark}#{fraction_str}"
363
+ end
364
+ else
365
+ # need to pad subunit to right position,
366
+ # for example 1 usd 3 cents should be 1.03 not 1.3
367
+ subunit_str.insert(0, '0') while subunit_str.length < currency.decimal_places
368
+
369
+ "#{unit_str}#{decimal_mark}#{subunit_str}#{fraction_str}"
370
+ end
371
+
372
+ absolute_str.tap do |str|
373
+ str.insert(0, "-") if fractional() < 0
276
374
  end
277
- subunit = (("0" * currency.decimal_places) + subunit)[(-1*currency.decimal_places)..-1]
278
- return "-#{unit}#{decimal_mark}#{subunit}" if cents < 0
279
- "#{unit}#{decimal_mark}#{subunit}"
280
375
  end
281
376
 
282
377
  # Return the amount of money as a BigDecimal.
@@ -286,7 +381,7 @@ class Money
286
381
  # @example
287
382
  # Money.us_dollar(100).to_d => BigDecimal.new("1.0")
288
383
  def to_d
289
- BigDecimal.new(cents.to_s) / BigDecimal.new(currency.subunit_to_unit.to_s)
384
+ BigDecimal.new(fractional.to_s) / BigDecimal.new(currency.subunit_to_unit.to_s)
290
385
  end
291
386
 
292
387
  # Return the amount of money as a float. Floating points cannot guarantee
@@ -377,20 +472,32 @@ class Money
377
472
  # Money.new(5, "USD").allocate([0.3,0.7)) #=> [Money.new(2), Money.new(3)]
378
473
  # Money.new(100, "USD").allocate([0.33,0.33,0.33]) #=> [Money.new(34), Money.new(33), Money.new(33)]
379
474
  def allocate(splits)
380
- allocations = splits.inject(0.0) {|sum, i| sum += i }
381
- raise ArgumentError, "splits add to more then 100%" if (allocations - 1.0) > Float::EPSILON
475
+ allocations = splits.inject(BigDecimal("0")) do |sum, n|
476
+ n = BigDecimal(n.to_s) unless n.is_a?(BigDecimal)
477
+ sum + n
478
+ end
382
479
 
383
- left_over = cents
480
+ if (allocations - BigDecimal("1")) > Float::EPSILON
481
+ raise ArgumentError, "splits add to more then 100%"
482
+ end
483
+
484
+ left_over = fractional
384
485
 
385
- amounts = splits.collect do |ratio|
386
- fraction = (cents * ratio / allocations).floor
387
- left_over -= fraction
388
- fraction
486
+ amounts = splits.map do |ratio|
487
+ if self.class.infinite_precision
488
+ fraction = fractional * ratio
489
+ else
490
+ fraction = (fractional * ratio / allocations).floor
491
+ left_over -= fraction
492
+ fraction
493
+ end
389
494
  end
390
495
 
391
- left_over.times { |i| amounts[i % amounts.length] += 1 }
496
+ unless self.class.infinite_precision
497
+ left_over.to_i.times { |i| amounts[i % amounts.length] += 1 }
498
+ end
392
499
 
393
- amounts.collect { |cents| Money.new(cents, currency) }
500
+ amounts.collect { |fractional| Money.new(fractional, currency) }
394
501
  end
395
502
 
396
503
  # Split money amongst parties evenly without loosing pennies.
@@ -403,10 +510,16 @@ class Money
403
510
  # Money.new(100, "USD").split(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
404
511
  def split(num)
405
512
  raise ArgumentError, "need at least one party" if num < 1
406
- low = Money.new(cents / num)
407
- high = Money.new(low.cents + 1)
408
513
 
409
- remainder = cents % num
514
+ if self.class.infinite_precision
515
+ amt = self.div(BigDecimal(num.to_s))
516
+ return 1.upto(num).map{amt}
517
+ end
518
+
519
+ low = Money.new(fractional / num, self.currency)
520
+ high = Money.new(low.fractional + 1, self.currency)
521
+
522
+ remainder = fractional % num
410
523
  result = []
411
524
 
412
525
  num.times do |index|