currency 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,73 @@
1
+ module Currency
2
+ class CurrencyFactory
3
+ # Default factory.
4
+ @@default = nil
5
+ def self.default
6
+ @@default ||= CurrencyFactory.new
7
+ end
8
+
9
+ def initialize
10
+ @currency_by_code = { }
11
+ @currency_by_symbol = { }
12
+ @currency = nil
13
+ @USD = nil
14
+ @CAD = nil
15
+ end
16
+
17
+ # Lookup table by code
18
+ def get_by_code(x)
19
+ x = Currency.cast_code(x)
20
+ # $stderr.puts "get_by_code(#{x})"
21
+ @currency_by_code[x] ||= load(Currency.new(x))
22
+ end
23
+
24
+ # Lookup table by symbol
25
+ def get_by_symbol(symbol)
26
+ @currency_by_symbol[symbol] ||= load(Currency.new(nil, symbol))
27
+ end
28
+
29
+ def load(currency)
30
+ # $stderr.puts "BEFORE: load(#{currency.code})"
31
+
32
+ # SAMPLE CODE
33
+ if currency.code == :USD || currency.symbol == '$'
34
+ # $stderr.puts "load('USD')"
35
+ currency.code= :USD
36
+ currency.symbol= '$'
37
+ currency.scale= 100
38
+ elsif currency.code == :CAD
39
+ # $stderr.puts "load('CAD')"
40
+ currency.symbol= '$'
41
+ currency.scale= 100
42
+ else
43
+ currency.symbol= nil
44
+ currency.scale= 100
45
+ end
46
+
47
+ # $stderr.puts "AFTER: load(#{currency.inspect})"
48
+
49
+ install(currency)
50
+ end
51
+
52
+ def install(currency)
53
+ @currency_by_symbol[currency.symbol] ||= currency unless currency.symbol.nil?
54
+ @currency_by_code[currency.code] = currency
55
+ end
56
+
57
+ # Standard Currencys
58
+ def USD
59
+ @USD ||= self.get_by_code(:USD)
60
+ end
61
+
62
+ def CAD
63
+ @CAD ||= self.get_by_code(:CAD)
64
+ end
65
+
66
+ # Default Currency
67
+ def currency
68
+ @currency ||= self.USD
69
+ end
70
+ end
71
+ # END MODULE
72
+ end
73
+
@@ -0,0 +1,6 @@
1
+ # DO NOT EDIT
2
+ # This file is auto-generated by build scripts.
3
+ # See: rake update_version
4
+ module Currency
5
+ CurrencyVersion = '0.1.0'
6
+ end
@@ -0,0 +1,10 @@
1
+ module Currency
2
+ class InvalidMoneyString < Exception
3
+ end
4
+
5
+ class InvalidCurrencyCode < Exception
6
+ end
7
+
8
+ class IncompatibleCurrency < Exception
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ module Currency
2
+ # Represents a convertion rate between two currencies
3
+ class ExchangeRate
4
+ def initialize(c1, c2, c1_to_c2_rate, source = "UNKNOWN", date = nil, recip = true)
5
+ @c1 = c1
6
+ @c2 = c2
7
+ @rate = c1_to_c2_rate
8
+ @source = source
9
+ @date = date || Time.now
10
+ @reciprocal = recip
11
+ end
12
+
13
+ def c1
14
+ @c1
15
+ end
16
+
17
+ def c2
18
+ @c2
19
+ end
20
+
21
+ def rate
22
+ @rate
23
+ end
24
+
25
+ def source
26
+ @source
27
+ end
28
+
29
+ def date
30
+ @date
31
+ end
32
+
33
+ def convert(m, c1)
34
+ m = m.to_f
35
+ if ( @c1 == c1 )
36
+ # $stderr.puts "Converting #{@c1} #{m} to #{@c2} #{m * @rate} using #{@rate}"
37
+ m * @rate
38
+ else
39
+ # $stderr.puts "Converting #{@c2} #{m} to #{@c1} #{m / @rate} using #{1.0 / @rate}; recip"
40
+ m / @rate
41
+ end
42
+ end
43
+ end
44
+
45
+ # END MODULE
46
+ end
47
+
@@ -0,0 +1,190 @@
1
+ module Currency
2
+
3
+ # Represents an amount of money in a particular currency.
4
+ #
5
+ # NOTE: do we need to store a time, so we can use
6
+ # historical FX rates to convert?
7
+ #
8
+ class Money
9
+ include Comparable
10
+
11
+
12
+ # Construct from a pre-scaled external representation:
13
+ # Float, Integer, String, etc.
14
+ # See #Money_rep(currency) mixin.
15
+ def initialize(x, currency = nil)
16
+ # Xform currency
17
+ currency = Currency.default if currency.nil?
18
+ currency = Currency.get(currency) unless currency.kind_of?(Currency)
19
+
20
+ # Set ivars
21
+ @currency = currency;
22
+ @rep = x.Money_rep(@currency)
23
+
24
+ # Handle conversion of "USD 123.45"
25
+ if @rep.kind_of?(Money)
26
+ @currency = @rep.currency
27
+ @rep = @rep.rep
28
+ end
29
+ end
30
+
31
+ # Compatibility with Money package.
32
+ def self.us_dollar(x)
33
+ self.new(x, :USD)
34
+ end
35
+ def cents
36
+ @rep
37
+ end
38
+
39
+
40
+ # Construct from post-scaled internal representation
41
+ def self.new_rep(r, currency = nil)
42
+ x = self.new(0, currency)
43
+ x.set_rep(r)
44
+ x
45
+ end
46
+ def new_rep(r)
47
+ x = self.class.new(0, @currency)
48
+ x.set_rep(r)
49
+ x
50
+ end
51
+
52
+ # CLIENTS SHOULD NEVER CALL set_rep DIRECTLY!
53
+ def set_rep(r)
54
+ r = r.to_i unless r.kind_of?(Integer)
55
+ @rep = r
56
+ end
57
+
58
+ def Money_rep(currency)
59
+ $stderr.puts "@currency != currency (#{@currency.inspect} != #{currency.inspect}" unless @currency == currency
60
+ @rep
61
+ end
62
+
63
+ def rep
64
+ @rep
65
+ end
66
+
67
+ # Get the money's Currency
68
+ def currency
69
+ @currency
70
+ end
71
+
72
+ # Convert Money to another Currency
73
+ def convert(currency)
74
+ currency = Currency.default if currency.nil?
75
+ currency = Currency.get(currency) unless currency.kind_of?(Currency)
76
+ if @currency == currency
77
+ self
78
+ else
79
+ CurrencyExchange.default.convert(self, currency)
80
+ end
81
+ end
82
+
83
+ # Relational operations on Money values.
84
+ def eql?(x)
85
+ @rep == x.rep && @currency == x.currency
86
+ end
87
+
88
+ def ==(x)
89
+ @rep == x.rep && @currency == x.currency
90
+ end
91
+
92
+ def <=>(x)
93
+ if @currency == x.currency
94
+ @rep <=> x.rep
95
+ else
96
+ @rep <=> convert(@currency).rep
97
+ end
98
+ end
99
+
100
+ # Operations on Money values.
101
+
102
+
103
+ def -@
104
+ # - Money => Money
105
+ new_rep(- @rep)
106
+ end
107
+
108
+ # Right side maybe coerced to Money.
109
+ def +(x)
110
+ # Money + (Number|Money) => Money
111
+ new_rep(@rep + x.Money_rep(@currency))
112
+ end
113
+
114
+ # Right side maybe coerced to Money.
115
+ def -(x)
116
+ # Money - (Number|Money) => Money
117
+ new_rep(@rep - x.Money_rep(@currency))
118
+ end
119
+
120
+ # Right side must be number
121
+ def *(x)
122
+ # Money * Number => Money
123
+ new_rep(@rep * x)
124
+ end
125
+
126
+ # Right side must be number or Money
127
+ def /(x)
128
+ if x.kind_of?(self.class)
129
+ # Money / Money => ratio
130
+ (@rep.to_f) / (x.Money_rep(@currency).to_f)
131
+ else
132
+ # Money / Number => Money
133
+ new_rep(@rep / x)
134
+ end
135
+ end
136
+
137
+ def format(*opt)
138
+ @currency.format(self, *opt)
139
+ end
140
+
141
+ # Coercions
142
+ def to_s(*opt)
143
+ @currency.format(self, *opt)
144
+ end
145
+
146
+ def to_f
147
+ Float(@rep) / @currency.scale
148
+ end
149
+
150
+ def to_i
151
+ @rep / @currency.scale
152
+ end
153
+
154
+ def zero?
155
+ @rep == 0
156
+ end
157
+
158
+ def positive?
159
+ @rep > 0
160
+ end
161
+
162
+ def negative?
163
+ @rep < 0
164
+ end
165
+
166
+ # Implicit currency conversions?
167
+ def Money_rep(currency)
168
+ # Attempt conversion?
169
+ if @currency != currency
170
+ self.convert(currency).rep
171
+ # raise("Incompatible Currency: #{@currency} != #{currency}")
172
+ else
173
+ @rep
174
+ end
175
+ end
176
+
177
+ def inspect(*opts)
178
+ self.format(:with_symbol, :with_currency).inspect
179
+ end
180
+
181
+ # How to alias a method defined in an object superclass in a different class:
182
+ define_method(:inspect_deep, Object.instance_method(:inspect))
183
+ # How call a method defined in a superclass from a method with a different name:
184
+ #def inspect_deep(*opts)
185
+ # self.class.superclass.instance_method(:inspect).bind(self).call
186
+ #end
187
+ end
188
+
189
+ # END MODULE
190
+ end
@@ -0,0 +1,12 @@
1
+ module ActionView
2
+ module Helpers
3
+ module MoneyHelper
4
+ def money_field(object, method, options = {})
5
+ InstanceTag.new(object, method, self).to_input_field_tag("text", options)
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+
12
+ ActionView::Base.load_helper(File.dirname(__FILE__))
data/scripts/gemdoc.rb ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ Gem.manage_gems
5
+ require 'rubygems/user_interaction'
6
+
7
+ include Gem::DefaultUserInteraction
8
+
9
+ $gm = Gem::CommandManager.instance
10
+
11
+ class CaptureSay
12
+ attr_reader :string
13
+ def initialize
14
+ @string = ''
15
+ end
16
+ def say(msg)
17
+ @string << msg << "\n"
18
+ end
19
+ end
20
+
21
+ def pre(cmd, opts)
22
+ puts "<pre>"
23
+ cmd.invoke opts
24
+ puts "</pre>"
25
+ end
26
+
27
+ def table_of_contents
28
+ cs = CaptureSay.new
29
+ use_ui(cs) do
30
+ $gm['help'].invoke 'commands'
31
+ end
32
+ # We're only interested in the lines that actually describe a command.
33
+ out = cs.string.grep(/^\s+(\w+)\s+(.*)$/).join("\n")
34
+ # Add a link to the relevant section in the margin.
35
+ out.gsub(/^\s+(\w+)/) {
36
+ cmd_name = $1
37
+ " [http://currency.rubyforge.org/wiki/wiki.pl?CurrencyReference##{cmd_name} -] #{cmd_name}"
38
+ }
39
+ end
40
+
41
+ while line = gets
42
+ if line =~ /^!/
43
+ cmd, arg = line.split
44
+ case cmd
45
+ when "!usage"
46
+ begin
47
+ cmdobj = $gm[arg]
48
+ pre(cmdobj, "--help")
49
+ rescue NoMethodError
50
+ puts "Usage of command #{arg} failed"
51
+ end
52
+ when "!toc"
53
+ puts table_of_contents()
54
+ when "!toc-link"
55
+ puts "\"Table of Contents\":http://currency.rubyforge.org/read/chapter/10#toc"
56
+ when "!version"
57
+ puts Gem::RubyGemsPackageVersion
58
+ end
59
+ else
60
+ puts line
61
+ end
62
+ end
@@ -0,0 +1,166 @@
1
+ # REAL
2
+ #require File.dirname(__FILE__) + '/../test_helper'
3
+
4
+ require 'test/test_base'
5
+ require 'currency' # For :type => :money
6
+
7
+ module Currency
8
+
9
+ class MoneyTest < TestBase
10
+ def setup
11
+ super
12
+ end
13
+
14
+ ############################################
15
+ # Simple stuff.
16
+ #
17
+
18
+ def test_create
19
+ assert_kind_of Money, m = Money.
20
+ new(1.99)
21
+
22
+ m
23
+ end
24
+
25
+ def test_zero
26
+ m = Money.new(0)
27
+ assert ! m.negative?
28
+ assert m.zero?
29
+ assert ! m.positive?
30
+ m
31
+ end
32
+
33
+ def test_negative
34
+ m = Money.new(-1.00, :USD)
35
+ assert m.negative?
36
+ assert ! m.zero?
37
+ assert ! m.positive?
38
+ m
39
+ end
40
+
41
+ def test_positive
42
+ m = Money.new(2.99, :USD)
43
+ assert ! m.negative?
44
+ assert ! m.zero?
45
+ assert m.positive?
46
+ m
47
+ end
48
+
49
+ def test_relational
50
+ n = test_negative
51
+ z = test_zero
52
+ p = test_positive
53
+
54
+ assert (n < p)
55
+ assert ! (n > p)
56
+ assert ! (p < n)
57
+ assert (p > n)
58
+ assert (p != n)
59
+
60
+ assert (z <= z)
61
+ assert (z >= z)
62
+
63
+ assert (z <= p)
64
+ assert (n <= z)
65
+ assert (z >= n)
66
+
67
+ assert n == n
68
+ assert p == p
69
+
70
+ assert z == test_zero
71
+ end
72
+
73
+ def test_rep
74
+ assert_not_nil m = Money.new(123, :USD)
75
+ assert m.rep == 12300
76
+
77
+ assert_not_nil m = Money.new(123.45, :USD)
78
+ assert m.rep == 12345
79
+
80
+ assert_not_nil m = Money.new("123.456", :USD)
81
+ assert m.rep == 12345
82
+ end
83
+
84
+ def test_convert
85
+ assert_not_nil m = Money.new("123.456", :USD)
86
+ assert m.rep == 12345
87
+
88
+ assert_equal 123, m.to_i
89
+ assert_equal 123.45, m.to_f
90
+ assert_equal "$123.45", m.to_s
91
+ end
92
+
93
+ def test_eql
94
+ assert_not_nil usd = Money.new(123, :USD)
95
+ assert_not_nil cad = Money.new(123, :CAD)
96
+
97
+ assert_equal :USD, usd.currency.code
98
+ assert_equal :CAD, cad.currency.code
99
+
100
+ assert_equal usd.rep, cad.rep
101
+ assert usd.currency != cad.currency
102
+
103
+ assert usd != cad
104
+
105
+ end
106
+
107
+ def test_op
108
+ # Using default load_exchange_rate
109
+ assert_not_nil usd = Money.new(123.45, :USD)
110
+ assert_not_nil cad = Money.new(123.45, :CAD)
111
+
112
+ # - Money => Money
113
+ assert_equal -12345, (- usd).rep
114
+ assert_equal :USD, (- usd).currency.code
115
+
116
+ assert_equal -12345, (- cad).rep
117
+ assert_equal :CAD, (- cad).currency.code
118
+
119
+ # Money + Money => Money
120
+ assert_kind_of Money, m = (usd + usd)
121
+ assert_equal 24690, m.rep
122
+ assert_equal :USD, m.currency.code
123
+
124
+ assert_kind_of Money, m = (usd + cad)
125
+ assert_equal 22889, m.rep
126
+ assert_equal :USD, m.currency.code
127
+
128
+ assert_kind_of Money, m = (cad + usd)
129
+ assert_equal 26798, m.rep
130
+ assert_equal :CAD, m.currency.code
131
+
132
+ # Money - Money => Money
133
+ assert_kind_of Money, m = (usd - usd)
134
+ assert_equal 0, m.rep
135
+ assert_equal :USD, m.currency.code
136
+
137
+ assert_kind_of Money, m = (usd - cad)
138
+ assert_equal 1801, m.rep
139
+ assert_equal :USD, m.currency.code
140
+
141
+ assert_kind_of Money, m = (cad - usd)
142
+ assert_equal -2108, m.rep
143
+ assert_equal :CAD, m.currency.code
144
+
145
+ # Money * Numeric => Money
146
+ assert_kind_of Money, m = (usd * 0.5)
147
+ assert_equal 6172, m.rep
148
+ assert_equal :USD, m.currency.code
149
+
150
+ # Money / Numeric => Money
151
+ assert_kind_of Money, m = usd / 3
152
+ assert_equal 4115, m.rep
153
+ assert_equal :USD, m.currency.code
154
+
155
+ # Money / Money => Numeric
156
+ assert_kind_of Numeric, m = usd / Money.new("41.15", :USD)
157
+ assert_equal_float 3.0, m
158
+
159
+ assert_kind_of Numeric, m = (usd / cad)
160
+ assert_equal_float CurrencyExchangeTest.USD_CAD, m, 0.0001
161
+ end
162
+
163
+ end
164
+
165
+ end # module
166
+