money 3.0.5 → 3.1.0.pre1

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/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