tiny-sharp-rb 0.0.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 +7 -0
- data/money-7.0.2/CHANGELOG.md +843 -0
- data/money-7.0.2/LICENSE +23 -0
- data/money-7.0.2/README.md +626 -0
- data/money-7.0.2/config/currency_backwards_compatible.json +225 -0
- data/money-7.0.2/config/currency_iso.json +2750 -0
- data/money-7.0.2/config/currency_non_iso.json +146 -0
- data/money-7.0.2/lib/money/bank/base.rb +130 -0
- data/money-7.0.2/lib/money/bank/single_currency.rb +26 -0
- data/money-7.0.2/lib/money/bank/variable_exchange.rb +285 -0
- data/money-7.0.2/lib/money/currency/heuristics.rb +11 -0
- data/money-7.0.2/lib/money/currency/loader.rb +28 -0
- data/money-7.0.2/lib/money/currency.rb +477 -0
- data/money-7.0.2/lib/money/locale_backend/base.rb +9 -0
- data/money-7.0.2/lib/money/locale_backend/currency.rb +13 -0
- data/money-7.0.2/lib/money/locale_backend/errors.rb +8 -0
- data/money-7.0.2/lib/money/locale_backend/i18n.rb +28 -0
- data/money-7.0.2/lib/money/money/allocation.rb +87 -0
- data/money-7.0.2/lib/money/money/arithmetic.rb +346 -0
- data/money-7.0.2/lib/money/money/constructors.rb +86 -0
- data/money-7.0.2/lib/money/money/formatter.rb +361 -0
- data/money-7.0.2/lib/money/money/formatting_rules.rb +92 -0
- data/money-7.0.2/lib/money/money/locale_backend.rb +20 -0
- data/money-7.0.2/lib/money/money.rb +664 -0
- data/money-7.0.2/lib/money/rates_store/memory.rb +122 -0
- data/money-7.0.2/lib/money/version.rb +5 -0
- data/money-7.0.2/lib/money.rb +9 -0
- data/money-7.0.2/money.gemspec +32 -0
- data/tiny-sharp-rb.gemspec +12 -0
- metadata +69 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "money/currency/loader"
|
|
5
|
+
require "money/currency/heuristics"
|
|
6
|
+
|
|
7
|
+
class Money
|
|
8
|
+
|
|
9
|
+
# Represents a specific currency unit.
|
|
10
|
+
#
|
|
11
|
+
# @see https://en.wikipedia.org/wiki/Currency
|
|
12
|
+
# @see https://www.iso.org/iso-4217-currency-codes.html
|
|
13
|
+
class Currency
|
|
14
|
+
include Comparable
|
|
15
|
+
extend Enumerable
|
|
16
|
+
extend Money::Currency::Heuristics
|
|
17
|
+
|
|
18
|
+
# Keeping cached instances in sync between threads
|
|
19
|
+
@@mutex = Mutex.new
|
|
20
|
+
@@instances = {}
|
|
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
|
+
|
|
33
|
+
# Thrown when an unknown currency is requested.
|
|
34
|
+
class UnknownCurrency < ArgumentError; end
|
|
35
|
+
|
|
36
|
+
# Thrown when currency is not provided.
|
|
37
|
+
class NoCurrency < ArgumentError; end
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
def new(id)
|
|
41
|
+
id = id.to_s.downcase
|
|
42
|
+
unless stringified_keys.include?(id)
|
|
43
|
+
raise UnknownCurrency, "Unknown currency '#{id}'"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
_instances[id] || @@mutex.synchronize { _instances[id] ||= super }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def _instances
|
|
50
|
+
@@instances
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Lookup a currency with given +id+ an returns a +Currency+ instance on
|
|
54
|
+
# success, +nil+ otherwise.
|
|
55
|
+
#
|
|
56
|
+
# @param [String, Symbol, #to_s] id Used to look into +table+ and
|
|
57
|
+
# retrieve the applicable attributes.
|
|
58
|
+
#
|
|
59
|
+
# @return [Money::Currency]
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# Money::Currency.find(:eur) #=> #<Money::Currency id: eur ...>
|
|
63
|
+
# Money::Currency.find(:foo) #=> nil
|
|
64
|
+
def find(id)
|
|
65
|
+
id = id.to_s.downcase.to_sym
|
|
66
|
+
new(id)
|
|
67
|
+
rescue UnknownCurrency
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Lookup a currency with given +num+ as an ISO 4217 numeric and returns an
|
|
72
|
+
# +Currency+ instance on success, +nil+ otherwise.
|
|
73
|
+
#
|
|
74
|
+
# @param [#to_s] num used to look into +table+ in +iso_numeric+ and find
|
|
75
|
+
# the right currency id.
|
|
76
|
+
#
|
|
77
|
+
# @return [Money::Currency]
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# Money::Currency.find_by_iso_numeric(978) #=> #<Money::Currency id: eur ...>
|
|
81
|
+
# Money::Currency.find_by_iso_numeric(51) #=> #<Money::Currency id: amd ...>
|
|
82
|
+
# Money::Currency.find_by_iso_numeric('001') #=> nil
|
|
83
|
+
def find_by_iso_numeric(num)
|
|
84
|
+
num = num.to_s.rjust(3, '0')
|
|
85
|
+
return if num.empty?
|
|
86
|
+
id = iso_numeric_index[num]
|
|
87
|
+
new(id) if id
|
|
88
|
+
rescue UnknownCurrency
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Wraps the object in a +Currency+ unless it's already a +Currency+
|
|
93
|
+
# object.
|
|
94
|
+
#
|
|
95
|
+
# @param [Object] object The object to attempt and wrap as a +Currency+
|
|
96
|
+
# object.
|
|
97
|
+
#
|
|
98
|
+
# @return [Money::Currency]
|
|
99
|
+
#
|
|
100
|
+
# @example
|
|
101
|
+
# c1 = Money::Currency.new(:usd)
|
|
102
|
+
# Money::Currency.wrap(nil) #=> nil
|
|
103
|
+
# Money::Currency.wrap(c1) #=> #<Money::Currency id: usd ...>
|
|
104
|
+
# Money::Currency.wrap("usd") #=> #<Money::Currency id: usd ...>
|
|
105
|
+
def wrap(object)
|
|
106
|
+
if object.nil?
|
|
107
|
+
nil
|
|
108
|
+
elsif object.is_a?(Currency)
|
|
109
|
+
object
|
|
110
|
+
else
|
|
111
|
+
Currency.new(object)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# List of known currencies.
|
|
116
|
+
#
|
|
117
|
+
# == monetary unit
|
|
118
|
+
# The standard unit of value of a currency, as the dollar in the United States or the peso in Mexico.
|
|
119
|
+
# https://www.answers.com/redirectSearch?query=monetary-unit
|
|
120
|
+
# == fractional monetary unit, subunit
|
|
121
|
+
# A monetary unit that is valued at a fraction (usually one hundredth) of the basic monetary unit
|
|
122
|
+
# https://www.answers.com/redirectSearch?query=fractional-monetary-unit-subunit
|
|
123
|
+
#
|
|
124
|
+
# See https://en.wikipedia.org/wiki/List_of_circulating_currencies and
|
|
125
|
+
# https://metacpan.org/release/TNGUYEN/Locale-Currency-Format-1.28/view/Format.pm
|
|
126
|
+
def table
|
|
127
|
+
@table ||= Loader.load_currencies
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# List the currencies imported and registered
|
|
131
|
+
# @return [Array]
|
|
132
|
+
#
|
|
133
|
+
# @example
|
|
134
|
+
# Money::Currency.all()
|
|
135
|
+
# [#<Currency ..USD>, 'CAD', 'EUR']...
|
|
136
|
+
def all
|
|
137
|
+
table.keys.map do |curr|
|
|
138
|
+
c = Currency.new(curr)
|
|
139
|
+
if c.priority.nil?
|
|
140
|
+
raise MissingAttributeError.new(:all, c.id, :priority)
|
|
141
|
+
end
|
|
142
|
+
c
|
|
143
|
+
end.sort_by(&:priority)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# We need a string-based validator before creating an unbounded number of
|
|
147
|
+
# symbols.
|
|
148
|
+
# http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/#11
|
|
149
|
+
# https://github.com/RubyMoney/money/issues/132
|
|
150
|
+
#
|
|
151
|
+
# @return [Set]
|
|
152
|
+
def stringified_keys
|
|
153
|
+
@stringified_keys ||= stringify_keys
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Register a new currency
|
|
157
|
+
#
|
|
158
|
+
# @param curr [Hash] information about the currency
|
|
159
|
+
# @option priority [Numeric] a numerical value you can use to sort/group
|
|
160
|
+
# the currency list
|
|
161
|
+
# @option iso_code [String] the international 3-letter code as defined
|
|
162
|
+
# by the ISO 4217 standard
|
|
163
|
+
# @option iso_numeric [Integer] the international 3-digit code as
|
|
164
|
+
# defined by the ISO 4217 standard
|
|
165
|
+
# @option name [String] the currency name
|
|
166
|
+
# @option symbol [String] the currency symbol (UTF-8 encoded)
|
|
167
|
+
# @option subunit [String] the name of the fractional monetary unit
|
|
168
|
+
# @option subunit_to_unit [Numeric] the proportion between the unit and
|
|
169
|
+
# the subunit
|
|
170
|
+
# @option separator [String] character between the whole and fraction
|
|
171
|
+
# amounts
|
|
172
|
+
# @option delimiter [String] character between each thousands place
|
|
173
|
+
def register(curr)
|
|
174
|
+
key = curr.fetch(:iso_code).downcase.to_sym
|
|
175
|
+
@@mutex.synchronize { _instances.delete(key.to_s) }
|
|
176
|
+
table[key] = curr
|
|
177
|
+
@stringified_keys = nil
|
|
178
|
+
clear_iso_numeric_cache
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Inherit a new currency from existing one
|
|
182
|
+
#
|
|
183
|
+
# @param parent_iso_code [String] the international 3-letter code as defined
|
|
184
|
+
# @param curr [Hash] See {register} method for hash structure
|
|
185
|
+
def inherit(parent_iso_code, curr)
|
|
186
|
+
parent_iso_code = parent_iso_code.downcase.to_sym
|
|
187
|
+
curr = table.fetch(parent_iso_code, {}).merge(curr)
|
|
188
|
+
register(curr)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Unregister a currency.
|
|
192
|
+
#
|
|
193
|
+
# @param [Object] curr A Hash with the key `:iso_code`, or the ISO code
|
|
194
|
+
# as a String or Symbol.
|
|
195
|
+
#
|
|
196
|
+
# @return [Boolean] true if the currency previously existed, false
|
|
197
|
+
# if it didn't.
|
|
198
|
+
def unregister(curr)
|
|
199
|
+
if curr.is_a?(Hash)
|
|
200
|
+
key = curr.fetch(:iso_code).downcase.to_sym
|
|
201
|
+
else
|
|
202
|
+
key = curr.downcase.to_sym
|
|
203
|
+
end
|
|
204
|
+
existed = @table.delete(key)
|
|
205
|
+
@stringified_keys = nil if existed
|
|
206
|
+
existed ? true : false
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def each
|
|
210
|
+
all.each { |c| yield(c) }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def reset!
|
|
214
|
+
@@instances = {}
|
|
215
|
+
@table = Loader.load_currencies
|
|
216
|
+
clear_iso_numeric_cache
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
private
|
|
220
|
+
|
|
221
|
+
def iso_numeric_index
|
|
222
|
+
@iso_numeric_index ||= table.each_with_object({}) do |(id, attrs), index|
|
|
223
|
+
index[attrs[:iso_numeric]] = id
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def clear_iso_numeric_cache
|
|
228
|
+
@iso_numeric_index = nil
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def stringify_keys
|
|
232
|
+
table.keys.each_with_object(Set.new) { |k, set| set.add(k.to_s.downcase) }
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# @!attribute [r] id
|
|
237
|
+
# @return [Symbol] The symbol used to identify the currency, usually THE
|
|
238
|
+
# lowercase +iso_code+ attribute.
|
|
239
|
+
# @!attribute [r] priority
|
|
240
|
+
# @return [Integer] A numerical value you can use to sort/group the
|
|
241
|
+
# currency list.
|
|
242
|
+
# @!attribute [r] iso_code
|
|
243
|
+
# @return [String] The international 3-letter code as defined by the ISO
|
|
244
|
+
# 4217 standard.
|
|
245
|
+
# @!attribute [r] iso_numeric
|
|
246
|
+
# @return [String] The international 3-numeric code as defined by the ISO
|
|
247
|
+
# 4217 standard.
|
|
248
|
+
# @!attribute [r] name
|
|
249
|
+
# @return [String] The currency name.
|
|
250
|
+
# @!attribute [r] symbol
|
|
251
|
+
# @return [String] The currency symbol (UTF-8 encoded).
|
|
252
|
+
# @!attribute [r] disambiguate_symbol
|
|
253
|
+
# @return [String] Alternative currency used if symbol is ambiguous
|
|
254
|
+
# @!attribute [r] html_entity
|
|
255
|
+
# @return [String] The html entity for the currency symbol
|
|
256
|
+
# @!attribute [r] subunit
|
|
257
|
+
# @return [String] The name of the fractional monetary unit.
|
|
258
|
+
# @!attribute [r] subunit_to_unit
|
|
259
|
+
# @return [Integer] The proportion between the unit and the subunit
|
|
260
|
+
# @!attribute [r] decimal_mark
|
|
261
|
+
# @return [String] The decimal mark, or character used to separate the
|
|
262
|
+
# whole unit from the subunit.
|
|
263
|
+
# @!attribute [r] thousands_separator
|
|
264
|
+
# @return [String] The character used to separate thousands grouping of
|
|
265
|
+
# the whole unit.
|
|
266
|
+
# @!attribute [r] symbol_first
|
|
267
|
+
# @return [Boolean] Should the currency symbol precede the amount, or
|
|
268
|
+
# should it come after?
|
|
269
|
+
# @!attribute [r] smallest_denomination
|
|
270
|
+
# @return [Integer] Smallest amount of cash possible (in the subunit of
|
|
271
|
+
# this currency)
|
|
272
|
+
|
|
273
|
+
attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
|
|
274
|
+
:disambiguate_symbol, :html_entity, :subunit, :subunit_to_unit, :decimal_mark,
|
|
275
|
+
:thousands_separator, :symbol_first, :smallest_denomination, :format
|
|
276
|
+
|
|
277
|
+
alias_method :separator, :decimal_mark
|
|
278
|
+
alias_method :delimiter, :thousands_separator
|
|
279
|
+
alias_method :eql?, :==
|
|
280
|
+
|
|
281
|
+
# Create a new +Currency+ object.
|
|
282
|
+
#
|
|
283
|
+
# @param [String, Symbol, #to_s] id Used to look into +table+ and retrieve
|
|
284
|
+
# the applicable attributes.
|
|
285
|
+
#
|
|
286
|
+
# @return [Money::Currency]
|
|
287
|
+
#
|
|
288
|
+
# @example
|
|
289
|
+
# Money::Currency.new(:usd) #=> #<Money::Currency id: usd ...>
|
|
290
|
+
def initialize(id)
|
|
291
|
+
@id = id.to_sym
|
|
292
|
+
initialize_data!
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Compares +self+ with +other_currency+ against the value of +priority+
|
|
296
|
+
# attribute.
|
|
297
|
+
#
|
|
298
|
+
# @param [Money::Currency] other_currency The currency to compare to.
|
|
299
|
+
#
|
|
300
|
+
# @return [-1,0,1] -1 if less than, 0 is equal to, 1 if greater than
|
|
301
|
+
#
|
|
302
|
+
# @example
|
|
303
|
+
# c1 = Money::Currency.new(:usd)
|
|
304
|
+
# c2 = Money::Currency.new(:jpy)
|
|
305
|
+
# c1 <=> c2 #=> 1
|
|
306
|
+
# c2 <=> c1 #=> -1
|
|
307
|
+
# c1 <=> c1 #=> 0
|
|
308
|
+
def <=>(other_currency)
|
|
309
|
+
# <=> returns nil when one of the values is nil
|
|
310
|
+
comparison = self.priority <=> other_currency.priority || 0
|
|
311
|
+
|
|
312
|
+
if comparison == 0
|
|
313
|
+
self.id <=> other_currency.id
|
|
314
|
+
else
|
|
315
|
+
comparison
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Compares +self+ with +other_currency+ and returns +true+ if the are the
|
|
320
|
+
# same or if their +id+ attributes match.
|
|
321
|
+
#
|
|
322
|
+
# @param [Money::Currency] other_currency The currency to compare to.
|
|
323
|
+
#
|
|
324
|
+
# @return [Boolean]
|
|
325
|
+
#
|
|
326
|
+
# @example
|
|
327
|
+
# c1 = Money::Currency.new(:usd)
|
|
328
|
+
# c2 = Money::Currency.new(:jpy)
|
|
329
|
+
# c1 == c1 #=> true
|
|
330
|
+
# c1 == c2 #=> false
|
|
331
|
+
def ==(other_currency)
|
|
332
|
+
self.equal?(other_currency) || compare_ids(other_currency)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def compare_ids(other_currency)
|
|
336
|
+
other_currency_id = if other_currency.is_a?(Currency)
|
|
337
|
+
other_currency.id.to_s.downcase
|
|
338
|
+
else
|
|
339
|
+
other_currency.to_s.downcase
|
|
340
|
+
end
|
|
341
|
+
self.id.to_s.downcase == other_currency_id
|
|
342
|
+
end
|
|
343
|
+
private :compare_ids
|
|
344
|
+
|
|
345
|
+
# Returns a Integer hash value based on the +id+ attribute in order to use
|
|
346
|
+
# functions like & (intersection), group_by, etc.
|
|
347
|
+
#
|
|
348
|
+
# @return [Integer]
|
|
349
|
+
#
|
|
350
|
+
# @example
|
|
351
|
+
# Money::Currency.new(:usd).hash #=> 428936
|
|
352
|
+
def hash
|
|
353
|
+
id.hash
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Returns a human readable representation.
|
|
357
|
+
#
|
|
358
|
+
# @return [String]
|
|
359
|
+
#
|
|
360
|
+
# @example
|
|
361
|
+
# Money::Currency.new(:usd) #=> #<Currency id: usd ...>
|
|
362
|
+
def inspect
|
|
363
|
+
"#<#{self.class.name} id: #{id}, priority: #{priority}, symbol_first: #{symbol_first}, thousands_separator: #{thousands_separator}, html_entity: #{html_entity}, decimal_mark: #{decimal_mark}, name: #{name}, symbol: #{symbol}, subunit_to_unit: #{subunit_to_unit}, exponent: #{exponent}, iso_code: #{iso_code}, iso_numeric: #{iso_numeric}, subunit: #{subunit}, smallest_denomination: #{smallest_denomination}, format: #{format}>"
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Returns a string representation corresponding to the upcase +id+
|
|
367
|
+
# attribute.
|
|
368
|
+
#
|
|
369
|
+
# --
|
|
370
|
+
# DEV: id.to_s.upcase corresponds to iso_code but don't use ISO_CODE for consistency.
|
|
371
|
+
#
|
|
372
|
+
# @return [String]
|
|
373
|
+
#
|
|
374
|
+
# @example
|
|
375
|
+
# Money::Currency.new(:usd).to_s #=> "USD"
|
|
376
|
+
# Money::Currency.new(:eur).to_s #=> "EUR"
|
|
377
|
+
def to_s
|
|
378
|
+
id.to_s.upcase
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Returns a string representation corresponding to the upcase +id+
|
|
382
|
+
# attribute. Useful in cases where only implicit conversions are made.
|
|
383
|
+
#
|
|
384
|
+
# @return [String]
|
|
385
|
+
#
|
|
386
|
+
# @example
|
|
387
|
+
# Money::Currency.new(:usd).to_str #=> "USD"
|
|
388
|
+
# Money::Currency.new(:eur).to_str #=> "EUR"
|
|
389
|
+
def to_str
|
|
390
|
+
id.to_s.upcase
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Returns a symbol representation corresponding to the upcase +id+
|
|
394
|
+
# attribute.
|
|
395
|
+
#
|
|
396
|
+
# @return [Symbol]
|
|
397
|
+
#
|
|
398
|
+
# @example
|
|
399
|
+
# Money::Currency.new(:usd).to_sym #=> :USD
|
|
400
|
+
# Money::Currency.new(:eur).to_sym #=> :EUR
|
|
401
|
+
def to_sym
|
|
402
|
+
id.to_s.upcase.to_sym
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Conversion to +self+.
|
|
406
|
+
#
|
|
407
|
+
# @return [self]
|
|
408
|
+
def to_currency
|
|
409
|
+
self
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Returns currency symbol or iso code for currencies with no symbol.
|
|
413
|
+
#
|
|
414
|
+
# @return [String]
|
|
415
|
+
def code
|
|
416
|
+
symbol || iso_code
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def symbol_first?
|
|
420
|
+
!!@symbol_first
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Returns true if a code currency is ISO.
|
|
424
|
+
#
|
|
425
|
+
# @return [Boolean]
|
|
426
|
+
#
|
|
427
|
+
# @example
|
|
428
|
+
# Money::Currency.new(:usd).iso?
|
|
429
|
+
#
|
|
430
|
+
def iso?
|
|
431
|
+
iso_numeric && iso_numeric != ''
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# Returns true if a subunit is cents-based.
|
|
435
|
+
#
|
|
436
|
+
# @return [Boolean]
|
|
437
|
+
#
|
|
438
|
+
# @example
|
|
439
|
+
# Money::Currency.new(:usd).cents_based?
|
|
440
|
+
#
|
|
441
|
+
def cents_based?
|
|
442
|
+
subunit_to_unit == 100
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Returns the relation between subunit and unit as a base 10 exponent.
|
|
446
|
+
#
|
|
447
|
+
# Note that MGA and MRU are exceptions and are rounded to 1
|
|
448
|
+
# @see https://en.wikipedia.org/wiki/ISO_4217#Active_codes
|
|
449
|
+
#
|
|
450
|
+
# @return [Integer]
|
|
451
|
+
def exponent
|
|
452
|
+
Math.log10(subunit_to_unit).round
|
|
453
|
+
end
|
|
454
|
+
alias decimal_places exponent
|
|
455
|
+
|
|
456
|
+
private
|
|
457
|
+
|
|
458
|
+
def initialize_data!
|
|
459
|
+
data = self.class.table[@id]
|
|
460
|
+
@alternate_symbols = data[:alternate_symbols]
|
|
461
|
+
@decimal_mark = data[:decimal_mark]
|
|
462
|
+
@disambiguate_symbol = data[:disambiguate_symbol]
|
|
463
|
+
@html_entity = data[:html_entity]
|
|
464
|
+
@iso_code = data[:iso_code]
|
|
465
|
+
@iso_numeric = data[:iso_numeric]
|
|
466
|
+
@name = data[:name]
|
|
467
|
+
@priority = data[:priority]
|
|
468
|
+
@smallest_denomination = data[:smallest_denomination]
|
|
469
|
+
@subunit = data[:subunit]
|
|
470
|
+
@subunit_to_unit = data[:subunit_to_unit]
|
|
471
|
+
@symbol = data[:symbol]
|
|
472
|
+
@symbol_first = data[:symbol_first]
|
|
473
|
+
@thousands_separator = data[:thousands_separator]
|
|
474
|
+
@format = data[:format]
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'money/locale_backend/base'
|
|
4
|
+
|
|
5
|
+
class Money
|
|
6
|
+
module LocaleBackend
|
|
7
|
+
class I18n < Base
|
|
8
|
+
KEY_MAP = {
|
|
9
|
+
thousands_separator: :delimiter,
|
|
10
|
+
decimal_mark: :separator,
|
|
11
|
+
symbol: :unit,
|
|
12
|
+
format: :format,
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
raise NotSupported, 'I18n not found' unless defined?(::I18n)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def lookup(key, _)
|
|
20
|
+
i18n_key = KEY_MAP[key]
|
|
21
|
+
|
|
22
|
+
::I18n.t i18n_key, scope: 'number.currency.format', raise: true
|
|
23
|
+
rescue ::I18n::MissingTranslationData
|
|
24
|
+
::I18n.t i18n_key, scope: 'number.format', default: nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Money
|
|
4
|
+
class Allocation
|
|
5
|
+
# Allocates a specified amount into parts based on their proportions or distributes
|
|
6
|
+
# it evenly when the number of parts is specified numerically.
|
|
7
|
+
#
|
|
8
|
+
# The total of the allocated amounts will always equal the original amount.
|
|
9
|
+
#
|
|
10
|
+
# The parts can be specified as:
|
|
11
|
+
# Numeric — performs the split between a given number of parties evenly
|
|
12
|
+
# Array<Numeric> — allocates the amounts proportionally to the given array
|
|
13
|
+
#
|
|
14
|
+
# @param amount [Numeric] The total amount to be allocated.
|
|
15
|
+
# @param parts [Numeric, Array<Numeric>] Number of parts to split into or an array (proportions for allocation)
|
|
16
|
+
# @param decimal_cutoff [Boolean, Integer] Controls per-split precision:
|
|
17
|
+
# - When true, splits are truncated to whole amounts.
|
|
18
|
+
# - When an Integer N, splits are rounded to N decimal places.
|
|
19
|
+
# - When false or nil, no rounding/truncation is applied.
|
|
20
|
+
# Defaults to true (whole amounts).
|
|
21
|
+
#
|
|
22
|
+
# @return [Array<Numeric>] An array containing the allocated amounts.
|
|
23
|
+
# @raise [ArgumentError] If parts is empty or not provided.
|
|
24
|
+
def self.generate(amount, parts, decimal_cutoff = true)
|
|
25
|
+
parts = if parts.is_a?(Numeric)
|
|
26
|
+
Array.new(parts, 1)
|
|
27
|
+
elsif parts.all?(&:zero?)
|
|
28
|
+
Array.new(parts.count, 1)
|
|
29
|
+
else
|
|
30
|
+
parts.dup
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
raise ArgumentError, 'need at least one part' if parts.empty?
|
|
34
|
+
|
|
35
|
+
if [amount, *parts].any? { |i| i.is_a?(BigDecimal) || i.is_a?(Float) || i.is_a?(Rational) }
|
|
36
|
+
amount = convert_to_big_decimal(amount)
|
|
37
|
+
parts.map! { |p| convert_to_big_decimal(p) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
result = []
|
|
41
|
+
remaining_amount = amount
|
|
42
|
+
round_to_whole = decimal_cutoff.is_a?(TrueClass)
|
|
43
|
+
round_to_precision = decimal_cutoff.is_a?(Integer)
|
|
44
|
+
|
|
45
|
+
parts_sum = parts.sum
|
|
46
|
+
|
|
47
|
+
until parts.empty? do
|
|
48
|
+
part = parts.pop
|
|
49
|
+
|
|
50
|
+
current_split = 0
|
|
51
|
+
if parts_sum > 0
|
|
52
|
+
current_split = remaining_amount * part / parts_sum
|
|
53
|
+
current_split =
|
|
54
|
+
if round_to_whole
|
|
55
|
+
current_split.truncate
|
|
56
|
+
elsif round_to_precision
|
|
57
|
+
current_split.round(decimal_cutoff)
|
|
58
|
+
else
|
|
59
|
+
current_split
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
result.unshift current_split
|
|
64
|
+
remaining_amount -= current_split
|
|
65
|
+
parts_sum -= part
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
result
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Converts a given number to BigDecimal.
|
|
72
|
+
# This method supports inputs of BigDecimal, Rational, and other numeric types by ensuring they are all returned
|
|
73
|
+
# as BigDecimal instances for consistent handling.
|
|
74
|
+
#
|
|
75
|
+
# @param number [Numeric, BigDecimal, Rational] The number to convert.
|
|
76
|
+
# @return [BigDecimal] The converted number as a BigDecimal.
|
|
77
|
+
def self.convert_to_big_decimal(number)
|
|
78
|
+
if number.is_a? BigDecimal
|
|
79
|
+
number
|
|
80
|
+
elsif number.is_a? Rational
|
|
81
|
+
BigDecimal(number.to_f.to_s)
|
|
82
|
+
else
|
|
83
|
+
BigDecimal(number.to_s)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|