mroch-BigMoney 0.1.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +10 -0
- data/Rakefile +6 -1
- data/bigmoney.gemspec +29 -24
- data/lib/big_money.rb +5 -1
- data/lib/big_money/big_money.rb +41 -24
- data/lib/big_money/currency.rb +75 -0
- data/lib/big_money/currency/iso4217.rb +1043 -0
- data/lib/big_money/exchange.rb +47 -0
- data/lib/big_money/exchange/yahoo.rb +32 -0
- data/rakelib/cultivate.rake +7 -0
- data/rakelib/iso4217.rake +16 -0
- data/rakelib/iso4217.rb +46 -0
- data/rakelib/iso4217.rb.erb +13 -0
- data/test/test_big_money.rb +35 -17
- data/test/test_currency.rb +23 -0
- data/test/test_exchange.rb +21 -0
- metadata +42 -17
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
class BigMoney
|
4
|
+
|
5
|
+
# Find the exchange rate between two currencies.
|
6
|
+
#
|
7
|
+
# Be aware no caching is done at all at the moment.
|
8
|
+
class Exchange
|
9
|
+
class ExchangeError < StandardError; end
|
10
|
+
class ConversionError < ExchangeError; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
@@services = []
|
14
|
+
def inherited(service) #:nodoc:
|
15
|
+
@@services << service
|
16
|
+
end
|
17
|
+
|
18
|
+
# Fetch the exchange rate between two currencies. The arguments may be anything that BigMoney::Currency can
|
19
|
+
# parse. The rate is returned as a BigDecimal.
|
20
|
+
def rate(from, to)
|
21
|
+
exchange = [from, to].map do |c|
|
22
|
+
Currency.parse(c) or raise BigMoney::UnknownCurrency
|
23
|
+
end
|
24
|
+
return BigDecimal(1.to_s) if exchange.uniq.length == 1
|
25
|
+
|
26
|
+
service = @@services.reverse.find do |service|
|
27
|
+
!!exchange.reject{|c| service.currencies.include?(c)}
|
28
|
+
end
|
29
|
+
|
30
|
+
service or raise ConversionError # TODO: Message?
|
31
|
+
BigDecimal.new(service.read_rate(*exchange).to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
# Exchange rate from the first currency to the second.
|
36
|
+
def read_rate(from, to)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# An array of supported currencies.
|
41
|
+
def currencies
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end # Exchange
|
46
|
+
|
47
|
+
end # Money
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
class BigMoney
|
4
|
+
class Exchange
|
5
|
+
|
6
|
+
# Yahoo finance exchange service.
|
7
|
+
#
|
8
|
+
# http://finance.yahoo.com
|
9
|
+
class Yahoo < Exchange
|
10
|
+
class << self
|
11
|
+
protected
|
12
|
+
def read_rate(from, to)
|
13
|
+
s = [from, to].map{|c| c.code}.join
|
14
|
+
open("http://download.finance.yahoo.com/d/quotes.csv?s=#{s}=X&f=l1&e=.csv").read.strip rescue nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def currencies
|
18
|
+
%w{
|
19
|
+
ALL DZD XAL ARS AWG AUD BSD BHD BDT BBD BYR BZD BMD BTN BOB BWP BRL GBP BND BGN BIF KHR CAD CVE KYD XOF XAF
|
20
|
+
CLP CNY COP KMF XCP CRC HRK CUP CYP CZK DKK DJF DOP XCD ECS EGP SVC ERN EEK ETB EUR FKP FJD GMD GHC GIP XAU
|
21
|
+
GTQ GNF GYD HTG HNL HKD HUF ISK INR IDR IRR IQD ILS JMD JPY JOD KZT KES KRW KWD LAK LVL LBP LSL LRD LYD LTL
|
22
|
+
MOP MKD MWK MYR MVR MTL MRO MUR MXN MDL MNT MAD MMK NAD NPR ANG TRY NZD NIO NGN KPW NOK OMR XPF PKR XPD PAB
|
23
|
+
PGK PYG PEN PHP XPT PLN QAR RON RUB RWF WST STD SAR SCR SLL XAG SGD SKK SIT SBD SOS ZAR LKR SHP SDD SZL SEK
|
24
|
+
CHF SYP TWD TZS THB TOP TTD TND USD AED UGX UAH UYU VUV VEB VND YER ZMK ZWD
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end # Yahoo
|
29
|
+
|
30
|
+
end # Exchange
|
31
|
+
end # Money
|
32
|
+
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Only works on unixy type systems.
|
2
|
+
# http://blog.behindlogic.com/2008/10/auto-generate-your-manifest-and-gemspec.html
|
3
|
+
desc 'Rebuild manifest and gemspec.'
|
4
|
+
task :cultivate do
|
5
|
+
system %q{touch Manifest.txt; rake check_manifest | grep -v "(in " | patch}
|
6
|
+
system %q{rake debug_gem | grep -v "(in " > `basename \`pwd\``.gemspec}
|
7
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
namespace :currency do
|
2
|
+
|
3
|
+
desc 'Create ISO 4217 currency classes from wikipedia ISO 4217 table.'
|
4
|
+
task :iso4217 do
|
5
|
+
dirname = File.dirname(__FILE__)
|
6
|
+
gen = File.join(dirname, %w{iso4217.rb})
|
7
|
+
lib = File.expand_path(File.join(dirname, %w{.. lib big_money currency iso4217.rb}))
|
8
|
+
require gen
|
9
|
+
modules = BigMoney::Task::ISO4217.get
|
10
|
+
File.open(lib, File::CREAT | File::TRUNC | File::WRONLY) do |f|
|
11
|
+
f.write modules
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end # :currency
|
16
|
+
|
data/rakelib/iso4217.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'erubis'
|
4
|
+
|
5
|
+
class BigMoney
|
6
|
+
module Task
|
7
|
+
module ISO4217
|
8
|
+
def self.get
|
9
|
+
doc = Hpricot(open('http://en.wikipedia.org/wiki/ISO_4217').read)
|
10
|
+
codes = doc.at('//a#Active_codes').parent
|
11
|
+
codes.name =~ /table/ && break while codes = codes.next_sibling
|
12
|
+
to_ruby(codes) if codes
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.to_ruby(codes)
|
16
|
+
currencies = (codes / 'tr').map{|tr| currency tr}.compact
|
17
|
+
currencies.reject!{|c| c[:code] !~ /[A-Z]{3}/ || c[:name] !~ /\w+/}
|
18
|
+
tmpl = File.read(File.join(File.dirname(__FILE__), 'iso4217.rb.erb'))
|
19
|
+
eruby = Erubis::Eruby.new(tmpl)
|
20
|
+
eruby.result(:currencies => currencies)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.currency(tr)
|
24
|
+
values = (tr / 'td')
|
25
|
+
return unless values && values.size >= 4
|
26
|
+
{
|
27
|
+
:code => code(values),
|
28
|
+
:name => name(values),
|
29
|
+
:offset => offset(values)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.code(chunks)
|
34
|
+
chunks[0].inner_html
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.offset(chunks)
|
38
|
+
chunks[2].inner_html.to_i rescue 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.name(chunks)
|
42
|
+
(chunks[3] / 'a').inner_html rescue chunks[3].inner_html
|
43
|
+
end
|
44
|
+
end # ISO4217
|
45
|
+
end # Task
|
46
|
+
end # BigMoney
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class BigMoney
|
2
|
+
class Currency
|
3
|
+
<% currencies.each do |currency| -%>
|
4
|
+
class <%= currency[:code].upcase %> < Currency #:nodoc:
|
5
|
+
self.name = %q{<%= currency[:name] %>}
|
6
|
+
self.code = %q{<%= currency[:code] %>}
|
7
|
+
self.offset = <%= currency[:offset] %>
|
8
|
+
end
|
9
|
+
|
10
|
+
<% end -%>
|
11
|
+
end # Currency
|
12
|
+
end # BigMoney
|
13
|
+
|
data/test/test_big_money.rb
CHANGED
@@ -3,47 +3,58 @@ require 'big_money'
|
|
3
3
|
require 'bigdecimal'
|
4
4
|
|
5
5
|
class TestBigMoney < Test::Unit::TestCase
|
6
|
-
|
6
|
+
|
7
7
|
def test_initialize_with_bigdecimal
|
8
8
|
val = BigDecimal.new("1.005")
|
9
9
|
assert_same val, BigMoney.new(val).amount, 'Should not create a clone of the input'
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def test_initialize_with_string
|
13
13
|
assert_equal BigDecimal.new("1.005"), BigMoney.new("1.005").amount
|
14
14
|
assert_equal BigDecimal.new("10"), BigMoney.new("10").amount
|
15
15
|
assert_equal BigDecimal.new("0"), BigMoney.new("0").amount
|
16
16
|
assert_equal BigDecimal.new("0"), BigMoney.new("foo").amount
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def test_initialize_with_integer
|
20
20
|
assert_equal BigDecimal.new("100"), BigMoney.new(100).amount
|
21
21
|
assert_equal BigDecimal.new("0"), BigMoney.new(0).amount
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def test_initialize_with_float
|
25
25
|
assert_equal BigDecimal.new("1.005"), BigMoney.new(1.005).amount
|
26
26
|
assert_equal BigDecimal.new("0"), BigMoney.new(0.0).amount
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
|
+
def test_initialize_without_currency
|
30
|
+
assert_equal BigMoney::Currency::USD.instance, BigMoney.new(1.23).currency
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_initialize_with_currency
|
34
|
+
assert_equal BigMoney::Currency::AUD.instance, BigMoney.new(1.23, :aud).currency
|
35
|
+
assert_raises BigMoney::UnknownCurrency do
|
36
|
+
BigMoney.new(1.23, :fud)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
29
40
|
def test_eql?
|
30
41
|
assert BigMoney.new("1.00", :usd).eql?(BigMoney.new("1.00", :usd))
|
31
42
|
assert !BigMoney.new("1.00", :usd).eql?(BigMoney.new("1.01", :usd))
|
32
43
|
assert !BigMoney.new("1.00", :usd).eql?(BigMoney.new("1.00", :cad))
|
33
44
|
end
|
34
|
-
|
45
|
+
|
35
46
|
def test_compare
|
36
47
|
m1, m2 = BigMoney.new("1.00", :usd), BigMoney.new("1.50", :usd)
|
37
48
|
assert_equal(-1, (m1 <=> m2))
|
38
49
|
assert_equal(0, (m1 <=> m1))
|
39
50
|
assert_equal(1, (m2 <=> m1))
|
40
|
-
|
51
|
+
|
41
52
|
m3 = BigMoney.new("1.00", :cad)
|
42
53
|
assert_raises BigMoney::UncomparableCurrency do
|
43
54
|
m1 <=> m3
|
44
55
|
end
|
45
56
|
end
|
46
|
-
|
57
|
+
|
47
58
|
def test_add
|
48
59
|
m1, m2, m3 = BigMoney.new(1.00, :usd), BigMoney.new(1.50, :usd), BigMoney.new(0, :usd)
|
49
60
|
assert_equal BigDecimal.new("2.50"), (m1 + m2).amount
|
@@ -55,7 +66,7 @@ class TestBigMoney < Test::Unit::TestCase
|
|
55
66
|
assert_equal BigDecimal.new("5.50"), (m2 + 4).amount
|
56
67
|
assert_equal BigDecimal.new("5.50"), (m2 + 4.00).amount
|
57
68
|
end
|
58
|
-
|
69
|
+
|
59
70
|
def test_subtract
|
60
71
|
m1, m2, m3 = BigMoney.new(1.00, :usd), BigMoney.new(1.50, :usd), BigMoney.new(0, :usd)
|
61
72
|
assert_equal BigDecimal.new("-0.50"), (m1 - m2).amount
|
@@ -65,7 +76,7 @@ class TestBigMoney < Test::Unit::TestCase
|
|
65
76
|
assert_equal BigDecimal.new("-2.50"), (m2 - 4).amount
|
66
77
|
assert_equal BigDecimal.new("-2.50"), (m2 - 4.00).amount
|
67
78
|
end
|
68
|
-
|
79
|
+
|
69
80
|
def test_multiply
|
70
81
|
m1, m2, m3 = BigMoney.new(2.00, :usd), BigMoney.new(1.50, :usd), BigMoney.new(0, :usd)
|
71
82
|
assert_equal BigDecimal.new("4.00"), (m1 * m1).amount
|
@@ -77,7 +88,7 @@ class TestBigMoney < Test::Unit::TestCase
|
|
77
88
|
assert_equal BigDecimal.new("3.00"), (m2 * 2).amount
|
78
89
|
assert_equal BigDecimal.new("3.00"), (m2 * 2.00).amount
|
79
90
|
end
|
80
|
-
|
91
|
+
|
81
92
|
def test_divide
|
82
93
|
m1, m2, m3 = BigMoney.new(2.00, :usd), BigMoney.new(1.50, :usd), BigMoney.new(0, :usd)
|
83
94
|
assert_equal BigDecimal.new("1.00"), (m1 / m1).amount
|
@@ -91,13 +102,13 @@ class TestBigMoney < Test::Unit::TestCase
|
|
91
102
|
assert_equal BigDecimal.new("0.75"), (m2 / 2).amount
|
92
103
|
assert_equal BigDecimal.new("0.75"), (m2 / 2.00).amount
|
93
104
|
end
|
94
|
-
|
105
|
+
|
95
106
|
def test_to_s
|
96
107
|
assert_equal '1.00', BigMoney.new(1).to_s
|
97
108
|
assert_equal '1.50', BigMoney.new(1.5).to_s
|
98
109
|
assert_equal '-11.50', BigMoney.new(-11.5).to_s
|
99
110
|
end
|
100
|
-
|
111
|
+
|
101
112
|
def test_to_formatted_s
|
102
113
|
assert_equal '1.00', BigMoney.new(1).to_formatted_s("%.2f")
|
103
114
|
assert_equal '$1.00', BigMoney.new(1).to_formatted_s("$%.2f")
|
@@ -106,23 +117,30 @@ class TestBigMoney < Test::Unit::TestCase
|
|
106
117
|
assert_equal '$1 USD', BigMoney.new(1).to_formatted_s("$%.0f %s")
|
107
118
|
assert_equal '$1.00 <span class="currency">USD</span>', BigMoney.new(1).to_formatted_s('$%.2f <span class="currency">%s</span>')
|
108
119
|
end
|
109
|
-
|
120
|
+
|
110
121
|
def test_to_i
|
111
122
|
assert_equal(1, BigMoney.new(1).to_i)
|
112
123
|
assert_equal(1, BigMoney.new(1.5).to_i)
|
113
124
|
assert_equal(-11, BigMoney.new(-11.5).to_i)
|
114
125
|
end
|
115
|
-
|
126
|
+
|
116
127
|
def test_to_f
|
117
128
|
assert_in_delta(1.0, BigMoney.new(1).to_f, 0.000001)
|
118
129
|
assert_in_delta(1.5, BigMoney.new(1.5).to_f, 0.000001)
|
119
130
|
assert_in_delta(-11.5, BigMoney.new(-11.5).to_f, 0.000001)
|
120
131
|
end
|
121
|
-
|
132
|
+
|
122
133
|
def test_neg
|
123
134
|
assert_equal BigMoney.new(-1), -BigMoney.new(1)
|
124
135
|
assert_equal BigMoney.new(1), -BigMoney.new(-1)
|
125
136
|
assert_equal BigMoney.new(1), -BigMoney.new(-1)
|
126
137
|
end
|
127
138
|
|
128
|
-
|
139
|
+
def test_currency
|
140
|
+
assert_equal BigMoney::Currency::AUD.instance, BigMoney.currency(:aud)
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_exchange
|
144
|
+
assert BigMoney.new(1.50).exchange(:aud)
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'big_money'
|
3
|
+
|
4
|
+
class TestCurrency < Test::Unit::TestCase
|
5
|
+
def test_parse
|
6
|
+
assert_kind_of BigMoney::Currency, BigMoney::Currency.parse(:aud)
|
7
|
+
assert_nil BigMoney::Currency.parse(:fud)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_compare
|
11
|
+
aud = BigMoney::Currency::AUD.instance
|
12
|
+
assert_operator aud, :==, :aud
|
13
|
+
assert_operator aud, :==, :AUD
|
14
|
+
assert_operator aud, :==, 'aud'
|
15
|
+
assert_operator aud, :==, 'AUD'
|
16
|
+
|
17
|
+
# assert_operator aud, '!=', :fud
|
18
|
+
assert aud != :fud
|
19
|
+
assert aud != :FUD
|
20
|
+
assert aud != 'fud'
|
21
|
+
assert aud != 'FUD'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'big_money'
|
3
|
+
|
4
|
+
class TestExchange < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@aud = BigMoney.currency(:aud)
|
7
|
+
@usd = BigMoney.currency(:usd)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_rate
|
11
|
+
assert_kind_of BigDecimal, BigMoney::Exchange.rate(@aud, @usd)
|
12
|
+
assert_kind_of BigDecimal, BigMoney::Exchange.rate(:aud, :usd)
|
13
|
+
assert_raise BigMoney::UnknownCurrency do
|
14
|
+
BigMoney::Exchange.rate(:aud, :fud)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_equal_rate
|
19
|
+
assert_equal BigDecimal(1.to_s), BigMoney::Exchange.rate(:usd, :usd)
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mroch-BigMoney
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marshall Roch
|
@@ -9,34 +9,57 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-20 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.8.0
|
23
|
+
version:
|
24
|
+
description: Represents an amount of money in a particular currency. Backed by BigDecimal, so is safe from float rounding errors.
|
25
|
+
email:
|
26
|
+
- mroch@cmu.edu
|
18
27
|
executables: []
|
19
28
|
|
20
29
|
extensions: []
|
21
30
|
|
22
|
-
extra_rdoc_files:
|
23
|
-
|
24
|
-
files:
|
25
|
-
- bigmoney.gemspec
|
31
|
+
extra_rdoc_files:
|
26
32
|
- History.txt
|
33
|
+
- LICENSE.txt
|
27
34
|
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
files:
|
37
|
+
- History.txt
|
28
38
|
- LICENSE.txt
|
29
|
-
-
|
39
|
+
- Manifest.txt
|
30
40
|
- README.txt
|
41
|
+
- Rakefile
|
42
|
+
- bigmoney.gemspec
|
31
43
|
- lib/big_money.rb
|
32
44
|
- lib/big_money/big_money.rb
|
33
45
|
- lib/big_money/core_extensions.rb
|
46
|
+
- lib/big_money/currency.rb
|
47
|
+
- lib/big_money/currency/iso4217.rb
|
48
|
+
- lib/big_money/exchange.rb
|
49
|
+
- lib/big_money/exchange/yahoo.rb
|
50
|
+
- rakelib/cultivate.rake
|
51
|
+
- rakelib/iso4217.rake
|
52
|
+
- rakelib/iso4217.rb
|
53
|
+
- rakelib/iso4217.rb.erb
|
34
54
|
- test/test_big_money.rb
|
35
|
-
|
36
|
-
|
55
|
+
- test/test_currency.rb
|
56
|
+
- test/test_exchange.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage:
|
37
59
|
post_install_message:
|
38
|
-
rdoc_options:
|
39
|
-
|
60
|
+
rdoc_options:
|
61
|
+
- --main
|
62
|
+
- README.txt
|
40
63
|
require_paths:
|
41
64
|
- lib
|
42
65
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -53,10 +76,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
76
|
version:
|
54
77
|
requirements: []
|
55
78
|
|
56
|
-
rubyforge_project:
|
79
|
+
rubyforge_project: bigmoney
|
57
80
|
rubygems_version: 1.2.0
|
58
81
|
signing_key:
|
59
82
|
specification_version: 2
|
60
|
-
summary: Represents an amount of money in a particular currency
|
83
|
+
summary: Represents an amount of money in a particular currency
|
61
84
|
test_files:
|
62
85
|
- test/test_big_money.rb
|
86
|
+
- test/test_currency.rb
|
87
|
+
- test/test_exchange.rb
|