money 6.5.1 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,10 +16,37 @@ class Money
16
16
  extend Money::Currency::Loader
17
17
  extend Money::Currency::Heuristics
18
18
 
19
+ # Keeping cached instances in sync between threads
20
+ @@mutex = Mutex.new
21
+
22
+ # Thrown when a Currency has been registered without all the attributes
23
+ # which are required for the current action.
24
+ class MissingAttributeError < StandardError
25
+ def initialize(method, currency, attribute)
26
+ super(
27
+ "Can't call Currency.#{method} - currency '#{currency}' is missing "\
28
+ "the attribute '#{attribute}'"
29
+ )
30
+ end
31
+ end
32
+
19
33
  # Thrown when an unknown currency is requested.
20
34
  class UnknownCurrency < ArgumentError; end
21
35
 
22
36
  class << self
37
+ alias_method :original_new, :new
38
+ def new(id)
39
+ id = id.to_s.downcase
40
+ unless stringified_keys.include?(id)
41
+ raise UnknownCurrency, "Unknown currency '#{id}'"
42
+ end
43
+
44
+ @@mutex.synchronize { instances[id] } || super
45
+ end
46
+
47
+ def instances
48
+ @instances ||= Hash.new { |h, k| h[k] = original_new(k) }
49
+ end
23
50
 
24
51
  # Lookup a currency with given +id+ an returns a +Currency+ instance on
25
52
  # success, +nil+ otherwise.
@@ -103,7 +130,13 @@ class Money
103
130
  # Money::Currency.iso_codes()
104
131
  # [#<Currency ..USD>, 'CAD', 'EUR']...
105
132
  def all
106
- table.keys.map {|curr| Currency.new(curr)}.sort_by(&:priority)
133
+ table.keys.map do |curr|
134
+ c = Currency.new(curr)
135
+ if c.priority.nil?
136
+ raise MissingAttributeError.new(:all, c.id, :priority)
137
+ end
138
+ c
139
+ end.sort_by(&:priority)
107
140
  end
108
141
 
109
142
  # We need a string-based validator before creating an unbounded number of
@@ -135,6 +168,7 @@ class Money
135
168
  # @option delimiter [String] character between each thousands place
136
169
  def register(curr)
137
170
  key = curr.fetch(:iso_code).downcase.to_sym
171
+ @@mutex.synchronize { instances.delete(key.to_s) }
138
172
  @table[key] = curr
139
173
  @stringified_keys = stringify_keys
140
174
  end
@@ -171,40 +205,40 @@ class Money
171
205
  end
172
206
  end
173
207
 
174
- # @!attribute [r] id
208
+ # @!attribute [r] id
175
209
  # @return [Symbol] The symbol used to identify the currency, usually THE
176
210
  # lowercase +iso_code+ attribute.
177
- # @!attribute [r] priority
211
+ # @!attribute [r] priority
178
212
  # @return [Integer] A numerical value you can use to sort/group the
179
213
  # currency list.
180
- # @!attribute [r] iso_code
214
+ # @!attribute [r] iso_code
181
215
  # @return [String] The international 3-letter code as defined by the ISO
182
216
  # 4217 standard.
183
- # @!attribute [r] iso_numeric
217
+ # @!attribute [r] iso_numeric
184
218
  # @return [String] The international 3-numeric code as defined by the ISO
185
219
  # 4217 standard.
186
- # @!attribute [r] name
220
+ # @!attribute [r] name
187
221
  # @return [String] The currency name.
188
- # @!attribute [r] symbol
222
+ # @!attribute [r] symbol
189
223
  # @return [String] The currency symbol (UTF-8 encoded).
190
- # @!attribute [r] disambiguate_symbol
224
+ # @!attribute [r] disambiguate_symbol
191
225
  # @return [String] Alternative currency used if symbol is ambiguous
192
- # @!attribute [r] html_entity
226
+ # @!attribute [r] html_entity
193
227
  # @return [String] The html entity for the currency symbol
194
- # @!attribute [r] subunit
228
+ # @!attribute [r] subunit
195
229
  # @return [String] The name of the fractional monetary unit.
196
- # @!attribute [r] subunit_to_unit
230
+ # @!attribute [r] subunit_to_unit
197
231
  # @return [Integer] The proportion between the unit and the subunit
198
- # @!attribute [r] decimal_mark
232
+ # @!attribute [r] decimal_mark
199
233
  # @return [String] The decimal mark, or character used to separate the
200
234
  # whole unit from the subunit.
201
- # @!attribute [r] The
235
+ # @!attribute [r] The
202
236
  # @return [String] character used to separate thousands grouping of the
203
237
  # whole unit.
204
- # @!attribute [r] symbol_first
238
+ # @!attribute [r] symbol_first
205
239
  # @return [Boolean] Should the currency symbol precede the amount, or
206
240
  # should it come after?
207
- # @!attribute [r] smallest_denomination
241
+ # @!attribute [r] smallest_denomination
208
242
  # @return [Integer] Smallest amount of cash possible (in the subunit of
209
243
  # this currency)
210
244
 
@@ -214,6 +248,7 @@ class Money
214
248
 
215
249
  alias_method :separator, :decimal_mark
216
250
  alias_method :delimiter, :thousands_separator
251
+ alias_method :eql?, :==
217
252
 
218
253
  # Create a new +Currency+ object.
219
254
  #
@@ -225,10 +260,6 @@ class Money
225
260
  # @example
226
261
  # Money::Currency.new(:usd) #=> #<Money::Currency id: usd ...>
227
262
  def initialize(id)
228
- id = id.to_s.downcase
229
- unless self.class.stringified_keys.include?(id)
230
- raise UnknownCurrency, "Unknown currency '#{id}'"
231
- end
232
263
  @id = id.to_sym
233
264
  initialize_data!
234
265
  end
@@ -283,22 +314,6 @@ class Money
283
314
  end
284
315
  private :compare_ids
285
316
 
286
- # Compares +self+ with +other_currency+ and returns +true+ if the are the
287
- # same or if their +id+ attributes match.
288
- #
289
- # @param [Money::Currency] other_currency The currency to compare to.
290
- #
291
- # @return [Boolean]
292
- #
293
- # @example
294
- # c1 = Money::Currency.new(:usd)
295
- # c2 = Money::Currency.new(:jpy)
296
- # c1.eql? c1 #=> true
297
- # c1.eql? c2 #=> false
298
- def eql?(other_currency)
299
- self == other_currency
300
- end
301
-
302
317
  # Returns a Fixnum hash value based on the +id+ attribute in order to use
303
318
  # functions like & (intersection), group_by, etc.
304
319
  #
@@ -12,25 +12,27 @@ class Money
12
12
  end
13
13
 
14
14
  # Checks whether two money objects have the same currency and the same
15
- # amount. Checks against money objects with a different currency and checks
16
- # against objects that do not respond to #to_money will always return false.
15
+ # amount. If money objects have a different currency it will only be true
16
+ # if the amounts are both zero. Checks against objects that do not respond
17
+ # to #to_money, in which case, it will always return false.
17
18
  #
18
19
  # @param [Money] other_money Value to compare with.
19
20
  #
20
21
  # @return [Boolean]
21
22
  #
22
23
  # @example
23
- # Money.new(100) == Money.new(101) #=> false
24
- # Money.new(100) == Money.new(100) #=> true
25
- def ==(other_money)
24
+ # Money.new(100).eql?(Money.new(101)) #=> false
25
+ # Money.new(100).eql?(Money.new(100)) #=> true
26
+ # Money.new(0, "USD").eql?(Money.new(0, "EUR")) #=> true
27
+ def eql?(other_money)
26
28
  if other_money.respond_to?(:to_money)
27
29
  other_money = other_money.to_money
28
- fractional == other_money.fractional && currency == other_money.currency
30
+ (fractional == other_money.fractional && currency == other_money.currency) ||
31
+ (fractional == 0 && other_money.fractional == 0)
29
32
  else
30
33
  false
31
34
  end
32
35
  end
33
- alias_method :eql?, :==
34
36
 
35
37
  def <=>(val)
36
38
  if val.respond_to?(:to_money)
@@ -39,8 +41,6 @@ class Money
39
41
  val = val.exchange_to(currency)
40
42
  end
41
43
  fractional <=> val.fractional
42
- else
43
- raise ArgumentError, "Comparison of #{self.class} with #{val.inspect} failed"
44
44
  end
45
45
  end
46
46
 
@@ -15,7 +15,7 @@ class Money
15
15
  if self.class.use_i18n
16
16
  begin
17
17
  I18n.t name, :scope => "number.currency.format", :raise => true
18
- rescue I18n::MissingTranslationData => e
18
+ rescue I18n::MissingTranslationData
19
19
  I18n.t name, :scope =>"number.format", :default => (currency.send(method) || character)
20
20
  end
21
21
  else
@@ -184,24 +184,36 @@ class Money
184
184
  # due to equal symbols for different currencies. Uses the `disambiguate_symbol`.
185
185
  #
186
186
  # @example
187
- # Money.new(100, "USD").format(:disambiguate => false) #=> "$100.00"
188
- # Money.new(100, "CAD").format(:disambiguate => false) #=> "$100.00"
189
- # Money.new(100, "USD").format(:disambiguate => true) #=> "$100.00"
190
- # Money.new(100, "CAD").format(:disambiguate => true) #=> "C$100.00"
187
+ # Money.new(10000, "USD").format(:disambiguate => false) #=> "$100.00"
188
+ # Money.new(10000, "CAD").format(:disambiguate => false) #=> "$100.00"
189
+ # Money.new(10000, "USD").format(:disambiguate => true) #=> "$100.00"
190
+ # Money.new(10000, "CAD").format(:disambiguate => true) #=> "C$100.00"
191
191
  #
192
192
  # @option *rules [Boolean] :html_wrap_symbol (false) Wraps the currency symbol
193
193
  # in a html <span> tag.
194
194
  #
195
195
  # @example
196
- # Money.new(100, "USD").format(:disambiguate => false)
196
+ # Money.new(10000, "USD").format(:disambiguate => false)
197
197
  # #=> "<span class=\"currency_symbol\">$100.00</span>
198
198
  #
199
199
  # @option *rules [Symbol] :symbol_position (:before) `:before` if the currency
200
200
  # symbol goes before the amount, `:after` if it goes after.
201
201
  #
202
202
  # @example
203
- # Money.new(100, "USD").format(:symbol_position => :before) #=> "$100.00"
204
- # Money.new(100, "USD").format(:symbol_position => :after) #=> "100.00 $"
203
+ # Money.new(10000, "USD").format(:symbol_position => :before) #=> "$100.00"
204
+ # Money.new(10000, "USD").format(:symbol_position => :after) #=> "100.00 $"
205
+ #
206
+ # @option *rules [Boolean] :translate (true) `true` Checks for custom
207
+ # symbol definitions using I18n.
208
+ #
209
+ # @example
210
+ # # With the following entry in the translation files:
211
+ # # en:
212
+ # # number:
213
+ # # currency:
214
+ # # symbol:
215
+ # # CAD: "CAD$"
216
+ # Money.new(10000, "CAD").format(:translate => true) #=> "CAD$100.00"
205
217
  #
206
218
  # Note that the default rules can be defined through +Money.default_formatting_rules+ hash.
207
219
  #
@@ -212,6 +224,7 @@ class Money
212
224
 
213
225
  rules = default_formatting_rules.merge(rules)
214
226
  rules = localize_formatting_rules(rules)
227
+ rules = translate_formatting_rules(rules) if rules[:translate]
215
228
 
216
229
  if fractional == 0
217
230
  if rules[:display_free].respond_to?(:to_str)
@@ -343,6 +356,15 @@ class Money
343
356
  end
344
357
  end
345
358
 
359
+ def translate_formatting_rules(rules)
360
+ begin
361
+ rules[:symbol] = I18n.t currency.iso_code, :scope => "number.currency.symbol", :raise => true
362
+ rescue I18n::MissingTranslationData
363
+ # Do nothing
364
+ end
365
+ rules
366
+ end
367
+
346
368
  def localize_formatting_rules(rules)
347
369
  if currency.iso_code == "JPY" && I18n.locale == :ja
348
370
  rules[:symbol] = "円" unless rules[:symbol] == false
data/lib/money/money.rb CHANGED
@@ -15,7 +15,7 @@ require "money/money/formatting"
15
15
  #
16
16
  # @see http://en.wikipedia.org/wiki/Money
17
17
  class Money
18
- include Money::Arithmetic, Money::Formatting, Comparable
18
+ include Comparable, Money::Arithmetic, Money::Formatting
19
19
  extend Constructors
20
20
 
21
21
  # Raised when smallest denomination of a currency is not defined
@@ -94,12 +94,6 @@ class Money
94
94
  # this property is an instance of +Bank::VariableExchange.+ It allows
95
95
  # one to specify custom exchange rates.
96
96
  #
97
- # @!attribute default_currency
98
- # @return [Money::Currency] The default currency, which is used when
99
- # +Money.new+ is called without an explicit currency argument. The
100
- # default value is Currency.new("USD"). The value must be a valid
101
- # +Money::Currency+ instance.
102
- #
103
97
  # @!attribute default_formatting_rules
104
98
  # @return [Hash] Use this to define a default hash of rules for everytime
105
99
  # +Money#format+ is called. Rules provided on method call will be
@@ -123,11 +117,17 @@ class Money
123
117
  # @!attribute [rw] conversion_precision
124
118
  # @return [Fixnum] Use this to specify precision for converting Rational
125
119
  # to BigDecimal
126
- attr_accessor :default_bank, :default_currency, :default_formatting_rules,
120
+ attr_accessor :default_bank, :default_formatting_rules,
127
121
  :use_i18n, :infinite_precision, :conversion_precision
128
122
 
129
123
  # @attr_writer rounding_mode Use this to specify the rounding mode
130
- attr_writer :rounding_mode
124
+ #
125
+ # @!attribute default_currency
126
+ # @return [Money::Currency] The default currency, which is used when
127
+ # +Money.new+ is called without an explicit currency argument. The
128
+ # default value is Currency.new("USD"). The value must be a valid
129
+ # +Money::Currency+ instance.
130
+ attr_writer :rounding_mode, :default_currency
131
131
 
132
132
  end
133
133
 
@@ -211,13 +211,35 @@ class Money
211
211
  self.default_bank = Bank::SingleCurrency.instance
212
212
  end
213
213
 
214
+ # Creates a new Money object of value given in the +unit+ of the given
215
+ # +currency+.
216
+ #
217
+ # @param [Numeric] amount The numerical value of the money.
218
+ # @param [Currency, String, Symbol] currency The currency format.
219
+ # @param [Money::Bank::*] bank The exchange bank to use.
220
+ #
221
+ # @example
222
+ # Money.from_amount(23.45, "USD") # => #<Money fractional:2345 currency:USD>
223
+ # Money.from_amount(23.45, "JPY") # => #<Money fractional:23 currency:JPY>
224
+ #
225
+ # @return [Money]
226
+ #
227
+ # @see #initialize
228
+ def self.from_amount(amount, currency = default_currency, bank = default_bank)
229
+ Numeric === amount or raise ArgumentError, "'amount' must be numeric"
230
+ currency = Currency.wrap(currency)
231
+ value = amount.to_d * currency.subunit_to_unit
232
+ value = value.round(0, rounding_mode) unless infinite_precision
233
+ new(value, currency, bank)
234
+ end
235
+
214
236
  # Creates a new Money object of value given in the
215
237
  # +fractional unit+ of the given +currency+.
216
238
  #
217
239
  # Alternatively you can use the convenience
218
240
  # methods like {Money.ca_dollar} and {Money.us_dollar}.
219
241
  #
220
- # @param [Object] obj Either The fractional value of the money,
242
+ # @param [Object] obj Either the fractional value of the money,
221
243
  # a Money object, or a currency. (If passed a currency as the first
222
244
  # argument, a Money will be created in that currency with fractional value
223
245
  # = 0.
@@ -0,0 +1,120 @@
1
+ class Money
2
+ module RatesStore
3
+
4
+ # Class for thread-safe storage of exchange rate pairs.
5
+ # Used by instances of +Money::Bank::VariableExchange+.
6
+ #
7
+ # @example
8
+ # store = Money::RatesStore::Memory.new
9
+ # store.add_rate 'USD', 'CAD', 0.98
10
+ # store.get_rate 'USD', 'CAD' # => 0.98
11
+ # # iterates rates
12
+ # store.each_rate {|iso_from, iso_to, rate| puts "#{from} -> #{to}: #{rate}" }
13
+ class Memory
14
+ INDEX_KEY_SEPARATOR = '_TO_'.freeze
15
+
16
+ # Initializes a new +Money::RatesStore::Memory+ object.
17
+ #
18
+ # @param [Hash] opts Optional store options.
19
+ # @option opts [Boolean] :without_mutex disables the usage of a mutex
20
+ # @param [Hash] rt Optional initial exchange rate data.
21
+ def initialize(opts = {}, rt = {})
22
+ @options, @index = opts, rt
23
+ @mutex = Mutex.new
24
+ @in_transaction = false
25
+ end
26
+
27
+ # Registers a conversion rate and returns it. Uses +Mutex+ to synchronize data access.
28
+ #
29
+ # @param [String] currency_iso_from Currency to exchange from.
30
+ # @param [String] currency_iso_to Currency to exchange to.
31
+ # @param [Numeric] rate Rate to use when exchanging currencies.
32
+ #
33
+ # @return [Numeric]
34
+ #
35
+ # @example
36
+ # store = Money::RatesStore::Memory.new
37
+ # store.add_rate("USD", "CAD", 1.24515)
38
+ # store.add_rate("CAD", "USD", 0.803115)
39
+ def add_rate(currency_iso_from, currency_iso_to, rate)
40
+ transaction { index[rate_key_for(currency_iso_from, currency_iso_to)] = rate }
41
+ end
42
+
43
+ # Retrieve the rate for the given currencies. Uses +Mutex+ to synchronize data access.
44
+ # Delegates to +Money::RatesStore::Memory+
45
+ #
46
+ # @param [String] currency_iso_from Currency to exchange from.
47
+ # @param [String] currency_iso_to Currency to exchange to.
48
+ #
49
+ # @return [Numeric]
50
+ #
51
+ # @example
52
+ # store = Money::RatesStore::Memory.new
53
+ # store.add_rate("USD", "CAD", 1.24515)
54
+ #
55
+ # store.get_rate("USD", "CAD") #=> 1.24515
56
+ def get_rate(currency_iso_from, currency_iso_to)
57
+ transaction { index[rate_key_for(currency_iso_from, currency_iso_to)] }
58
+ end
59
+
60
+ def marshal_dump
61
+ [self.class, index, options]
62
+ end
63
+
64
+
65
+ # Wraps block execution in a thread-safe transaction
66
+ def transaction(&block)
67
+ if @in_transaction || options[:without_mutex]
68
+ block.call self
69
+ else
70
+ @mutex.synchronize do
71
+ @in_transaction = true
72
+ result = block.call
73
+ @in_transaction = false
74
+ result
75
+ end
76
+ end
77
+ end
78
+
79
+ # Iterate over rate tuples (iso_from, iso_to, rate)
80
+ #
81
+ # @yieldparam iso_from [String] Currency ISO string.
82
+ # @yieldparam iso_to [String] Currency ISO string.
83
+ # @yieldparam rate [Numeric] Exchange rate.
84
+ #
85
+ # @return [Enumerator]
86
+ #
87
+ # @example
88
+ # store.each_rate do |iso_from, iso_to, rate|
89
+ # puts [iso_from, iso_to, rate].join
90
+ # end
91
+ def each_rate(&block)
92
+ enum = Enumerator.new do |yielder|
93
+ index.each do |key, rate|
94
+ iso_from, iso_to = key.split(INDEX_KEY_SEPARATOR)
95
+ yielder.yield iso_from, iso_to, rate
96
+ end
97
+ end
98
+
99
+ block_given? ? enum.each(&block) : enum
100
+ end
101
+
102
+ private
103
+
104
+ attr_reader :index, :options
105
+
106
+ # Return the rate hashkey for the given currencies.
107
+ #
108
+ # @param [String] currency_iso_from The currency to exchange from.
109
+ # @param [String] currency_iso_to The currency to exchange to.
110
+ #
111
+ # @return [String]
112
+ #
113
+ # @example
114
+ # rate_key_for("USD", "CAD") #=> "USD_TO_CAD"
115
+ def rate_key_for(currency_iso_from, currency_iso_to)
116
+ [currency_iso_from, currency_iso_to].join(INDEX_KEY_SEPARATOR).upcase
117
+ end
118
+ end
119
+ end
120
+ end
data/lib/money/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Money
2
- VERSION = "6.5.1"
2
+ VERSION = "6.6.0"
3
3
  end
data/money.gemspec CHANGED
@@ -29,7 +29,7 @@ MSG
29
29
 
30
30
  s.add_development_dependency "bundler", "~> 1.3"
31
31
  s.add_development_dependency "rake"
32
- s.add_development_dependency "rspec", "~> 3.0.0"
32
+ s.add_development_dependency "rspec", "~> 3.2.0"
33
33
  s.add_development_dependency "yard", "~> 0.8"
34
34
  s.add_development_dependency "kramdown", "~> 1.1"
35
35
 
@@ -1,77 +1,81 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Money::Bank::Base do
3
+ class Money
4
+ module Bank
5
+ describe Base do
4
6
 
5
- describe ".instance" do
6
- it "is local to one class" do
7
- klass = Money::Bank::Base
8
- subclass = Class.new(Money::Bank::Base)
9
- expect(klass.instance).not_to eq subclass.instance
10
- end
11
- end
7
+ describe ".instance" do
8
+ it "is local to one class" do
9
+ klass = Base
10
+ subclass = Class.new(Base)
11
+ expect(klass.instance).not_to eq subclass.instance
12
+ end
13
+ end
12
14
 
13
- describe "#initialize" do
14
- it "accepts a block and stores @rounding_method" do
15
- proc = Proc.new { |n| n.ceil }
16
- bank = Money::Bank::Base.new(&proc)
17
- expect(bank.rounding_method).to eq proc
18
- end
19
- end
15
+ describe "#initialize" do
16
+ it "accepts a block and stores @rounding_method" do
17
+ proc = Proc.new { |n| n.ceil }
18
+ bank = Base.new(&proc)
19
+ expect(bank.rounding_method).to eq proc
20
+ end
21
+ end
20
22
 
21
- describe "#setup" do
22
- it "calls #setup after #initialize" do
23
- class MyBank < Money::Bank::Base
24
- attr_reader :setup_called
23
+ describe "#setup" do
24
+ it "calls #setup after #initialize" do
25
+ class MyBank < Base
26
+ attr_reader :setup_called
25
27
 
26
- def setup
27
- @setup_called = true
28
+ def setup
29
+ @setup_called = true
30
+ end
31
+ end
32
+
33
+ bank = MyBank.new
34
+ expect(bank.setup_called).to eq true
28
35
  end
29
36
  end
30
37
 
31
- bank = MyBank.new
32
- expect(bank.setup_called).to eq true
33
- end
34
- end
35
-
36
- describe "#exchange_with" do
37
- it "is not implemented" do
38
- expect { subject.exchange_with(Money.new(100, 'USD'), 'EUR') }.to raise_exception(NotImplementedError)
39
- end
40
- end
38
+ describe "#exchange_with" do
39
+ it "is not implemented" do
40
+ expect { subject.exchange_with(Money.new(100, 'USD'), 'EUR') }.to raise_exception(NotImplementedError)
41
+ end
42
+ end
41
43
 
42
- describe "#same_currency?" do
43
- it "accepts str/str" do
44
- expect { subject.send(:same_currency?, 'USD', 'EUR') }.to_not raise_exception
45
- end
44
+ describe "#same_currency?" do
45
+ it "accepts str/str" do
46
+ expect { subject.send(:same_currency?, 'USD', 'EUR') }.to_not raise_exception
47
+ end
46
48
 
47
- it "accepts currency/str" do
48
- expect { subject.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR') }.to_not raise_exception
49
- end
49
+ it "accepts currency/str" do
50
+ expect { subject.send(:same_currency?, Currency.wrap('USD'), 'EUR') }.to_not raise_exception
51
+ end
50
52
 
51
- it "accepts str/currency" do
52
- expect { subject.send(:same_currency?, 'USD', Money::Currency.wrap('EUR')) }.to_not raise_exception
53
- end
53
+ it "accepts str/currency" do
54
+ expect { subject.send(:same_currency?, 'USD', Currency.wrap('EUR')) }.to_not raise_exception
55
+ end
54
56
 
55
- it "accepts currency/currency" do
56
- expect { subject.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')) }.to_not raise_exception
57
- end
57
+ it "accepts currency/currency" do
58
+ expect { subject.send(:same_currency?, Currency.wrap('USD'), Currency.wrap('EUR')) }.to_not raise_exception
59
+ end
58
60
 
59
- it "returns true when currencies match" do
60
- expect(subject.send(:same_currency?, 'USD', 'USD')).to be true
61
- expect(subject.send(:same_currency?, Money::Currency.wrap('USD'), 'USD')).to be true
62
- expect(subject.send(:same_currency?, 'USD', Money::Currency.wrap('USD'))).to be true
63
- expect(subject.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('USD'))).to be true
64
- end
61
+ it "returns true when currencies match" do
62
+ expect(subject.send(:same_currency?, 'USD', 'USD')).to be true
63
+ expect(subject.send(:same_currency?, Currency.wrap('USD'), 'USD')).to be true
64
+ expect(subject.send(:same_currency?, 'USD', Currency.wrap('USD'))).to be true
65
+ expect(subject.send(:same_currency?, Currency.wrap('USD'), Currency.wrap('USD'))).to be true
66
+ end
65
67
 
66
- it "returns false when currencies do not match" do
67
- expect(subject.send(:same_currency?, 'USD', 'EUR')).to be false
68
- expect(subject.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR')).to be false
69
- expect(subject.send(:same_currency?, 'USD', Money::Currency.wrap('EUR'))).to be false
70
- expect(subject.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))).to be false
71
- end
68
+ it "returns false when currencies do not match" do
69
+ expect(subject.send(:same_currency?, 'USD', 'EUR')).to be false
70
+ expect(subject.send(:same_currency?, Currency.wrap('USD'), 'EUR')).to be false
71
+ expect(subject.send(:same_currency?, 'USD', Currency.wrap('EUR'))).to be false
72
+ expect(subject.send(:same_currency?, Currency.wrap('USD'), Currency.wrap('EUR'))).to be false
73
+ end
72
74
 
73
- it "raises an UnknownCurrency exception when an unknown currency is passed" do
74
- expect { subject.send(:same_currency?, 'AAA', 'BBB') }.to raise_exception(Money::Currency::UnknownCurrency)
75
+ it "raises an UnknownCurrency exception when an unknown currency is passed" do
76
+ expect { subject.send(:same_currency?, 'AAA', 'BBB') }.to raise_exception(Currency::UnknownCurrency)
77
+ end
78
+ end
75
79
  end
76
80
  end
77
81
  end
@@ -1,11 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Money::Bank::SingleCurrency do
4
- describe "#exchange_with" do
5
- it "raises when called" do
6
- expect {
7
- subject.exchange_with(Money.new(100, 'USD'), 'EUR')
8
- }.to raise_exception(Money::Bank::DifferentCurrencyError, "No exchanging of currencies allowed: 1.00 USD to EUR")
3
+ class Money
4
+ module Bank
5
+ describe SingleCurrency do
6
+ describe "#exchange_with" do
7
+ it "raises when called" do
8
+ expect {
9
+ subject.exchange_with(Money.new(100, 'USD'), 'EUR')
10
+ }.to raise_exception(DifferentCurrencyError, "No exchanging of currencies allowed: 1.00 USD to EUR")
11
+ end
12
+ end
9
13
  end
10
14
  end
11
15
  end