Class: Amount
- Inherits:
-
Object
- Object
- Amount
- Extended by:
- Forwardable
- Includes:
- Allocation, Arithmetic, Comparison, Conversion, Serialization
- Defined in:
- lib/amount.rb,
lib/amount/parser.rb,
lib/amount/display.rb,
lib/amount/version.rb,
lib/amount/registry.rb,
lib/amount/allocation.rb,
lib/amount/arithmetic.rb,
lib/amount/comparison.rb,
lib/amount/conversion.rb,
lib/amount/active_record.rb,
lib/amount/rspec/support.rb,
lib/amount/serialization.rb,
lib/amount/rspec/matchers.rb,
lib/amount/active_record/type.rb,
lib/amount/active_record/model.rb,
lib/amount/active_record/rspec/matchers.rb,
lib/amount/active_record/amount_validator.rb,
lib/amount/active_record/migration_methods.rb,
lib/amount/registry/generated_constructors.rb,
lib/amount/active_record/attribute_definition.rb
Overview
Represents a precise quantity of a registered fungible type.
‘Amount` stores its value as an arbitrary-precision atomic `Integer` in the smallest unit configured for the registered symbol. UI values are parsed from strings or decimals, while integer inputs are treated as atomic counts unless `from:` overrides inference.
Behavior is composed from a set of focused mixins:
-
Arithmetic — ‘+`, `-`, `*`, `/`, `abs`, `-@`
-
Comparison — ‘<=>`, `==`, `eql?`, `hash`, `same_type?`, sign predicates
-
Conversion — ‘to(:SYMBOL, rate:)`
-
Allocation — ‘split(n)`, `allocate(weights)`
-
Serialization — ‘to_h`
Defined Under Namespace
Modules: ActiveRecord, Allocation, Arithmetic, Comparison, Conversion, RSpec, Serialization Classes: Display, Error, InvalidInput, Parser, Registry, TypeMismatch, UnregisteredType
Constant Summary collapse
- VERSION =
"0.0.5"
Instance Attribute Summary collapse
-
#atomic ⇒ Object
readonly
Returns the value of attribute atomic.
-
#display ⇒ Object
readonly
Returns the value of attribute display.
-
#symbol ⇒ Object
readonly
Returns the value of attribute symbol.
Class Method Summary collapse
-
.coerce_decimal(value) ⇒ BigDecimal
Coerces a numeric input to BigDecimal in a way that preserves Rational values.
-
.new(value, symbol, from: nil) ⇒ Amount
When called as ‘Amount.new` for a symbol whose registry entry binds a custom class, dispatch the construction to that class instead of raising.
-
.parse(str) ⇒ Amount
Parses the compact client-facing string representation.
- .register(symbol, **opts) ⇒ void
- .register_default_rate(from, to, rate) ⇒ void
- .registry ⇒ Amount::Registry
-
.with_registry(registry) { ... } ⇒ Object
Temporarily swaps the global registry.
Instance Method Summary collapse
- #decimal ⇒ BigDecimal
- #decimals ⇒ Integer
-
#initialize(value, symbol, from: nil) ⇒ Amount
constructor
Creates an amount for a registered symbol.
- #inspect ⇒ String
- #registry_entry ⇒ Amount::Registry::Entry
Methods included from Serialization
Methods included from Conversion
Methods included from Comparison
#<=>, #==, #eql?, #hash, #negative?, #positive?, #same_type?, #zero?
Methods included from Allocation
Methods included from Arithmetic
Constructor Details
#initialize(value, symbol, from: nil) ⇒ Amount
Creates an amount for a registered symbol.
Input inference rules:
-
‘Integer` => atomic units
-
‘String` => UI decimal value
-
‘Float`, `BigDecimal`, `Rational` => UI decimal value
-
‘from:` overrides inference explicitly
188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/amount.rb', line 188 def initialize(value, symbol, from: nil) @symbol = symbol.to_sym @entry = self.class.registry.lookup(@symbol) expected = @entry.amount_class if expected && expected != Amount && self.class != Amount && !instance_of?(expected) raise InvalidInput, "use #{expected}.new for #{@symbol}" end @atomic = infer_value(from, value) @display = Display.new(self) end |
Instance Attribute Details
#atomic ⇒ Object (readonly)
Returns the value of attribute atomic.
166 167 168 |
# File 'lib/amount.rb', line 166 def atomic @atomic end |
#display ⇒ Object (readonly)
Returns the value of attribute display.
166 167 168 |
# File 'lib/amount.rb', line 166 def display @display end |
#symbol ⇒ Object (readonly)
Returns the value of attribute symbol.
166 167 168 |
# File 'lib/amount.rb', line 166 def symbol @symbol end |
Class Method Details
.coerce_decimal(value) ⇒ BigDecimal
Coerces a numeric input to BigDecimal in a way that preserves Rational values. ‘BigDecimal(value.to_s)` raises `ArgumentError` for Rational because `Rational#to_s` produces strings like `“3/2”`. This helper is the single place every call site should use to convert a scalar / rate / display-unit scale into a BigDecimal.
129 130 131 132 133 134 135 |
# File 'lib/amount.rb', line 129 def coerce_decimal(value) case value when BigDecimal then value when Rational then BigDecimal(value, Float::DIG + 4) else BigDecimal(value.to_s) end end |
.new(value, symbol, from: nil) ⇒ Amount
When called as ‘Amount.new` for a symbol whose registry entry binds a custom class, dispatch the construction to that class instead of raising. Direct calls to a subclass (`GoldAmount.new(…)`) still go through the default `Class.new` path. Calls that target the wrong subclass continue to raise from `#initialize`.
113 114 115 116 117 118 119 |
# File 'lib/amount.rb', line 113 def new(value, symbol, from: nil) if equal?(::Amount) entry_class = registry.lookup(symbol.to_sym).amount_class return entry_class.new(value, symbol, from:) if entry_class && entry_class != ::Amount end super end |
.parse(str) ⇒ Amount
Parses the compact client-facing string representation.
Accepts either the default form ‘SYMBOL|amount` or the explicit versioned form `v1:SYMBOL|amount`.
99 100 101 |
# File 'lib/amount.rb', line 99 def parse(str) Parser.new(str).parse end |
.register(symbol, **opts) ⇒ void
This method returns an undefined value.
72 73 74 |
# File 'lib/amount.rb', line 72 def register(symbol, **opts) registry.register(symbol, **opts) end |
.register_default_rate(from, to, rate) ⇒ void
This method returns an undefined value.
82 83 84 |
# File 'lib/amount.rb', line 82 def register_default_rate(from, to, rate) registry.register_default_rate(from, to, rate) end |
.registry ⇒ Amount::Registry
58 59 60 61 |
# File 'lib/amount.rb', line 58 def registry Amount.instance_variable_get(:@registry) || replace_registry(Registry.new) end |
.with_registry(registry) { ... } ⇒ Object
Temporarily swaps the global registry. Intended for tests.
147 148 149 150 151 152 153 |
# File 'lib/amount.rb', line 147 def with_registry(registry) original = Amount.instance_variable_get(:@registry) replace_registry(registry) yield ensure replace_registry(original) end |
Instance Method Details
#decimal ⇒ BigDecimal
221 222 223 |
# File 'lib/amount.rb', line 221 def decimal BigDecimal(@atomic) / (BigDecimal(10)**decimals) end |
#decimals ⇒ Integer
213 214 215 |
# File 'lib/amount.rb', line 213 def decimals @entry.decimals end |
#inspect ⇒ String
231 232 233 |
# File 'lib/amount.rb', line 231 def inspect "#<#{self.class} #{symbol} #{ui}>" end |
#registry_entry ⇒ Amount::Registry::Entry
205 206 207 |
# File 'lib/amount.rb', line 205 def registry_entry @entry end |