acvwilson-currency 0.5.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 (57) 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 +83 -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 +48 -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 +310 -0
  38. data/lib/currency/macro.rb +321 -0
  39. data/lib/currency/money.rb +298 -0
  40. data/lib/currency/money_helper.rb +13 -0
  41. data/lib/currency/parser.rb +193 -0
  42. data/spec/ar_column_spec.rb +76 -0
  43. data/spec/ar_core_spec.rb +68 -0
  44. data/spec/ar_simple_spec.rb +23 -0
  45. data/spec/config_spec.rb +29 -0
  46. data/spec/federal_reserve_spec.rb +75 -0
  47. data/spec/formatter_spec.rb +72 -0
  48. data/spec/historical_writer_spec.rb +187 -0
  49. data/spec/macro_spec.rb +109 -0
  50. data/spec/money_spec.rb +355 -0
  51. data/spec/new_york_fed_spec.rb +73 -0
  52. data/spec/parser_spec.rb +105 -0
  53. data/spec/spec_helper.rb +25 -0
  54. data/spec/time_quantitizer_spec.rb +115 -0
  55. data/spec/timed_cache_spec.rb +95 -0
  56. data/spec/xe_spec.rb +50 -0
  57. 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,83 @@
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
+ # module Asa
23
+ # module Rounding
24
+ # def self.included(base) #:nodoc:
25
+ # puts "included by #{base.inspect}"
26
+ # base.class_eval do
27
+ # alias_method :round_without_precision, :round
28
+ # alias_method :round, :round_with_precision
29
+ # end
30
+ # end
31
+ #
32
+ # # Rounds the float with the specified precision.
33
+ # #
34
+ # # x = 1.337
35
+ # # x.round # => 1
36
+ # # x.round(1) # => 1.3
37
+ # # x.round(2) # => 1.34
38
+ # def round_with_precision(precision = nil)
39
+ # precision.nil? ? round_without_precision : (self * (10 ** precision)).round / (10 ** precision).to_f
40
+ # end
41
+ #
42
+ # end
43
+ # end
44
+ #
45
+ # class Float
46
+ # include Asa::Rounding
47
+ # # Inexact conversion to Money representation value.
48
+ # def Money_rep(currency, time = nil)
49
+ # Integer(Currency::Config.current.float_ref_filter.call(self * currency.scale))
50
+ # end
51
+ # end
52
+
53
+ class Float
54
+ # Inexact conversion to Money representation value.
55
+ def Money_rep(currency, time = nil)
56
+ Integer(Currency::Config.current.float_ref_filter.call(self * currency.scale))
57
+ end
58
+
59
+ # def round_with_awesome_precision(precision = nil)
60
+ # # puts "self: #{self.inspect}"
61
+ # # puts "precision: #{precision.inspect}"
62
+ # # puts "round_without_precision: #{round_without_precision.inspect}"
63
+ # # puts "self * (10 ** precision): #{(self * (10 ** precision)).inspect}"
64
+ # # puts "(self * (10 ** precision)).round_without_precision: #{((self * (10 ** precision)).round_without_precision).inspect}"
65
+ # # self.to_s.to_f.round_without_precision
66
+ # precision.nil? ? round_without_awesome_precision : (self * (10 ** precision)).round_without_awesome_precision / (10 ** precision).to_f
67
+ # end
68
+ # alias_method :round_without_awesome_precision, :round
69
+ # alias_method :round, :round_with_awesome_precision
70
+ end
71
+
72
+
73
+
74
+
75
+ class String
76
+ # Exact conversion to Money representation value.
77
+ def Money_rep(currency, time = nil)
78
+ x = currency.parse(self, :currency => currency, :time => time)
79
+ x = x.rep if x.respond_to?(:rep)
80
+ x
81
+ end
82
+ end
83
+
@@ -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
+