money 5.0.0 → 5.1.0.beta1

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 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|