acvwilson-acvwilson-currency 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/COPYING.txt +339 -0
  2. data/ChangeLog +8 -0
  3. data/LICENSE.txt +65 -0
  4. data/Manifest.txt +58 -0
  5. data/README.txt +51 -0
  6. data/Releases.txt +155 -0
  7. data/TODO.txt +9 -0
  8. data/currency.gemspec +18 -0
  9. data/examples/ex1.rb +13 -0
  10. data/examples/xe1.rb +20 -0
  11. data/lib/currency.rb +143 -0
  12. data/lib/currency/active_record.rb +265 -0
  13. data/lib/currency/config.rb +91 -0
  14. data/lib/currency/core_extensions.rb +48 -0
  15. data/lib/currency/currency.rb +175 -0
  16. data/lib/currency/currency/factory.rb +121 -0
  17. data/lib/currency/currency_version.rb +6 -0
  18. data/lib/currency/exception.rb +119 -0
  19. data/lib/currency/exchange.rb +50 -0
  20. data/lib/currency/exchange/rate.rb +214 -0
  21. data/lib/currency/exchange/rate/deriver.rb +157 -0
  22. data/lib/currency/exchange/rate/source.rb +89 -0
  23. data/lib/currency/exchange/rate/source/base.rb +166 -0
  24. data/lib/currency/exchange/rate/source/failover.rb +63 -0
  25. data/lib/currency/exchange/rate/source/federal_reserve.rb +160 -0
  26. data/lib/currency/exchange/rate/source/historical.rb +79 -0
  27. data/lib/currency/exchange/rate/source/historical/rate.rb +184 -0
  28. data/lib/currency/exchange/rate/source/historical/rate_loader.rb +186 -0
  29. data/lib/currency/exchange/rate/source/historical/writer.rb +220 -0
  30. data/lib/currency/exchange/rate/source/new_york_fed.rb +127 -0
  31. data/lib/currency/exchange/rate/source/provider.rb +120 -0
  32. data/lib/currency/exchange/rate/source/test.rb +50 -0
  33. data/lib/currency/exchange/rate/source/the_financials.rb +191 -0
  34. data/lib/currency/exchange/rate/source/timed_cache.rb +198 -0
  35. data/lib/currency/exchange/rate/source/xe.rb +165 -0
  36. data/lib/currency/exchange/time_quantitizer.rb +111 -0
  37. data/lib/currency/formatter.rb +300 -0
  38. data/lib/currency/macro.rb +321 -0
  39. data/lib/currency/money.rb +296 -0
  40. data/lib/currency/money_helper.rb +13 -0
  41. data/lib/currency/parser.rb +193 -0
  42. data/spec/ar_base_spec.rb +140 -0
  43. data/spec/ar_column_spec.rb +69 -0
  44. data/spec/ar_core_spec.rb +64 -0
  45. data/spec/ar_simple_spec.rb +31 -0
  46. data/spec/config_spec.rb +29 -0
  47. data/spec/federal_reserve_spec.rb +75 -0
  48. data/spec/formatter_spec.rb +72 -0
  49. data/spec/historical_writer_spec.rb +187 -0
  50. data/spec/macro_spec.rb +109 -0
  51. data/spec/money_spec.rb +347 -0
  52. data/spec/new_york_fed_spec.rb +73 -0
  53. data/spec/parser_spec.rb +105 -0
  54. data/spec/spec_helper.rb +25 -0
  55. data/spec/time_quantitizer_spec.rb +115 -0
  56. data/spec/timed_cache_spec.rb +95 -0
  57. data/spec/xe_spec.rb +50 -0
  58. metadata +117 -0
@@ -0,0 +1,91 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ # The Currency::Config class is responsible for
5
+ # maintaining global configuration for the Currency package.
6
+ #
7
+ # TO DO:
8
+ #
9
+ # Migrate all class variable configurations to this object.
10
+ class Currency::Config
11
+ @@default = nil
12
+
13
+ # Returns the default Currency::Config object.
14
+ #
15
+ # If one is not specfied an instance is
16
+ # created. This is a global, not thread-local.
17
+ def self.default
18
+ @@default ||=
19
+ self.new
20
+ end
21
+
22
+ # Sets the default Currency::Config object.
23
+ def self.default=(x)
24
+ @@default = x
25
+ end
26
+
27
+ # Returns the current Currency::Config object used during
28
+ # in the current thread.
29
+ #
30
+ # If #current= has not been called and #default= has not been called,
31
+ # then UndefinedExchange is raised.
32
+ def self.current
33
+ Thread.current[:Currency__Config] ||=
34
+ self.default ||
35
+ (raise ::Currency::Exception::UndefinedConfig, "Currency::Config.default not defined")
36
+ end
37
+
38
+ # Sets the current Currency::Config object used
39
+ # in the current thread.
40
+ def self.current=(x)
41
+ Thread.current[:Currency__Config] = x
42
+ end
43
+
44
+ # Clones the current configuration and makes it current
45
+ # during the execution of a block. After block completes,
46
+ # the previous configuration is restored.
47
+ #
48
+ # Currency::Config.configure do | c |
49
+ # c.float_ref_filter = Proc.new { | x | x.round }
50
+ # "123.448".money.rep == 12345
51
+ # end
52
+ def self.configure(&blk)
53
+ c_prev = current
54
+ c_new = self.current = current.clone
55
+ result = nil
56
+ begin
57
+ result = yield c_new
58
+ ensure
59
+ self.current = c_prev
60
+ end
61
+ result
62
+ end
63
+
64
+
65
+ @@identity = Proc.new { |x| x } # :nodoc:
66
+
67
+ # Returns the current Float conversion filter.
68
+ # Can be used to set rounding or truncation policies when converting
69
+ # Float values to Money values.
70
+ # Defaults to an identity function.
71
+ # See Float#Money_rep.
72
+ def float_ref_filter
73
+ @float_ref_filter ||=
74
+ @@identity
75
+ end
76
+
77
+ # Sets the current Float conversion filter.
78
+ def float_ref_filter=(x)
79
+ @float_ref_filter = x
80
+ end
81
+
82
+
83
+ # Defines the table name for Historical::Rate records.
84
+ # Defaults to 'currency_historical_rates'.
85
+ attr_accessor :historical_table_name
86
+ def historical_table_name
87
+ @historical_table_name ||= 'currency_historical_rates'
88
+ end
89
+
90
+ end # module
91
+
@@ -0,0 +1,48 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+
5
+
6
+ class Object
7
+ # Exact conversion to Money representation value.
8
+ def money(*opts)
9
+ Currency::Money(self, *opts)
10
+ end
11
+ end
12
+
13
+
14
+
15
+ class Integer
16
+ # Exact conversion to Money representation value.
17
+ def Money_rep(currency, time = nil)
18
+ Integer(self * currency.scale)
19
+ end
20
+ end
21
+
22
+
23
+
24
+ class Float
25
+ # Inexact conversion to Money representation value.
26
+ def Money_rep(currency, time = nil)
27
+ Integer(Currency::Config.current.float_ref_filter.call(self * currency.scale))
28
+ end
29
+
30
+ def round_with_precision(precision = nil)
31
+ precision.nil? ? round_without_precision : (self * (10 ** precision)).round_without_precision / (10 ** precision).to_f
32
+ end
33
+ alias_method :round_without_precision, :round
34
+ alias_method :round, :round_with_precision
35
+ end
36
+
37
+
38
+
39
+
40
+ class String
41
+ # Exact conversion to Money representation value.
42
+ def Money_rep(currency, time = nil)
43
+ x = currency.parse(self, :currency => currency, :time => time)
44
+ x = x.rep if x.respond_to?(:rep)
45
+ x
46
+ end
47
+ end
48
+
@@ -0,0 +1,175 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+
5
+ # Represents a currency.
6
+ #
7
+ # Currency objects are created on-demand by Currency::Currency::Factory.
8
+ #
9
+ # See Currency.get method.
10
+ #
11
+ class Currency::Currency
12
+ # Returns the ISO three-letter currency code as a symbol.
13
+ # e.g. :USD, :CAD, etc.
14
+ attr_reader :code
15
+
16
+ # The Currency's scale factor.
17
+ # e.g: the :USD scale factor is 100.
18
+ attr_reader :scale
19
+
20
+ # The Currency's scale factor.
21
+ # e.g: the :USD scale factor is 2, where 10 ^ 2 == 100.
22
+ attr_reader :scale_exp
23
+
24
+ # Used by Formatter.
25
+ attr_reader :format_right
26
+
27
+ # Used by Formatter.
28
+ attr_reader :format_left
29
+
30
+ # The Currency's symbol.
31
+ # e.g: USD symbol is '$'
32
+ attr_accessor :symbol
33
+
34
+ # The Currency's symbol as HTML.
35
+ # e.g: EUR symbol is '&#8364;' (:html &#8364; :) or '&euro;' (:html &euro; :)
36
+ attr_accessor :symbol_html
37
+
38
+ # The default Formatter.
39
+ attr_accessor :formatter
40
+
41
+ # The default parser.
42
+ attr_accessor :parser
43
+
44
+
45
+ # Create a new currency.
46
+ # This should only be called from Currency::Currency::Factory.
47
+ def initialize(code, symbol = nil, scale = 1000000)
48
+ self.code = code
49
+ self.symbol = symbol
50
+ self.scale = scale
51
+
52
+ @formatter =
53
+ @parser =
54
+ nil
55
+ end
56
+
57
+
58
+ # Returns the Currency object from the default Currency::Currency::Factory
59
+ # by its three-letter uppercase Symbol, such as :USD, or :CAD.
60
+ def self.get(code)
61
+ # $stderr.puts "#{self}.get(#{code.inspect})"
62
+ return nil unless code
63
+ return code if code.kind_of?(::Currency::Currency)
64
+ Factory.default.get_by_code(code)
65
+ end
66
+
67
+
68
+ # Internal method for converting currency codes to internal
69
+ # Symbol format.
70
+ def self.cast_code(x)
71
+ x = x.upcase.intern if x.kind_of?(String)
72
+ raise ::Currency::Exception::InvalidCurrencyCode, x unless x.kind_of?(Symbol)
73
+ raise ::Currency::Exception::InvalidCurrencyCode, x unless x.to_s.length == 3
74
+ x
75
+ end
76
+
77
+
78
+ # Returns the hash of the Currency's code.
79
+ def hash
80
+ @code.hash
81
+ end
82
+
83
+
84
+ # Returns true if the Currency's are equal.
85
+ def eql?(x)
86
+ self.class == x.class && @code == x.code
87
+ end
88
+
89
+
90
+ # Returns true if the Currency's are equal.
91
+ def ==(x)
92
+ self.class == x.class && @code == x.code
93
+ end
94
+
95
+
96
+ # Clients should never call this directly.
97
+ def code=(x)
98
+ x = self.class.cast_code(x) unless x.nil?
99
+ @code = x
100
+ #$stderr.puts "#{self}.code = #{@code}"; x
101
+ end
102
+
103
+
104
+ # Clients should never call this directly.
105
+ def scale=(x)
106
+ @scale = x
107
+ return x if x.nil?
108
+ @scale_exp = Integer(Math.log10(@scale));
109
+ @format_right = - @scale_exp
110
+ @format_left = @format_right - 1
111
+ x
112
+ end
113
+
114
+
115
+ # Parse a Money string in this Currency.
116
+ #
117
+ # See Currency::Parser#parse.
118
+ #
119
+ def parse(str, *opt)
120
+ parser_or_default.parse(str, *opt)
121
+ end
122
+
123
+
124
+ def parser_or_default
125
+ (@parser || ::Currency::Parser.default)
126
+ end
127
+
128
+
129
+ # Formats the Money value as a string using the current Formatter.
130
+ # See Currency::Formatter#format.
131
+ def format(m, *opt)
132
+ formatter_or_default.format(m, *opt)
133
+ end
134
+
135
+
136
+ def formatter_or_default
137
+ (@formatter || ::Currency::Formatter.default)
138
+ end
139
+
140
+
141
+ # Returns the Currency code as a String.
142
+ def to_s
143
+ @code.to_s
144
+ end
145
+
146
+
147
+ # Returns the default Factory's currency.
148
+ def self.default
149
+ Factory.default.currency
150
+ end
151
+
152
+
153
+ # Sets the default Factory's currency.
154
+ def self.default=(x)
155
+ x = self.get(x) unless x.kind_of?(self)
156
+ Factory.default.currency = x
157
+ end
158
+
159
+
160
+ # If selector is [A-Z][A-Z][A-Z], load the currency via Factory.default.
161
+ #
162
+ # Currency::Currency.USD
163
+ # => #<Currency::Currency:0xb7d0917c @formatter=nil, @scale_exp=2, @scale=100, @symbol="$", @format_left=-3, @code=:USD, @parser=nil, @format_right=-2>
164
+ #
165
+ def self.method_missing(sel, *args, &blk)
166
+ if args.size == 0 && (! block_given?) && /^[A-Z][A-Z][A-Z]$/.match(sel.to_s)
167
+ Factory.default.get_by_code(sel)
168
+ else
169
+ super
170
+ end
171
+ end
172
+
173
+ end # class
174
+
175
+
@@ -0,0 +1,121 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+
5
+ # Responsible for creating Currency::Currency objects on-demand.
6
+ class Currency::Currency::Factory
7
+ @@default = nil
8
+
9
+ # Returns the default Currency::Factory.
10
+ def self.default
11
+ @@default ||= self.new
12
+ end
13
+ # Sets the default Currency::Factory.
14
+ def self.default=(x)
15
+ @@default = x
16
+ end
17
+
18
+
19
+ def initialize(*opts)
20
+ @currency_by_code = { }
21
+ @currency_by_symbol = { }
22
+ @currency = nil
23
+ end
24
+
25
+
26
+ # Lookup Currency by code.
27
+ def get_by_code(x)
28
+ x = ::Currency::Currency.cast_code(x)
29
+ # $stderr.puts "get_by_code(#{x})"
30
+ @currency_by_code[x] ||= install(load(::Currency::Currency.new(x)))
31
+ end
32
+
33
+
34
+ # Lookup Currency by symbol.
35
+ def get_by_symbol(symbol)
36
+ @currency_by_symbol[symbol] ||= install(load(::Currency::Currency.new(nil, symbol)))
37
+ end
38
+
39
+
40
+ # This method initializes a Currency object as
41
+ # requested from #get_by_code or #get_by_symbol.
42
+ #
43
+ # This method must initialize:
44
+ #
45
+ # currency.code
46
+ # currency.scale
47
+ #
48
+ # Optionally:
49
+ #
50
+ # currency.symbol
51
+ # currency.symbol_html
52
+ #
53
+ # Subclasses that provide Currency metadata should override this method.
54
+ # For example, loading Currency metadata from a database or YAML file.
55
+ def load(currency)
56
+ # $stderr.puts "BEFORE: load(#{currency.code})"
57
+
58
+ # Basic
59
+ if currency.code == :USD || currency.symbol == '$'
60
+ # $stderr.puts "load('USD')"
61
+ currency.code = :USD
62
+ currency.symbol = '$'
63
+ currency.scale = 1000000
64
+ elsif currency.code == :CAD
65
+ # $stderr.puts "load('CAD')"
66
+ currency.symbol = '$'
67
+ currency.scale = 1000000
68
+ elsif currency.code == :EUR
69
+ # $stderr.puts "load('CAD')"
70
+ currency.symbol = nil
71
+ currency.symbol_html = '&#8364;'
72
+ currency.scale = 1000000
73
+ else
74
+ currency.symbol = nil
75
+ currency.scale = 1000000
76
+ end
77
+
78
+ # $stderr.puts "AFTER: load(#{currency.inspect})"
79
+
80
+ currency
81
+ end
82
+
83
+
84
+ # Installs a new Currency for #get_by_symbol and #get_by_code.
85
+ def install(currency)
86
+ raise ::Currency::Exception::UnknownCurrency unless currency
87
+ @currency_by_symbol[currency.symbol] ||= currency unless currency.symbol.nil?
88
+ @currency_by_code[currency.code] = currency
89
+ end
90
+
91
+
92
+ # Returns the default Currency.
93
+ # Defaults to self.get_by_code(:USD).
94
+ def currency
95
+ @currency ||= self.get_by_code(:USD)
96
+ end
97
+
98
+
99
+ # Sets the default Currency.
100
+ def currency=(x)
101
+ @currency = x
102
+ end
103
+
104
+
105
+ # If selector is [A-Z][A-Z][A-Z], load the currency.
106
+ #
107
+ # factory.USD
108
+ # => #<Currency::Currency:0xb7d0917c @formatter=nil, @scale_exp=2, @scale=100, @symbol="$", @format_left=-3, @code=:USD, @parser=nil, @format_right=-2>
109
+ #
110
+ def method_missing(sel, *args, &blk)
111
+ if args.size == 0 && (! block_given?) && /^[A-Z][A-Z][A-Z]$/.match(sel.to_s)
112
+ self.get_by_code(sel)
113
+ else
114
+ super
115
+ end
116
+ end
117
+
118
+ end # class
119
+
120
+
121
+
@@ -0,0 +1,6 @@
1
+ module Currency
2
+ CurrencyVersion = '0.4.11'
3
+ end
4
+ # DO NOT EDIT
5
+ # This file is auto-generated by build scripts.
6
+ # See: rake update_version
@@ -0,0 +1,119 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ module Currency::Exception
5
+ # Base class for all Currency::Exception objects.
6
+ #
7
+ # raise Currency::Exception [ "msg", :opt1, 1, :opt2, 2 ]
8
+ #
9
+ class Base < ::Exception
10
+ EMPTY_HASH = { }.freeze
11
+
12
+ def initialize(arg1, *args)
13
+ case arg1
14
+ # [ description, ... ]
15
+ when Array
16
+ @opts = arg1
17
+ arg1 = arg1.shift
18
+ else
19
+ @opts = nil
20
+ end
21
+
22
+ case @opts
23
+ when Array
24
+ if @opts.size == 1 && @opts.first.kind_of?(Hash)
25
+ # [ description, { ... } ]
26
+ @opts = @opts.first
27
+ else
28
+ # [ description, :key, value, ... ]
29
+ @opts = Hash[*@opts]
30
+ end
31
+ end
32
+
33
+ case @opts
34
+ when Hash
35
+ @opts = @opts.dup.freeze
36
+ else
37
+ @opts = { :info => @opts }.freeze
38
+ end
39
+
40
+ @opts ||= EMPTY_HASH
41
+
42
+ super(arg1, *args)
43
+ end
44
+
45
+
46
+ def method_missing(sel, *args, &blk)
47
+ sel = sel.to_sym
48
+ if args.empty? && ! block_given? && @opts.key?(sel)
49
+ return @opts[sel]
50
+ end
51
+ super
52
+ end
53
+
54
+ def to_s
55
+ super + ": #{@opts.inspect}"
56
+ end
57
+
58
+ end
59
+
60
+ # Generic Error.
61
+ class Generic < Base
62
+ end
63
+
64
+ # Error during parsing of Money values from String.
65
+ class InvalidMoneyString < Base
66
+ end
67
+
68
+ # Error during coercion of external Money values.
69
+ class InvalidMoneyValue < Base
70
+ end
71
+
72
+ # Error in Currency code formeat.
73
+ class InvalidCurrencyCode < Base
74
+ end
75
+
76
+ # Error during conversion between currencies.
77
+ class IncompatibleCurrency < Base
78
+ end
79
+
80
+ # Error during locating currencies.
81
+ class MissingCurrency < Base
82
+ end
83
+
84
+ # Error if an Exchange is not defined.
85
+ class UndefinedExchange < Base
86
+ end
87
+
88
+ # Error if a Currency is unknown.
89
+ class UnknownCurrency < Base
90
+ end
91
+
92
+ # Error if an Exchange Rate Source cannot provide an Exchange::Rate.
93
+ class UnknownRate < Base
94
+ end
95
+
96
+ # Error if an Exchange Rate Source.
97
+ class RateSourceError < Base
98
+ end
99
+
100
+ # Error if an Exchange Rate Source cannot supply any rates.
101
+ class UnavailableRates < Base
102
+ end
103
+
104
+ # Error if an Exchange::Rate is not valid.
105
+ class InvalidRate < Base
106
+ end
107
+
108
+ # Error if a subclass is responsible for implementing a method.
109
+ class SubclassResponsibility < Base
110
+ end
111
+
112
+ # Error if some functionality is unimplemented
113
+ class Unimplemented < Base
114
+ end
115
+
116
+ # Error if reentrantancy is d
117
+ class InvalidReentrancy < Base
118
+ end
119
+ end # module