money 6.5.0 → 6.5.1

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