currency 0.2.1 → 0.3.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.
- data/Manifest.txt +2 -0
- data/Rakefile +1 -1
- data/Releases +5 -0
- data/lib/currency/active_record.rb +43 -24
- data/lib/currency/currency_version.rb +1 -1
- data/lib/currency/exception.rb +4 -0
- data/test/ar_column_test.rb +66 -0
- data/test/ar_simple_test.rb +28 -0
- data/test/ar_test_base.rb +29 -24
- metadata +3 -1
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ require 'hoe'
|
|
12
12
|
PKG_Name = 'Currency'
|
13
13
|
PKG_NAME = PKG_Name.gsub(/[a-z][A-Z]/) {|x| "#{x[0,1]}_#{x[1,1]}"}.downcase
|
14
14
|
|
15
|
-
hoe = Hoe.new("currency", '0.
|
15
|
+
hoe = Hoe.new("currency", '0.3.0') do |p|
|
16
16
|
p.author = 'Kurt Stephens'
|
17
17
|
p.description = %{Currency models currencies, monetary values, foreign exchanges.}
|
18
18
|
p.email = "ruby-#{PKG_NAME}@umleta.com"
|
data/Releases
CHANGED
@@ -12,7 +12,7 @@ module Currency
|
|
12
12
|
|
13
13
|
# == ActiveRecord Suppport
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# Support for Money attributes in ActiveRecord::Base subclasses:
|
16
16
|
#
|
17
17
|
# require 'currency'
|
18
18
|
# require 'currency/active_record'
|
@@ -33,90 +33,109 @@ module Currency
|
|
33
33
|
# :currency => :USD
|
34
34
|
#
|
35
35
|
# Defines the Currency to use for storing a normalized Money
|
36
|
-
# value.
|
36
|
+
# value.
|
37
|
+
#
|
38
|
+
# All Money values will be converted to this Currency before
|
39
|
+
# storing in the column. This allows SQL summary operations,
|
37
40
|
# like SUM(), MAX(), AVG(), etc., to produce meaningful results,
|
38
41
|
# regardless of the initial currency specified. If this
|
39
42
|
# option is used, subsequent reads will be in the specified
|
40
43
|
# normalization :currency. Defaults to :USD.
|
41
44
|
#
|
42
|
-
# :
|
45
|
+
# :currency_column => undef
|
43
46
|
#
|
44
|
-
# Defines the name of the CHAR(3) column
|
47
|
+
# Defines the name of the CHAR(3) column used to store and
|
45
48
|
# retrieve the Money's Currency code. If this option is used, each
|
46
49
|
# record may use a different Currency to store the result, such
|
47
50
|
# that SQL summary operations, like SUM(), MAX(), AVG(),
|
48
51
|
# may return meaningless results.
|
49
52
|
#
|
50
|
-
# :
|
53
|
+
# :currency_preferred_column => undef
|
51
54
|
#
|
52
55
|
# Defines the name of a CHAR(3) column used to store and
|
53
56
|
# retrieve the Money's Currency code. This option can be used
|
54
|
-
# with
|
57
|
+
# with normalized Money values to retrieve the Money value
|
55
58
|
# in its original Currency, while
|
56
|
-
# allowing SQL summary operations on
|
59
|
+
# allowing SQL summary operations on the normalized Money values
|
57
60
|
# to still be valid.
|
58
61
|
#
|
59
62
|
def money(attr_name, *opts)
|
60
|
-
opts = Hash
|
63
|
+
opts = Hash[*opts]
|
61
64
|
|
62
65
|
attr_name = attr_name.to_s
|
63
66
|
|
64
67
|
currency = opts[:currency]
|
65
68
|
|
66
|
-
|
67
|
-
if
|
68
|
-
|
69
|
+
currency_column = opts[:currency_column]
|
70
|
+
if currency_column && ! currency_column.kind_of?(String)
|
71
|
+
currency_column = currency_column.to_s
|
72
|
+
currency_column = "#{attr_name}_currency"
|
73
|
+
end
|
74
|
+
if currency_column
|
75
|
+
read_currency = "read_attribute(:#{currency_column.to_s})"
|
76
|
+
write_currency = "write_attribute(:#{currency_column}, #{attr_name}_money.nil? ? nil : #{attr_name}_money.currency.code.to_s)"
|
69
77
|
end
|
70
78
|
|
71
|
-
|
72
|
-
if
|
73
|
-
|
74
|
-
|
79
|
+
currency_preferred_column = opts[:currency_preferred_column]
|
80
|
+
if currency_preferred_column
|
81
|
+
currency_preferred_column = currency_preferred_column.to_s
|
82
|
+
read_preferred_currency = "@#{attr_name} = @#{attr_name}.convert(read_attribute(:#{currency_preferred_column}))"
|
83
|
+
write_preferred_currency = "write_attribute(:#{currency_preferred_column}, @#{attr_name}_money.currency.code)"
|
75
84
|
end
|
76
85
|
|
77
86
|
currency ||= ':USD'
|
78
87
|
|
79
|
-
|
80
|
-
|
88
|
+
read_currency ||= currency
|
89
|
+
|
90
|
+
validate = "# Validation\n"
|
91
|
+
validate << "\nvalidates_numericality_of :#{attr_name}\n" unless opts[:allow_nil]
|
92
|
+
validate << "\nvalidates_format_of :#{currency_column}, :with => /^[A-Z][A-Z][A-Z]$/\n" if currency_column && ! opts[:allow_nil]
|
81
93
|
|
82
94
|
module_eval (x = <<-"end_eval"), __FILE__, __LINE__
|
83
95
|
#{validate}
|
96
|
+
|
84
97
|
def #{attr_name}
|
85
98
|
# $stderr.puts " \#{self.class.name}##{attr_name}"
|
86
99
|
unless @#{attr_name}
|
87
100
|
#{attr_name}_rep = read_attribute(:#{attr_name})
|
88
101
|
unless #{attr_name}_rep.nil?
|
89
|
-
@#{attr_name} = Money.new_rep(#{attr_name}_rep, #{currency})
|
102
|
+
@#{attr_name} = Currency::Money.new_rep(#{attr_name}_rep, #{read_currency} || #{currency})
|
90
103
|
#{read_preferred_currency}
|
91
104
|
end
|
92
105
|
end
|
93
106
|
@#{attr_name}
|
94
107
|
end
|
108
|
+
|
95
109
|
def #{attr_name}=(value)
|
96
110
|
if value.nil?
|
97
111
|
;
|
98
112
|
elsif value.kind_of?(Integer) || value.kind_of?(String) || value.kind_of?(Float)
|
99
|
-
#{attr_name}_money = Money.new(value, #{currency})
|
113
|
+
#{attr_name}_money = Currency::Money.new(value, #{currency})
|
100
114
|
#{write_preferred_currency}
|
101
115
|
elsif value.kind_of?(Money)
|
116
|
+
#{attr_name}_money = value
|
102
117
|
#{write_preferred_currency}
|
103
|
-
#{attr_name}_money =
|
118
|
+
#{write_currency ? write_currency : "#{attr_name}_money = #{attr_name}_money.convert(#{currency})"}
|
104
119
|
else
|
105
|
-
throw
|
120
|
+
throw Currency::Exception::InvalidMoneyValue.new(value)
|
106
121
|
end
|
122
|
+
|
107
123
|
@#{attr_name} = #{attr_name}_money
|
108
|
-
|
124
|
+
|
109
125
|
write_attribute(:#{attr_name}, #{attr_name}_money.nil? ? nil : #{attr_name}_money.rep)
|
126
|
+
|
110
127
|
value
|
111
128
|
end
|
129
|
+
|
112
130
|
def #{attr_name}_before_type_cast
|
113
|
-
#
|
131
|
+
# FIXME: User cannot specify Currency
|
114
132
|
x = #{attr_name}
|
115
133
|
x &&= x.format(:no_symbol, :no_currency, :no_thousands)
|
116
134
|
x
|
117
135
|
end
|
136
|
+
|
118
137
|
end_eval
|
119
|
-
|
138
|
+
$stderr.puts " CODE = #{x}"
|
120
139
|
end
|
121
140
|
end
|
122
141
|
end
|
data/lib/currency/exception.rb
CHANGED
@@ -12,6 +12,10 @@ module Currency
|
|
12
12
|
# Error during string parsing.
|
13
13
|
class InvalidMoneyString < Base
|
14
14
|
end
|
15
|
+
|
16
|
+
# Error during coercion of external Money values.
|
17
|
+
class InvalidMoneyValue < Base
|
18
|
+
end
|
15
19
|
|
16
20
|
# Error in Currency code formeat.
|
17
21
|
class InvalidCurrencyCode < Base
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'test/ar_test_base'
|
2
|
+
require 'currency'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_record'
|
6
|
+
require 'active_record/migration'
|
7
|
+
require 'currency/active_record'
|
8
|
+
|
9
|
+
module Currency
|
10
|
+
|
11
|
+
class ArFieldTest < ArTestBase
|
12
|
+
|
13
|
+
##################################################
|
14
|
+
# Basic CurrenyTest AR::B class
|
15
|
+
#
|
16
|
+
|
17
|
+
TABLE_NAME = 'currency_column_test'
|
18
|
+
|
19
|
+
class CurrencyColumnTestMigration < AR_M
|
20
|
+
def self.up
|
21
|
+
create_table TABLE_NAME.intern do |t|
|
22
|
+
t.column :name, :string
|
23
|
+
t.column :amount, :integer # Money
|
24
|
+
t.column :amount_currency, :string, :size => 3 # Money.currency.code
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.down
|
29
|
+
drop_table TABLE_NAME.intern
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class CurrencyColumnTest < AR_B
|
34
|
+
set_table_name TABLE_NAME
|
35
|
+
money :amount, :currency_column => true
|
36
|
+
end
|
37
|
+
|
38
|
+
##################################################
|
39
|
+
|
40
|
+
def setup
|
41
|
+
@currency_test_migration ||= CurrencyColumnTestMigration
|
42
|
+
@currency_test ||= CurrencyColumnTest
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def teardown
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
##################################################
|
51
|
+
|
52
|
+
|
53
|
+
def test_field
|
54
|
+
insert_records
|
55
|
+
|
56
|
+
assert_not_nil usd = @currency_test.find(@usd.id)
|
57
|
+
assert_equal_currency usd, @usd
|
58
|
+
|
59
|
+
assert_not_nil cad = @currency_test.find(@cad.id)
|
60
|
+
assert_equal_currency cad, @cad
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end # module
|
66
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test/ar_test_base'
|
2
|
+
require 'currency'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_record'
|
6
|
+
require 'active_record/migration'
|
7
|
+
require 'currency/active_record'
|
8
|
+
|
9
|
+
module Currency
|
10
|
+
|
11
|
+
class ArSimpleTest < ArTestBase
|
12
|
+
|
13
|
+
def test_simple
|
14
|
+
insert_records
|
15
|
+
|
16
|
+
assert_not_nil usd = @currency_test.find(@usd.id)
|
17
|
+
assert_equal_currency usd, @usd
|
18
|
+
|
19
|
+
assert_not_nil cad = @currency_test.find(@cad.id)
|
20
|
+
assert_equal_money cad, @cad
|
21
|
+
|
22
|
+
assert_equal cad.amount.currency.code, :USD
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end # module
|
28
|
+
|
data/test/ar_test_base.rb
CHANGED
@@ -21,12 +21,6 @@ class ArTestBase < TestBase
|
|
21
21
|
|
22
22
|
class CurrencyTestMigration < AR_M
|
23
23
|
def self.up
|
24
|
-
begin
|
25
|
-
down
|
26
|
-
rescue Object => e
|
27
|
-
announce("Warning: #{e}")
|
28
|
-
end
|
29
|
-
|
30
24
|
create_table TABLE_NAME.intern do |t|
|
31
25
|
t.column :name, :string
|
32
26
|
t.column :amount, :integer # Money
|
@@ -53,12 +47,14 @@ class ArTestBase < TestBase
|
|
53
47
|
@currency_test_migration ||= CurrencyTestMigration
|
54
48
|
@currency_test ||= CurrencyTest
|
55
49
|
|
50
|
+
# schema_down
|
51
|
+
|
56
52
|
schema_up
|
57
53
|
end
|
58
54
|
|
59
55
|
def teardown
|
60
56
|
super
|
61
|
-
schema_down
|
57
|
+
# schema_down
|
62
58
|
end
|
63
59
|
|
64
60
|
def database_spec
|
@@ -73,12 +69,19 @@ class ArTestBase < TestBase
|
|
73
69
|
end
|
74
70
|
|
75
71
|
def schema_up
|
76
|
-
|
77
|
-
|
72
|
+
begin
|
73
|
+
@currency_test_migration.migrate(:up)
|
74
|
+
rescue Object =>e
|
75
|
+
$stderr.puts "Warning: #{e}"
|
76
|
+
end
|
78
77
|
end
|
79
78
|
|
80
79
|
def schema_down
|
81
|
-
|
80
|
+
begin
|
81
|
+
@currency_test_migration.migrate(:down)
|
82
|
+
rescue Object => e
|
83
|
+
$stderr.puts "Warning: #{e}"
|
84
|
+
end
|
82
85
|
end
|
83
86
|
|
84
87
|
##################################################
|
@@ -86,6 +89,8 @@ class ArTestBase < TestBase
|
|
86
89
|
#
|
87
90
|
|
88
91
|
def insert_records
|
92
|
+
delete_records
|
93
|
+
|
89
94
|
@currency_test.reset_column_information
|
90
95
|
|
91
96
|
@usd = @currency_test.new(:name => '#1: USD', :amount => Money.new("12.34", :USD))
|
@@ -96,12 +101,12 @@ class ArTestBase < TestBase
|
|
96
101
|
end
|
97
102
|
|
98
103
|
def delete_records
|
99
|
-
|
104
|
+
@currency_test.destroy_all
|
100
105
|
end
|
101
106
|
|
102
107
|
##################################################
|
103
108
|
|
104
|
-
def
|
109
|
+
def assert_equal_money(a,b)
|
105
110
|
assert_not_nil a
|
106
111
|
assert_not_nil b
|
107
112
|
# Make sure a and b are not the same object.
|
@@ -116,22 +121,22 @@ class ArTestBase < TestBase
|
|
116
121
|
assert_equal a.amount.convert(b.amount.currency).rep, b.amount.rep
|
117
122
|
end
|
118
123
|
|
119
|
-
def
|
120
|
-
insert_records
|
121
|
-
|
122
|
-
assert_not_nil usd = @currency_test.find(@usd.id)
|
123
|
-
assert_currency_test usd, @usd
|
124
|
+
def assert_equal_currency(a,b)
|
124
125
|
|
125
|
-
|
126
|
-
assert_equal usd.amount.currency, @usd.amount.currency
|
127
|
-
assert_equal usd.amount.currency.code, @usd.amount.currency.code
|
126
|
+
assert_equal_money a, b
|
128
127
|
|
129
|
-
|
130
|
-
|
128
|
+
assert_equal a.amount.rep, b.amount.rep
|
129
|
+
assert_equal a.amount.currency, b.amount.currency
|
130
|
+
assert_equal a.amount.currency.code, b.amount.currency.code
|
131
131
|
|
132
|
-
|
132
|
+
end
|
133
133
|
|
134
|
-
|
134
|
+
##################################################
|
135
|
+
#
|
136
|
+
#
|
137
|
+
|
138
|
+
def test_dummy
|
139
|
+
insert_records
|
135
140
|
end
|
136
141
|
|
137
142
|
end
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: currency
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
6
|
+
version: 0.3.0
|
7
7
|
date: 2006-10-31 00:00:00 -05:00
|
8
8
|
summary: Currency models currencies, monetary values, foreign exchanges.
|
9
9
|
require_paths:
|
@@ -52,6 +52,8 @@ files:
|
|
52
52
|
- README
|
53
53
|
- Releases
|
54
54
|
- test/ar_test_base.rb
|
55
|
+
- test/ar_simple_test.rb
|
56
|
+
- test/ar_column_test.rb
|
55
57
|
- test/money_test.rb
|
56
58
|
- test/test_base.rb
|
57
59
|
- test/xe_test.rb
|