Class: Money
- Inherits:
-
Object
show all
- Extended by:
- Forwardable
- Includes:
- Comparable
- Defined in:
- lib/money/money.rb,
lib/money/config.rb,
lib/money/errors.rb,
lib/money/helpers.rb,
lib/money/railtie.rb,
lib/money/version.rb,
lib/money/currency.rb,
lib/money/splitter.rb,
lib/money/allocator.rb,
lib/money/parser/fuzzy.rb,
lib/money/null_currency.rb,
lib/money/parser/simple.rb,
lib/money/currency/loader.rb,
lib/money/parser/accounting.rb,
lib/money/parser/locale_aware.rb,
lib/money/rails/job_argument_serializer.rb
Defined Under Namespace
Modules: Helpers, Parser, Rails
Classes: Allocator, Config, Currency, Error, IncompatibleCurrencyError, NullCurrency, Railtie, ReverseOperationProxy, Splitter
Constant Summary
collapse
- NULL_CURRENCY =
NullCurrency.new.freeze
- VERSION =
"3.0.2"
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(value, currency) ⇒ Money
Returns a new instance of Money.
150
151
152
153
154
155
|
# File 'lib/money/money.rb', line 150
def initialize(value, currency)
raise ArgumentError if value.nan?
@currency = Helpers.value_to_currency(currency)
@value = BigDecimal(value.round(@currency.minor_units))
freeze
end
|
Instance Attribute Details
#currency ⇒ Object
Returns the value of attribute currency.
12
13
14
|
# File 'lib/money/money.rb', line 12
def currency
@currency
end
|
#value ⇒ Object
Returns the value of attribute value.
12
13
14
|
# File 'lib/money/money.rb', line 12
def value
@value
end
|
Class Method Details
.config ⇒ Object
44
45
46
|
# File 'lib/money/money.rb', line 44
def config
Thread.current[:shopify_money__config] ||= @config.dup
end
|
.config=(config) ⇒ Object
48
49
50
|
# File 'lib/money/money.rb', line 48
def config=(config)
Thread.current[:shopify_money__config] = config
end
|
52
53
54
55
|
# File 'lib/money/money.rb', line 52
def configure
@config ||= Config.new
yield(@config) if block_given?
end
|
.current_currency ⇒ Object
104
105
106
|
# File 'lib/money/money.rb', line 104
def current_currency
Thread.current[:money_currency]
end
|
.current_currency=(currency) ⇒ Object
108
109
110
|
# File 'lib/money/money.rb', line 108
def current_currency=(currency)
Thread.current[:money_currency] = currency
end
|
.from_hash(hash) ⇒ Object
92
93
94
95
|
# File 'lib/money/money.rb', line 92
def from_hash(hash)
hash = hash.transform_keys(&:to_sym)
Money.new(hash.fetch(:value), hash.fetch(:currency))
end
|
.from_json(string) ⇒ Object
87
88
89
90
|
# File 'lib/money/money.rb', line 87
def from_json(string)
hash = JSON.parse(string, symbolize_names: true)
Money.new(hash.fetch(:value), hash.fetch(:currency))
end
|
.from_subunits(subunits, currency_iso, format: :iso4217) ⇒ Object
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
# File 'lib/money/money.rb', line 72
def from_subunits(subunits, currency_iso, format: :iso4217)
currency = Helpers.value_to_currency(currency_iso)
subunit_to_unit_value = if format == :iso4217
currency.subunit_to_unit
elsif format == :stripe
Helpers::STRIPE_SUBUNIT_OVERRIDE.fetch(currency.iso_code, currency.subunit_to_unit)
else
raise ArgumentError, "unknown format #{format}"
end
value = Helpers.value_to_decimal(subunits) / subunit_to_unit_value
new(value, currency)
end
|
.new(value = 0, currency = nil) ⇒ Object
Also known as:
from_amount
57
58
59
60
61
62
63
64
65
66
67
68
69
|
# File 'lib/money/money.rb', line 57
def new(value = 0, currency = nil)
return new_from_money(value, currency) if value.is_a?(Money)
value = Helpers.value_to_decimal(value)
currency = Helpers.value_to_currency(currency)
if value.zero?
@@zero_money ||= {}
@@zero_money[currency.iso_code] ||= super(Helpers::DECIMAL_ZERO, currency)
else
super(value, currency)
end
end
|
.rational(money1, money2) ⇒ Object
97
98
99
100
101
102
|
# File 'lib/money/money.rb', line 97
def rational(money1, money2)
money1.send(:arithmetic, money2) do
factor = money1.currency.subunit_to_unit * money2.currency.subunit_to_unit
Rational((money1.value * factor).to_i, (money2.value * factor).to_i)
end
end
|
.with_currency(new_currency) ⇒ Object
Set Money.default_currency inside the supplied block, resets it to the previous value when done to prevent leaking state. Similar to I18n.with_locale and ActiveSupport’s Time.use_zone. This won’t affect instances being created with explicitly set currency.
Instance Method Details
#*(other) ⇒ Object
207
208
209
210
211
212
|
# File 'lib/money/money.rb', line 207
def *(other)
raise ArgumentError, "Money objects can only be multiplied by a Numeric" unless other.is_a?(Numeric)
return self if other == 1
Money.new(value.to_r * other, currency)
end
|
#+(other) ⇒ Object
193
194
195
196
197
198
|
# File 'lib/money/money.rb', line 193
def +(other)
arithmetic(other) do |money|
return self if money.value.zero? && !no_currency?
Money.new(value + money.value, calculated_currency(money.currency))
end
end
|
#-(other) ⇒ Object
200
201
202
203
204
205
|
# File 'lib/money/money.rb', line 200
def -(other)
arithmetic(other) do |money|
return self if money.value.zero? && !no_currency?
Money.new(value - money.value, calculated_currency(money.currency))
end
end
|
#-@ ⇒ Object
182
183
184
|
# File 'lib/money/money.rb', line 182
def -@
Money.new(-value, currency)
end
|
#/(other) ⇒ Object
214
215
216
|
# File 'lib/money/money.rb', line 214
def /(other)
raise "[Money] Dividing money objects can lose pennies. Use #split instead"
end
|
#<=>(other) ⇒ Object
186
187
188
189
190
191
|
# File 'lib/money/money.rb', line 186
def <=>(other)
return unless other.respond_to?(:to_money)
arithmetic(other) do |money|
value <=> money.value
end
end
|
#==(other) ⇒ Object
222
223
224
|
# File 'lib/money/money.rb', line 222
def ==(other)
eql?(other)
end
|
#abs ⇒ Object
305
306
307
308
309
|
# File 'lib/money/money.rb', line 305
def abs
abs = value.abs
return self if value == abs
Money.new(abs, currency)
end
|
#allocate(splits, strategy = :roundrobin) ⇒ Object
331
332
333
|
# File 'lib/money/money.rb', line 331
def allocate(splits, strategy = :roundrobin)
Money::Allocator.new(self).allocate(splits, strategy)
end
|
#allocate_max_amounts(maximums) ⇒ Object
336
337
338
|
# File 'lib/money/money.rb', line 336
def allocate_max_amounts(maximums)
Money::Allocator.new(self).allocate_max_amounts(maximums)
end
|
#as_json(options = nil) ⇒ Object
Also known as:
to_h
296
297
298
299
300
301
302
|
# File 'lib/money/money.rb', line 296
def as_json(options = nil)
if (options.is_a?(Hash) && options[:legacy_format]) || Money.config.legacy_json_format
to_s
else
{ value: to_s(:amount), currency: currency.to_s }
end
end
|
#calculate_splits(num) ⇒ Hash<Money, Integer>
Calculate the splits evenly without losing pennies. Returns the number of high and low splits and the value of the high and low splits. Where high represents the Money value with the extra penny and low a Money without the extra penny.
363
364
365
|
# File 'lib/money/money.rb', line 363
def calculate_splits(num)
Splitter.new(self, num).split.dup
end
|
#clamp(min, max) ⇒ Object
Clamps the value to be within the specified minimum and maximum. Returns self if the value is within bounds, otherwise a new Money object with the closest min or max value.
375
376
377
378
379
380
381
382
383
384
385
386
|
# File 'lib/money/money.rb', line 375
def clamp(min, max)
raise ArgumentError, 'min cannot be greater than max' if min > max
clamped_value = min if value < min
clamped_value = max if value > max
if clamped_value.nil?
self
else
Money.new(clamped_value, currency)
end
end
|
#coerce(other) ⇒ Object
233
234
235
236
|
# File 'lib/money/money.rb', line 233
def coerce(other)
raise TypeError, "Money can't be coerced into #{other.class}" unless other.is_a?(Numeric)
[ReverseOperationProxy.new(other), self]
end
|
#convert_currency(exchange_rate, new_currency) ⇒ Object
238
239
240
|
# File 'lib/money/money.rb', line 238
def convert_currency(exchange_rate, new_currency)
Money.new(value * exchange_rate, new_currency)
end
|
#encode_with(coder) ⇒ Object
161
162
163
164
|
# File 'lib/money/money.rb', line 161
def encode_with(coder)
coder['value'] = @value.to_s('F')
coder['currency'] = @currency.iso_code
end
|
#eql?(other) ⇒ Boolean
TODO: Remove once cross-currency mathematical operations are no longer allowed
227
228
229
230
231
|
# File 'lib/money/money.rb', line 227
def eql?(other)
return false unless other.is_a?(Money)
return false unless currency.compatible?(other.currency)
value == other.value
end
|
#floor ⇒ Object
311
312
313
314
315
|
# File 'lib/money/money.rb', line 311
def floor
floor = value.floor
return self if floor == value
Money.new(floor, currency)
end
|
#fraction(rate) ⇒ Object
323
324
325
326
327
328
|
# File 'lib/money/money.rb', line 323
def fraction(rate)
raise ArgumentError, "rate should be positive" if rate < 0
result = value / (1 + rate)
Money.new(result, currency)
end
|
#init_with(coder) ⇒ Object
157
158
159
|
# File 'lib/money/money.rb', line 157
def init_with(coder)
initialize(Helpers.value_to_decimal(coder['value']), coder['currency'])
end
|
#inspect ⇒ Object
218
219
220
|
# File 'lib/money/money.rb', line 218
def inspect
"#<#{self.class} value:#{self} currency:#{currency}>"
end
|
#no_currency? ⇒ Boolean
178
179
180
|
# File 'lib/money/money.rb', line 178
def no_currency?
currency.is_a?(NullCurrency)
end
|
#round(ndigits = 0) ⇒ Object
317
318
319
320
321
|
# File 'lib/money/money.rb', line 317
def round(ndigits = 0)
round = value.round(ndigits)
return self if round == value
Money.new(round, currency)
end
|
Split money amongst parties evenly without losing pennies.
348
349
350
|
# File 'lib/money/money.rb', line 348
def split(num)
Splitter.new(self, num)
end
|
#subunits(format: :iso4217) ⇒ Object
166
167
168
169
170
171
172
173
174
175
176
|
# File 'lib/money/money.rb', line 166
def subunits(format: :iso4217)
subunit_to_unit_value = if format == :iso4217
@currency.subunit_to_unit
elsif format == :stripe
Helpers::STRIPE_SUBUNIT_OVERRIDE.fetch(@currency.iso_code, @currency.subunit_to_unit)
else
raise ArgumentError, "unknown format #{format}"
end
(@value * subunit_to_unit_value).to_i
end
|
#to_d ⇒ Object
259
260
261
|
# File 'lib/money/money.rb', line 259
def to_d
value
end
|
#to_fs(style = nil) ⇒ Object
Also known as:
to_s, to_formatted_s
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
# File 'lib/money/money.rb', line 263
def to_fs(style = nil)
units = case style
when :legacy_dollars
2
when :amount, nil
currency.minor_units
else
raise ArgumentError, "Unexpected format: #{style}"
end
rounded_value = value.round(units)
if units == 0
format("%d", rounded_value)
else
formatted = rounded_value.to_s("F")
decimal_digits = formatted.size - formatted.index(".") - 1
(units - decimal_digits).times do
formatted << '0'
end
formatted
end
end
|
#to_json(options = nil) ⇒ Object
288
289
290
291
292
293
294
|
# File 'lib/money/money.rb', line 288
def to_json(options = nil)
if (options.is_a?(Hash) && options[:legacy_format]) || Money.config.legacy_json_format
to_s
else
as_json(options).to_json
end
end
|
#to_money(new_currency = nil) ⇒ Object
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
# File 'lib/money/money.rb', line 242
def to_money(new_currency = nil)
if new_currency.nil?
return self
end
if no_currency?
return Money.new(value, new_currency)
end
ensure_compatible_currency(
Helpers.value_to_currency(new_currency),
"to_money is attempting to change currency of an existing money object from #{currency} to #{new_currency}",
)
self
end
|