money 1.7.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +2 -1
- data/README.rdoc +97 -0
- data/Rakefile +18 -0
- data/lib/money.rb +4 -8
- data/lib/money/core_extensions.rb +22 -10
- data/lib/money/errors.rb +4 -0
- data/lib/money/money.rb +83 -89
- data/lib/money/variable_exchange_bank.rb +72 -0
- data/money.gemspec +23 -0
- data/test/core_extensions_spec.rb +33 -0
- data/test/exchange_bank_spec.rb +45 -0
- data/test/money_spec.rb +124 -0
- metadata +56 -40
- data/README +0 -75
- data/lib/bank/no_exchange_bank.rb +0 -9
- data/lib/bank/variable_exchange_bank.rb +0 -30
- data/lib/support/cattr_accessor.rb +0 -57
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
Copyright (c) 2005 Tobias Lutke
|
2
|
+
Copyright (c) 2008 Phusion
|
2
3
|
|
3
4
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
5
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +18,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
18
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
19
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
20
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
= Introduction
|
2
|
+
|
3
|
+
This library aids one in handling money and different currencies. Features:
|
4
|
+
|
5
|
+
- Provides a Money class which encapsulates all information about an certain
|
6
|
+
amount of money, such as its value and its currency.
|
7
|
+
- Represents monetary values as integers, in cents. This avoids floating point
|
8
|
+
rounding errors.
|
9
|
+
- Provides APIs for exchanging money from one currency to another.
|
10
|
+
- Has the ability to parse a money string into a Money object.
|
11
|
+
|
12
|
+
Resources:
|
13
|
+
|
14
|
+
- Website: http://money.rubyforge.org
|
15
|
+
- RDoc API: http://money.rubyforge.org
|
16
|
+
- Git repository: http://github.com/FooBarWidget/money/tree/master
|
17
|
+
|
18
|
+
== Download
|
19
|
+
|
20
|
+
Install stable releases with the following command:
|
21
|
+
|
22
|
+
gem install money
|
23
|
+
|
24
|
+
The development version (hosted on Github) can be installed with:
|
25
|
+
|
26
|
+
gem sources -a http://gems.github.com
|
27
|
+
gem install FooBarWidget-money
|
28
|
+
|
29
|
+
== Usage
|
30
|
+
|
31
|
+
=== Synopsis
|
32
|
+
|
33
|
+
require 'money'
|
34
|
+
|
35
|
+
# 10.00 USD
|
36
|
+
money = Money.new(1000, "USD")
|
37
|
+
money.cents # => 1000
|
38
|
+
money.currency # => "USD"
|
39
|
+
|
40
|
+
Money.new(1000, "USD") == Money.new(1000, "USD") # => true
|
41
|
+
Money.new(1000, "USD") == Money.new(100, "USD") # => false
|
42
|
+
Money.new(1000, "USD") == Money.new(1000, "EUR") # => false
|
43
|
+
|
44
|
+
=== Currency Exchange
|
45
|
+
|
46
|
+
Exchanging money is performed through an exchange bank object. The default
|
47
|
+
exchange bank object requires one to manually specify the exchange rate. Here's
|
48
|
+
an example of how it works:
|
49
|
+
|
50
|
+
Money.add_rate("USD", "CAD", 1.24515)
|
51
|
+
Money.add_rate("CAD", "USD", 0.803115)
|
52
|
+
|
53
|
+
Money.us_dollar(100).exchange_to("CAD") # => Money.new(124, "CAD")
|
54
|
+
Money.ca_dollar(100).exchange_to("USD") # => Money.new(80, "USD")
|
55
|
+
|
56
|
+
Comparison and arithmetic operations work as expected:
|
57
|
+
|
58
|
+
Money.new(1000, "USD") <=> Money.new(900, "USD") # => 1; 9.00 USD is smaller
|
59
|
+
Money.new(1000, "EUR") + Money.new(10, "EUR") == Money.new(1010, "EUR")
|
60
|
+
|
61
|
+
Money.add_rate("USD", "EUR", 0.5)
|
62
|
+
Money.new(1000, "EUR") + Money.new(1000, "USD") == Money.new(1500, "EUR")
|
63
|
+
|
64
|
+
There is nothing stopping you from creating bank objects which scrapes
|
65
|
+
www.xe.com for the current rates or just returns <tt>rand(2)</tt>:
|
66
|
+
|
67
|
+
Money.default_bank = ExchangeBankWhichScrapesXeDotCom.new
|
68
|
+
|
69
|
+
=== Ruby on Rails
|
70
|
+
|
71
|
+
Use the +compose_of+ helper to let Active Record deal with embedding the money
|
72
|
+
object in your models. The following example requires a +cents+ and a +currency+
|
73
|
+
field.
|
74
|
+
|
75
|
+
class ProductUnit < ActiveRecord::Base
|
76
|
+
belongs_to :product
|
77
|
+
composed_of :price, :class_name => "Money", :mapping => [%w(cents cents), %w(currency currency)]
|
78
|
+
|
79
|
+
private
|
80
|
+
validate :cents_not_zero
|
81
|
+
|
82
|
+
def cents_not_zero
|
83
|
+
errors.add("cents", "cannot be zero or less") unless cents > 0
|
84
|
+
end
|
85
|
+
|
86
|
+
validates_presence_of :sku, :currency
|
87
|
+
validates_uniqueness_of :sku
|
88
|
+
end
|
89
|
+
|
90
|
+
=== Default Currency
|
91
|
+
|
92
|
+
By default Money defaults to USD as its currency. This can be overwritten using
|
93
|
+
|
94
|
+
Money.default_currency = "CAD"
|
95
|
+
|
96
|
+
If you use Rails, then environment.rb is a very good place to put this.
|
97
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
desc "Build a gem"
|
2
|
+
task :gem do
|
3
|
+
sh "gem build money.gemspec"
|
4
|
+
end
|
5
|
+
|
6
|
+
task "Generate RDoc documentation"
|
7
|
+
task :rdoc do
|
8
|
+
sh "hanna README.rdoc lib -U"
|
9
|
+
end
|
10
|
+
|
11
|
+
task :upload => :rdoc do
|
12
|
+
sh "scp -r doc/* rubyforge.org:/var/www/gforge-projects/money/"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run unit tests"
|
16
|
+
task :test do
|
17
|
+
sh "spec -f s -c test/*_spec.rb"
|
18
|
+
end
|
data/lib/money.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#--
|
2
1
|
# Copyright (c) 2005 Tobias Luetke
|
2
|
+
# Copyright (c) 2008 Phusion
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -19,11 +19,7 @@
|
|
19
19
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
20
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
-
#++
|
23
22
|
|
24
|
-
|
25
|
-
require
|
26
|
-
require
|
27
|
-
require File.dirname(__FILE__) + '/bank/variable_exchange_bank'
|
28
|
-
require File.dirname(__FILE__) + '/money/money'
|
29
|
-
require File.dirname(__FILE__) + '/money/core_extensions'
|
23
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
|
24
|
+
require 'money/money'
|
25
|
+
require 'money/core_extensions'
|
@@ -1,25 +1,37 @@
|
|
1
|
-
# Allows Writing of 100.to_money for +Numeric+ types
|
2
|
-
# 100.to_money => #<Money @cents=10000>
|
3
|
-
# 100.37.to_money => #<Money @cents=10037>
|
4
1
|
class Numeric
|
2
|
+
# Converts this numeric to a Money object in the default currency. It
|
3
|
+
# multiplies the numeric value by 100 and treats that as cents.
|
4
|
+
#
|
5
|
+
# 100.to_money => #<Money @cents=10000>
|
6
|
+
# 100.37.to_money => #<Money @cents=10037>
|
5
7
|
def to_money
|
6
8
|
Money.new(self * 100)
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
10
|
-
# Allows Writing of '100'.to_money for +String+ types
|
11
|
-
# Excess characters will be discarded
|
12
|
-
# '100'.to_money => #<Money @cents=10000>
|
13
|
-
# '100.37'.to_money => #<Money @cents=10037>
|
14
12
|
class String
|
13
|
+
# Parses the current string and converts it to a Money object.
|
14
|
+
# Excess characters will be discarded.
|
15
|
+
#
|
16
|
+
# '100'.to_money # => #<Money @cents=10000>
|
17
|
+
# '100.37'.to_money # => #<Money @cents=10037>
|
18
|
+
# '100 USD'.to_money # => #<Money @cents=10000, @currency="USD">
|
19
|
+
# 'USD 100'.to_money # => #<Money @cents=10000, @currency="USD">
|
20
|
+
# '$100 USD'.to_money # => #<Money @cents=10000, @currency="USD">
|
15
21
|
def to_money
|
16
|
-
# Get the currency
|
22
|
+
# Get the currency.
|
17
23
|
matches = scan /([A-Z]{2,3})/
|
18
24
|
currency = matches[0] ? matches[0][0] : Money.default_currency
|
19
25
|
|
20
26
|
# Get the cents amount
|
21
|
-
matches = scan /(
|
22
|
-
cents =
|
27
|
+
matches = scan /(\-?[\d ]+([\.,](\d+))?)/
|
28
|
+
cents = if matches[0]
|
29
|
+
value = matches[0][0].gsub(/,/, '.')
|
30
|
+
value.gsub!(/ +/, '')
|
31
|
+
value.to_f * 100
|
32
|
+
else
|
33
|
+
0
|
34
|
+
end
|
23
35
|
|
24
36
|
Money.new(cents, currency)
|
25
37
|
end
|
data/lib/money/errors.rb
ADDED
data/lib/money/money.rb
CHANGED
@@ -1,64 +1,89 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
# object in your models. The following example requires a cents and a currency field.
|
5
|
-
#
|
6
|
-
# class ProductUnit < ActiveRecord::Base
|
7
|
-
# belongs_to :product
|
8
|
-
# composed_of :price, :class_name => "Money", :mapping => [ %w(cents cents), %w(currency currency) ]
|
9
|
-
#
|
10
|
-
# private
|
11
|
-
# validate :cents_not_zero
|
12
|
-
#
|
13
|
-
# def cents_not_zero
|
14
|
-
# errors.add("cents", "cannot be zero or less") unless cents > 0
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# validates_presence_of :sku, :currency
|
18
|
-
# validates_uniqueness_of :sku
|
19
|
-
# end
|
20
|
-
#
|
1
|
+
require 'money/variable_exchange_bank'
|
2
|
+
|
3
|
+
# Represents an amount of money in a certain currency.
|
21
4
|
class Money
|
22
5
|
include Comparable
|
23
|
-
|
24
|
-
attr_reader :cents, :currency
|
25
|
-
|
26
|
-
class
|
6
|
+
|
7
|
+
attr_reader :cents, :currency, :bank
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Each Money object is associated to a bank object, which is responsible
|
11
|
+
# for currency exchange. This property allows one to specify the default
|
12
|
+
# bank object.
|
13
|
+
#
|
14
|
+
# bank1 = MyBank.new
|
15
|
+
# bank2 = MyOtherBank.new
|
16
|
+
#
|
17
|
+
# Money.default_bank = bank1
|
18
|
+
# money1 = Money.new(10)
|
19
|
+
# money1.bank # => bank1
|
20
|
+
#
|
21
|
+
# Money.default_bank = bank2
|
22
|
+
# money2 = Money.new(10)
|
23
|
+
# money2.bank # => bank2
|
24
|
+
# money1.bank # => bank1
|
25
|
+
#
|
26
|
+
# The default value for this property is an instance if VariableExchangeBank.
|
27
|
+
# It allows one to specify custom exchange rates:
|
28
|
+
#
|
29
|
+
# Money.default_bank.add_rate("USD", "CAD", 1.24515)
|
30
|
+
# Money.default_bank.add_rate("CAD", "USD", 0.803115)
|
31
|
+
# Money.us_dollar(100).exchange_to("CAD") # => Money.ca_dollar(124)
|
32
|
+
# Money.ca_dollar(100).exchange_to("USD") # => Money.us_dollar(80)
|
33
|
+
attr_accessor :default_bank
|
34
|
+
|
35
|
+
# The default currency, which is used when <tt>Money.new</tt> is called
|
36
|
+
# without an explicit currency argument. The default value is "USD".
|
37
|
+
attr_accessor :default_currency
|
38
|
+
end
|
39
|
+
|
40
|
+
self.default_bank = VariableExchangeBank.instance
|
41
|
+
self.default_currency = "USD"
|
42
|
+
|
43
|
+
|
44
|
+
# Create a new money object with value 0.
|
45
|
+
def self.empty(currency = default_currency)
|
46
|
+
Money.new(0, currency)
|
27
47
|
end
|
28
48
|
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# custom excahnge rates:
|
34
|
-
#
|
35
|
-
# Money.bank = VariableExchangeBank.new
|
36
|
-
# Money.bank.add_rate("USD", "CAD", 1.24515)
|
37
|
-
# Money.bank.add_rate("CAD", "USD", 0.803115)
|
38
|
-
# Money.us_dollar(100).exchange_to("CAD") => Money.ca_dollar(124)
|
39
|
-
# Money.ca_dollar(100).exchange_to("USD") => Money.us_dollar(80)
|
40
|
-
@@bank = NoExchangeBank.new
|
41
|
-
cattr_accessor :bank
|
42
|
-
|
43
|
-
@@default_currency = "USD"
|
44
|
-
cattr_accessor :default_currency
|
49
|
+
# Creates a new Money object of the given value, using the Canadian dollar currency.
|
50
|
+
def self.ca_dollar(cents)
|
51
|
+
Money.new(cents, "CAD")
|
52
|
+
end
|
45
53
|
|
54
|
+
# Creates a new Money object of the given value, using the American dollar currency.
|
55
|
+
def self.us_dollar(cents)
|
56
|
+
Money.new(cents, "USD")
|
57
|
+
end
|
58
|
+
|
59
|
+
# Creates a new Money object of the given value, using the Euro currency.
|
60
|
+
def self.euro(cents)
|
61
|
+
Money.new(cents, "EUR")
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.add_rate(from_currency, to_currency, rate)
|
65
|
+
Money.default_bank.add_rate(from_currency, to_currency, rate)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
46
69
|
# Creates a new money object.
|
47
70
|
# Money.new(100)
|
48
71
|
#
|
49
72
|
# Alternativly you can use the convinience methods like
|
50
73
|
# Money.ca_dollar and Money.us_dollar
|
51
|
-
def initialize(cents, currency = default_currency)
|
52
|
-
@cents
|
74
|
+
def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
|
75
|
+
@cents = cents.round
|
76
|
+
@currency = currency
|
77
|
+
@bank = bank
|
53
78
|
end
|
54
79
|
|
55
80
|
# Do two money objects equal? Only works if both objects are of the same currency
|
56
|
-
def
|
57
|
-
cents == other_money.cents && currency
|
81
|
+
def ==(other_money)
|
82
|
+
cents == other_money.cents && bank.same_currency?(currency, other_money.currency)
|
58
83
|
end
|
59
84
|
|
60
85
|
def <=>(other_money)
|
61
|
-
if currency
|
86
|
+
if bank.same_currency?(currency, other_money.currency)
|
62
87
|
cents <=> other_money.cents
|
63
88
|
else
|
64
89
|
cents <=> other_money.exchange_to(currency).cents
|
@@ -66,9 +91,6 @@ class Money
|
|
66
91
|
end
|
67
92
|
|
68
93
|
def +(other_money)
|
69
|
-
return other_money.dup if cents.zero?
|
70
|
-
return dup if other_money.cents.zero?
|
71
|
-
|
72
94
|
if currency == other_money.currency
|
73
95
|
Money.new(cents + other_money.cents, other_money.currency)
|
74
96
|
else
|
@@ -77,24 +99,16 @@ class Money
|
|
77
99
|
end
|
78
100
|
|
79
101
|
def -(other_money)
|
80
|
-
|
81
102
|
if currency == other_money.currency
|
82
103
|
Money.new(cents - other_money.cents, other_money.currency)
|
83
104
|
else
|
84
|
-
|
85
|
-
return other_money.dup if self.cents.zero?
|
86
|
-
|
87
|
-
return self.dup if other_money.cents.zero?
|
88
|
-
|
89
|
-
|
90
105
|
Money.new(cents - other_money.exchange_to(currency).cents, currency)
|
91
|
-
|
92
106
|
end
|
93
107
|
end
|
94
108
|
|
95
109
|
# get the cents value of the object
|
96
110
|
def cents
|
97
|
-
@cents
|
111
|
+
@cents
|
98
112
|
end
|
99
113
|
|
100
114
|
# multiply money by fixnum
|
@@ -153,57 +167,37 @@ class Money
|
|
153
167
|
end
|
154
168
|
formatted
|
155
169
|
end
|
156
|
-
|
170
|
+
|
157
171
|
# Money.ca_dollar(100).to_s => "1.00"
|
158
172
|
def to_s
|
159
|
-
sprintf("%.2f", cents
|
173
|
+
sprintf("%.2f", cents / 100.00)
|
160
174
|
end
|
161
|
-
|
162
|
-
# Recieve the amount of this money object in another currency
|
175
|
+
|
176
|
+
# Recieve the amount of this money object in another currency.
|
163
177
|
def exchange_to(other_currency)
|
164
|
-
|
178
|
+
Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
|
165
179
|
end
|
166
|
-
|
167
|
-
# Create a new money object with value 0
|
168
|
-
def self.empty(currency = default_currency)
|
169
|
-
Money.new(0, currency)
|
170
|
-
end
|
171
|
-
|
172
|
-
# Create a new money object using the Canadian dollar currency
|
173
|
-
def self.ca_dollar(num)
|
174
|
-
Money.new(num, "CAD")
|
175
|
-
end
|
176
|
-
|
177
|
-
# Create a new money object using the American dollar currency
|
178
|
-
def self.us_dollar(num)
|
179
|
-
Money.new(num, "USD")
|
180
|
-
end
|
181
|
-
|
182
|
-
# Create a new money object using the Euro currency
|
183
|
-
def self.euro(num)
|
184
|
-
Money.new(num, "EUR")
|
185
|
-
end
|
186
|
-
|
180
|
+
|
187
181
|
# Recieve a money object with the same amount as the current Money object
|
188
182
|
# in american dollar
|
189
183
|
def as_us_dollar
|
190
184
|
exchange_to("USD")
|
191
185
|
end
|
192
|
-
|
186
|
+
|
193
187
|
# Recieve a money object with the same amount as the current Money object
|
194
188
|
# in canadian dollar
|
195
189
|
def as_ca_dollar
|
196
190
|
exchange_to("CAD")
|
197
191
|
end
|
198
|
-
|
192
|
+
|
199
193
|
# Recieve a money object with the same amount as the current Money object
|
200
194
|
# in euro
|
201
|
-
def
|
195
|
+
def as_euro
|
202
196
|
exchange_to("EUR")
|
203
|
-
end
|
204
|
-
|
197
|
+
end
|
198
|
+
|
205
199
|
# Conversation to self
|
206
200
|
def to_money
|
207
201
|
self
|
208
202
|
end
|
209
|
-
end
|
203
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'money/errors'
|
3
|
+
|
4
|
+
# Class for aiding in exchanging money between different currencies.
|
5
|
+
# By default, the Money class uses an object of this class (accessible through
|
6
|
+
# Money#bank) for performing currency exchanges.
|
7
|
+
#
|
8
|
+
# By default, VariableExchangeBank has no knowledge about conversion rates.
|
9
|
+
# One must manually specify them with +add_rate+, after which one can perform
|
10
|
+
# exchanges with +exchange+. For example:
|
11
|
+
#
|
12
|
+
# bank = Money::VariableExchangeBank.new
|
13
|
+
# bank.add_rate("USD", "CAD", 1.24515)
|
14
|
+
# bank.add_rate("CAD", "USD", 0.803115)
|
15
|
+
#
|
16
|
+
# # Exchange 100 CAD to USD:
|
17
|
+
# bank.exchange(100_00, "CAD", "USD") # => 124
|
18
|
+
# # Exchange 100 USD to CAD:
|
19
|
+
# bank.exchange(100_00, "USD", "CAD") # => 80
|
20
|
+
class Money
|
21
|
+
class VariableExchangeBank
|
22
|
+
# Returns the singleton instance of VariableExchangeBank.
|
23
|
+
#
|
24
|
+
# By default, <tt>Money.default_bank</tt> returns the same object.
|
25
|
+
def self.instance
|
26
|
+
@@singleton
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@rates = {}
|
31
|
+
@mutex = Mutex.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# Registers a conversion rate. +from+ and +to+ are both currency names.
|
35
|
+
def add_rate(from, to, rate)
|
36
|
+
@mutex.synchronize do
|
37
|
+
@rates["#{from}_TO_#{to}".upcase] = rate
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gets the rate for exchanging the currency named +from+ to the currency
|
42
|
+
# named +to+. Returns nil if the rate is unknown.
|
43
|
+
def get_rate(from, to)
|
44
|
+
@mutex.synchronize do
|
45
|
+
@rates["#{from}_TO_#{to}".upcase]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Given two currency names, checks whether they're both the same currency.
|
50
|
+
#
|
51
|
+
# bank = VariableExchangeBank.new
|
52
|
+
# bank.same_currency?("usd", "USD") # => true
|
53
|
+
# bank.same_currency?("usd", "EUR") # => false
|
54
|
+
def same_currency?(currency1, currency2)
|
55
|
+
currency1.upcase == currency2.upcase
|
56
|
+
end
|
57
|
+
|
58
|
+
# Exchange the given amount of cents in +from_currency+ to +to_currency+.
|
59
|
+
# Returns the amount of cents in +to_currency+ as an integer, rounded down.
|
60
|
+
#
|
61
|
+
# If the conversion rate is unknown, then Money::UnknownRate will be raised.
|
62
|
+
def exchange(cents, from_currency, to_currency)
|
63
|
+
rate = get_rate(from_currency, to_currency)
|
64
|
+
if !rate
|
65
|
+
raise Money::UnknownRate, "No conversion rate known for '#{from_currency}' -> '#{to_currency}'"
|
66
|
+
end
|
67
|
+
(cents * rate).floor
|
68
|
+
end
|
69
|
+
|
70
|
+
@@singleton = VariableExchangeBank.new
|
71
|
+
end
|
72
|
+
end
|
data/money.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "money"
|
3
|
+
s.version = "2.0.0"
|
4
|
+
s.summary = "Money and currency exchange support library"
|
5
|
+
s.email = "hongli@phusion.nl"
|
6
|
+
s.homepage = "http://money.rubyforge.org/"
|
7
|
+
s.description = "Money and currency exchange support library."
|
8
|
+
s.has_rdoc = true
|
9
|
+
s.rubyforge_project = "money"
|
10
|
+
s.authors = ["Tobias Luetke", "Hongli Lai"]
|
11
|
+
|
12
|
+
s.files = [
|
13
|
+
"README.rdoc", "MIT-LICENSE", "money.gemspec", "Rakefile",
|
14
|
+
"lib/money.rb",
|
15
|
+
"lib/money/core_extensions.rb",
|
16
|
+
"lib/money/errors.rb",
|
17
|
+
"lib/money/money.rb",
|
18
|
+
"lib/money/variable_exchange_bank.rb",
|
19
|
+
"test/core_extensions_spec.rb",
|
20
|
+
"test/exchange_bank_spec.rb",
|
21
|
+
"test/money_spec.rb"
|
22
|
+
]
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
2
|
+
require 'money/core_extensions'
|
3
|
+
|
4
|
+
describe "Money core extensions" do
|
5
|
+
specify "Numberic#to_money works" do
|
6
|
+
money = 1234.to_money
|
7
|
+
money.cents.should == 1234_00
|
8
|
+
money.currency.should == Money.default_currency
|
9
|
+
|
10
|
+
money = 100.37.to_money
|
11
|
+
money.cents.should == 100_37
|
12
|
+
money.currency.should == Money.default_currency
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "String#to_money works" do
|
16
|
+
"100".to_money.should == Money.new(100_00)
|
17
|
+
"100.37".to_money.should == Money.new(100_37)
|
18
|
+
"100,37".to_money.should == Money.new(100_37)
|
19
|
+
"100 000".to_money.should == Money.new(100_000_00)
|
20
|
+
|
21
|
+
"100 USD".to_money.should == Money.new(100_00, "USD")
|
22
|
+
"100 EUR".to_money.should == Money.new(100_00, "EUR")
|
23
|
+
"100.37 EUR".to_money.should == Money.new(100_37, "EUR")
|
24
|
+
"100,37 EUR".to_money.should == Money.new(100_37, "EUR")
|
25
|
+
|
26
|
+
"USD 100".to_money.should == Money.new(100_00, "USD")
|
27
|
+
"EUR 100".to_money.should == Money.new(100_00, "EUR")
|
28
|
+
"EUR 100.37".to_money.should == Money.new(100_37, "EUR")
|
29
|
+
"EUR 100,37".to_money.should == Money.new(100_37, "EUR")
|
30
|
+
|
31
|
+
"$100 USD".to_money.should == Money.new(100_00, "USD")
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
2
|
+
require 'money/variable_exchange_bank'
|
3
|
+
|
4
|
+
describe Money::VariableExchangeBank do
|
5
|
+
before :each do
|
6
|
+
@bank = Money::VariableExchangeBank.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns the previously specified conversion rate" do
|
10
|
+
@bank.add_rate("USD", "EUR", 0.788332676)
|
11
|
+
@bank.add_rate("EUR", "YEN", 122.631477)
|
12
|
+
@bank.get_rate("USD", "EUR").should == 0.788332676
|
13
|
+
@bank.get_rate("EUR", "YEN").should == 122.631477
|
14
|
+
end
|
15
|
+
|
16
|
+
it "treats currency names case-insensitively" do
|
17
|
+
@bank.add_rate("usd", "eur", 1)
|
18
|
+
@bank.get_rate("USD", "EUR").should == 1
|
19
|
+
@bank.same_currency?("USD", "usd").should be_true
|
20
|
+
@bank.same_currency?("EUR", "usd").should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns nil if the conversion rate is unknown" do
|
24
|
+
@bank.get_rate("American Pesos", "EUR").should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "exchanges money from one currency to another according to the specified conversion rates" do
|
28
|
+
@bank.add_rate("USD", "EUR", 0.5)
|
29
|
+
@bank.add_rate("EUR", "YEN", 10)
|
30
|
+
@bank.exchange(10_00, "USD", "EUR").should == 5_00
|
31
|
+
@bank.exchange(500_00, "EUR", "YEN").should == 5000_00
|
32
|
+
end
|
33
|
+
|
34
|
+
it "rounds the exchanged result down" do
|
35
|
+
@bank.add_rate("USD", "EUR", 0.788332676)
|
36
|
+
@bank.add_rate("EUR", "YEN", 122.631477)
|
37
|
+
@bank.exchange(10_00, "USD", "EUR").should == 788
|
38
|
+
@bank.exchange(500_00, "EUR", "YEN").should == 6131573
|
39
|
+
end
|
40
|
+
|
41
|
+
it "raises Money::UnknownRate upon conversion if the conversion rate is unknown" do
|
42
|
+
block = lambda { @bank.exchange(10, "USD", "EUR") }
|
43
|
+
block.should raise_error(Money::UnknownRate)
|
44
|
+
end
|
45
|
+
end
|
data/test/money_spec.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
2
|
+
require 'money/money'
|
3
|
+
|
4
|
+
describe Money do
|
5
|
+
it "is associated to the singleton instance of VariableExchangeBank by default" do
|
6
|
+
Money.new(0).bank.object_id.should == Money::VariableExchangeBank.instance.object_id
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "#cents returns the amount of cents passed to the constructor" do
|
10
|
+
Money.new(200_00, "USD").cents.should == 200_00
|
11
|
+
end
|
12
|
+
|
13
|
+
it "rounds the given cents to an integer" do
|
14
|
+
Money.new(1.00, "USD").cents.should == 1
|
15
|
+
Money.new(1.01, "USD").cents.should == 1
|
16
|
+
Money.new(1.50, "USD").cents.should == 2
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "#currency returns the currency passed to the constructor" do
|
20
|
+
Money.new(200_00, "USD").currency.should == "USD"
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "#zero? returns whether the amount is 0" do
|
24
|
+
Money.new(0, "USD").should be_zero
|
25
|
+
Money.new(0, "EUR").should be_zero
|
26
|
+
Money.new(1, "USD").should_not be_zero
|
27
|
+
Money.new(10, "YEN").should_not be_zero
|
28
|
+
Money.new(-1, "EUR").should_not be_zero
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "#exchange_to exchanges the amount via its exchange bank" do
|
32
|
+
money = Money.new(100_00, "USD")
|
33
|
+
money.bank.should_receive(:exchange).with(100_00, "USD", "EUR").and_return(200_00)
|
34
|
+
money.exchange_to("EUR")
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "#exchange_to exchanges the amount properly" do
|
38
|
+
money = Money.new(100_00, "USD")
|
39
|
+
money.bank.should_receive(:exchange).with(100_00, "USD", "EUR").and_return(200_00)
|
40
|
+
money.exchange_to("EUR").should == Money.new(200_00, "EUR")
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "#== returns true if and only if their amount and currency are equal" do
|
44
|
+
Money.new(1_00, "USD").should == Money.new(1_00, "USD")
|
45
|
+
Money.new(1_00, "USD").should_not == Money.new(1_00, "EUR")
|
46
|
+
Money.new(1_00, "USD").should_not == Money.new(2_00, "USD")
|
47
|
+
Money.new(1_00, "USD").should_not == Money.new(99_00, "EUR")
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "#* multiplies the money's amount by the multiplier while retaining the currency" do
|
51
|
+
(Money.new(1_00, "USD") * 10).should == Money.new(10_00, "USD")
|
52
|
+
end
|
53
|
+
|
54
|
+
specify "#* divides the money's amount by the divisor while retaining the currency" do
|
55
|
+
(Money.new(10_00, "USD") / 10).should == Money.new(1_00, "USD")
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "Money.empty creates a new Money object of 0 cents" do
|
59
|
+
Money.empty.should == Money.new(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
specify "Money.ca_dollar creates a new Money object of the given value in CAD" do
|
63
|
+
Money.ca_dollar(50).should == Money.new(50, "CAD")
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "Money.ca_dollar creates a new Money object of the given value in USD" do
|
67
|
+
Money.us_dollar(50).should == Money.new(50, "USD")
|
68
|
+
end
|
69
|
+
|
70
|
+
specify "Money.ca_dollar creates a new Money object of the given value in EUR" do
|
71
|
+
Money.euro(50).should == Money.new(50, "EUR")
|
72
|
+
end
|
73
|
+
|
74
|
+
specify "Money.add_rate works" do
|
75
|
+
Money.add_rate("EUR", "USD", 10)
|
76
|
+
Money.new(10_00, "EUR").exchange_to("USD").should == Money.new(100_00, "USD")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "Actions involving two Money objects" do
|
81
|
+
describe "if the other Money object has the same currency" do
|
82
|
+
specify "#<=> compares the two objects' amounts" do
|
83
|
+
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
84
|
+
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
85
|
+
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
86
|
+
end
|
87
|
+
|
88
|
+
specify "#+ adds the other object's amount to the current object's amount while retaining the currency" do
|
89
|
+
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
90
|
+
end
|
91
|
+
|
92
|
+
specify "#- substracts the other object's amount from the current object's amount while retaining the currency" do
|
93
|
+
(Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "if the other Money object has a different currency" do
|
98
|
+
specify "#<=> compares the two objects' amount after converting the other object's amount to its own currency" do
|
99
|
+
target = Money.new(200_00, "EUR")
|
100
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(300_00, "USD"))
|
101
|
+
(Money.new(100_00, "USD") <=> target).should < 0
|
102
|
+
|
103
|
+
target = Money.new(200_00, "EUR")
|
104
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(100_00, "USD"))
|
105
|
+
(Money.new(100_00, "USD") <=> target).should == 0
|
106
|
+
|
107
|
+
target = Money.new(200_00, "EUR")
|
108
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(99_00, "USD"))
|
109
|
+
(Money.new(100_00, "USD") <=> target).should > 0
|
110
|
+
end
|
111
|
+
|
112
|
+
specify "#+ adds the other object's amount, converted to this object's currency, to this object's amount while retaining its currency" do
|
113
|
+
other = Money.new(90, "EUR")
|
114
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
115
|
+
(Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
|
116
|
+
end
|
117
|
+
|
118
|
+
specify "#- substracts the other object's amount, converted to this object's currency, from this object's amount while retaining its currency" do
|
119
|
+
other = Money.new(90, "EUR")
|
120
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
121
|
+
(Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
metadata
CHANGED
@@ -1,49 +1,65 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.11
|
3
|
-
specification_version: 1
|
4
2
|
name: money
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
homepage: http://leetsoft.com/rails/money
|
13
|
-
rubyforge_project:
|
14
|
-
description: Class aiding in the handling of Money and Currencies.\nIt supports easy pluggable bank objects for customized exchange strategies.\nCan be used as composite in ActiveRecord tables.
|
15
|
-
autorequire: money
|
16
|
-
default_executable:
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tobias Luetke
|
8
|
+
- Hongli Lai
|
9
|
+
autorequire:
|
17
10
|
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2008-10-31 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Money and currency exchange support library.
|
18
|
+
email: hongli@phusion.nl
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- README.rdoc
|
27
|
+
- MIT-LICENSE
|
28
|
+
- money.gemspec
|
29
|
+
- Rakefile
|
30
|
+
- lib/money.rb
|
31
|
+
- lib/money/core_extensions.rb
|
32
|
+
- lib/money/errors.rb
|
33
|
+
- lib/money/money.rb
|
34
|
+
- lib/money/variable_exchange_bank.rb
|
35
|
+
- test/core_extensions_spec.rb
|
36
|
+
- test/exchange_bank_spec.rb
|
37
|
+
- test/money_spec.rb
|
18
38
|
has_rdoc: true
|
19
|
-
|
39
|
+
homepage: http://money.rubyforge.org/
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
20
46
|
requirements:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
version: 0.0.0
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
25
50
|
version:
|
26
|
-
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project: money
|
60
|
+
rubygems_version: 1.2.0
|
27
61
|
signing_key:
|
28
|
-
|
29
|
-
|
30
|
-
- Tobias Luetke
|
31
|
-
files:
|
32
|
-
- README
|
33
|
-
- MIT-LICENSE
|
34
|
-
- lib/bank
|
35
|
-
- lib/money
|
36
|
-
- lib/money.rb
|
37
|
-
- lib/support
|
38
|
-
- lib/bank/no_exchange_bank.rb
|
39
|
-
- lib/bank/variable_exchange_bank.rb
|
40
|
-
- lib/money/core_extensions.rb
|
41
|
-
- lib/money/money.rb
|
42
|
-
- lib/support/cattr_accessor.rb
|
62
|
+
specification_version: 2
|
63
|
+
summary: Money and currency exchange support library
|
43
64
|
test_files: []
|
44
|
-
|
45
|
-
extra_rdoc_files: []
|
46
|
-
executables: []
|
47
|
-
extensions: []
|
48
|
-
requirements: []
|
49
|
-
dependencies: []
|
65
|
+
|
data/README
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
== Money class
|
2
|
-
|
3
|
-
This money class is based on the example from the ActiveRecord doc:
|
4
|
-
http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html
|
5
|
-
|
6
|
-
Its in production use at http://www.snowdevil.ca and I haven't found any major issues
|
7
|
-
so far.
|
8
|
-
The main reason to open source it is because It might be useful to other people and
|
9
|
-
I hope i'll get some feedback on how to improve the class.
|
10
|
-
|
11
|
-
I bundled the exporter with the money class since some tests depend on it and I figured
|
12
|
-
that most applications which need to deal with Money also need to deal with proper
|
13
|
-
exporting.
|
14
|
-
|
15
|
-
== Download
|
16
|
-
|
17
|
-
Preferred method of installation is gem:
|
18
|
-
|
19
|
-
gem install --source http://dist.leetsoft.com money
|
20
|
-
|
21
|
-
Alternatively you can get the library packed
|
22
|
-
|
23
|
-
http://dist.leetsoft.com/pkg/
|
24
|
-
|
25
|
-
== Usage
|
26
|
-
|
27
|
-
Use the compose_of helper to let active record deal with embedding the money
|
28
|
-
object in your models. The following example requires a cents and a currency field.
|
29
|
-
|
30
|
-
class ProductUnit < ActiveRecord::Base
|
31
|
-
belongs_to :product
|
32
|
-
composed_of :price, :class_name => "Money", :mapping => [%w(cents cents) %(currency currency)]
|
33
|
-
|
34
|
-
private
|
35
|
-
validate :cents_not_zero
|
36
|
-
|
37
|
-
def cents_not_zero
|
38
|
-
errors.add("cents", "cannot be zero or less") unless cents > 0
|
39
|
-
end
|
40
|
-
|
41
|
-
validates_presence_of :sku, :currency
|
42
|
-
validates_uniqueness_of :sku
|
43
|
-
end
|
44
|
-
|
45
|
-
== Class configuration
|
46
|
-
|
47
|
-
Two const class variables are available to tailor Money to your needs.
|
48
|
-
If you don't need currency exchange at all, just ignore those.
|
49
|
-
|
50
|
-
=== Default Currency
|
51
|
-
|
52
|
-
By default Money defaults to USD as its currency. This can be overwritten using
|
53
|
-
|
54
|
-
Money.default_currency = "CAD"
|
55
|
-
|
56
|
-
If you use rails, the environment.rb is a very good place to put this.
|
57
|
-
|
58
|
-
=== Currency Exchange
|
59
|
-
|
60
|
-
The second parameter is a bit more complex. It lets you provide your own implementation of the
|
61
|
-
currency exchange service. By default Money throws an exception when trying to call .exchange_to.
|
62
|
-
|
63
|
-
A second minimalist implementation is provided which lets you supply custom exchange rates:
|
64
|
-
|
65
|
-
Money.bank = VariableExchangeBank.new
|
66
|
-
Money.bank.add_rate("USD", "CAD", 1.24515)
|
67
|
-
Money.bank.add_rate("CAD", "USD", 0.803115)
|
68
|
-
Money.us_dollar(100).exchange_to("CAD") => Money.ca_dollar(124)
|
69
|
-
Money.ca_dollar(100).exchange_to("USD") => Money.us_dollar(80)
|
70
|
-
|
71
|
-
There is nothing stopping you from creating bank objects which scrape www.xe.com for the current rates or just return rand(2)
|
72
|
-
|
73
|
-
== Code
|
74
|
-
|
75
|
-
If you have any improvements please email them to tobi [at] leetsoft.com
|
@@ -1,9 +0,0 @@
|
|
1
|
-
class NoExchangeBank# :nodoc:
|
2
|
-
|
3
|
-
def reduce(money, currency)
|
4
|
-
return money if money.currency == currency
|
5
|
-
raise Money::MoneyError.new("Current Money::bank does not support money exchange. Please implement a bank object that does and assign it to the Money class.")
|
6
|
-
end
|
7
|
-
|
8
|
-
|
9
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# Example useage:
|
2
|
-
#
|
3
|
-
# Money.bank = VariableExchangeBank.new
|
4
|
-
# Money.bank.add_rate("USD", "CAD", 1.24515)
|
5
|
-
# Money.bank.add_rate("CAD", "USD", 0.803115)
|
6
|
-
# Money.us_dollar(100).exchange_to("CAD") => Money.ca_dollar(124)
|
7
|
-
# Money.ca_dollar(100).exchange_to("USD") => Money.us_dollar(80)
|
8
|
-
class VariableExchangeBank
|
9
|
-
|
10
|
-
def add_rate(from, to, rate)
|
11
|
-
rates["#{from}_TO_#{to}".upcase] = rate
|
12
|
-
end
|
13
|
-
|
14
|
-
def get_rate(from, to)
|
15
|
-
rates["#{from}_TO_#{to}".upcase]
|
16
|
-
end
|
17
|
-
|
18
|
-
def reduce(money, currency)
|
19
|
-
rate = get_rate(money.currency, currency) or raise Money::MoneyError.new("Can't find required exchange rate")
|
20
|
-
|
21
|
-
Money.new((money.cents * rate).floor, currency)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def rates
|
27
|
-
@rates ||= {}
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# Extends the class object with class and instance accessors for class attributes,
|
2
|
-
# just like the native attr* accessors for instance attributes.
|
3
|
-
class Class # :nodoc:
|
4
|
-
def cattr_reader(*syms)
|
5
|
-
syms.each do |sym|
|
6
|
-
class_eval <<-EOS
|
7
|
-
if ! defined? @@#{sym.id2name}
|
8
|
-
@@#{sym.id2name} = nil
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.#{sym.id2name}
|
12
|
-
@@#{sym}
|
13
|
-
end
|
14
|
-
|
15
|
-
def #{sym.id2name}
|
16
|
-
@@#{sym}
|
17
|
-
end
|
18
|
-
|
19
|
-
def call_#{sym.id2name}
|
20
|
-
case @@#{sym.id2name}
|
21
|
-
when Symbol then send(@@#{sym})
|
22
|
-
when Proc then @@#{sym}.call(self)
|
23
|
-
when String then @@#{sym}
|
24
|
-
else nil
|
25
|
-
end
|
26
|
-
end
|
27
|
-
EOS
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def cattr_writer(*syms)
|
32
|
-
syms.each do |sym|
|
33
|
-
class_eval <<-EOS
|
34
|
-
if ! defined? @@#{sym.id2name}
|
35
|
-
@@#{sym.id2name} = nil
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.#{sym.id2name}=(obj)
|
39
|
-
@@#{sym.id2name} = obj
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.set_#{sym.id2name}(obj)
|
43
|
-
@@#{sym.id2name} = obj
|
44
|
-
end
|
45
|
-
|
46
|
-
def #{sym.id2name}=(obj)
|
47
|
-
@@#{sym} = obj
|
48
|
-
end
|
49
|
-
EOS
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def cattr_accessor(*syms)
|
54
|
-
cattr_reader(*syms)
|
55
|
-
cattr_writer(*syms)
|
56
|
-
end
|
57
|
-
end
|