currency 0.1.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.
@@ -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
+