currency 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/COPYING.txt +339 -0
  2. data/LICENSE.txt +62 -0
  3. data/Manifest.txt +37 -14
  4. data/README.txt +8 -0
  5. data/Rakefile +42 -8
  6. data/Releases.txt +26 -0
  7. data/TODO.txt +1 -0
  8. data/examples/ex1.rb +3 -3
  9. data/examples/xe1.rb +3 -2
  10. data/lib/currency.rb +71 -9
  11. data/lib/currency/active_record.rb +138 -21
  12. data/lib/currency/core_extensions.rb +7 -5
  13. data/lib/currency/currency.rb +94 -177
  14. data/lib/currency/{currency_factory.rb → currency/factory.rb} +46 -25
  15. data/lib/currency/currency_version.rb +3 -3
  16. data/lib/currency/exception.rb +14 -14
  17. data/lib/currency/exchange.rb +14 -12
  18. data/lib/currency/exchange/rate.rb +159 -28
  19. data/lib/currency/exchange/rate/deriver.rb +146 -0
  20. data/lib/currency/exchange/rate/source.rb +84 -0
  21. data/lib/currency/exchange/rate/source/base.rb +156 -0
  22. data/lib/currency/exchange/rate/source/failover.rb +57 -0
  23. data/lib/currency/exchange/rate/source/historical.rb +79 -0
  24. data/lib/currency/exchange/rate/source/historical/rate.rb +181 -0
  25. data/lib/currency/exchange/rate/source/historical/writer.rb +203 -0
  26. data/lib/currency/exchange/rate/source/new_york_fed.rb +91 -0
  27. data/lib/currency/exchange/rate/source/provider.rb +105 -0
  28. data/lib/currency/exchange/rate/source/test.rb +50 -0
  29. data/lib/currency/exchange/rate/source/the_financials.rb +190 -0
  30. data/lib/currency/exchange/rate/source/timed_cache.rb +144 -0
  31. data/lib/currency/exchange/rate/source/xe.rb +166 -0
  32. data/lib/currency/exchange/time_quantitizer.rb +111 -0
  33. data/lib/currency/formatter.rb +159 -0
  34. data/lib/currency/macro.rb +321 -0
  35. data/lib/currency/money.rb +90 -64
  36. data/lib/currency/money_helper.rb +6 -5
  37. data/lib/currency/parser.rb +153 -0
  38. data/test/ar_column_test.rb +6 -3
  39. data/test/ar_simple_test.rb +5 -2
  40. data/test/ar_test_base.rb +39 -33
  41. data/test/ar_test_core.rb +64 -0
  42. data/test/formatter_test.rb +81 -0
  43. data/test/historical_writer_test.rb +184 -0
  44. data/test/macro_test.rb +109 -0
  45. data/test/money_test.rb +72 -4
  46. data/test/new_york_fed_test.rb +57 -0
  47. data/test/parser_test.rb +60 -0
  48. data/test/test_base.rb +13 -3
  49. data/test/time_quantitizer_test.rb +136 -0
  50. data/test/xe_test.rb +29 -5
  51. metadata +41 -18
  52. data/lib/currency/exchange/base.rb +0 -84
  53. data/lib/currency/exchange/test.rb +0 -39
  54. data/lib/currency/exchange/xe.rb +0 -250
@@ -0,0 +1,109 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'test/test_base'
5
+ require 'currency'
6
+ require 'currency/macro'
7
+
8
+ module Currency
9
+
10
+ class MacroTest < TestBase
11
+ class Record
12
+ include ::Currency::Macro
13
+
14
+ attr_accessor :date
15
+ attr_accessor :gross
16
+ attr_accessor :tax
17
+ attr_accessor :net
18
+ attr_accessor :currency
19
+
20
+ attr_money :gross_money, :value => :gross, :time => :date, :currency => :currency
21
+ attr_money :tax_money, :value => :tax, :time => :date, :currency => :currency
22
+ attr_money :net_money, :value => :net, :time => :date, :currency => :currency
23
+
24
+ def initialize
25
+ self.date = Time.now
26
+ self.currency = :USD
27
+ end
28
+
29
+ def compute_net
30
+ self.net = self.gross - self.tax
31
+ end
32
+
33
+ def compute_net_money
34
+ self.net_money = self.gross_money - self.tax_money
35
+ end
36
+
37
+ end
38
+
39
+ def setup
40
+ super
41
+ end
42
+
43
+ ############################################
44
+ # Tests
45
+ #
46
+
47
+ def test_read_money
48
+ assert_kind_of Record, r = Record.new
49
+ assert_not_nil r.gross = 10.00
50
+ assert_equal r.gross_money.to_f, r.gross
51
+ assert_equal r.gross_money.currency.code, r.currency
52
+ assert_equal r.gross_money.time, r.date
53
+
54
+ r
55
+ end
56
+
57
+
58
+ def test_write_money_rep
59
+ assert_kind_of Record, r = Record.new
60
+ assert_not_nil r.gross_money = 10.00
61
+ assert_equal r.gross_money.to_f, r.gross
62
+ assert_equal r.gross_money.currency.code, r.currency
63
+ assert_equal r.gross_money.time, r.date
64
+
65
+ r
66
+ end
67
+
68
+
69
+ def test_money_cache
70
+ r = test_read_money
71
+
72
+ assert_not_nil r_gross = r.gross
73
+ assert r_gross.object_id == r.gross.object_id
74
+
75
+ # Cache flush
76
+ assert_not_nil r.gross = 12.00
77
+ assert_equal r.gross_money.to_f, r.gross
78
+ assert r_gross.object_id != r.gross.object_id
79
+ end
80
+
81
+
82
+ def test_currency
83
+ r = test_read_money
84
+
85
+ assert_not_nil r.gross_money.currency
86
+ assert_equal r.gross_money.currency.code, r.currency
87
+
88
+ end
89
+
90
+
91
+ def test_compute
92
+ assert_kind_of Record, r = Record.new
93
+ assert_not_nil r.gross = 10.00
94
+ assert_not_nil r.tax = 1.50
95
+ r.compute_net
96
+
97
+ assert_equal 8.50, r.net
98
+ assert_equal r.net, r.net_money.to_f
99
+
100
+ r.compute_net_money
101
+
102
+ assert_equal 8.50, r.net
103
+ assert_equal r.net, r.net_money.to_f
104
+ end
105
+
106
+ end # class
107
+
108
+ end # module
109
+
@@ -1,5 +1,4 @@
1
1
 
2
- #require File.dirname(__FILE__) + '/../test_helper'
3
2
 
4
3
  require 'test/test_base'
5
4
  require 'currency'
@@ -35,6 +34,10 @@ class MoneyTest < TestBase
35
34
  assert_kind_of Money, m = "13.98".money(:CAD)
36
35
  assert_equal m.currency.code, :CAD
37
36
  assert_equal m.rep, 1398
37
+
38
+ assert_kind_of Money, m = "45.99".money(:EUR)
39
+ assert_equal m.currency.code, :EUR
40
+ assert_equal m.rep, 4599
38
41
  end
39
42
 
40
43
  def test_zero
@@ -167,10 +170,10 @@ class MoneyTest < TestBase
167
170
  assert_not_nil cad = Money.new(123.45, :CAD)
168
171
 
169
172
  # - Money => Money
170
- assert_equal -12345, (- usd).rep
173
+ assert_equal(-12345, (- usd).rep)
171
174
  assert_equal :USD, (- usd).currency.code
172
175
 
173
- assert_equal -12345, (- cad).rep
176
+ assert_equal(-12345, (- cad).rep)
174
177
  assert_equal :CAD, (- cad).currency.code
175
178
 
176
179
  # Money + Money => Money
@@ -214,9 +217,29 @@ class MoneyTest < TestBase
214
217
  assert_equal_float 3.0, m
215
218
 
216
219
  assert_kind_of Numeric, m = (usd / cad)
217
- assert_equal_float Exchange::Test.USD_CAD, m, 0.0001
220
+ assert_equal_float Exchange::Rate::Source::Test.USD_CAD, m, 0.0001
221
+ end
222
+
223
+
224
+ def test_pivot_conversions
225
+ # Using default get_rate
226
+ assert_not_nil cad = Money.new(123.45, :CAD)
227
+ assert_not_nil eur = cad.convert(:EUR)
228
+ assert_kind_of Numeric, m = (eur.to_f / cad.to_f)
229
+ m_expected = (1.0 / Exchange::Rate::Source::Test.USD_CAD) * Exchange::Rate::Source::Test.USD_EUR
230
+ # $stderr.puts "m = #{m}, expected = #{m_expected}"
231
+ assert_equal_float m_expected, m, 0.001
232
+
233
+
234
+ assert_not_nil gbp = Money.new(123.45, :GBP)
235
+ assert_not_nil eur = gbp.convert(:EUR)
236
+ assert_kind_of Numeric, m = (eur.to_f / gbp.to_f)
237
+ m_expected = (1.0 / Exchange::Rate::Source::Test.USD_GBP) * Exchange::Rate::Source::Test.USD_EUR
238
+ # $stderr.puts "m = #{m}, expected = #{m_expected}"
239
+ assert_equal_float m_expected, m, 0.001
218
240
  end
219
241
 
242
+
220
243
  def test_invalid_currency_code
221
244
  assert_raise Exception::InvalidCurrencyCode do
222
245
  Money.new(123, :asdf)
@@ -225,6 +248,51 @@ class MoneyTest < TestBase
225
248
  Money.new(123, 5)
226
249
  end
227
250
  end
251
+
252
+
253
+ def test_time_default
254
+ Money.default_time = nil
255
+
256
+ assert_not_nil usd = Money.new(123.45, :USD)
257
+ assert_nil usd.time
258
+
259
+ Money.default_time = Time.now
260
+ assert_not_nil usd = Money.new(123.45, :USD)
261
+ assert_equal usd.time, Money.default_time
262
+ end
263
+
264
+
265
+ def test_time_now
266
+ # Test
267
+ Money.default_time = :now
268
+
269
+ assert_not_nil usd = Money.new(123.45, :USD)
270
+ assert_not_nil usd.time
271
+
272
+ sleep 1
273
+
274
+ assert_not_nil usd2 = Money.new(123.45, :USD)
275
+ assert_not_nil usd2.time
276
+ assert usd.time != usd2.time
277
+
278
+ Money.default_time = nil
279
+ end
280
+
281
+
282
+ def test_time_fixed
283
+ # Test for fixed time.
284
+ Money.default_time = Time.new
285
+
286
+ assert_not_nil usd = Money.new(123.45, :USD)
287
+ assert_not_nil usd.time
288
+
289
+ sleep 1
290
+
291
+ assert_not_nil usd2 = Money.new(123.45, :USD)
292
+ assert_not_nil usd2.time
293
+ assert usd.time == usd2.time
294
+ end
295
+
228
296
  end
229
297
 
230
298
  end # module
@@ -0,0 +1,57 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'test/test_base'
5
+
6
+ require 'currency' # For :type => :money
7
+ require 'currency/exchange/rate/source/new_york_fed'
8
+
9
+ module Currency
10
+
11
+ class NewYorkFedTest < TestBase
12
+ def setup
13
+ super
14
+ end
15
+
16
+
17
+ def get_rate_source
18
+ # Force XE Exchange.
19
+ verbose = false
20
+ source = Exchange::Rate::Source::NewYorkFed.new(:verbose => verbose)
21
+ deriver = Exchange::Rate::Deriver.new(:source => source, :verbose => source.verbose)
22
+ end
23
+
24
+
25
+
26
+ def test_usd_cad
27
+ assert_not_nil rates = Exchange::Rate::Source.default.source.raw_rates
28
+ assert_not_nil rates[:USD]
29
+ assert_not_nil usd_cad = rates[:USD][:CAD]
30
+
31
+ assert_not_nil usd = Money.new(123.45, :USD)
32
+ assert_not_nil cad = usd.convert(:CAD)
33
+
34
+ assert_kind_of Numeric, m = (cad.to_f / usd.to_f)
35
+ # $stderr.puts "m = #{m}"
36
+ assert_equal_float usd_cad, m, 0.001
37
+ end
38
+
39
+
40
+ def test_cad_eur
41
+ assert_not_nil rates = Exchange::Rate::Source.default.source.raw_rates
42
+ assert_not_nil rates[:USD]
43
+ assert_not_nil usd_cad = rates[:USD][:CAD]
44
+ assert_not_nil usd_eur = rates[:USD][:EUR]
45
+
46
+ assert_not_nil cad = Money.new(123.45, :CAD)
47
+ assert_not_nil eur = cad.convert(:EUR)
48
+
49
+ assert_kind_of Numeric, m = (eur.to_f / cad.to_f)
50
+ # $stderr.puts "m = #{m}"
51
+ assert_equal_float (1.0 / usd_cad) * usd_eur, m, 0.001
52
+ end
53
+
54
+ end
55
+
56
+ end # module
57
+
@@ -0,0 +1,60 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'test/test_base'
5
+ require 'currency'
6
+
7
+ module Currency
8
+
9
+ class ParserTest < TestBase
10
+ def setup
11
+ super
12
+ @parser = ::Currency::Currency.USD.parser_or_default
13
+ end
14
+
15
+ ############################################
16
+ # Simple stuff.
17
+ #
18
+
19
+ def test_default
20
+
21
+ end
22
+
23
+
24
+ def test_thousands
25
+ assert_equal 123456789, @parser.parse("1234567.89").rep
26
+ assert_equal 123456789, @parser.parse("1,234,567.89").rep
27
+ end
28
+
29
+
30
+ def test_cents
31
+ assert_equal 123456789, @parser.parse("1234567.89").rep
32
+ assert_equal 123456700, @parser.parse("1234567").rep
33
+ assert_equal 123456700, @parser.parse("1234567.").rep
34
+ assert_equal 123456780, @parser.parse("1234567.8").rep
35
+ assert_equal 123456789, @parser.parse("1234567.891").rep
36
+ assert_equal -123456700, @parser.parse("-1234567").rep
37
+ assert_equal 123456700, @parser.parse("+1234567").rep
38
+ end
39
+
40
+
41
+ def test_misc
42
+ assert_not_nil m = "123.45 USD".money + "100 CAD"
43
+ assert ! (m.rep == 200.45)
44
+ end
45
+
46
+
47
+ def test_round_trip
48
+ ::Currency::Currency.default = :USD
49
+ assert_not_nil m = ::Currency::Money("1234567.89", :CAD)
50
+ assert_not_nil m2 = ::Currency::Money(m.inspect)
51
+ assert_equal m.rep, m2.rep
52
+ assert_equal m.currency, m2.currency
53
+ assert_nil m2.time
54
+ assert_equal m.inspect, m2.inspect
55
+ end
56
+
57
+ end
58
+
59
+ end # module
60
+
@@ -1,16 +1,25 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
1
4
  # Base Test class
2
5
 
3
6
  require 'test/unit'
4
7
  require 'currency'
5
- require 'currency/exchange/test'
8
+ require 'currency/exchange/rate/source/test'
6
9
 
7
10
  module Currency
8
11
 
9
12
  class TestBase < Test::Unit::TestCase
10
13
  def setup
11
14
  super
12
- # Force Test Exchange.
13
- Exchange.default = Exchange::Test.instance
15
+ @rate_source ||= get_rate_source
16
+ Exchange::Rate::Source.default = @rate_source
17
+ end
18
+
19
+
20
+ def get_rate_source
21
+ source = Exchange::Rate::Source::Test.instance
22
+ deriver = Exchange::Rate::Deriver.new(:source => source)
14
23
  end
15
24
 
16
25
  # Avoid "No test were specified" error.
@@ -18,6 +27,7 @@ class TestBase < Test::Unit::TestCase
18
27
  assert true
19
28
  end
20
29
 
30
+
21
31
  # Helpers.
22
32
  def assert_equal_float(x, y, eps = 1.0e-8)
23
33
  d = (x * eps).abs
@@ -0,0 +1,136 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ #require File.dirname(__FILE__) + '/../test_helper'
5
+
6
+ require 'test/test_base'
7
+ require 'currency/exchange/time_quantitizer'
8
+
9
+ module Currency
10
+ module Exchange
11
+
12
+ class TimeQuantitizerTest < TestBase
13
+ def setup
14
+ super
15
+ end
16
+
17
+ ############################################
18
+ #
19
+ #
20
+
21
+ def test_create
22
+ assert_kind_of TimeQuantitizer, tq = TimeQuantitizer.new()
23
+ assert_equal 60 * 60 * 24, tq.time_quant_size
24
+ assert_nil tq.quantitize_time(nil)
25
+
26
+ tq
27
+ end
28
+
29
+
30
+ def test_localtime
31
+ t1 = assert_test_day(t0 = Time.new)
32
+ assert_equal t0.utc_offset, t1.utc_offset
33
+ assert_equal Time.now.utc_offset, t1.utc_offset
34
+ end
35
+
36
+
37
+ def test_utc
38
+ t1 = assert_test_day(t0 = Time.new.utc)
39
+ assert_equal t0.utc_offset, t1.utc_offset
40
+ end
41
+
42
+
43
+ def test_random
44
+ (1 .. 1000).each do
45
+ t0 = Time.at(rand(1234567901).to_i)
46
+ assert_test_day(t0)
47
+ assert_test_hour(t0)
48
+ # Problem year?
49
+ t0 = Time.parse('1977/01/01') + rand(60 * 60 * 24 * 7 * 52).to_i
50
+ assert_test_day(t0)
51
+ assert_test_hour(t0)
52
+ # Problem year?
53
+ t0 = Time.parse('1995/01/01') + rand(60 * 60 * 24 * 7 * 52).to_i
54
+ assert_test_day(t0)
55
+ assert_test_hour(t0)
56
+ end
57
+
58
+
59
+ end
60
+
61
+
62
+ def assert_test_day(t0)
63
+ tq = test_create
64
+
65
+ begin
66
+ assert_not_nil t1 = tq.quantitize_time(t0)
67
+ #$stderr.puts "t0 = #{t0}"
68
+ #$stderr.puts "t1 = #{t1}"
69
+
70
+ assert_equal t0.year, t1.year
71
+ assert_equal t0.month, t1.month
72
+ assert_equal t0.day, t1.day
73
+ assert_time_beginning_of_day(t1)
74
+ rescue Object => err
75
+ raise("#{err}\nDuring quantitize_time(#{t0} (#{t0.to_i}))")
76
+ end
77
+
78
+ t1
79
+ end
80
+
81
+
82
+ def assert_test_hour(t0)
83
+ tq = TimeQuantitizer.new(:time_quant_size => 60 * 60) # 1 hour
84
+
85
+ assert_not_nil t1 = tq.quantitize_time(t0)
86
+ #$stderr.puts "t0 = #{t0}"
87
+ #$stderr.puts "t1 = #{t1}"
88
+
89
+ assert_equal t0.year, t1.year
90
+ assert_equal t0.month, t1.month
91
+ assert_equal t0.day, t1.day
92
+ assert_time_beginning_of_hour(t1)
93
+
94
+ t1
95
+ end
96
+
97
+
98
+ def assert_test_minute(t0)
99
+ tq = TimeQuantitizer.new(:time_quant_size => 60) # 1 minute
100
+
101
+ tq.local_timezone_offset
102
+
103
+ assert_not_nil t1 = tq.quantitize_time(t0)
104
+ $stderr.puts "t0 = #{t0}"
105
+ $stderr.puts "t1 = #{t1}"
106
+
107
+ assert_equal t0.year, t1.year
108
+ assert_equal t0.month, t1.month
109
+ assert_equal t0.day, t1.day
110
+ assert_time_beginning_of_day(t1)
111
+
112
+ t1
113
+ end
114
+
115
+
116
+ def assert_time_beginning_of_day(t1)
117
+ assert_equal 0, t1.hour
118
+ assert_time_beginning_of_hour(t1)
119
+ end
120
+
121
+
122
+ def assert_time_beginning_of_hour(t1)
123
+ assert_equal 0, t1.min
124
+ assert_time_beginning_of_min(t1)
125
+ end
126
+
127
+
128
+ def assert_time_beginning_of_min(t1)
129
+ assert_equal 0, t1.sec
130
+ end
131
+
132
+ end # class
133
+ end # module
134
+ end # module
135
+
136
+