money 6.5.0 → 6.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9de08a1673530ca427fb70f37d70526155a27d1
4
- data.tar.gz: 2481e79b2b71853d316865cf7831d14b3738a67b
3
+ metadata.gz: 2e625dd2f1daa9df7fbe2c8c5ed8972c5bf69f9a
4
+ data.tar.gz: 50addb5ca7123aff716961c6e782d43846d90c55
5
5
  SHA512:
6
- metadata.gz: 603c5318cff748cd533db8dbd596eebf448d7f040bb8fd6f6c43c97eddff07da74b039864e7faf75d3abe4ba06fa933912ae949c1e814a183ee861b1401f3906
7
- data.tar.gz: 5ffd14941b9f53f33e3dd731472cd7f9fc4c4831c353f1c970ec12ea50ce6fa7d9f088952439d1d69e11ab61ebaaedf261b717fca0147ad812061742b3e03eae
6
+ metadata.gz: 51736bea340dca0e23da25018aad574380bdfea0281e20aaf0af90c306f50d48be15ffb15136daff9caac4c5c6a5f563042c6703dc6403c83ce32fee85d42e30
7
+ data.tar.gz: 2b32b72aa148550d672ae639b0c9c96a13c5d3c35676fdf7649f130bd79a4d41880444d2d1b0fdbe3d442d840518fcf2e35bdb3156ddd46a8f1490f68e6ada24
data/.travis.yml CHANGED
@@ -3,6 +3,7 @@ rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
5
  - 2.1.2
6
+ - 2.2.0
6
7
  - rbx-2
7
8
  script: bundle exec rspec spec
8
9
  notifications:
data/AUTHORS CHANGED
@@ -35,6 +35,7 @@ François Klingler
35
35
  Francisco Trindade
36
36
  Gabriel Gilder
37
37
  Gee-Hsien Chuang
38
+ George Millo
38
39
  Hakan Ensari
39
40
  Hongli Lai
40
41
  Ilia Lobsanov
@@ -47,6 +48,7 @@ Jell
47
48
  Jeremy McNevin
48
49
  Jérémy Lecour
49
50
  Jesse Cooke
51
+ Jim Kingdon
50
52
  John Duff
51
53
  John Gakos
52
54
  Jonathon M. Abbott
data/CHANGELOG.md CHANGED
@@ -1,6 +1,26 @@
1
1
  # Changelog
2
2
 
3
3
  ## Next release
4
+
5
+ - `Currency` implements `Enumerable`.
6
+ - `Currency#<=>` sorts alphabetically by `id` if the `priority`s are the same,
7
+ and no longer raises an error if one of the priorities is missing.
8
+ - `Money::Currency.unregister` can take an ISO code argument in addition
9
+ to a hash.
10
+ - `Money::Currency.unregister` returns `true` if the given currency
11
+ previously existed, and `false` if it didn't.
12
+ - Fix symbol for SZL currency
13
+ - Trying to create a Currency without an `iso_code` now raises a more
14
+ helpful error message.
15
+ - Add `Money.usd`, `.cad` and `.eur` as aliases for `.us_dollar`,
16
+ `.ca_dollar`, and `.euro`.
17
+ - Add helper methods for British pounds: `Money.pound_sterling` and
18
+ `Money.gbp`.
19
+
20
+ ## 6.5.1
21
+ - Fix format for BYR currency
22
+
23
+ ## 6.5.0
4
24
  - Add method to round a given amount of money to the nearest possible value in cash (aka Swedish rounding).
5
25
  - Fixed the subunit_to_unit values of the CLP and KRW currencies
6
26
  - Add option for `disambiguate` symbols for formatting
data/README.md CHANGED
@@ -135,10 +135,10 @@ The pre-defined set of attributes includes:
135
135
  - `:separator` character between the whole and fraction amounts
136
136
  - `:delimiter` character between each thousands place
137
137
 
138
- All attributes are optional. Some attributes, such as `:symbol`, are used by
139
- the Money class to print out a representation of the object. Other attributes,
140
- such as `:name` or `:priority`, exist to provide a basic API you can take
141
- advantage of to build your application.
138
+ All attributes except `:iso_code` are optional. Some attributes, such as
139
+ `:symbol`, are used by the Money class to print out a representation of the
140
+ object. Other attributes, such as `:name` or `:priority`, exist to provide a
141
+ basic API you can take advantage of to build your application.
142
142
 
143
143
  ### :priority
144
144
 
@@ -357,15 +357,15 @@
357
357
  "name": "Belarusian Ruble",
358
358
  "symbol": "Br",
359
359
  "disambiguate_symbol": "BYR",
360
- "alternate_symbols": [""],
361
- "subunit": "Kapyeyka",
362
- "subunit_to_unit": 100,
360
+ "alternate_symbols": ["бел. руб.", "б.р.", "руб.", "р."],
361
+ "subunit": null,
362
+ "subunit_to_unit": 1,
363
363
  "symbol_first": false,
364
364
  "html_entity": "",
365
- "decimal_mark": ".",
366
- "thousands_separator": ",",
365
+ "decimal_mark": ",",
366
+ "thousands_separator": " ",
367
367
  "iso_numeric": "974",
368
- "smallest_denomination": 5000
368
+ "smallest_denomination": 50
369
369
  },
370
370
  "bzd": {
371
371
  "priority": 100,
@@ -1005,14 +1005,14 @@
1005
1005
  "name": "Icelandic Króna",
1006
1006
  "symbol": "kr",
1007
1007
  "alternate_symbols": ["Íkr"],
1008
- "subunit": "Eyrir",
1009
- "subunit_to_unit": 100,
1008
+ "subunit": null,
1009
+ "subunit_to_unit": 1,
1010
1010
  "symbol_first": true,
1011
1011
  "html_entity": "",
1012
1012
  "decimal_mark": ",",
1013
1013
  "thousands_separator": ".",
1014
1014
  "iso_numeric": "352",
1015
- "smallest_denomination": 100
1015
+ "smallest_denomination": 1
1016
1016
  },
1017
1017
  "jmd": {
1018
1018
  "priority": 100,
@@ -2060,9 +2060,8 @@
2060
2060
  "priority": 100,
2061
2061
  "iso_code": "SZL",
2062
2062
  "name": "Swazi Lilangeni",
2063
- "symbol": "L",
2063
+ "symbol": "E",
2064
2064
  "disambiguate_symbol": "SZL",
2065
- "alternate_symbols": ["E"],
2066
2065
  "subunit": "Cent",
2067
2066
  "subunit_to_unit": 100,
2068
2067
  "symbol_first": true,
@@ -2155,8 +2154,8 @@
2155
2154
  "alternate_symbols": ["TL"],
2156
2155
  "subunit": "kuruş",
2157
2156
  "subunit_to_unit": 100,
2158
- "symbol_first": false,
2159
- "html_entity": "",
2157
+ "symbol_first": true,
2158
+ "html_entity": "&#8378;",
2160
2159
  "decimal_mark": ",",
2161
2160
  "thousands_separator": ".",
2162
2161
  "iso_numeric": "949",
@@ -12,6 +12,7 @@ class Money
12
12
  # @see http://iso4217.net/
13
13
  class Currency
14
14
  include Comparable
15
+ extend Enumerable
15
16
  extend Money::Currency::Loader
16
17
  extend Money::Currency::Heuristics
17
18
 
@@ -105,25 +106,64 @@ class Money
105
106
  table.keys.map {|curr| Currency.new(curr)}.sort_by(&:priority)
106
107
  end
107
108
 
108
- # We need a string-based validator before creating an unbounded number of symbols.
109
+ # We need a string-based validator before creating an unbounded number of
110
+ # symbols.
109
111
  # http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol#11
110
112
  # https://github.com/RubyMoney/money/issues/132
113
+ #
114
+ # @return [Set]
111
115
  def stringified_keys
112
116
  @stringified_keys ||= stringify_keys
113
117
  end
114
118
 
119
+ # Register a new currency
120
+ #
121
+ # @param curr [Hash] information about the currency
122
+ # @option priority [Numeric] a numerical value you can use to sort/group
123
+ # the currency list
124
+ # @option iso_code [String] the international 3-letter code as defined
125
+ # by the ISO 4217 standard
126
+ # @option iso_numeric [Integer] the international 3-digit code as
127
+ # defined by the ISO 4217 standard
128
+ # @option name [String] the currency name
129
+ # @option symbol [String] the currency symbol (UTF-8 encoded)
130
+ # @option subunit [String] the name of the fractional monetary unit
131
+ # @option subunit_to_unit [Numeric] the proportion between the unit and
132
+ # the subunit
133
+ # @option separator [String] character between the whole and fraction
134
+ # amounts
135
+ # @option delimiter [String] character between each thousands place
115
136
  def register(curr)
116
- key = curr[:iso_code].downcase.to_sym
137
+ key = curr.fetch(:iso_code).downcase.to_sym
117
138
  @table[key] = curr
118
139
  @stringified_keys = stringify_keys
119
140
  end
120
141
 
142
+
143
+ # Unregister a currency.
144
+ #
145
+ # @param [Object] curr A Hash with the key `:iso_code`, or the ISO code
146
+ # as a String or Symbol.
147
+ #
148
+ # @return [Boolean] true if the currency previously existed, false
149
+ # if it didn't.
121
150
  def unregister(curr)
122
- key = curr[:iso_code].downcase.to_sym
123
- @table.delete(key)
151
+ if curr.is_a?(Hash)
152
+ key = curr.fetch(:iso_code).downcase.to_sym
153
+ else
154
+ key = curr.downcase.to_sym
155
+ end
156
+ existed = @table.delete(key)
124
157
  @stringified_keys = stringify_keys
158
+ existed ? true : false
159
+ end
160
+
161
+
162
+ def each
163
+ all.each { |c| yield(c) }
125
164
  end
126
165
 
166
+
127
167
  private
128
168
 
129
169
  def stringify_keys
@@ -131,29 +171,42 @@ class Money
131
171
  end
132
172
  end
133
173
 
134
- # @attr_reader [Symbol] id The symbol used to identify the currency,
135
- # usually the lowercase +iso_code+ attribute.
136
- # @attr_reader [Integer] priority A numerical value you can use to
137
- # sort/group the currency list.
138
- # @attr_reader [String] iso_code The international 3-letter code as defined
139
- # by the ISO 4217 standard.
140
- # @attr_reader [String] iso_numeric The international 3-numeric code as
141
- # defined by the ISO 4217 standard.
142
- # @attr_reader [String] name The currency name.
143
- # @attr_reader [String] symbol The currency symbol (UTF-8 encoded).
144
- # @attr_reader [String] disambiguate_symbol Alternative currency used if symbol is ambiguous
145
- # @attr_reader [String] html_entity The html entity for the currency symbol
146
- # @attr_reader [String] subunit The name of the fractional monetary unit.
147
- # @attr_reader [Integer] subunit_to_unit The proportion between the unit
148
- # and the subunit
149
- # @attr_reader [String] decimal_mark The decimal mark, or character used to
150
- # separate the whole unit from the subunit.
151
- # @attr_reader [String] The character used to separate thousands grouping
152
- # of the whole unit.
153
- # @attr_reader [Boolean] symbol_first Should the currency symbol precede
154
- # the amount, or should it come after?
155
- # @attr_reader [Integer] smallest_denomination Smallest amount of cash
156
- # possible (in the subunit of this currency)
174
+ # @!attribute [r] id
175
+ # @return [Symbol] The symbol used to identify the currency, usually THE
176
+ # lowercase +iso_code+ attribute.
177
+ # @!attribute [r] priority
178
+ # @return [Integer] A numerical value you can use to sort/group the
179
+ # currency list.
180
+ # @!attribute [r] iso_code
181
+ # @return [String] The international 3-letter code as defined by the ISO
182
+ # 4217 standard.
183
+ # @!attribute [r] iso_numeric
184
+ # @return [String] The international 3-numeric code as defined by the ISO
185
+ # 4217 standard.
186
+ # @!attribute [r] name
187
+ # @return [String] The currency name.
188
+ # @!attribute [r] symbol
189
+ # @return [String] The currency symbol (UTF-8 encoded).
190
+ # @!attribute [r] disambiguate_symbol
191
+ # @return [String] Alternative currency used if symbol is ambiguous
192
+ # @!attribute [r] html_entity
193
+ # @return [String] The html entity for the currency symbol
194
+ # @!attribute [r] subunit
195
+ # @return [String] The name of the fractional monetary unit.
196
+ # @!attribute [r] subunit_to_unit
197
+ # @return [Integer] The proportion between the unit and the subunit
198
+ # @!attribute [r] decimal_mark
199
+ # @return [String] The decimal mark, or character used to separate the
200
+ # whole unit from the subunit.
201
+ # @!attribute [r] The
202
+ # @return [String] character used to separate thousands grouping of the
203
+ # whole unit.
204
+ # @!attribute [r] symbol_first
205
+ # @return [Boolean] Should the currency symbol precede the amount, or
206
+ # should it come after?
207
+ # @!attribute [r] smallest_denomination
208
+ # @return [Integer] Smallest amount of cash possible (in the subunit of
209
+ # this currency)
157
210
 
158
211
  attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
159
212
  :disambiguate_symbol, :html_entity, :subunit, :subunit_to_unit, :decimal_mark,
@@ -173,27 +226,11 @@ class Money
173
226
  # Money::Currency.new(:usd) #=> #<Money::Currency id: usd ...>
174
227
  def initialize(id)
175
228
  id = id.to_s.downcase
176
-
177
- if self.class.stringified_keys.include?(id)
178
- @id = id.to_sym
179
- data = self.class.table[@id]
180
- @priority = data[:priority]
181
- @iso_code = data[:iso_code]
182
- @name = data[:name]
183
- @symbol = data[:symbol]
184
- @disambiguate_symbol = data[:disambiguate_symbol]
185
- @alternate_symbols = data[:alternate_symbols]
186
- @subunit = data[:subunit]
187
- @subunit_to_unit = data[:subunit_to_unit]
188
- @symbol_first = data[:symbol_first]
189
- @html_entity = data[:html_entity]
190
- @decimal_mark = data[:decimal_mark]
191
- @thousands_separator = data[:thousands_separator]
192
- @iso_numeric = data[:iso_numeric]
193
- @smallest_denomination = data[:smallest_denomination]
194
- else
229
+ unless self.class.stringified_keys.include?(id)
195
230
  raise UnknownCurrency, "Unknown currency '#{id}'"
196
231
  end
232
+ @id = id.to_sym
233
+ initialize_data!
197
234
  end
198
235
 
199
236
  # Compares +self+ with +other_currency+ against the value of +priority+
@@ -210,7 +247,14 @@ class Money
210
247
  # c2 <=> c1 #=> -1
211
248
  # c1 <=> c1 #=> 0
212
249
  def <=>(other_currency)
213
- self.priority <=> other_currency.priority
250
+ # <=> returns nil when one of the values is nil
251
+ comparison = self.priority <=> other_currency.priority || 0
252
+
253
+ if comparison == 0
254
+ self.id <=> other_currency.id
255
+ else
256
+ comparison
257
+ end
214
258
  end
215
259
 
216
260
  # Compares +self+ with +other_currency+ and returns +true+ if the are the
@@ -352,10 +396,11 @@ class Money
352
396
  cache[subunit_to_unit] ||= calculate_decimal_places(subunit_to_unit)
353
397
  end
354
398
 
399
+ private
400
+
355
401
  def cache
356
402
  self.class.decimal_places_cache
357
403
  end
358
- private :cache
359
404
 
360
405
  # If we need to figure out how many decimal places we need we
361
406
  # use repeated integer division.
@@ -367,6 +412,23 @@ class Money
367
412
  end
368
413
  i
369
414
  end
370
- private :calculate_decimal_places
415
+
416
+ def initialize_data!
417
+ data = self.class.table[@id]
418
+ @alternate_symbols = data[:alternate_symbols]
419
+ @decimal_mark = data[:decimal_mark]
420
+ @disambiguate_symbol = data[:disambiguate_symbol]
421
+ @html_entity = data[:html_entity]
422
+ @iso_code = data[:iso_code]
423
+ @iso_numeric = data[:iso_numeric]
424
+ @name = data[:name]
425
+ @priority = data[:priority]
426
+ @smallest_denomination = data[:smallest_denomination]
427
+ @subunit = data[:subunit]
428
+ @subunit_to_unit = data[:subunit_to_unit]
429
+ @symbol = data[:symbol]
430
+ @symbol_first = data[:symbol_first]
431
+ @thousands_separator = data[:thousands_separator]
432
+ end
371
433
  end
372
434
  end
data/lib/money/money.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  require "money/bank/variable_exchange"
3
3
  require "money/bank/single_currency"
4
4
  require "money/money/arithmetic"
5
+ require "money/money/constructors"
5
6
  require "money/money/formatting"
6
7
 
7
8
  # "Money is any object or record that is generally accepted as payment for
@@ -14,7 +15,8 @@ require "money/money/formatting"
14
15
  #
15
16
  # @see http://en.wikipedia.org/wiki/Money
16
17
  class Money
17
- include Comparable, Money::Arithmetic, Money::Formatting
18
+ include Money::Arithmetic, Money::Formatting, Comparable
19
+ extend Constructors
18
20
 
19
21
  # Raised when smallest denomination of a currency is not defined
20
22
  class UndefinedSmallestDenomination < StandardError; end
@@ -74,63 +76,59 @@ class Money
74
76
  return_value(rounded_value)
75
77
  end
76
78
 
77
- # @attr_reader [Currency] currency The currency the money is in.
78
- # @attr_reader [Money::Bank::*] bank The +Money::Bank+ based object used to
79
- # perform currency exchanges with.
79
+ # @!attribute [r] currency
80
+ # @return [Currency] The money's currency.
81
+ # @!attribute [r] bank
82
+ # @return [Money::Bank::Base] The +Money::Bank+-based object which currency
83
+ # exchanges are performed with.
84
+
80
85
  attr_reader :currency, :bank
81
86
 
82
87
  # Class Methods
83
88
  class << self
84
- # @attr_accessor [Money::Bank::*] default_bank Each Money object is
85
- # associated to a bank object, which is responsible for currency exchange.
86
- # This property allows you to specify the default bank object. The default
87
- # value for this property is an instance of +Bank::VariableExchange.+ It
88
- # allows one to specify custom exchange rates.
89
+
90
+ # @!attribute [rw] default_bank
91
+ # @return [Money::Bank::Base] Each Money object is associated to a bank
92
+ # object, which is responsible for currency exchange. This property
93
+ # allows you to specify the default bank object. The default value for
94
+ # this property is an instance of +Bank::VariableExchange.+ It allows
95
+ # one to specify custom exchange rates.
89
96
  #
90
- # @attr_accessor [Money::Currency] default_currency The default currency,
91
- # which is used when +Money.new+ is called without an explicit currency
92
- # argument. The default value is Currency.new("USD"). The value must be a
93
- # valid +Money::Currency+ instance.
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.
94
102
  #
95
- # @attr_accessor [Hash] default_formatting_rules Use this to define a default
96
- # hash of rules for everytime +Money#format+ is called.
97
- # Rules provided on method call will be merged with the default ones.
98
- # To overwrite a rule, just provide the intended value while calling +format+.
103
+ # @!attribute default_formatting_rules
104
+ # @return [Hash] Use this to define a default hash of rules for everytime
105
+ # +Money#format+ is called. Rules provided on method call will be
106
+ # merged with the default ones. To overwrite a rule, just provide the
107
+ # intended value while calling +format+.
99
108
  #
100
- # @see +Money::Formatting#format+ for more details.
109
+ # @see +Money::Formatting#format+ for more details.
101
110
  #
102
- # @example
103
- # Money.default_formatting_rules = { :display_free => true }
104
- # Money.new(0, "USD").format # => "free"
105
- # Money.new(0, "USD").format(:display_free => false) # => "$0.00"
111
+ # @example
112
+ # Money.default_formatting_rules = { :display_free => true }
113
+ # Money.new(0, "USD").format # => "free"
114
+ # Money.new(0, "USD").format(:display_free => false) # => "$0.00"
106
115
  #
107
- # @attr_accessor [true, false] use_i18n Use this to disable i18n even if
108
- # it's used by other objects in your app.
116
+ # @!attribute [rw] use_i18n
117
+ # @return [Boolean] Use this to disable i18n even if it's used by other
118
+ # objects in your app.
109
119
  #
110
- # @attr_accessor [true, false] infinite_precision Use this to enable
111
- # infinite precision cents
120
+ # @!attribute [rw] infinite_precision
121
+ # @return [Boolean] Use this to enable infinite precision cents
112
122
  #
113
- # @attr_accessor [Integer] conversion_precision Use this to specify
114
- # precision for converting Rational to BigDecimal
123
+ # @!attribute [rw] conversion_precision
124
+ # @return [Fixnum] Use this to specify precision for converting Rational
125
+ # to BigDecimal
115
126
  attr_accessor :default_bank, :default_currency, :default_formatting_rules,
116
127
  :use_i18n, :infinite_precision, :conversion_precision
117
128
 
118
129
  # @attr_writer rounding_mode Use this to specify the rounding mode
119
130
  attr_writer :rounding_mode
120
131
 
121
- # Create a new money object with value 0.
122
- #
123
- # @param [Currency, String, Symbol] currency The currency to use.
124
- #
125
- # @return [Money]
126
- #
127
- # @example
128
- # Money.empty #=> #<Money @fractional=0>
129
- def empty(currency = default_currency)
130
- @empty ||= {}
131
- @empty[currency] ||= Money.new(0, currency).freeze
132
- end
133
- alias_method :zero, :empty
134
132
  end
135
133
 
136
134
  def self.default_currency
@@ -171,7 +169,7 @@ class Money
171
169
  # rounding mode and a block to temporatly change it. It will
172
170
  # then return the results of the block instead.
173
171
  #
174
- # @param [BigDecimal::ROUND_MODE] optional
172
+ # @param [BigDecimal::ROUND_MODE] mode
175
173
  #
176
174
  # @return [BigDecimal::ROUND_MODE,Yield] rounding mode or block results
177
175
  #
@@ -192,49 +190,6 @@ class Money
192
190
  end
193
191
  end
194
192
 
195
- # Creates a new Money object of the given value, using the Canadian
196
- # dollar currency.
197
- #
198
- # @param [Integer] cents The cents value.
199
- #
200
- # @return [Money]
201
- #
202
- # @example
203
- # n = Money.ca_dollar(100)
204
- # n.cents #=> 100
205
- # n.currency #=> #<Money::Currency id: cad>
206
- def self.ca_dollar(cents)
207
- Money.new(cents, "CAD")
208
- end
209
-
210
- # Creates a new Money object of the given value, using the American dollar
211
- # currency.
212
- #
213
- # @param [Integer] cents The cents value.
214
- #
215
- # @return [Money]
216
- #
217
- # @example
218
- # n = Money.us_dollar(100)
219
- # n.cents #=> 100
220
- # n.currency #=> #<Money::Currency id: usd>
221
- def self.us_dollar(cents)
222
- Money.new(cents, "USD")
223
- end
224
-
225
- # Creates a new Money object of the given value, using the Euro currency.
226
- #
227
- # @param [Integer] cents The cents value.
228
- #
229
- # @return [Money]
230
- #
231
- # @example
232
- # n = Money.euro(100)
233
- # n.cents #=> 100
234
- # n.currency #=> #<Money::Currency id: eur>
235
- def self.euro(cents)
236
- Money.new(cents, "EUR")
237
- end
238
193
 
239
194
  # Adds a new exchange rate to the default bank and return the rate.
240
195
  #
@@ -262,7 +217,10 @@ class Money
262
217
  # Alternatively you can use the convenience
263
218
  # methods like {Money.ca_dollar} and {Money.us_dollar}.
264
219
  #
265
- # @param [Numeric] fractional The value given in the fractional unit.
220
+ # @param [Object] obj Either The fractional value of the money,
221
+ # a Money object, or a currency. (If passed a currency as the first
222
+ # argument, a Money will be created in that currency with fractional value
223
+ # = 0.
266
224
  # @param [Currency, String, Symbol] currency The currency format.
267
225
  # @param [Money::Bank::*] bank The exchange bank to use.
268
226
  #
@@ -30,17 +30,7 @@ class Money
30
30
  false
31
31
  end
32
32
  end
33
-
34
- # Synonymous with +#==+.
35
- #
36
- # @param [Money] other_money Value to compare with.
37
- #
38
- # @return [Money]
39
- #
40
- # @see #==
41
- def eql?(other_money)
42
- self == other_money
43
- end
33
+ alias_method :eql?, :==
44
34
 
45
35
  def <=>(val)
46
36
  if val.respond_to?(:to_money)
@@ -0,0 +1,85 @@
1
+ class Money
2
+ module Constructors
3
+
4
+ # Create a new money object with value 0.
5
+ #
6
+ # @param [Currency, String, Symbol] currency The currency to use.
7
+ #
8
+ # @return [Money]
9
+ #
10
+ # @example
11
+ # Money.empty #=> #<Money @fractional=0>
12
+ def empty(currency = default_currency)
13
+ @empty ||= {}
14
+ @empty[currency] ||= Money.new(0, currency).freeze
15
+ end
16
+ alias_method :zero, :empty
17
+
18
+
19
+ # Creates a new Money object of the given value, using the Canadian
20
+ # dollar currency.
21
+ #
22
+ # @param [Integer] cents The cents value.
23
+ #
24
+ # @return [Money]
25
+ #
26
+ # @example
27
+ # n = Money.ca_dollar(100)
28
+ # n.cents #=> 100
29
+ # n.currency #=> #<Money::Currency id: cad>
30
+ def ca_dollar(cents)
31
+ Money.new(cents, "CAD")
32
+ end
33
+ alias_method :cad, :ca_dollar
34
+
35
+
36
+ # Creates a new Money object of the given value, using the American dollar
37
+ # currency.
38
+ #
39
+ # @param [Integer] cents The cents value.
40
+ #
41
+ # @return [Money]
42
+ #
43
+ # @example
44
+ # n = Money.us_dollar(100)
45
+ # n.cents #=> 100
46
+ # n.currency #=> #<Money::Currency id: usd>
47
+ def us_dollar(cents)
48
+ Money.new(cents, "USD")
49
+ end
50
+ alias_method :usd, :us_dollar
51
+
52
+
53
+ # Creates a new Money object of the given value, using the Euro currency.
54
+ #
55
+ # @param [Integer] cents The cents value.
56
+ #
57
+ # @return [Money]
58
+ #
59
+ # @example
60
+ # n = Money.euro(100)
61
+ # n.cents #=> 100
62
+ # n.currency #=> #<Money::Currency id: eur>
63
+ def euro(cents)
64
+ Money.new(cents, "EUR")
65
+ end
66
+ alias_method :eur, :euro
67
+
68
+
69
+ # Creates a new Money object of the given value, in British pounds.
70
+ #
71
+ # @param [Integer] pence The pence value.
72
+ #
73
+ # @return [Money]
74
+ #
75
+ # @example
76
+ # n = Money.pound_sterling(100)
77
+ # n.fractional #=> 100
78
+ # n.currency #=> #<Money::Currency id: gbp>
79
+ def pound_sterling(pence)
80
+ Money.new(pence, "GBP")
81
+ end
82
+ alias_method :gbp, :pound_sterling
83
+
84
+ end
85
+ end
@@ -189,10 +189,23 @@ class Money
189
189
  # Money.new(100, "USD").format(:disambiguate => true) #=> "$100.00"
190
190
  # Money.new(100, "CAD").format(:disambiguate => true) #=> "C$100.00"
191
191
  #
192
+ # @option *rules [Boolean] :html_wrap_symbol (false) Wraps the currency symbol
193
+ # in a html <span> tag.
194
+ #
195
+ # @example
196
+ # Money.new(100, "USD").format(:disambiguate => false)
197
+ # #=> "<span class=\"currency_symbol\">$100.00</span>
198
+ #
199
+ # @option *rules [Symbol] :symbol_position (:before) `:before` if the currency
200
+ # symbol goes before the amount, `:after` if it goes after.
201
+ #
202
+ # @example
203
+ # Money.new(100, "USD").format(:symbol_position => :before) #=> "$100.00"
204
+ # Money.new(100, "USD").format(:symbol_position => :after) #=> "100.00 $"
205
+ #
192
206
  # Note that the default rules can be defined through +Money.default_formatting_rules+ hash.
193
207
  #
194
208
  # @see +Money.default_formatting_rules+ for more information.
195
-
196
209
  def format(*rules)
197
210
  # support for old format parameters
198
211
  rules = normalize_formatting_rules(rules)
@@ -359,7 +372,11 @@ class Money
359
372
 
360
373
  def symbol_position_from(rules)
361
374
  if rules.has_key?(:symbol_position)
362
- rules[:symbol_position]
375
+ if [:before, :after].include?(rules[:symbol_position])
376
+ return rules[:symbol_position]
377
+ else
378
+ raise ArgumentError, ":symbol_position must be ':before' or ':after'"
379
+ end
363
380
  elsif currency.symbol_first?
364
381
  :before
365
382
  else
data/lib/money/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Money
2
- VERSION = "6.5.0"
2
+ VERSION = "6.5.1"
3
3
  end
@@ -6,6 +6,13 @@ describe Money::Currency do
6
6
 
7
7
  FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 450, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
8
8
 
9
+ def register_foo
10
+ Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
11
+ end
12
+
13
+ def unregister_foo
14
+ Money::Currency.unregister(JSON.parse(FOO, :symbolize_names => true))
15
+ end
9
16
 
10
17
  describe "UnknownMoney::Currency" do
11
18
  it "is a subclass of ArgumentError" do
@@ -14,16 +21,10 @@ describe Money::Currency do
14
21
  end
15
22
 
16
23
  describe ".find" do
17
- before :each do
18
- Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
19
- end
20
-
21
- after :each do
22
- Money::Currency.unregister(JSON.parse(FOO, :symbolize_names => true))
23
- end
24
+ before { register_foo }
25
+ after { unregister_foo }
24
26
 
25
27
  it "returns currency matching given id" do
26
-
27
28
  expected = Money::Currency.new(:foo)
28
29
  expect(Money::Currency.find(:foo)).to eq expected
29
30
  expect(Money::Currency.find(:FOO)).to eq expected
@@ -70,15 +71,92 @@ describe Money::Currency do
70
71
  expect(Money::Currency.all).to include Money::Currency.new(:usd)
71
72
  end
72
73
  it "includes registered currencies" do
73
- Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
74
+ register_foo
74
75
  expect(Money::Currency.all).to include Money::Currency.new(:foo)
75
- Money::Currency.unregister(JSON.parse(FOO, :symbolize_names => true))
76
+ unregister_foo
76
77
  end
77
78
  it 'is sorted by priority' do
78
79
  expect(Money::Currency.all.first.priority).to eq 1
79
80
  end
80
81
  end
81
82
 
83
+
84
+ describe ".register" do
85
+ after { Money::Currency.unregister(iso_code: "XXX") if Money::Currency.find("XXX") }
86
+
87
+ it "registers a new currency" do
88
+ Money::Currency.register(
89
+ iso_code: "XXX",
90
+ name: "Golden Doubloon",
91
+ symbol: "%",
92
+ subunit_to_unit: 100
93
+ )
94
+ new_currency = Money::Currency.find("XXX")
95
+ expect(new_currency).not_to be_nil
96
+ expect(new_currency.name).to eq "Golden Doubloon"
97
+ expect(new_currency.symbol).to eq "%"
98
+ end
99
+
100
+ specify ":iso_code must be present" do
101
+ expect {
102
+ Money::Currency.register(name: "New Currency")
103
+ }.to raise_error(KeyError)
104
+ end
105
+ end
106
+
107
+
108
+ describe ".unregister" do
109
+ it "unregisters a currency" do
110
+ Money::Currency.register(iso_code: "XXX")
111
+ expect(Money::Currency.find("XXX")).not_to be_nil # Sanity check
112
+ Money::Currency.unregister(iso_code: "XXX")
113
+ expect(Money::Currency.find("XXX")).to be_nil
114
+ end
115
+
116
+ it "returns true iff the currency existed" do
117
+ Money::Currency.register(iso_code: "XXX")
118
+ expect(Money::Currency.unregister(iso_code: "XXX")).to be_truthy
119
+ expect(Money::Currency.unregister(iso_code: "XXX")).to be_falsey
120
+ end
121
+
122
+ it "can be passed an ISO code" do
123
+ Money::Currency.register(iso_code: "XXX")
124
+ Money::Currency.register(iso_code: "YYZ")
125
+ # Test with string:
126
+ Money::Currency.unregister("XXX")
127
+ expect(Money::Currency.find("XXX")).to be_nil
128
+ # Test with symbol:
129
+ Money::Currency.unregister(:yyz)
130
+ expect(Money::Currency.find(:yyz)).to be_nil
131
+ end
132
+ end
133
+
134
+
135
+ describe ".each" do
136
+ it "yields each currency to the block" do
137
+ expect(Money::Currency).to respond_to(:each)
138
+ currencies = []
139
+ Money::Currency.each do |currency|
140
+ currencies.push(currency)
141
+ end
142
+
143
+ # Don't bother testing every single currency
144
+ expect(currencies[0]).to eq Money::Currency.all[0]
145
+ expect(currencies[1]).to eq Money::Currency.all[1]
146
+ expect(currencies[-1]).to eq Money::Currency.all[-1]
147
+ end
148
+ end
149
+
150
+
151
+ it "implements Enumerable" do
152
+ expect(Money::Currency).to respond_to(:all?)
153
+ expect(Money::Currency).to respond_to(:each_with_index)
154
+ expect(Money::Currency).to respond_to(:map)
155
+ expect(Money::Currency).to respond_to(:select)
156
+ expect(Money::Currency).to respond_to(:reject)
157
+ end
158
+
159
+
82
160
  describe "#initialize" do
83
161
  it "lookups data from loaded config" do
84
162
  currency = Money::Currency.new("USD")
@@ -104,6 +182,30 @@ describe Money::Currency do
104
182
  expect(Money::Currency.new(:cad)).to be > Money::Currency.new(:usd)
105
183
  expect(Money::Currency.new(:usd)).to be < Money::Currency.new(:eur)
106
184
  end
185
+
186
+ it "compares by id when priority is the same" do
187
+ Money::Currency.register(iso_code: "ABD", priority: 15)
188
+ Money::Currency.register(iso_code: "ABC", priority: 15)
189
+ Money::Currency.register(iso_code: "ABE", priority: 15)
190
+ abd = Money::Currency.find("ABD")
191
+ abc = Money::Currency.find("ABC")
192
+ abe = Money::Currency.find("ABE")
193
+ expect(abd).to be > abc
194
+ expect(abe).to be > abd
195
+ Money::Currency.unregister("ABD")
196
+ Money::Currency.unregister("ABC")
197
+ Money::Currency.unregister("ABE")
198
+ end
199
+
200
+ context "when one of the currencies has no 'priority' set" do
201
+ it "compares by id" do
202
+ Money::Currency.register(iso_code: "ABD") # No priority
203
+ abd = Money::Currency.find(:abd)
204
+ usd = Money::Currency.find(:usd)
205
+ expect(abd).to be < usd
206
+ Money::Currency.unregister(iso_code: "ABD")
207
+ end
208
+ end
107
209
  end
108
210
 
109
211
  describe "#==" do
@@ -211,9 +313,9 @@ describe Money::Currency do
211
313
  end
212
314
 
213
315
  it "proper places for custom currency" do
214
- Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
316
+ register_foo
215
317
  Money::Currency.new(:foo).decimal_places == 3
216
- Money::Currency.unregister(JSON.parse(FOO, :symbolize_names => true))
318
+ unregister_foo
217
319
  end
218
320
  end
219
321
  end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Money::Constructors do
6
+
7
+ describe "::empty" do
8
+ it "creates a new Money object of 0 cents" do
9
+ expect(Money.empty).to eq Money.new(0)
10
+ end
11
+
12
+ it "memoizes the result" do
13
+ expect(Money.empty.object_id).to eq Money.empty.object_id
14
+ end
15
+
16
+ it "memoizes a result for each currency" do
17
+ expect(Money.empty(:cad).object_id).to eq Money.empty(:cad).object_id
18
+ end
19
+
20
+ it "doesn't allow money to be modified for a currency" do
21
+ expect(Money.empty).to be_frozen
22
+ end
23
+ end
24
+
25
+
26
+ describe "::zero" do
27
+ subject { Money.zero }
28
+ it { is_expected.to eq Money.empty }
29
+ end
30
+
31
+
32
+ describe "::ca_dollar" do
33
+ it "creates a new Money object of the given value in CAD" do
34
+ expect(Money.ca_dollar(50)).to eq Money.new(50, "CAD")
35
+ end
36
+
37
+ it "is aliased to ::cad" do
38
+ expect(Money.cad(50)).to eq Money.ca_dollar(50)
39
+ end
40
+ end
41
+
42
+
43
+ describe "::us_dollar" do
44
+ it "creates a new Money object of the given value in USD" do
45
+ expect(Money.us_dollar(50)).to eq Money.new(50, "USD")
46
+ end
47
+
48
+ it "is aliased to ::usd" do
49
+ expect(Money.usd(50)).to eq Money.us_dollar(50)
50
+ end
51
+ end
52
+
53
+
54
+ describe "::euro" do
55
+ it "creates a new Money object of the given value in EUR" do
56
+ expect(Money.euro(50)).to eq Money.new(50, "EUR")
57
+ end
58
+
59
+ it "is aliased to ::eur" do
60
+ expect(Money.eur(50)).to eq Money.euro(50)
61
+ end
62
+ end
63
+
64
+
65
+ describe "::pound_sterling" do
66
+ it "creates a new Money object of the given value in GBP" do
67
+ expect(Money.pound_sterling(50)).to eq Money.new(50, "GBP")
68
+ end
69
+
70
+ it "is aliased to ::gbp" do
71
+ expect(Money.gbp(50)).to eq Money.pound_sterling(50)
72
+ end
73
+ end
74
+
75
+ end
@@ -439,6 +439,10 @@ describe Money, "formatting" do
439
439
  it "inserts currency symbol after the amount when set to :after" do
440
440
  expect(Money.us_dollar(1_000_000_000_12).format(:symbol_position => :after)).to eq "1,000,000,000.12 $"
441
441
  end
442
+
443
+ it "raises an ArgumentError when passed an invalid option" do
444
+ expect{Money.euro(0).format(:symbol_position => :befor)}.to raise_error(ArgumentError)
445
+ end
442
446
  end
443
447
 
444
448
  describe ":sign_before_symbol option" do
data/spec/money_spec.rb CHANGED
@@ -90,47 +90,6 @@ describe Money do
90
90
  end
91
91
  end
92
92
 
93
- describe ".empty" do
94
- it "creates a new Money object of 0 cents" do
95
- expect(Money.empty).to eq Money.new(0)
96
- end
97
-
98
- it "memoizes the result" do
99
- expect(Money.empty.object_id).to eq Money.empty.object_id
100
- end
101
-
102
- it "memoizes a result for each currency" do
103
- expect(Money.empty(:cad).object_id).to eq Money.empty(:cad).object_id
104
- end
105
-
106
- it "doesn't allow money to be modified for a currency" do
107
- expect(Money.empty).to be_frozen
108
- end
109
- end
110
-
111
- describe ".zero" do
112
- subject { Money.zero }
113
- it { is_expected.to eq Money.empty }
114
- end
115
-
116
- describe ".ca_dollar" do
117
- it "creates a new Money object of the given value in CAD" do
118
- expect(Money.ca_dollar(50)).to eq Money.new(50, "CAD")
119
- end
120
- end
121
-
122
- describe ".us_dollar" do
123
- it "creates a new Money object of the given value in USD" do
124
- expect(Money.us_dollar(50)).to eq Money.new(50, "USD")
125
- end
126
- end
127
-
128
- describe ".euro" do
129
- it "creates a new Money object of the given value in EUR" do
130
- expect(Money.euro(50)).to eq Money.new(50, "EUR")
131
- end
132
- end
133
-
134
93
  describe ".add_rate" do
135
94
  before do
136
95
  @default_bank = Money.default_bank
@@ -162,13 +121,15 @@ describe Money do
162
121
  end
163
122
  end
164
123
 
165
- describe "#cents" do
166
- it "is a synonym of #fractional" do
167
- expectation = Money.new(0)
168
- def expectation.fractional
169
- "expectation"
124
+ %w[cents pence].each do |units|
125
+ describe "##{units}" do
126
+ it "is a synonym of #fractional" do
127
+ expectation = Money.new(0)
128
+ def expectation.fractional
129
+ "expectation"
130
+ end
131
+ expect(expectation.cents).to eq "expectation"
170
132
  end
171
- expect(expectation.cents).to eq "expectation"
172
133
  end
173
134
  end
174
135
 
@@ -300,55 +261,55 @@ YAML
300
261
  expect(money.round_to_nearest_cash_value).to eq 2350
301
262
 
302
263
  money = Money.new(-2350, "AED")
303
- expect(money.round_to_nearest_cash_value).to eq -2350
264
+ expect(money.round_to_nearest_cash_value).to eq(-2350)
304
265
 
305
266
  money = Money.new(2213, "AED")
306
267
  expect(money.round_to_nearest_cash_value).to eq 2225
307
268
 
308
269
  money = Money.new(-2213, "AED")
309
- expect(money.round_to_nearest_cash_value).to eq -2225
270
+ expect(money.round_to_nearest_cash_value).to eq(-2225)
310
271
 
311
272
  money = Money.new(2212, "AED")
312
273
  expect(money.round_to_nearest_cash_value).to eq 2200
313
274
 
314
275
  money = Money.new(-2212, "AED")
315
- expect(money.round_to_nearest_cash_value).to eq -2200
276
+ expect(money.round_to_nearest_cash_value).to eq(-2200)
316
277
 
317
278
  money = Money.new(178, "CHF")
318
279
  expect(money.round_to_nearest_cash_value).to eq 180
319
280
 
320
281
  money = Money.new(-178, "CHF")
321
- expect(money.round_to_nearest_cash_value).to eq -180
282
+ expect(money.round_to_nearest_cash_value).to eq(-180)
322
283
 
323
284
  money = Money.new(177, "CHF")
324
285
  expect(money.round_to_nearest_cash_value).to eq 175
325
286
 
326
287
  money = Money.new(-177, "CHF")
327
- expect(money.round_to_nearest_cash_value).to eq -175
288
+ expect(money.round_to_nearest_cash_value).to eq(-175)
328
289
 
329
290
  money = Money.new(175, "CHF")
330
291
  expect(money.round_to_nearest_cash_value).to eq 175
331
292
 
332
293
  money = Money.new(-175, "CHF")
333
- expect(money.round_to_nearest_cash_value).to eq -175
294
+ expect(money.round_to_nearest_cash_value).to eq(-175)
334
295
 
335
296
  money = Money.new(299, "USD")
336
297
  expect(money.round_to_nearest_cash_value).to eq 299
337
298
 
338
299
  money = Money.new(-299, "USD")
339
- expect(money.round_to_nearest_cash_value).to eq -299
300
+ expect(money.round_to_nearest_cash_value).to eq(-299)
340
301
 
341
302
  money = Money.new(300, "USD")
342
303
  expect(money.round_to_nearest_cash_value).to eq 300
343
304
 
344
305
  money = Money.new(-300, "USD")
345
- expect(money.round_to_nearest_cash_value).to eq -300
306
+ expect(money.round_to_nearest_cash_value).to eq(-300)
346
307
 
347
308
  money = Money.new(301, "USD")
348
309
  expect(money.round_to_nearest_cash_value).to eq 301
349
310
 
350
311
  money = Money.new(-301, "USD")
351
- expect(money.round_to_nearest_cash_value).to eq -301
312
+ expect(money.round_to_nearest_cash_value).to eq(-301)
352
313
  end
353
314
 
354
315
  it "raises an exception if smallest denomination is not defined" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.5.0
4
+ version: 6.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Emmons
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-16 00:00:00.000000000 Z
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -129,6 +129,7 @@ files:
129
129
  - lib/money/currency/loader.rb
130
130
  - lib/money/money.rb
131
131
  - lib/money/money/arithmetic.rb
132
+ - lib/money/money/constructors.rb
132
133
  - lib/money/money/formatting.rb
133
134
  - lib/money/version.rb
134
135
  - money.gemspec
@@ -138,6 +139,7 @@ files:
138
139
  - spec/currency/heuristics_spec.rb
139
140
  - spec/currency_spec.rb
140
141
  - spec/money/arithmetic_spec.rb
142
+ - spec/money/constructors_spec.rb
141
143
  - spec/money/formatting_spec.rb
142
144
  - spec/money_spec.rb
143
145
  - spec/spec_helper.rb
@@ -180,6 +182,7 @@ test_files:
180
182
  - spec/currency/heuristics_spec.rb
181
183
  - spec/currency_spec.rb
182
184
  - spec/money/arithmetic_spec.rb
185
+ - spec/money/constructors_spec.rb
183
186
  - spec/money/formatting_spec.rb
184
187
  - spec/money_spec.rb
185
188
  - spec/spec_helper.rb