mumboe-currency 0.5
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.
- data/bin/currency_historical_rate_load +105 -0
- data/examples/ex1.rb +13 -0
- data/examples/xe1.rb +20 -0
- data/lib/currency/active_record.rb +265 -0
- data/lib/currency/config.rb +91 -0
- data/lib/currency/core_extensions.rb +41 -0
- data/lib/currency/currency/factory.rb +228 -0
- data/lib/currency/currency.rb +175 -0
- data/lib/currency/currency_version.rb +6 -0
- data/lib/currency/exception.rb +119 -0
- data/lib/currency/exchange/rate/deriver.rb +157 -0
- data/lib/currency/exchange/rate/source/base.rb +166 -0
- data/lib/currency/exchange/rate/source/failover.rb +63 -0
- data/lib/currency/exchange/rate/source/federal_reserve.rb +160 -0
- data/lib/currency/exchange/rate/source/historical/rate.rb +184 -0
- data/lib/currency/exchange/rate/source/historical/rate_loader.rb +186 -0
- data/lib/currency/exchange/rate/source/historical/writer.rb +220 -0
- data/lib/currency/exchange/rate/source/historical.rb +79 -0
- data/lib/currency/exchange/rate/source/new_york_fed.rb +127 -0
- data/lib/currency/exchange/rate/source/provider.rb +120 -0
- data/lib/currency/exchange/rate/source/test.rb +50 -0
- data/lib/currency/exchange/rate/source/the_financials.rb +191 -0
- data/lib/currency/exchange/rate/source/timed_cache.rb +198 -0
- data/lib/currency/exchange/rate/source/xe.rb +165 -0
- data/lib/currency/exchange/rate/source.rb +89 -0
- data/lib/currency/exchange/rate.rb +214 -0
- data/lib/currency/exchange/time_quantitizer.rb +111 -0
- data/lib/currency/exchange.rb +50 -0
- data/lib/currency/formatter.rb +290 -0
- data/lib/currency/macro.rb +321 -0
- data/lib/currency/money.rb +295 -0
- data/lib/currency/money_helper.rb +13 -0
- data/lib/currency/parser.rb +151 -0
- data/lib/currency.rb +143 -0
- data/test/string_test.rb +54 -0
- data/test/test_base.rb +44 -0
- metadata +90 -0
@@ -0,0 +1,295 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
# See LICENSE.txt for details.
|
3
|
+
|
4
|
+
#
|
5
|
+
# Represents an amount of money in a particular currency.
|
6
|
+
#
|
7
|
+
# A Money object stores its value using a scaled Integer representation
|
8
|
+
# and a Currency object.
|
9
|
+
#
|
10
|
+
# A Money object also has a time, which is used in conversions
|
11
|
+
# against historical exchange rates.
|
12
|
+
#
|
13
|
+
class Currency::Money
|
14
|
+
include Comparable
|
15
|
+
|
16
|
+
@@default_time = nil
|
17
|
+
def self.default_time
|
18
|
+
@@default_time
|
19
|
+
end
|
20
|
+
def self.default_time=(x)
|
21
|
+
@@default_time = x
|
22
|
+
end
|
23
|
+
|
24
|
+
@@empty_hash = { }
|
25
|
+
@@empty_hash.freeze
|
26
|
+
|
27
|
+
#
|
28
|
+
# DO NOT CALL THIS DIRECTLY:
|
29
|
+
#
|
30
|
+
# See Currency.Money() function.
|
31
|
+
#
|
32
|
+
# Construct a Money value object
|
33
|
+
# from a pre-scaled external representation:
|
34
|
+
# where x is a Float, Integer, String, etc.
|
35
|
+
#
|
36
|
+
# If a currency is not specified, Currency.default is used.
|
37
|
+
#
|
38
|
+
# x.Money_rep(currency)
|
39
|
+
#
|
40
|
+
# is invoked to coerce x into a Money representation value.
|
41
|
+
#
|
42
|
+
# For example:
|
43
|
+
#
|
44
|
+
# 123.Money_rep(:USD) => 12300
|
45
|
+
#
|
46
|
+
# Because the USD Currency object has a #scale of 100
|
47
|
+
#
|
48
|
+
# See #Money_rep(currency) mixin.
|
49
|
+
#
|
50
|
+
def initialize(x, currency = nil, time = nil)
|
51
|
+
opts ||= @@empty_hash
|
52
|
+
|
53
|
+
# Set ivars.
|
54
|
+
currency = ::Currency::Currency.get(currency)
|
55
|
+
@currency = currency
|
56
|
+
@time = time || ::Currency::Money.default_time
|
57
|
+
@time = ::Currency::Money.now if @time == :now
|
58
|
+
if x.kind_of?(String)
|
59
|
+
if currency
|
60
|
+
m = currency.parser_or_default.parse(x, :currency => currency)
|
61
|
+
else
|
62
|
+
m = ::Currency::Parser.default.parse(x)
|
63
|
+
end
|
64
|
+
@currency = m.currency unless @currency
|
65
|
+
@time = m.time if m.time
|
66
|
+
@rep = m.rep
|
67
|
+
else
|
68
|
+
@currency = ::Currency::Currency.default unless @currency
|
69
|
+
@rep = x.Money_rep(@currency)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns a Time.new
|
75
|
+
# Can be modifed for special purposes.
|
76
|
+
def self.now
|
77
|
+
Time.new
|
78
|
+
end
|
79
|
+
|
80
|
+
# Compatibility with Money package.
|
81
|
+
def self.us_dollar(x)
|
82
|
+
self.new(x, :USD)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Compatibility with Money package.
|
87
|
+
def cents
|
88
|
+
@rep
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Construct from post-scaled internal representation.
|
93
|
+
def self.new_rep(r, currency = nil, time = nil)
|
94
|
+
x = self.new(0, currency, time)
|
95
|
+
x.set_rep(r)
|
96
|
+
x
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Construct from post-scaled internal representation.
|
101
|
+
# using the same currency.
|
102
|
+
#
|
103
|
+
# x = Currency.Money("1.98", :USD)
|
104
|
+
# x.new_rep(123) => USD $1.23
|
105
|
+
#
|
106
|
+
# time defaults to self.time.
|
107
|
+
def new_rep(r, time = nil)
|
108
|
+
time ||= @time
|
109
|
+
x = self.class.new(0, @currency, time)
|
110
|
+
x.set_rep(r)
|
111
|
+
x
|
112
|
+
end
|
113
|
+
|
114
|
+
# Do not call this method directly.
|
115
|
+
# CLIENTS SHOULD NEVER CALL set_rep DIRECTLY.
|
116
|
+
# You have been warned in ALL CAPS.
|
117
|
+
def set_rep(r) # :nodoc:
|
118
|
+
r = r.to_i unless r.kind_of?(Integer)
|
119
|
+
@rep = r
|
120
|
+
end
|
121
|
+
|
122
|
+
# Do not call this method directly.
|
123
|
+
# CLIENTS SHOULD NEVER CALL set_time DIRECTLY.
|
124
|
+
# You have been warned in ALL CAPS.
|
125
|
+
def set_time(time) # :nodoc:
|
126
|
+
@time = time
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the Money representation (usually an Integer).
|
130
|
+
def rep
|
131
|
+
@rep
|
132
|
+
end
|
133
|
+
|
134
|
+
# Get the Money's Currency.
|
135
|
+
def currency
|
136
|
+
@currency
|
137
|
+
end
|
138
|
+
|
139
|
+
# Get the Money's time.
|
140
|
+
def time
|
141
|
+
@time
|
142
|
+
end
|
143
|
+
|
144
|
+
# Convert Money to another Currency.
|
145
|
+
# currency can be a Symbol or a Currency object.
|
146
|
+
# If currency is nil, the Currency.default is used.
|
147
|
+
def convert(currency, time = nil)
|
148
|
+
currency = ::Currency::Currency.default if currency.nil?
|
149
|
+
currency = ::Currency::Currency.get(currency) unless currency.kind_of?(Currency)
|
150
|
+
if @currency == currency
|
151
|
+
self
|
152
|
+
else
|
153
|
+
time = self.time if time == :money
|
154
|
+
::Currency::Exchange::Rate::Source.current.convert(self, currency, time)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
# Hash for hash table: both value and currency.
|
160
|
+
# See #eql? below.
|
161
|
+
def hash
|
162
|
+
@rep.hash ^ @currency.hash
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
# True if money values have the same value and currency.
|
167
|
+
def eql?(x)
|
168
|
+
self.class == x.class &&
|
169
|
+
@rep == x.rep &&
|
170
|
+
@currency == x.currency
|
171
|
+
end
|
172
|
+
|
173
|
+
# True if money values have the same value and currency.
|
174
|
+
def ==(x)
|
175
|
+
self.class == x.class &&
|
176
|
+
@rep == x.rep &&
|
177
|
+
@currency == x.currency
|
178
|
+
end
|
179
|
+
|
180
|
+
# Compares Money values.
|
181
|
+
# Will convert x to self.currency before comparision.
|
182
|
+
def <=>(x)
|
183
|
+
if @currency == x.currency
|
184
|
+
@rep <=> x.rep
|
185
|
+
else
|
186
|
+
@rep <=> convert(@currency, @time).rep
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
# - Money => Money
|
192
|
+
#
|
193
|
+
# Negates a Money value.
|
194
|
+
def -@
|
195
|
+
new_rep(- @rep)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Money + (Money | Number) => Money
|
199
|
+
#
|
200
|
+
# Right side may be coerced to left side's Currency.
|
201
|
+
def +(x)
|
202
|
+
new_rep(@rep + x.Money_rep(@currency))
|
203
|
+
end
|
204
|
+
|
205
|
+
# Money - (Money | Number) => Money
|
206
|
+
#
|
207
|
+
# Right side may be coerced to left side's Currency.
|
208
|
+
def -(x)
|
209
|
+
new_rep(@rep - x.Money_rep(@currency))
|
210
|
+
end
|
211
|
+
|
212
|
+
# Money * Number => Money
|
213
|
+
#
|
214
|
+
# Right side must be Number.
|
215
|
+
def *(x)
|
216
|
+
new_rep(@rep * x)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Money / Money => Float (ratio)
|
220
|
+
# Money / Number => Money
|
221
|
+
#
|
222
|
+
# Right side must be Money or Number.
|
223
|
+
# Right side Integers are not coerced to Float before
|
224
|
+
# division.
|
225
|
+
def /(x)
|
226
|
+
if x.kind_of?(self.class)
|
227
|
+
(@rep.to_f) / (x.Money_rep(@currency).to_f)
|
228
|
+
else
|
229
|
+
new_rep(@rep / x)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Formats the Money value as a String using the Currency's Formatter.
|
234
|
+
def format(*opt)
|
235
|
+
@currency.format(self, *opt)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Formats the Money value as a String.
|
239
|
+
def to_s(*opt)
|
240
|
+
@currency.format(self, *opt)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Coerces the Money's value to a Float.
|
244
|
+
# May cause loss of precision.
|
245
|
+
def to_f
|
246
|
+
Float(@rep) / @currency.scale
|
247
|
+
end
|
248
|
+
|
249
|
+
# Coerces the Money's value to an Integer.
|
250
|
+
# May cause loss of precision.
|
251
|
+
def to_i
|
252
|
+
@rep / @currency.scale
|
253
|
+
end
|
254
|
+
|
255
|
+
# True if the Money's value is zero.
|
256
|
+
def zero?
|
257
|
+
@rep == 0
|
258
|
+
end
|
259
|
+
|
260
|
+
# True if the Money's value is greater than zero.
|
261
|
+
def positive?
|
262
|
+
@rep > 0
|
263
|
+
end
|
264
|
+
|
265
|
+
# True if the Money's value is less than zero.
|
266
|
+
def negative?
|
267
|
+
@rep < 0
|
268
|
+
end
|
269
|
+
|
270
|
+
# Returns the Money's value representation in another currency.
|
271
|
+
def Money_rep(currency, time = nil)
|
272
|
+
# Attempt conversion?
|
273
|
+
if @currency != currency || (time && @time != time)
|
274
|
+
self.convert(currency, time).rep
|
275
|
+
# raise ::Currency::Exception::Generic, "Incompatible Currency: #{@currency} != #{currency}"
|
276
|
+
else
|
277
|
+
@rep
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Basic inspection, with symbol, currency code and time.
|
282
|
+
# The standard #inspect method is available as #inspect_deep.
|
283
|
+
def inspect(*opts)
|
284
|
+
self.format(:symbol => true, :code => true, :time => true)
|
285
|
+
end
|
286
|
+
|
287
|
+
# How to alias a method defined in an object superclass in a different class:
|
288
|
+
define_method(:inspect_deep, Object.instance_method(:inspect))
|
289
|
+
# How call a method defined in a superclass from a method with a different name:
|
290
|
+
# def inspect_deep(*opts)
|
291
|
+
# self.class.superclass.instance_method(:inspect).bind(self).call
|
292
|
+
# end
|
293
|
+
|
294
|
+
end # class
|
295
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
# See LICENSE.txt for details.
|
3
|
+
|
4
|
+
module ActionView::Helpers::MoneyHelper
|
5
|
+
# Creates a suitable HTML element for a Money value field.
|
6
|
+
def money_field(object, method, options = {})
|
7
|
+
InstanceTag.new(object, method, self).to_input_field_tag("text", options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
ActionView::Base.load_helper(File.dirname(__FILE__))
|
13
|
+
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
# See LICENSE.txt for details.
|
3
|
+
|
4
|
+
|
5
|
+
require 'rss/rss' # Time#xmlschema
|
6
|
+
|
7
|
+
|
8
|
+
# This class parses a Money value from a String.
|
9
|
+
# Each Currency has a default Parser.
|
10
|
+
class Currency::Parser
|
11
|
+
|
12
|
+
SYMBOLS_FOR_PATTERN = Currency::Currency::Factory::UNIQUE_SYMBOLS.collect {|symbol| symbol.split("").collect {|c| c =~ /[a-z]/i ? c : '\\'+c }.join }.join("|")
|
13
|
+
VALID_MONEY_PATTERN = /^(([a-zA-z][a-zA-z][a-zA-z])|(#{SYMBOLS_FOR_PATTERN}))?\s*([\-\+\d,\.]+)\s*(([a-zA-z][a-zA-z][a-zA-z])|(#{SYMBOLS_FOR_PATTERN}))?(\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z)?$/
|
14
|
+
|
15
|
+
# The default Currency to use if no Currency is specified.
|
16
|
+
attr_accessor :currency
|
17
|
+
|
18
|
+
# If true and a parsed string contains a ISO currency code
|
19
|
+
# that is not the same as currency,
|
20
|
+
# #parse() will raise IncompatibleCurrency.
|
21
|
+
# Defaults to false.
|
22
|
+
attr_accessor :enforce_currency
|
23
|
+
|
24
|
+
# The default Time to use if no Time is specified in the string.
|
25
|
+
# If :now, time is set to Time.new.
|
26
|
+
attr_accessor :time
|
27
|
+
|
28
|
+
@@default = nil
|
29
|
+
# Get the default Formatter.
|
30
|
+
def self.default
|
31
|
+
@@default ||=
|
32
|
+
self.new
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Set the default Formatter.
|
37
|
+
def self.default=(x)
|
38
|
+
@@default = x
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def initialize(opt = { })
|
43
|
+
@time =
|
44
|
+
@enforce_currency =
|
45
|
+
@currency =
|
46
|
+
nil
|
47
|
+
opt.each_pair{ | k, v | self.send("#{k}=", v) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def _parse(str) # :nodoc:
|
51
|
+
x = str
|
52
|
+
md = nil
|
53
|
+
time = nil
|
54
|
+
|
55
|
+
convert_currency = nil
|
56
|
+
md = VALID_MONEY_PATTERN.match(x)
|
57
|
+
if md && !@currency
|
58
|
+
symbol = md[3] || md[7]
|
59
|
+
code = md[2] || md[6]
|
60
|
+
curr = Currency::Currency.get(code ? code.upcase : nil) || (symbol ? Currency::Currency::Factory.get_currency_from_symbol(symbol) : Currency::Currency.default)
|
61
|
+
x = md[4]
|
62
|
+
time = Time.xmlschema(md[8]) if md[8]
|
63
|
+
time ||= @time
|
64
|
+
time = Time.new if time == :now
|
65
|
+
currency = curr
|
66
|
+
else
|
67
|
+
currency = @currency || ::Currency::Currency.default
|
68
|
+
currency = ::Currency::Currency.get(currency)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Remove placeholders and symbol.
|
72
|
+
x.gsub!(/[, ]/, '')
|
73
|
+
symbol = currency.symbol if symbol.nil?
|
74
|
+
x.gsub!(symbol, '') if symbol
|
75
|
+
|
76
|
+
# Match: whole Currency value.
|
77
|
+
if md = /^([-+]?\d+)\.?$/.match(x)
|
78
|
+
x = ::Currency::Money.new_rep(md[1].to_i * currency.scale, currency, @time)
|
79
|
+
|
80
|
+
# Match: fractional Currency value.
|
81
|
+
elsif md = /^([-+]?)(\d*)\.(\d+)$/.match(x)
|
82
|
+
sign = md[1]
|
83
|
+
whole = md[2]
|
84
|
+
part = md[3]
|
85
|
+
|
86
|
+
if part.length != currency.scale
|
87
|
+
currency.scale = 10**part.length if part.length > 1
|
88
|
+
|
89
|
+
# Pad decimal places with additional '0'
|
90
|
+
while part.length < currency.scale_exp
|
91
|
+
part << '0'
|
92
|
+
end
|
93
|
+
|
94
|
+
# Truncate to Currency's decimal size.
|
95
|
+
part = part[0 ... currency.scale_exp]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Put the string back together:
|
99
|
+
whole = sign + whole + part
|
100
|
+
|
101
|
+
x = whole.to_i
|
102
|
+
x = ::Currency::Money.new_rep(x, currency, time)
|
103
|
+
else
|
104
|
+
raise ::Currency::Exception::InvalidMoneyString,
|
105
|
+
[
|
106
|
+
"#{str.inspect} #{currency} #{x.inspect}",
|
107
|
+
:string, str,
|
108
|
+
:currency, currency,
|
109
|
+
:state, x,
|
110
|
+
]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Do conversion.
|
114
|
+
if convert_currency
|
115
|
+
x = x.convert(convert_currency)
|
116
|
+
end
|
117
|
+
|
118
|
+
x
|
119
|
+
end
|
120
|
+
|
121
|
+
@@empty_hash = { }
|
122
|
+
@@empty_hash.freeze
|
123
|
+
|
124
|
+
# Parse a Money string in this Currency.
|
125
|
+
#
|
126
|
+
# "123.45".money # Using default Currency.
|
127
|
+
# => USD $123.45
|
128
|
+
#
|
129
|
+
# "$123.45 USD".money # Explicit Currency.
|
130
|
+
# => USD $123.45
|
131
|
+
#
|
132
|
+
# "CAD 123.45".money
|
133
|
+
# => CAD $123.45
|
134
|
+
#
|
135
|
+
# "123.45 CAD".money(:USD) # Incompatible explicit Currency.
|
136
|
+
# !!! "123.45 CAD" USD (Currency::Exception::IncompatibleCurrency)
|
137
|
+
#
|
138
|
+
def parse(str, opt = @@empty_hash)
|
139
|
+
prs = self
|
140
|
+
|
141
|
+
unless opt.empty?
|
142
|
+
prs = prs.clone
|
143
|
+
opt.each_pair{ | k, v | prs.send("#{k}=", v) }
|
144
|
+
end
|
145
|
+
|
146
|
+
prs._parse(str)
|
147
|
+
end
|
148
|
+
|
149
|
+
end # class
|
150
|
+
|
151
|
+
|
data/lib/currency.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
#
|
3
|
+
# See LICENSE.txt for details.
|
4
|
+
#
|
5
|
+
# = Currency
|
6
|
+
#
|
7
|
+
# The Currency package provides an object-oriented model of:
|
8
|
+
#
|
9
|
+
# * currencies
|
10
|
+
# * exchanges
|
11
|
+
# * exchange rates
|
12
|
+
# * exchange rate sources
|
13
|
+
# * monetary values
|
14
|
+
#
|
15
|
+
# The core classes are:
|
16
|
+
#
|
17
|
+
# * Currency::Money - uses a scaled integer representation of a monetary value and performs accurate conversions to and from string values.
|
18
|
+
# * Currency::Currency - provides an object-oriented representation of a currency.
|
19
|
+
# * Currency::Exchange::Base - the base class for a currency exchange rate provider.
|
20
|
+
# * Currency::Exchange::Rate::Source::Base - the base class for an on-demand currency exchange rate data provider.
|
21
|
+
# * Currency::Exchange::Rate::Source::Provider - the base class for a bulk exchange rate data provider.
|
22
|
+
# * Currency::Exchange::Rate - represents a exchange rate between two currencies.
|
23
|
+
#
|
24
|
+
#
|
25
|
+
# The example below uses Currency::Exchange::Xe to automatically get
|
26
|
+
# exchange rates from http://xe.com/ :
|
27
|
+
#
|
28
|
+
# require 'currency'
|
29
|
+
# require 'currency/exchange/rate/deriver'
|
30
|
+
# require 'currency/exchange/rate/source/xe'
|
31
|
+
# require 'currency/exchange/rate/source/timed_cache'
|
32
|
+
#
|
33
|
+
# # Rate source initialization
|
34
|
+
# provider = Currency::Exchange::Rate::Source::Xe.new
|
35
|
+
# deriver = Currency::Exchange::Rate::Deriver.new(:source => provider)
|
36
|
+
# cache = Currency::Exchange::Rate::Source::TimedCache.new(:source => deriver)
|
37
|
+
# Currency::Exchange::Rate::Source.default = cache
|
38
|
+
#
|
39
|
+
# usd = Currency::Money('6.78', :USD)
|
40
|
+
# puts "usd = #{usd.format}"
|
41
|
+
# cad = usd.convert(:CAD)
|
42
|
+
# puts "cad = #{cad.format}"
|
43
|
+
#
|
44
|
+
# == ActiveRecord Suppport
|
45
|
+
#
|
46
|
+
# This package also contains ActiveRecord support for money values:
|
47
|
+
#
|
48
|
+
# require 'currency'
|
49
|
+
# require 'currency/active_record'
|
50
|
+
#
|
51
|
+
# class Entry < ActiveRecord::Base
|
52
|
+
# money :amount
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# In the example above, the entries.amount database column is an INTEGER that represents US cents.
|
56
|
+
# The currency code of the money value can be stored in an additional database column or a default currency can be used.
|
57
|
+
#
|
58
|
+
# == Recent Enhancements
|
59
|
+
#
|
60
|
+
# === Storage and retrival of historical exchange rates
|
61
|
+
#
|
62
|
+
# * See Currency::Exchange::Rate::Source::Historical
|
63
|
+
# * See Currency::Exchange::Rate::Source::Historical::Writer
|
64
|
+
#
|
65
|
+
# === Automatic derivation of rates from base rates.
|
66
|
+
#
|
67
|
+
# * See Currency::Exchange::Rate::Deriver
|
68
|
+
#
|
69
|
+
# === Rate caching
|
70
|
+
#
|
71
|
+
# * See Currency::Exchange::Rate::Source::TimedCache
|
72
|
+
#
|
73
|
+
# === Rate Providers
|
74
|
+
#
|
75
|
+
# * See Currency::Exchange::Rate::Source::Xe
|
76
|
+
# * See Currency::Exchange::Rate::Source::NewYorkFed
|
77
|
+
# * See Currency::Exchange::Rate::Source::TheFinancials
|
78
|
+
#
|
79
|
+
# === Customizable formatting and parsing
|
80
|
+
#
|
81
|
+
# * See Currency::Formatter
|
82
|
+
# * See Currency::Parser
|
83
|
+
#
|
84
|
+
# == Future Enhancements
|
85
|
+
#
|
86
|
+
# * Support for inflationary rates within a currency, e.g. $10 USD in the year 1955 converted to 2006 USD.
|
87
|
+
#
|
88
|
+
# == SVN Repo
|
89
|
+
#
|
90
|
+
# svn checkout svn://rubyforge.org/var/svn/currency/currency/trunk
|
91
|
+
#
|
92
|
+
# == Examples
|
93
|
+
#
|
94
|
+
# See the examples/ and test/ directorys
|
95
|
+
#
|
96
|
+
# == Author
|
97
|
+
#
|
98
|
+
# Kurt Stephens http://kurtstephens.com
|
99
|
+
#
|
100
|
+
# == Support
|
101
|
+
#
|
102
|
+
# http://rubyforge.org/forum/forum.php?forum_id=7643
|
103
|
+
#
|
104
|
+
# == Copyright
|
105
|
+
#
|
106
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
107
|
+
#
|
108
|
+
# See LICENSE.txt for details.
|
109
|
+
#
|
110
|
+
module Currency
|
111
|
+
# Use this function instead of Money#new:
|
112
|
+
#
|
113
|
+
# Currency::Money("12.34", :CAD)
|
114
|
+
#
|
115
|
+
# Do not do this:
|
116
|
+
#
|
117
|
+
# Currency::Money.new("12.34", :CAD)
|
118
|
+
#
|
119
|
+
# See Money#new.
|
120
|
+
def self.Money(*opts)
|
121
|
+
Money.new(*opts)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__))) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
126
|
+
|
127
|
+
require 'currency/currency_version'
|
128
|
+
require 'currency/config'
|
129
|
+
require 'currency/exception'
|
130
|
+
require 'currency/money'
|
131
|
+
require 'currency/currency'
|
132
|
+
require 'currency/currency/factory'
|
133
|
+
require 'currency/money'
|
134
|
+
require 'currency/formatter'
|
135
|
+
require 'currency/parser'
|
136
|
+
require 'currency/exchange'
|
137
|
+
require 'currency/exchange/rate'
|
138
|
+
require 'currency/exchange/rate/deriver'
|
139
|
+
require 'currency/exchange/rate/source'
|
140
|
+
require 'currency/exchange/rate/source/test'
|
141
|
+
require 'currency/exchange/time_quantitizer'
|
142
|
+
require 'currency/core_extensions'
|
143
|
+
|
data/test/string_test.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# various tests to verify latest revisions
|
2
|
+
require "test/test_base"
|
3
|
+
require 'currency'
|
4
|
+
|
5
|
+
module Currency
|
6
|
+
|
7
|
+
class StringTest < TestBase
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_beginning_with_code
|
13
|
+
assert_equal "¥424,535,953.00", "CNY424,535,953".money.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_beginning_with_symbol
|
17
|
+
assert_equal "₡42,525,924.87", "₡ 42525924.87".money.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_ending_with_code
|
21
|
+
assert_equal "₱324,402,482.00", "324402482 CUP".money.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_ending_with_symbol
|
25
|
+
assert_equal "IRR 2,445.00", "2445﷼".money.to_s(:symbol => false, :code => true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_unknown_code
|
29
|
+
assert_equal "CFA 15,000.00", "15000CFA".money.to_s(:symbol => true, :code => true)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_invalid_symbol
|
33
|
+
assert_raise ::Currency::Exception::InvalidMoneyString do
|
34
|
+
"Σ424,000".money
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_invalid_format
|
39
|
+
assert_raise ::Currency::Exception::InvalidMoneyString do
|
40
|
+
"17 dollars".money
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_does_not_truncate_precision
|
45
|
+
assert_equal "$425.002", "425.002".money.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_pads_precision
|
49
|
+
assert_equal "$0.50", "0.5".money.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/test/test_base.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
# See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# Base Test class
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'currency'
|
8
|
+
require 'currency/exchange/rate/source/test'
|
9
|
+
|
10
|
+
module Currency
|
11
|
+
|
12
|
+
class TestBase < Test::Unit::TestCase
|
13
|
+
def setup
|
14
|
+
super
|
15
|
+
@rate_source ||= get_rate_source
|
16
|
+
Exchange::Rate::Source.default = @rate_source
|
17
|
+
|
18
|
+
# Force non-historical money values.
|
19
|
+
Money.default_time = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def get_rate_source
|
24
|
+
source = Exchange::Rate::Source::Test.instance
|
25
|
+
deriver = Exchange::Rate::Deriver.new(:source => source)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Avoid "No test were specified" error.
|
29
|
+
def test_foo
|
30
|
+
assert true
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Helpers.
|
35
|
+
def assert_equal_float(x, y, eps = 1.0e-8)
|
36
|
+
d = (x * eps).abs
|
37
|
+
assert((x - d) <= y)
|
38
|
+
assert(y <= (x + d))
|
39
|
+
end
|
40
|
+
|
41
|
+
end # class
|
42
|
+
|
43
|
+
end # module
|
44
|
+
|