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 +14 -0
- data/README.md +12 -42
- data/config/currency.json +6 -19
- data/config/currency_bc.json +72 -7
- data/lib/money.rb +7 -28
- data/lib/money/bank/base.rb +1 -1
- data/lib/money/bank/variable_exchange.rb +8 -9
- data/lib/money/core_extensions.rb +9 -9
- data/lib/money/currency.rb +1 -1
- data/lib/money/money.rb +164 -51
- data/lib/money/money/arithmetic.rb +39 -34
- data/lib/money/money/formatting.rb +24 -13
- data/lib/money/money/parsing.rb +33 -33
- data/money.gemspec +7 -9
- data/spec/bank/base_spec.rb +8 -0
- data/spec/bank/variable_exchange_spec.rb +1 -1
- data/spec/currency_spec.rb +2 -3
- data/spec/money/arithmetic_spec.rb +66 -2
- data/spec/money/formatting_spec.rb +22 -7
- data/spec/money_spec.rb +179 -33
- data/spec/spec_helper.rb +2 -17
- metadata +48 -44
- data/.gemtest +0 -0
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
|
-
|
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
|
-
###
|
33
|
+
### Notes
|
37
34
|
|
38
|
-
Your app must use UTF-8 to function with this library. There are a
|
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.
|
77
|
-
Money.
|
78
|
-
Money.
|
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/
|
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
|
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
|
-
|
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":
|
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
|
-
"
|
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": "
|
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
|
+
}
|
data/config/currency_bc.json
CHANGED
@@ -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": "₵",
|
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
|
-
"
|
54
|
+
"zwd": {
|
29
55
|
"priority": 100,
|
30
|
-
"iso_code": "
|
31
|
-
"name": "
|
32
|
-
"symbol": "
|
33
|
-
"subunit": "
|
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": "
|
62
|
+
"html_entity": "$",
|
37
63
|
"decimal_mark": ".",
|
38
64
|
"thousands_separator": ",",
|
39
|
-
"iso_numeric": "
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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"
|
data/lib/money/bank/base.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'money/bank/base'
|
2
|
-
|
3
|
-
|
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 @
|
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 @
|
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 @
|
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 @
|
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
|
-
|
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 =
|
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 @
|
13
|
-
# 100.37.to_money #=> #<Money @
|
14
|
-
# BigDecimal.new('100').to_money #=> #<Money @
|
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 @
|
37
|
-
# '100.37'.to_money #=> #<Money @
|
38
|
-
# '100 USD'.to_money #=> #<Money @
|
39
|
-
# 'USD 100'.to_money #=> #<Money @
|
40
|
-
# '$100 USD'.to_money #=> #<Money @
|
41
|
-
# 'hello 2000 world'.to_money #=> #<Money @
|
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
|
#
|
data/lib/money/currency.rb
CHANGED
data/lib/money/money.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
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
|
-
#
|
14
|
+
# Convenience method for fractional part of the amount. Synonym of #fractional
|
15
15
|
#
|
16
16
|
# @return [Integer]
|
17
|
-
|
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 @
|
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
|
162
|
+
# Creates a new Money object of +amount+ value ,
|
126
163
|
# with given +currency+.
|
127
164
|
#
|
128
|
-
# The amount value is expressed in
|
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
|
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.
|
141
|
-
# #=> #<Money @
|
142
|
-
# Money.
|
143
|
-
# #=> #<Money @
|
144
|
-
# Money.
|
145
|
-
# #=> #<Money @
|
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.
|
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
|
172
|
-
#
|
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]
|
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 @
|
228
|
+
# #=> #<Money @fractional=100 @currency="USD">
|
186
229
|
# Money.new(100, "USD")
|
187
|
-
# #=> #<Money @
|
230
|
+
# #=> #<Money @fractional=100 @currency="USD">
|
188
231
|
# Money.new(100, "EUR")
|
189
|
-
# #=> #<Money @
|
232
|
+
# #=> #<Money @fractional=100 @currency="EUR">
|
190
233
|
#
|
191
234
|
# @see Money.new_with_dollars
|
192
235
|
#
|
193
|
-
def initialize(
|
194
|
-
@
|
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
|
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 +
|
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
|
-
[
|
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
|
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
|
273
|
-
|
274
|
-
|
275
|
-
|
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(
|
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
|
381
|
-
|
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
|
-
|
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.
|
386
|
-
|
387
|
-
|
388
|
-
|
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
|
-
|
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 { |
|
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
|
-
|
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|
|