class Bank
A bank defines exchange rates and formatting rules for resources. It is a centralised location for resource formatting and metadata.
Definitions
def initialize(*imports)
Imports all given currencies.
Implementation
def initialize(*imports)
@rates = []
@exchange = {}
# This implementation may change:
@currencies = {}
@formatters = {}
# Symbols and their associated priorities
@symbols = {}
imports&.each do |resources|
import(resources)
end
end
def import(resources)
Import a list of resource templates, e.g. currencies.
Implementation
def import(resources)
resources.each do |name, config|
name = (config[:name] || name).to_s
@currencies[name] = config
# Create a formatter:
@formatters[name] = config[:formatter].new(**config)
if config[:symbol]
symbols = (@symbols[config[:symbol]] ||= [])
symbols << [config.fetch(:priority, -1), name.to_s]
symbols.sort!.uniq!
end
end
end
def [](name)
Look up a currency by name.
Implementation
def [](name)
@currencies[name]
end
attr :symbols
A map of all recognised symbols ordered by priority.
Signature
-
attribute
Hash(String, Tuple(Integer, Name))
attr :currencies
The supported currents and assocaited formatting details.
Signature
-
attribute
Hash(String, Hash)
def <<(rate)
Add an exchange rate to the bank.
Signature
-
parameter
rate
ExchangeRate
The exchange rate to add.
Implementation
def <<(rate)
@rates << rate
@exchange[rate.input] ||= {}
@exchange[rate.input][rate.output] = rate
end
def exchange(resource, for_name)
Exchange one resource for another using internally specified rates.
Implementation
def exchange(resource, for_name)
unless rate = @exchange.dig(resource.name, for_name)
raise ArgumentError.new("Rate #{rate} unavailable")
end
config = self[for_name]
return resource.exchange(rate.factor, for_name, config[:precision])
end
def parse(string, default_name: nil)
Parse a string according to the loaded currencies.
Implementation
def parse(string, default_name: nil)
parts = string.strip.split(/\s+/, 2)
if parts.size == 2
return parse_named_resource(parts[1], parts[0])
else
# Lookup the named symbol, e.g. '$', and get the highest priority name:
symbol = @symbols.fetch(string.gsub(/[\-\.,0-9]/, ''), []).last
if symbol
name = symbol.last.to_s
elsif default_name
name = default_name
else
raise ArgumentError, "Could not parse #{string}, could not determine resource name!"
end
return parse_named_resource(name, string)
end
end
def round(resource)
Rounds the specified resource to the maximum precision as specified by the formatter. Whe computing things like tax, you often get fractional amounts which are unpayable because they are smaller than the minimum discrete unit of the currency. This method helps to round a currency to a payable amount.
Signature
-
parameter
resource
Resource
The resource to round.
-
returns
Resource
A copy of the resource with the amount rounded as per the formatter.
Implementation
def round(resource)
unless formatter = @formatters[resource.name]
raise ArgumentError.new("No formatter found for #{resource.name}")
end
return Resource.new(formatter.round(resource.amount), resource.name)
end
def format(resource, *arguments, **options)
Format a resource as a string according to the loaded currencies.
Signature
-
parameter
resource
Resource
The resource to format.
Implementation
def format(resource, *arguments, **options)
unless formatter = @formatters[resource.name]
raise ArgumentError.new("No formatter found for #{resource.name}")
end
formatter.format(resource.amount, *arguments, **options)
end
def to_integral(resource)
Convert the resource to an integral representation based on the currency's precision.
Signature
-
parameter
resource
Resource
The resource to convert.
-
returns
Integer
The integer representation.
Implementation
def to_integral(resource)
formatter = @formatters[resource.name]
formatter.to_integral(resource.amount)
end
def from_integral(amount, name)
Convert the resource from an integral representation based on the currency's precision.
Signature
-
parameter
amount
Integer
The integral resource amount.
-
parameter
name
String
The resource name.
-
returns
Resource
The converted resource.
Implementation
def from_integral(amount, name)
formatter = @formatters[name]
Resource.new(formatter.from_integral(amount), name)
end