money 3.0.5 → 3.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,11 +1,23 @@
1
+ == Money 3.1.0.pre1
2
+ * Implemented Money::Bank::Base (closes #14)
3
+ * Added Bank::Base#exchange_with
4
+ * Deprecated Bank::Base#exchange (removal in 3.2.0)
5
+ * Implented Money::Bank::VariableExchange
6
+ * Deprecated Money::VariableExchangeBank (removal in 3.2.0)
7
+ * Set Money::SYMBOLS, Money::SEPARATORS and Money::DELIMITERS
8
+ deprecation target to Money 3.2.0. (closes #16)
9
+ * Fixed rounding error in Numeric#to_money (closes #15)
10
+ * Implemented #has for Money and Money::Currency
11
+ * Refactored test suite to conform to RSpec conventions
12
+ * Moved project from github.com/FooBarWidget to github.com/RubyMoney
13
+ * Added Simone Carletti to list of authors
14
+
1
15
  == Money 3.0.5
2
16
  * Added Money#abs
3
- * Updated Currency#subunit_to_unit documentation (it's an integer not a
4
- string)
5
- * Fixed issue when exchanging currencies with different :subunit_to_unit
6
- values
17
+ * Updated Currency#subunit_to_unit documentation (it's an integer not a string)
18
+ * Fixed issue when exchanging currencies with different :subunit_to_unit values
7
19
  * Added ability to pass a block to VariableExchangeBank#new or #exchange,
8
- specifying a custom truncation method
20
+ specifying a custom truncation method
9
21
  * Added optional currency argument to Numeric#to_money
10
22
  * Added optional currency argument to String#to_money
11
23
  * Numeric#to_money now respects :subunit_to_unit
@@ -20,7 +32,7 @@
20
32
 
21
33
  == Money 3.0.3
22
34
  * Added #currency_as_string and #currency_as_string= for easier integration
23
- with ActiveRecord/Rails
35
+ with ActiveRecord/Rails
24
36
 
25
37
  == Money 3.0.2
26
38
  * Added #div, #divmod, #modulo, #% and #remainder methods
@@ -28,12 +40,11 @@
28
40
  == Money 3.0.1
29
41
  * Added #eql? method
30
42
  * Updated Numeric#to_money to work with all children of Numeric
31
- (BigDecimal, Integer, Fixnum, etc)
43
+ (BigDecimal, Integer, Fixnum, etc)
32
44
 
33
45
  == Money 3.0.0
34
46
  * Version Bump due to compatibility changes with ActiveRecord. See
35
- http://github.com/FooBarWidget/money/issues#issue/4/comment/224880 for more
36
- information.
47
+ http://github.com/RubyMoney/money/issues#issue/4/comment/224880 for more information.
37
48
 
38
49
  == Money 2.3.0
39
50
  * Currency is now represented by a Currency Object instead of a string.
data/README.rdoc CHANGED
@@ -4,8 +4,8 @@ This library aids one in handling money and different currencies. Features:
4
4
 
5
5
  - Provides a <tt>Money</tt> class which encapsulates all information about an certain
6
6
  amount of money, such as its value and its currency.
7
- - Provies a <tt>Money::Currency</tt> class which encapsulates all information about
8
- a monerary unit.
7
+ - Provides a <tt>Money::Currency</tt> class which encapsulates all information about
8
+ a monetary unit.
9
9
  - Represents monetary values as integers, in cents. This avoids floating point
10
10
  rounding errors.
11
11
  - Represents currency as <tt>Money::Currency</tt> instances providing an high level of flexibility.
@@ -17,7 +17,7 @@ Resources:
17
17
 
18
18
  - Website: http://money.rubyforge.org
19
19
  - RDoc API: http://money.rubyforge.org
20
- - Git repository: http://github.com/FooBarWidget/money
20
+ - Git repository: http://github.com/RubyMoney/money
21
21
 
22
22
  == Attention
23
23
 
@@ -36,7 +36,7 @@ Install stable releases with the following command:
36
36
 
37
37
  The development version (hosted on Github) can be installed with:
38
38
 
39
- git clone git://github.com/FooBarWidget/money.git
39
+ git clone git://github.com/RubyMoney/money.git
40
40
  cd money
41
41
  rake install
42
42
 
@@ -107,7 +107,7 @@ exist to provide a basic API you can take advantage of to build your application
107
107
  The priority attribute is an arbitrary numerical value you can assign to the <tt>Money::Currency</tt>
108
108
  and use in sorting/grouping operation.
109
109
 
110
- For instance, let's assume your Rails application needs to a currency selector
110
+ For instance, let's assume your Rails application needs to render a currency selector
111
111
  like the one available at http://finance.yahoo.com/currency-converter/
112
112
  You can create a couple of custom methods to return the list of major_currencies
113
113
  and all_currencies as follows:
@@ -183,23 +183,10 @@ Use the +compose_of+ helper to let Active Record deal with embedding the money
183
183
  object in your models. The following example requires a +cents+ and a +currency+
184
184
  field.
185
185
 
186
- class ProductUnit < ActiveRecord::Base
187
- belongs_to :product
188
- composed_of :price,
189
- :class_name => "Money",
190
- :mapping => [%w(cents cents), %w(currency currency_as_string)],
191
- :constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) }
192
-
193
- private
194
- validate :cents_not_zero
195
-
196
- def cents_not_zero
197
- errors.add("cents", "cannot be zero or less") unless cents > 0
198
- end
199
-
200
- validates_presence_of :sku, :currency
201
- validates_uniqueness_of :sku
202
- end
186
+ composed_of :price,
187
+ :class_name => "Money",
188
+ :mapping => [%w(cents cents), %w(currency currency_as_string)],
189
+ :constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) }
203
190
 
204
191
  For Money 2.2.x and previous versions, simply use the following
205
192
  <tt>composed_of</tt> definition:
@@ -209,4 +196,4 @@ For Money 2.2.x and previous versions, simply use the following
209
196
  :mapping => [%w(cents cents), %w(currency currency)],
210
197
  :constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) }
211
198
 
212
- For further details read the full discussion at http://github.com/FooBarWidget/money/issues/4#comment_224880
199
+ For further details read the full discussion at http://github.com/RubyMoney/money/issues/4#comment_224880
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ begin
12
12
  gem.description = "Money and currency exchange support library."
13
13
  gem.email = "hongli@phusion.nl"
14
14
  gem.homepage = "http://money.rubyforge.org/"
15
- gem.authors = ["Tobias Luetke", "Hongli Lai", "Jeremy McNevin", "Shane Emmons"]
15
+ gem.authors = ["Tobias Luetke", "Hongli Lai", "Jeremy McNevin", "Shane Emmons", "Simone Carletti"]
16
16
  gem.rubyforge_project = "money"
17
17
  gem.add_development_dependency "rspec", ">= 1.2.9"
18
18
  gem.add_development_dependency "hanna", ">= 0.1.12"
@@ -26,10 +26,10 @@ rescue LoadError
26
26
  end
27
27
 
28
28
  require 'spec/rake/spectask'
29
- Spec::Rake::SpecTask.new(:spec) do |spec|
30
- spec.libs << 'lib' << 'test'
31
- spec.spec_files = FileList['test/**/*_spec.rb']
32
- spec.spec_opts << '--format specdoc'
29
+ Spec::Rake::SpecTask.new(:test) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.spec_files = Dir['spec/**/*_spec.rb']
32
+ spec.spec_opts << '--color'
33
33
  end
34
34
 
35
35
  task :spec => :check_dependencies
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.5
1
+ 3.1.0.pre1
data/lib/money.rb CHANGED
@@ -24,3 +24,4 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
24
24
  require 'money/money'
25
25
  require 'money/defaults'
26
26
  require 'money/core_extensions'
27
+ require 'money/deprecations'
@@ -0,0 +1,84 @@
1
+ require 'thread'
2
+
3
+ class Money
4
+ module Bank
5
+
6
+ # The lowest Money::Bank error class.
7
+ # All Money::Bank errors should inherit from it.
8
+ class Error < StandardError
9
+ end
10
+
11
+ # Raised when the bank doesn't know about the conversion rate
12
+ # for specified currencies.
13
+ class UnknownRate < Error
14
+ end
15
+
16
+
17
+ #
18
+ # Money::Bank::Base is the basic interface for creating a money exchange object,
19
+ # also called Bank.
20
+ #
21
+ # A Bank is responsible for storing exchange rates,
22
+ # take a Money object as input and returns the corresponding Money object
23
+ # converted into an other currency.
24
+ #
25
+ # This class exists for aiding in the creating of other classes to exchange money between
26
+ # different currencies. When creating a subclass you will need to implement
27
+ # the following methods to exchange money between currencies:
28
+ #
29
+ # * #exchange_with(Money) # => Money
30
+ #
31
+ # See Money::Bank::VariableExchange for a real example.
32
+ #
33
+ # Also, you can extend Money::Bank::VariableExchange
34
+ # instead of Money::Bank::Base if your bank implementation
35
+ # needs to store rates internally.
36
+ #
37
+ class Base
38
+
39
+ # Returns the singleton instance of the Base bank.
40
+ def self.instance
41
+ @@singleton ||= self.new
42
+ end
43
+
44
+
45
+ # @deprecated +#exchange+ will be removed in v3.2.0, use +#exchange_with+
46
+ #
47
+ # Exchanges the given amount of cents in +from_currency+ to +to_currency+.
48
+ #
49
+ # Returns the amount of cents in +to_currency+ as an integer, rounded down.
50
+ def exchange(cents, from_currency, to_currency, &block)
51
+ Money.deprecate "`Money::Bank::Base#exchange' will be removed in v3.2.0, use #exchange_with instead"
52
+ exchange_with(Money.new(cents, from_currency), to_currency, &block).cents
53
+ end
54
+
55
+ # Exchanges the given +Money+ object to a new +Money+ object in
56
+ # +to_currency+.
57
+ #
58
+ # You should implement this in a subclass,
59
+ # otherwise it will throw a NotImplementedError as a reminder.
60
+ #
61
+ # Returns a new +Money+ object.
62
+ # Raises <tt>NotImplementedError</tt>.
63
+ def exchange_with(from, to_currency, &block)
64
+ raise NotImplementedError, "#exchange_with must be implemented"
65
+ end
66
+
67
+
68
+ # Given two currency strings or object,
69
+ # checks whether they're both the same currency.
70
+ #
71
+ # same_currency?("usd", "USD") # => true
72
+ # same_currency?("usd", "EUR") # => false
73
+ # same_currency?("usd", Currency.new("USD") # => true
74
+ # same_currency?("usd", "USD") # => true
75
+ #
76
+ # Return +true+ if the currencies are the same, +false+ otherwise.
77
+ def same_currency?(currency1, currency2)
78
+ Currency.wrap(currency1) == Currency.wrap(currency2)
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,85 @@
1
+ require 'money/bank/base'
2
+
3
+ class Money
4
+ module Bank
5
+
6
+ # Class for aiding in exchanging money between different currencies.
7
+ # By default, the Money class uses an object of this class (accessible through
8
+ # Money#bank) for performing currency exchanges.
9
+ #
10
+ # By default, Bank::VariableExchange has no knowledge about conversion rates.
11
+ # One must manually specify them with +add_rate+, after which one can perform
12
+ # exchanges with +exchange+. For example:
13
+ #
14
+ # bank = Money::Bank::VariableExchange.new
15
+ # bank.add_rate("USD", "CAD", 1.24515)
16
+ # bank.add_rate("CAD", "USD", 0.803115)
17
+ #
18
+ # # Exchange 100 CAD to USD:
19
+ # bank.exchange(100_00, "CAD", "USD") # => 124
20
+ # # Exchange 100 USD to CAD:
21
+ # bank.exchange(100_00, "USD", "CAD") # => 80
22
+ #
23
+ class VariableExchange < Base
24
+
25
+ def initialize(&block)
26
+ @rates = {}
27
+ @mutex = Mutex.new
28
+ @rounding_method = block
29
+ end
30
+
31
+
32
+ # Exchanges the given +Money+ object to a new +Money+ object in
33
+ # +to_currency+. Returns a new +Money+ object.
34
+ #
35
+ # Raises <tt>Money::Bank::UnknownRate</tt> if the conversion rate is unknown.
36
+ def exchange_with(from, to_currency, &block)
37
+ return from if same_currency?(from.currency, to_currency)
38
+
39
+ rate = get_rate(from.currency, to_currency)
40
+ unless rate
41
+ raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
42
+ end
43
+ _to_currency_ = Currency.wrap(to_currency)
44
+
45
+ cents = from.cents / (from.currency.subunit_to_unit.to_f / _to_currency_.subunit_to_unit.to_f)
46
+
47
+ ex = cents * rate
48
+ ex = if block_given?
49
+ block.call(ex)
50
+ elsif @rounding_method
51
+ @rounding_method.call(ex)
52
+ else
53
+ ex.to_s.to_i
54
+ end
55
+ Money.new(ex, _to_currency_)
56
+ end
57
+
58
+
59
+ # Registers a conversion rate. +from+ and +to+ are both currency names or
60
+ # +Currency+ objects.
61
+ def add_rate(from, to, rate)
62
+ set_rate(from, to, rate)
63
+ end
64
+
65
+ # Set the rate for the given currencies.
66
+ def set_rate(from, to, rate)
67
+ @mutex.synchronize { @rates[rate_key_for(from, to)] = rate }
68
+ end
69
+
70
+ # Retrieve the rate for the given currencies.
71
+ def get_rate(from, to)
72
+ @mutex.synchronize { @rates[rate_key_for(from, to)] }
73
+ end
74
+
75
+ private
76
+
77
+ # Return the rate hashkey for the given currencies.
78
+ def rate_key_for(from, to)
79
+ "#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}".upcase
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+ end
@@ -8,7 +8,14 @@ class Numeric
8
8
  # BigDecimal.new('100').to_money => #<Money @cents=10000>
9
9
  def to_money(currency = Money.default_currency)
10
10
  currency = Money::Currency.new(currency) unless currency.is_a?(Money::Currency)
11
- Money.new((self * currency.subunit_to_unit).to_int, currency)
11
+ amt = self * currency.subunit_to_unit
12
+ amt = case amt.class.to_s
13
+ when 'BigDecimal'
14
+ amt.to_s('F')
15
+ else
16
+ amt.to_s
17
+ end
18
+ Money.new(amt.to_i, currency)
12
19
  end
13
20
  end
14
21
 
@@ -217,6 +217,21 @@ class Money
217
217
  self.id == other_currency.id
218
218
  end
219
219
 
220
+ # synonymous with #==
221
+ def eql?(other_currency)
222
+ self == other_currency
223
+ end
224
+
225
+ # Returns a Fixnum hash value based on the <tt>id</tt> attribute
226
+ # in order to use functions like & (intersection), group_by, etc.
227
+ #
228
+ # [Currency.new(:usd), Currency.new(:eur)] & [Currency.new(:usd)]
229
+ # # => [Currency.new(:usd)]
230
+ #
231
+ def hash
232
+ id.hash
233
+ end
234
+
220
235
  # Returns a string representation
221
236
  # corresponding to the upcase <tt>id</tt> attribute.
222
237
  #
@@ -242,7 +257,7 @@ class Money
242
257
 
243
258
 
244
259
  def method_missing(method, *args, &block)
245
- warn "DEPRECATION MESSAGE: `currency' is now a Currency instance. Call `currency.to_s.#{method}' instead."
260
+ Money.deprecate "`currency' is now a Currency instance. Call `currency.to_s.#{method}' instead."
246
261
  iso_code.send(method, *args, &block)
247
262
  end
248
263
 
@@ -10,21 +10,15 @@ class Money
10
10
  end
11
11
 
12
12
  def [](key)
13
- deprecate
13
+ Money.deprecate(@message)
14
14
  super
15
15
  end
16
16
 
17
17
  def []=(value)
18
- deprecate
18
+ Money.deprecate(@message)
19
19
  super
20
20
  end
21
21
 
22
- private
23
-
24
- def deprecate
25
- warn "DEPRECATION MESSAGE: #{@message}"
26
- end
27
-
28
22
  end
29
23
 
30
24
  # @deprecated See Money::Currency#symbol
@@ -42,16 +36,16 @@ class Money
42
36
  "GHC" => "¢",
43
37
  "BRL" => "R$ ",
44
38
  # Everything else defaults to '¤'
45
- }, "Money::SYMBOLS has no longer effect. See Money::Currency#symbol.")
39
+ }, "Money::SYMBOLS has no longer effect and will be removed in v3.2.0. See Money::Currency#symbol.")
46
40
 
47
41
  SEPARATORS = DeprecatedHash.new({
48
42
  "BRL" => ",",
49
43
  # Everything else defaults to '.'
50
- }, "Money::SEPARATORS is deprecated. See Money::Currency#separator.")
44
+ }, "Money::SEPARATORS is deprecated and will be removed in v3.2.0. See Money::Currency#separator.")
51
45
 
52
46
  DELIMITERS = DeprecatedHash.new({
53
47
  "BRL" => ".",
54
48
  # Everything else defaults to ","
55
- }, "Money::DELIMITERS is deprecated. See Money::Currency#delimiter.")
49
+ }, "Money::DELIMITERS is deprecated and will be removed in Money v3.2.0. See Money::Currency#delimiter.")
56
50
 
57
51
  end
@@ -0,0 +1,21 @@
1
+ class Money
2
+
3
+ def self.deprecate(message)
4
+ warn "DEPRECATION WARNING: #{message}"
5
+ end
6
+
7
+
8
+ # Money::VariableExchangeBank is the legacy default bank
9
+ # shipped with Money. The class has been superseded by
10
+ # Money::Bank::VariableExchange.
11
+ #
12
+ # @deprecate Use Money::Bank::VariableExchange instead.
13
+ class VariableExchangeBank < Bank::VariableExchange # :nodoc:
14
+ def initialize(*args)
15
+ Money.deprecate "Money::VariableExchangeBank is deprecated and will be removed in v3.2.0. " +
16
+ "Use Money::Bank::VariableExchange instead."
17
+ super
18
+ end
19
+ end
20
+
21
+ end
data/lib/money/money.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require 'money/currency'
3
- require 'money/variable_exchange_bank'
3
+ require 'money/bank/variable_exchange'
4
4
 
5
5
  # Represents an amount of money in a certain currency.
6
6
  class Money
@@ -25,7 +25,7 @@ class Money
25
25
  # money2.bank # => bank2
26
26
  # money1.bank # => bank1
27
27
  #
28
- # The default value for this property is an instance if VariableExchangeBank.
28
+ # The default value for this property is an instance if Bank::VariableExchange.
29
29
  # It allows one to specify custom exchange rates:
30
30
  #
31
31
  # Money.default_bank.add_rate("USD", "CAD", 1.24515)
@@ -40,7 +40,7 @@ class Money
40
40
  attr_accessor :default_currency
41
41
  end
42
42
 
43
- self.default_bank = VariableExchangeBank.instance
43
+ self.default_bank = Bank::VariableExchange.instance
44
44
  self.default_currency = Currency.new("USD")
45
45
 
46
46
 
@@ -106,7 +106,7 @@ class Money
106
106
  def ==(other_money)
107
107
  if other_money.respond_to?(:to_money)
108
108
  other_money = other_money.to_money
109
- cents == other_money.cents && bank.same_currency?(currency, other_money.currency)
109
+ cents == other_money.cents && self.currency == other_money.currency
110
110
  else
111
111
  false
112
112
  end
@@ -117,6 +117,17 @@ class Money
117
117
  self == other_money
118
118
  end
119
119
 
120
+ # Returns a Fixnum hash value based on the <tt>cents</tt> and
121
+ # <tt>currency</tt> attributes in order to use functions like
122
+ # & (intersection), group_by, etc.
123
+ #
124
+ # [Money.new(1_00, :eur), Money.new(2_00, :usd)] & [Money.new(1_00, :eur)]
125
+ # # => [Money.new(1_00, :eur)]
126
+ #
127
+ def hash
128
+ [cents.hash, currency.hash].hash
129
+ end
130
+
120
131
  # Compares this money object against another object. +other_money+ must respond
121
132
  # to #to_money.
122
133
  #
@@ -128,7 +139,7 @@ class Money
128
139
  def <=>(other_money)
129
140
  if other_money.respond_to?(:to_money)
130
141
  other_money = other_money.to_money
131
- if bank.same_currency?(currency, other_money.currency)
142
+ if self.currency == other_money.currency
132
143
  cents <=> other_money.cents
133
144
  else
134
145
  cents <=> other_money.exchange_to(currency).cents
@@ -444,7 +455,7 @@ class Money
444
455
  #
445
456
  def exchange_to(other_currency)
446
457
  other_currency = Currency.wrap(other_currency)
447
- Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
458
+ @bank.exchange_with(self, other_currency)
448
459
  end
449
460
 
450
461
  # Receive a money object with the same amount as the current Money object
@@ -484,4 +495,5 @@ class Money
484
495
  end
485
496
  rules
486
497
  end
498
+
487
499
  end
data/money.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{money}
8
- s.version = "3.0.5"
8
+ s.version = "3.1.0.pre1"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Tobias Luetke", "Hongli Lai", "Jeremy McNevin", "Shane Emmons"]
12
- s.date = %q{2010-07-16}
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tobias Luetke", "Hongli Lai", "Jeremy McNevin", "Shane Emmons", "Simone Carletti"]
12
+ s.date = %q{2010-08-03}
13
13
  s.description = %q{Money and currency exchange support library.}
14
14
  s.email = %q{hongli@phusion.nl}
15
15
  s.extra_rdoc_files = [
@@ -26,17 +26,21 @@ Gem::Specification.new do |s|
26
26
  "Rakefile",
27
27
  "VERSION",
28
28
  "lib/money.rb",
29
+ "lib/money/bank/base.rb",
30
+ "lib/money/bank/variable_exchange.rb",
29
31
  "lib/money/core_extensions.rb",
30
32
  "lib/money/currency.rb",
31
33
  "lib/money/defaults.rb",
32
- "lib/money/errors.rb",
34
+ "lib/money/deprecations.rb",
33
35
  "lib/money/money.rb",
34
- "lib/money/variable_exchange_bank.rb",
35
36
  "money.gemspec",
36
- "test/core_extensions_spec.rb",
37
- "test/currency_spec.rb",
38
- "test/exchange_bank_spec.rb",
39
- "test/money_spec.rb"
37
+ "spec/bank/base_spec.rb",
38
+ "spec/bank/variable_exchange_spec.rb",
39
+ "spec/core_extensions_spec.rb",
40
+ "spec/currency_spec.rb",
41
+ "spec/deprecations_spec.rb",
42
+ "spec/money_spec.rb",
43
+ "spec/spec_helper.rb"
40
44
  ]
41
45
  s.homepage = %q{http://money.rubyforge.org/}
42
46
  s.rdoc_options = ["--charset=UTF-8"]
@@ -45,10 +49,13 @@ Gem::Specification.new do |s|
45
49
  s.rubygems_version = %q{1.3.7}
46
50
  s.summary = %q{Money and currency exchange support library}
47
51
  s.test_files = [
48
- "test/core_extensions_spec.rb",
49
- "test/currency_spec.rb",
50
- "test/exchange_bank_spec.rb",
51
- "test/money_spec.rb"
52
+ "spec/bank/base_spec.rb",
53
+ "spec/bank/variable_exchange_spec.rb",
54
+ "spec/core_extensions_spec.rb",
55
+ "spec/currency_spec.rb",
56
+ "spec/deprecations_spec.rb",
57
+ "spec/money_spec.rb",
58
+ "spec/spec_helper.rb"
52
59
  ]
53
60
 
54
61
  if s.respond_to? :specification_version then
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+
3
+ describe Money::Bank::Base do
4
+
5
+ before :each do
6
+ @bank = Money::Bank::Base.new
7
+ end
8
+
9
+ describe '#exchange' do
10
+ it 'should raise NotImplementedError' do
11
+ lambda { @bank.exchange(100, 'USD', 'EUR') }.should raise_exception(NotImplementedError)
12
+ end
13
+ end
14
+
15
+ describe '#exchange_with' do
16
+ it 'should raise NotImplementedError' do
17
+ lambda { @bank.exchange_with(Money.new(100, 'USD'), 'EUR') }.should raise_exception(NotImplementedError)
18
+ end
19
+ end
20
+
21
+ describe '#same_currency?' do
22
+ it 'should accept str/str' do
23
+ lambda{@bank.send(:same_currency?, 'USD', 'EUR')}.should_not raise_exception
24
+ end
25
+
26
+ it 'should accept currency/str' do
27
+ lambda{@bank.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR')}.should_not raise_exception
28
+ end
29
+
30
+ it 'should accept str/currency' do
31
+ lambda{@bank.send(:same_currency?, 'USD', Money::Currency.wrap('EUR'))}.should_not raise_exception
32
+ end
33
+
34
+ it 'should accept currency/currency' do
35
+ lambda{@bank.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception
36
+ end
37
+
38
+ it 'should return `true` when currencies match' do
39
+ @bank.send(:same_currency?, 'USD', 'USD').should == true
40
+ @bank.send(:same_currency?, Money::Currency.wrap('USD'), 'USD').should == true
41
+ @bank.send(:same_currency?, 'USD', Money::Currency.wrap('USD')).should == true
42
+ @bank.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('USD')).should == true
43
+ end
44
+
45
+ it 'should return `false` when currencies do not match' do
46
+ @bank.send(:same_currency?, 'USD', 'EUR').should == false
47
+ @bank.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR').should == false
48
+ @bank.send(:same_currency?, 'USD', Money::Currency.wrap('EUR')).should == false
49
+ @bank.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')).should == false
50
+ end
51
+
52
+ it 'should raise an UnknownCurrency exception when an unknown currency is passed' do
53
+ lambda{@bank.send(:same_currency?, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency)
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,230 @@
1
+ require "spec_helper"
2
+
3
+ describe Money::Bank::VariableExchange do
4
+
5
+ describe '#new without block' do
6
+ before :each do
7
+ @bank = Money::Bank::VariableExchange.new
8
+ end
9
+
10
+ describe '#exchange' do
11
+ before :each do
12
+ @bank.send(:set_rate, 'USD', 'EUR', 1.33)
13
+ end
14
+
15
+ it 'should accept str/str' do
16
+ lambda{@bank.exchange(100, 'USD', 'EUR')}.should_not raise_exception
17
+ end
18
+
19
+ it 'should accept currency/str' do
20
+ lambda{@bank.exchange(100, Money::Currency.wrap('USD'), 'EUR')}.should_not raise_exception
21
+ end
22
+
23
+ it 'should accept str/currency' do
24
+ lambda{@bank.exchange(100, 'USD', Money::Currency.wrap('EUR'))}.should_not raise_exception
25
+ end
26
+
27
+ it 'should accept currency/currency' do
28
+ lambda{@bank.exchange(100, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception
29
+ end
30
+
31
+ it 'should exchange one currency to another' do
32
+ @bank.exchange(100, 'USD', 'EUR').should == 133
33
+ end
34
+
35
+ it 'should truncate extra digits' do
36
+ @bank.exchange(10, 'USD', 'EUR').should == 13
37
+ end
38
+
39
+ it 'should raise an UnknownCurrency exception when an unknown currency is requested' do
40
+ lambda{@bank.exchange(100, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency)
41
+ end
42
+
43
+ it 'should raise an UnknownRate exception when an unknown rate is requested' do
44
+ lambda{@bank.exchange(100, 'USD', 'JPY')}.should raise_exception(Money::Bank::UnknownRate)
45
+ end
46
+
47
+ it 'should round the exchanged result down' do
48
+ @bank.add_rate("USD", "EUR", 0.788332676)
49
+ @bank.add_rate("EUR", "YEN", 122.631477)
50
+ @bank.exchange(10_00, "USD", "EUR").should == 788
51
+ @bank.exchange(500_00, "EUR", "YEN").should == 6131573
52
+ end
53
+
54
+ it 'should accept a custom truncation method' do
55
+ proc = Proc.new { |n| n.ceil }
56
+ @bank.exchange(10, 'USD', 'EUR', &proc).should == 14
57
+ end
58
+
59
+
60
+ context 'sterling to euros using a rate of 1.39' do
61
+ it 'returns the correct amount' do
62
+ @bank.add_rate('GBP', 'EUR', 1.38)
63
+ @bank.exchange(10000, 'GBP', 'EUR').should == 13800
64
+ end
65
+ end
66
+
67
+ context 'dollars to euros using a rate of 0.86' do
68
+ it 'returns the correct amount' do
69
+ @bank.add_rate('USD', 'EUR', 0.86)
70
+ @bank.exchange(10000, 'USD', 'EUR').should == 8600
71
+ end
72
+ end
73
+
74
+ context 'TND to USD using a rate of 0.67138' do
75
+ it 'returns the correct amount' do
76
+ @bank.add_rate('TND', 'USD', 0.67138)
77
+ @bank.exchange(1000, 'TND', 'USD').should == 67
78
+ end
79
+ end
80
+
81
+ context 'USD to TND using a rate of 1.32862' do
82
+ it 'returns the correct amount' do
83
+ @bank.add_rate('USD', 'TND', 1.32862)
84
+ @bank.exchange(1000, 'USD', 'TND').should == 13286
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#exchange_with' do
90
+ before :each do
91
+ @bank.send(:set_rate, 'USD', 'EUR', 1.33)
92
+ end
93
+
94
+ it 'should accept str' do
95
+ lambda{@bank.exchange_with(Money.new(100, 'USD'), 'EUR')}.should_not raise_exception
96
+ end
97
+
98
+ it 'should accept currency' do
99
+ lambda{@bank.exchange_with(Money.new(100, 'USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception
100
+ end
101
+
102
+ it 'should exchange one currency to another' do
103
+ @bank.exchange_with(Money.new(100, 'USD'), 'EUR').should == Money.new(133, 'EUR')
104
+ end
105
+
106
+ it 'should truncate extra digits' do
107
+ @bank.exchange_with(Money.new(10, 'USD'), 'EUR').should == Money.new(13, 'EUR')
108
+ end
109
+
110
+ it 'should raise an UnknownCurrency exception when an unknown currency is requested' do
111
+ lambda{@bank.exchange_with(Money.new(100, 'USD'), 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency)
112
+ end
113
+
114
+ it 'should raise an UnknownRate exception when an unknown rate is requested' do
115
+ lambda{@bank.exchange_with(Money.new(100, 'USD'), 'JPY')}.should raise_exception(Money::Bank::UnknownRate)
116
+ end
117
+
118
+ #it 'should round the exchanged result down' do
119
+ # @bank.add_rate("USD", "EUR", 0.788332676)
120
+ # @bank.add_rate("EUR", "YEN", 122.631477)
121
+ # @bank.exchange_with(Money.new(10_00, "USD"), "EUR").should == Money.new(788, "EUR")
122
+ # @bank.exchange_with(Money.new(500_00, "EUR"), "YEN").should == Money.new(6131573, "YEN")
123
+ #end
124
+
125
+ it 'should accept a custom truncation method' do
126
+ proc = Proc.new { |n| n.ceil }
127
+ @bank.exchange_with(Money.new(10, 'USD'), 'EUR', &proc).should == Money.new(14, 'EUR')
128
+ end
129
+ end
130
+
131
+ describe "#add_rate" do
132
+ it "should add rates correctly" do
133
+ @bank.add_rate("USD", "EUR", 0.788332676)
134
+ @bank.add_rate("EUR", "YEN", 122.631477)
135
+
136
+ @bank.instance_variable_get(:@rates)['USD_TO_EUR'].should == 0.788332676
137
+ @bank.instance_variable_get(:@rates)['EUR_TO_JPY'].should == 122.631477
138
+ end
139
+
140
+ it "should treat currency names case-insensitively" do
141
+ @bank.add_rate("usd", "eur", 1)
142
+ @bank.instance_variable_get(:@rates)['USD_TO_EUR'].should == 1
143
+ end
144
+ end
145
+
146
+ describe '#set_rate' do
147
+ it 'should set a rate' do
148
+ @bank.set_rate('USD', 'EUR', 1.25)
149
+ @bank.instance_variable_get(:@rates)['USD_TO_EUR'].should == 1.25
150
+ end
151
+
152
+ it 'should raise an UnknownCurrency exception when an unknown currency is passed' do
153
+ lambda{ @bank.set_rate('AAA', 'BBB', 1.25) }.should raise_exception(Money::Currency::UnknownCurrency)
154
+ end
155
+ end
156
+
157
+ describe '#get_rate' do
158
+ it 'should return a rate' do
159
+ @bank.set_rate('USD', 'EUR', 1.25)
160
+ @bank.get_rate('USD', 'EUR').should == 1.25
161
+ end
162
+
163
+ it 'should raise an UnknownCurrency exception when an unknown currency is requested' do
164
+ lambda{ @bank.get_rate('AAA', 'BBB') }.should raise_exception(Money::Currency::UnknownCurrency)
165
+ end
166
+ end
167
+
168
+ describe '#rate_key_for' do
169
+ it 'should accept str/str' do
170
+ lambda{@bank.send(:rate_key_for, 'USD', 'EUR')}.should_not raise_exception
171
+ end
172
+
173
+ it 'should accept currency/str' do
174
+ lambda{@bank.send(:rate_key_for, Money::Currency.wrap('USD'), 'EUR')}.should_not raise_exception
175
+ end
176
+
177
+ it 'should accept str/currency' do
178
+ lambda{@bank.send(:rate_key_for, 'USD', Money::Currency.wrap('EUR'))}.should_not raise_exception
179
+ end
180
+
181
+ it 'should accept currency/currency' do
182
+ lambda{@bank.send(:rate_key_for, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception
183
+ end
184
+
185
+ it 'should return a hashkey based on the passed arguments' do
186
+ @bank.send(:rate_key_for, 'USD', 'EUR').should == 'USD_TO_EUR'
187
+ @bank.send(:rate_key_for, Money::Currency.wrap('USD'), 'EUR').should == 'USD_TO_EUR'
188
+ @bank.send(:rate_key_for, 'USD', Money::Currency.wrap('EUR')).should == 'USD_TO_EUR'
189
+ @bank.send(:rate_key_for, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')).should == 'USD_TO_EUR'
190
+ end
191
+
192
+ it 'should raise an UnknownCurrency exception when an unknown currency is passed' do
193
+ lambda{@bank.send(:rate_key_for, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency)
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+
200
+ describe '#new with &block' do
201
+ before :each do
202
+ proc = Proc.new { |n| n.ceil }
203
+ @bank = Money::Bank::VariableExchange.new(&proc)
204
+ @bank.add_rate('USD', 'EUR', 1.33)
205
+ end
206
+
207
+ describe '#exchange' do
208
+ it 'should use a stored truncation method' do
209
+ @bank.exchange(10, 'USD', 'EUR').should == 14
210
+ end
211
+
212
+ it 'should use a custom truncation method over a stored one' do
213
+ proc = Proc.new { |n| n.ceil + 1 }
214
+ @bank.exchange(10, 'USD', 'EUR', &proc).should == 15
215
+ end
216
+ end
217
+
218
+ describe '#exchange_with' do
219
+ it 'should use a stored truncation method' do
220
+ @bank.exchange_with(Money.new(10, 'USD'), 'EUR').should == Money.new(14, 'EUR')
221
+ end
222
+
223
+ it 'should use a custom truncation method over a stored one' do
224
+ proc = Proc.new { |n| n.ceil + 1 }
225
+ @bank.exchange_with(Money.new(10, 'USD'), 'EUR', &proc).should == Money.new(15, 'EUR')
226
+ end
227
+ end
228
+ end
229
+
230
+ end
@@ -1,6 +1,4 @@
1
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
-
3
- require 'money/core_extensions'
1
+ require "spec_helper"
4
2
 
5
3
  describe "Money core extensions" do
6
4
  specify "Numberic#to_money works" do
@@ -17,6 +15,11 @@ describe "Money core extensions" do
17
15
  money.cents.should == 1234_00
18
16
  money.currency.should == Money.default_currency
19
17
  end
18
+
19
+ specify "#issue/15" do
20
+ amount = 555.55.to_money
21
+ amount.should == Money.new(55555)
22
+ end
20
23
 
21
24
  specify "Numeric#to_money accepts optional currency" do
22
25
  1234.to_money('USD').should == Money.new(123400, 'USD')
@@ -1,10 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
-
5
- require 'money/money'
6
- require 'money/currency'
7
- require 'money/defaults'
3
+ require "spec_helper"
8
4
 
9
5
  describe Money::Currency do
10
6
 
@@ -36,6 +32,21 @@ describe Money::Currency do
36
32
  Money::Currency.new(:eur).should == Money::Currency.new(:eur)
37
33
  Money::Currency.new(:eur).should_not == Money::Currency.new(:usd)
38
34
  end
35
+
36
+ specify "#eql? should return true if #== returns true" do
37
+ Money::Currency.new(:eur).eql?(Money::Currency.new(:eur)).should be true
38
+ Money::Currency.new(:eur).eql?(Money::Currency.new(:usd)).should be false
39
+ end
40
+
41
+ specify "#hash should return the same value for equal objects" do
42
+ Money::Currency.new(:eur).hash.should == Money::Currency.new(:eur).hash
43
+ Money::Currency.new(:eur).hash.should_not == Money::Currency.new(:usd).hash
44
+ end
45
+
46
+ specify "#hash can be used to return the intersection of Currency object arrays" do
47
+ intersection = [Money::Currency.new(:eur), Money::Currency.new(:usd)] & [Money::Currency.new(:eur)]
48
+ intersection.should == [Money::Currency.new(:eur)]
49
+ end
39
50
 
40
51
  specify "#<=> should compare objects by priority" do
41
52
  Money::Currency.new(:cad).should > Money::Currency.new(:usd)
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ describe "Money deprecations" do
4
+
5
+ describe Money::VariableExchangeBank do
6
+ it "should be deprecated" do
7
+ Money.should_receive(:deprecate)
8
+ Money::VariableExchangeBank.new.should_not be_nil
9
+ end
10
+
11
+ it "should extend Money::Bank::VariableExchange" do
12
+ Money::VariableExchangeBank.new.should be_kind_of Money::Bank::VariableExchange
13
+ end
14
+ end
15
+
16
+ end
@@ -1,11 +1,10 @@
1
1
  # encoding: utf-8
2
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
3
- require 'money/money'
4
- require 'money/defaults'
2
+
3
+ require "spec_helper"
5
4
 
6
5
  describe Money do
7
- it "is associated to the singleton instance of VariableExchangeBank by default" do
8
- Money.new(0).bank.object_id.should == Money::VariableExchangeBank.instance.object_id
6
+ it "is associated to the singleton instance of Bank::VariableExchange by default" do
7
+ Money.new(0).bank.should be_equal Money::Bank::VariableExchange.instance
9
8
  end
10
9
 
11
10
  specify "#cents returns the amount of cents passed to the constructor" do
@@ -64,13 +63,13 @@ describe Money do
64
63
 
65
64
  specify "#exchange_to exchanges the amount via its exchange bank" do
66
65
  money = Money.new(100_00, "USD")
67
- money.bank.should_receive(:exchange).with(100_00, Money::Currency.new("USD"), Money::Currency.new("EUR")).and_return(200_00)
66
+ money.bank.should_receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
68
67
  money.exchange_to("EUR")
69
68
  end
70
69
 
71
70
  specify "#exchange_to exchanges the amount properly" do
72
71
  money = Money.new(100_00, "USD")
73
- money.bank.should_receive(:exchange).with(100_00, Money::Currency.new("USD"), Money::Currency.new("EUR")).and_return(200_00)
72
+ money.bank.should_receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
74
73
  money.exchange_to("EUR").should == Money.new(200_00, "EUR")
75
74
  end
76
75
 
@@ -163,6 +162,19 @@ describe Money do
163
162
  Money.new(1_00, "USD").eql?(/foo/).should be false
164
163
  Money.new(1_00, "USD").eql?(nil).should be false
165
164
  end
165
+
166
+ specify "#hash should return the same value for equal objects" do
167
+ Money.new(1_00, :eur).hash.should == Money.new(1_00, :eur).hash
168
+ Money.new(2_00, :usd).hash.should == Money.new(2_00, :usd).hash
169
+ Money.new(1_00, :eur).hash.should_not == Money.new(2_00, :eur).hash
170
+ Money.new(1_00, :eur).hash.should_not == Money.new(1_00, :usd).hash
171
+ Money.new(1_00, :eur).hash.should_not == Money.new(2_00, :usd).hash
172
+ end
173
+
174
+ specify "#hash can be used to return the intersection of Money object arrays" do
175
+ intersection = [Money.new(1_00, :eur), Money.new(1_00, :usd)] & [Money.new(1_00, :eur)]
176
+ intersection.should == [Money.new(1_00, :eur)]
177
+ end
166
178
 
167
179
  specify "#<=> can be used to compare with a String money value" do
168
180
  (Money.new(1_00) <=> "1.00").should == 0
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ require "spec"
5
+ require "money"
6
+
7
+ Spec::Runner.configure do |config|
8
+ end
metadata CHANGED
@@ -1,24 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
5
- prerelease: false
4
+ hash: 33582789
5
+ prerelease: true
6
6
  segments:
7
7
  - 3
8
+ - 1
8
9
  - 0
9
- - 5
10
- version: 3.0.5
10
+ - pre1
11
+ version: 3.1.0.pre1
11
12
  platform: ruby
12
13
  authors:
13
14
  - Tobias Luetke
14
15
  - Hongli Lai
15
16
  - Jeremy McNevin
16
17
  - Shane Emmons
18
+ - Simone Carletti
17
19
  autorequire:
18
20
  bindir: bin
19
21
  cert_chain: []
20
22
 
21
- date: 2010-07-16 00:00:00 -04:00
23
+ date: 2010-08-03 00:00:00 -04:00
22
24
  default_executable:
23
25
  dependencies:
24
26
  - !ruby/object:Gem::Dependency
@@ -72,17 +74,21 @@ files:
72
74
  - Rakefile
73
75
  - VERSION
74
76
  - lib/money.rb
77
+ - lib/money/bank/base.rb
78
+ - lib/money/bank/variable_exchange.rb
75
79
  - lib/money/core_extensions.rb
76
80
  - lib/money/currency.rb
77
81
  - lib/money/defaults.rb
78
- - lib/money/errors.rb
82
+ - lib/money/deprecations.rb
79
83
  - lib/money/money.rb
80
- - lib/money/variable_exchange_bank.rb
81
84
  - money.gemspec
82
- - test/core_extensions_spec.rb
83
- - test/currency_spec.rb
84
- - test/exchange_bank_spec.rb
85
- - test/money_spec.rb
85
+ - spec/bank/base_spec.rb
86
+ - spec/bank/variable_exchange_spec.rb
87
+ - spec/core_extensions_spec.rb
88
+ - spec/currency_spec.rb
89
+ - spec/deprecations_spec.rb
90
+ - spec/money_spec.rb
91
+ - spec/spec_helper.rb
86
92
  has_rdoc: true
87
93
  homepage: http://money.rubyforge.org/
88
94
  licenses: []
@@ -104,12 +110,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
110
  required_rubygems_version: !ruby/object:Gem::Requirement
105
111
  none: false
106
112
  requirements:
107
- - - ">="
113
+ - - ">"
108
114
  - !ruby/object:Gem::Version
109
- hash: 3
115
+ hash: 25
110
116
  segments:
111
- - 0
112
- version: "0"
117
+ - 1
118
+ - 3
119
+ - 1
120
+ version: 1.3.1
113
121
  requirements: []
114
122
 
115
123
  rubyforge_project: money
@@ -118,7 +126,10 @@ signing_key:
118
126
  specification_version: 3
119
127
  summary: Money and currency exchange support library
120
128
  test_files:
121
- - test/core_extensions_spec.rb
122
- - test/currency_spec.rb
123
- - test/exchange_bank_spec.rb
124
- - test/money_spec.rb
129
+ - spec/bank/base_spec.rb
130
+ - spec/bank/variable_exchange_spec.rb
131
+ - spec/core_extensions_spec.rb
132
+ - spec/currency_spec.rb
133
+ - spec/deprecations_spec.rb
134
+ - spec/money_spec.rb
135
+ - spec/spec_helper.rb
data/lib/money/errors.rb DELETED
@@ -1,4 +0,0 @@
1
- class Money
2
- class UnknownRate < StandardError
3
- end
4
- end
@@ -1,82 +0,0 @@
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
- #
21
- class Money
22
- class VariableExchangeBank
23
- # Returns the singleton instance of VariableExchangeBank.
24
- #
25
- # By default, <tt>Money.default_bank</tt> returns the same object.
26
- def self.instance
27
- @@singleton
28
- end
29
-
30
- def initialize(&block)
31
- @rates = {}
32
- @mutex = Mutex.new
33
- @rounding_method = block
34
- end
35
-
36
- # Registers a conversion rate. +from+ and +to+ are both currency names.
37
- def add_rate(from, to, rate)
38
- @mutex.synchronize do
39
- @rates["#{from}_TO_#{to}".upcase] = rate
40
- end
41
- end
42
-
43
- # Gets the rate for exchanging the currency named +from+ to the currency
44
- # named +to+. Returns nil if the rate is unknown.
45
- def get_rate(from, to)
46
- @mutex.synchronize do
47
- @rates["#{from}_TO_#{to}".upcase]
48
- end
49
- end
50
-
51
- # Given two currency names, checks whether they're both the same currency.
52
- #
53
- # bank = VariableExchangeBank.new
54
- # bank.same_currency?("usd", "USD") # => true
55
- # bank.same_currency?("usd", "EUR") # => false
56
- def same_currency?(currency1, currency2)
57
- Currency.wrap(currency1) == Currency.wrap(currency2)
58
- end
59
-
60
- # Exchange the given amount of cents in +from_currency+ to +to_currency+.
61
- # Returns the amount of cents in +to_currency+ as an integer, rounded down.
62
- #
63
- # If the conversion rate is unknown, then Money::UnknownRate will be raised.
64
- def exchange(cents, from_currency, to_currency, &block)
65
- rate = get_rate(from_currency, to_currency)
66
- if !rate
67
- raise Money::UnknownRate, "No conversion rate known for '#{from_currency}' -> '#{to_currency}'"
68
- end
69
- _from_currency_ = Currency.wrap(from_currency)
70
- _to_currency_ = Currency.wrap(to_currency)
71
-
72
- _cents_ = cents / (_from_currency_.subunit_to_unit.to_f / _to_currency_.subunit_to_unit.to_f)
73
-
74
- ex = _cents_ * rate
75
- return block.call(ex) if block_given?
76
- return @rounding_method.call(ex) unless @rounding_method.nil?
77
- ex.to_s.to_i
78
- end
79
-
80
- @@singleton = VariableExchangeBank.new
81
- end
82
- end
@@ -1,101 +0,0 @@
1
- $LOAD_PATH.unshift(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
-
46
- describe '.exchange' do
47
- context 'sterling to euros using a rate of 1.39' do
48
- it 'returns the correct amount' do
49
- @bank.add_rate('GBP', 'EUR', 1.38)
50
- @bank.exchange(10000, 'GBP', 'EUR').should == 13800
51
- end
52
- end
53
-
54
- context 'dollars to euros using a rate of 0.86' do
55
- it 'returns the correct amount' do
56
- @bank.add_rate('USD', 'EUR', 0.86)
57
- @bank.exchange(10000, 'USD', 'EUR').should == 8600
58
- end
59
- end
60
-
61
- context 'TND to USD using a rate of 0.67138' do
62
- it 'returns the correct amount' do
63
- @bank.add_rate('TND', 'USD', 0.67138)
64
- @bank.exchange(1000, 'TND', 'USD').should == 67
65
- end
66
- end
67
-
68
- context 'USD to TND using a rate of 1.32862' do
69
- it 'returns the correct amount' do
70
- @bank.add_rate('USD', 'TND', 1.32862)
71
- @bank.exchange(1000, 'USD', 'TND').should == 13286
72
- end
73
- end
74
- end
75
-
76
- context 'using custom rounding methods' do
77
- describe 'passing a rounding method to #new' do
78
- before :each do
79
- mth = Proc.new{|ex| ex.ceil }
80
- @bank = Money::VariableExchangeBank.new(&mth)
81
- end
82
-
83
- it 'should use @rounding_method' do
84
- @bank.add_rate('USD', 'EUR', 0.86)
85
- @bank.exchange(10, 'USD', 'EUR').should == 9
86
- end
87
- end
88
-
89
- describe 'passing a rounding method to #exchange' do
90
- it 'should use &block' do
91
- @bank.add_rate('USD', 'EUR', 0.86)
92
- @bank.exchange(10, 'USD', 'EUR').should == 8
93
-
94
- mth = Proc.new{|ex| ex.ceil }
95
- @bank.exchange(10, 'USD', 'EUR', &mth).should == 9
96
-
97
- @bank.exchange(10, 'USD', 'EUR'){|ex| ex.ceil }.should == 9
98
- end
99
- end
100
- end
101
- end