currency 0.3.3 → 0.4.0

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