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 +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
|
[](http://travis-ci.org/RubyMoney/money)
|
4
|
-
[](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|
|