minting 1.7.0 → 1.7.3

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -112
  3. data/Rakefile +13 -1
  4. data/bin/bench_check +46 -0
  5. data/doc/Mint/Currency.html +446 -46
  6. data/doc/Mint/CurrencyRegistry.html +7 -7
  7. data/doc/Mint/Money.html +203 -177
  8. data/doc/Mint/RangeStepPatch.html +277 -0
  9. data/doc/Mint/Registry.html +842 -0
  10. data/doc/Mint/UnknownCurrency.html +2 -2
  11. data/doc/Mint.html +385 -66
  12. data/doc/Minting.html +3 -3
  13. data/doc/_index.html +24 -9
  14. data/doc/agents/api_review-2026-06-15.md +342 -0
  15. data/doc/class_list.html +1 -1
  16. data/doc/file.README.html +97 -89
  17. data/doc/index.html +97 -89
  18. data/doc/method_list.html +97 -25
  19. data/doc/top-level-namespace.html +13 -5
  20. data/lib/minting/currency/currency.rb +75 -0
  21. data/lib/minting/mint/aliases.rb +3 -0
  22. data/lib/minting/mint/dsl/{refinements.rb → numeric.rb} +6 -5
  23. data/lib/minting/mint/dsl/range.rb +31 -18
  24. data/lib/minting/mint/dsl/string.rb +12 -0
  25. data/lib/minting/mint/dsl/top_level.rb +3 -0
  26. data/lib/minting/mint/locale_backend.rb +29 -0
  27. data/lib/minting/mint/mint.rb +28 -12
  28. data/lib/minting/mint/parser/parser.rb +62 -0
  29. data/lib/minting/mint/parser/separators.rb +39 -0
  30. data/lib/minting/mint/registry/registration.rb +33 -0
  31. data/lib/minting/mint/registry/registry.rb +38 -0
  32. data/lib/minting/mint/registry/symbols.rb +49 -0
  33. data/lib/minting/mint/registry/zeros.rb +18 -0
  34. data/lib/minting/mint.rb +13 -16
  35. data/lib/minting/money/allocation/allocation.rb +25 -0
  36. data/lib/minting/money/{allocation.rb → allocation/split.rb} +1 -19
  37. data/lib/minting/money/arithmetics/methods.rb +27 -0
  38. data/lib/minting/money/{arithmetics.rb → arithmetics/operators.rb} +0 -21
  39. data/lib/minting/money/clamp.rb +66 -0
  40. data/lib/minting/money/coercion.rb +10 -0
  41. data/lib/minting/money/comparable.rb +6 -0
  42. data/lib/minting/money/constructors.rb +14 -9
  43. data/lib/minting/money/format/formatting.rb +60 -0
  44. data/lib/minting/money/{formatting.rb → format/to_s.rb} +13 -36
  45. data/lib/minting/money/money.rb +12 -58
  46. data/lib/minting/version.rb +1 -1
  47. metadata +29 -19
  48. data/lib/minting/mint/currency/currency.rb +0 -36
  49. data/lib/minting/mint/currency/currency_registry.rb +0 -67
  50. data/lib/minting/mint/currency/world_currencies.rb +0 -16
  51. data/lib/minting/mint/parser.rb +0 -85
  52. /data/doc/agents/{AGENTS.md → expired/AGENTS.md} +0 -0
  53. /data/doc/agents/{copilot-instructions.md → expired/copilot-instructions.md} +0 -0
  54. /data/doc/agents/{gemini_gem_evaluation.md → expired/gemini_gem_evaluation.md} +0 -0
  55. /data/doc/agents/{recommendations.md → expired/recommendations.md} +0 -0
  56. /data/doc/agents/{rubocop-issues.md → expired/rubocop-issues.md} +0 -0
@@ -3,25 +3,6 @@
3
3
  module Mint
4
4
  # Allocation and splitting
5
5
  class Money
6
- # Proportionally allocates the monetary amount among a list of ratios.
7
- # Disperses any subunit rounding amounts across the initial slots
8
- # @param proportions [Array<Numeric>] a list of numeric proportions/ratios to allocate by
9
- # @return [Array<Money>] the list of newly allocated Money objects
10
- # @raise [ArgumentError] if the proportions list is empty or sums to zero
11
- #
12
- # @example Proportional allocation
13
- # money = Mint.money(10.00, 'USD')
14
- # money.allocate([1, 2, 3]) #=> [[USD 1.67], [USD 3.33], [USD 5.00]]
15
- def allocate(proportions)
16
- whole = proportions.sum.to_r
17
- raise ArgumentError, 'Need at least 1 proportion element' if proportions.empty?
18
- raise ArgumentError, 'Proportions total must not be zero' if whole.zero?
19
-
20
- subunit = currency.subunit
21
- amounts = proportions.map { |rate| Rational(amount * rate, whole).round(subunit) }
22
- allocate_left_over(amounts: amounts, left_over: amount - amounts.sum)
23
- end
24
-
25
6
  # Splits the monetary amount into a given quantity of equal parts.
26
7
  # Disperses any fractional subunit rounding differences across the initial slots
27
8
  # so that the sum is preserved.
@@ -46,6 +27,7 @@ module Mint
46
27
  # Distributes any leftover amount across the allocation slots by adjusting
47
28
  # individual amounts by the currency's minimum unit, and converting to Money.
48
29
  # Caution: amounts array is mutated by this method
30
+ # @private
49
31
  def allocate_left_over(amounts:, left_over:)
50
32
  if left_over.nonzero?
51
33
  minimum = currency.minimum_amount
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mint
4
+ # Money Arithmetics
5
+ class Money
6
+ # Returns the absolute value of the monetary amount as a new {Money} instance.
7
+ #
8
+ # @return [Money] the absolute value
9
+ def abs = mint(amount.abs)
10
+
11
+ # Returns true if the monetary amount is less than zero.
12
+ #
13
+ # @return [Boolean] true if negative, false otherwise
14
+ def negative? = amount.negative?
15
+
16
+ # Returns true if the monetary amount is greater than zero.
17
+ #
18
+ # @return [Boolean] true if positive, false otherwise
19
+ def positive? = amount.positive?
20
+
21
+ # Returns the successor of the Money instance by adding the minimum possible subunit amount.
22
+ # Enables standard ranges and stepping (e.g. `1.dollar..10.dollars`).
23
+ #
24
+ # @return [Money] successor Money instance
25
+ def succ = mint(amount + currency.minimum_amount)
26
+ end
27
+ end
@@ -3,27 +3,6 @@
3
3
  module Mint
4
4
  # Money Arithmetics
5
5
  class Money
6
- # Returns the absolute value of the monetary amount as a new {Money} instance.
7
- #
8
- # @return [Money] the absolute value
9
- def abs = mint(amount.abs)
10
-
11
- # Returns true if the monetary amount is less than zero.
12
- #
13
- # @return [Boolean] true if negative, false otherwise
14
- def negative? = amount.negative?
15
-
16
- # Returns true if the monetary amount is greater than zero.
17
- #
18
- # @return [Boolean] true if positive, false otherwise
19
- def positive? = amount.positive?
20
-
21
- # Returns the successor of the Money instance by adding the minimum possible subunit amount.
22
- # Enables standard ranges and stepping (e.g. `1.dollar..10.dollars`).
23
- #
24
- # @return [Money] successor Money instance
25
- def succ = mint(amount + currency.minimum_amount)
26
-
27
6
  # Performs addition with another {Money} instance or standard zero Numeric.
28
7
  #
29
8
  # @param addend [Money, Numeric] the value to add
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mint
4
+ # Money clamp
5
+ class Money
6
+ # Constrains +self+ to the inclusive range [+min+, +max+].
7
+ #
8
+ # Bounds may be:
9
+ # - nil meaning no boundary
10
+ # - same-currency {Money} or Range
11
+ # - Numeric amount, or Range
12
+ #
13
+ # Numeric is interpreted as an amount in +self+'s currency, so the common
14
+ # pricing idiom +price.clamp(0, 100)+ reads as "0 to 100 in the same
15
+ # currency as +price+".
16
+ #
17
+ # When +self+ is already in range the receiver is returned (no new object
18
+ # allocated). When out of range, the nearest bound is returned as a new
19
+ # frozen {Money} in +self+'s currency.
20
+ #
21
+ # @param min_or_range [Money, Numeric, Range, nil] lower bound (inclusive), or range
22
+ # @param max [Money, Numeric, nil] upper bound (inclusive)
23
+ # @return [Money] +self+ if in range, otherwise the nearer bound
24
+ # @raise [ArgumentError] if +min+ or +max+ is not a Money, Numeric or nil; if
25
+ # a Money operand has a different currency; if +min+ > +max+;
26
+ # if min is a Range, and max is not nil
27
+ #
28
+ # @example In range
29
+ # Mint.money(5, 'USD').clamp(0, 10) #=> [USD 5.00] (returns self)
30
+ #
31
+ # @example Out of range, with Numeric bounds
32
+ # Mint.money(50, 'USD').clamp(0, 10) #=> [USD 10.00]
33
+ #
34
+ # @example Out of range, with Money bounds
35
+ # loss = Mint.money(-5, 'USD')
36
+ # floor = Mint.money(0, 'USD')
37
+ # ceil = Mint.money(10, 'USD')
38
+ # loss.clamp(floor, ceil) #=> [USD 0.00]
39
+ #
40
+ # @example Subunit-0 currency (JPY)
41
+ # Mint.money(500, 'JPY').clamp(0, 100) #=> [JPY 100]
42
+ def clamp(min_or_range, max = nil)
43
+ if min_or_range.is_a?(Range)
44
+ raise(ArgumentError, "Either amount range alone or two amounts accepted: #{max}") if max
45
+
46
+ min, max = min_or_range.minmax
47
+ else
48
+ min = min_or_range
49
+ end
50
+ mint(amount.clamp(normalize_boundary(min), normalize_boundary(max)))
51
+ end
52
+
53
+ private
54
+
55
+ # Converts a clamp boundary to a numeric amount.
56
+ # @private
57
+ def normalize_boundary(boundary)
58
+ case boundary
59
+ in NilClass | Numeric then boundary
60
+ in Money if same_currency?(boundary) then boundary.amount
61
+ in Money then raise ArgumentError, "oundary currency must be: #{currency_code}"
62
+ else raise ArgumentError, "Boundary must be Numeric or Money #{boundary}"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -23,6 +23,8 @@ module Mint
23
23
  def initialize(value) = @value = value
24
24
 
25
25
  # @private
26
+ # Adds a CoercedNumber to a Money object.
27
+ # Only zero is a valid additive identity (returns the Money unchanged).
26
28
  def +(other)
27
29
  raise_coercion_error(:+, other) unless @value.zero?
28
30
 
@@ -30,6 +32,8 @@ module Mint
30
32
  end
31
33
 
32
34
  # @private
35
+ # Subtracts a Money object from a CoercedNumber.
36
+ # Only zero is valid (returns the negated Money).
33
37
  def -(other)
34
38
  raise_coercion_error(:-, other) unless @value.zero?
35
39
 
@@ -37,15 +41,20 @@ module Mint
37
41
  end
38
42
 
39
43
  # @private
44
+ # Multiplies a Money object by the wrapped numeric value.
45
+ # This is the standard coercion path for `Numeric * Money`.
40
46
  def *(other)
41
47
  other.mint(@value * other.amount)
42
48
  end
43
49
 
44
50
  # @private
51
+ # Divides a CoercedNumber by a Money object.
52
+ # Not a meaningful operation (what currency is the result?).
45
53
  def /(other)
46
54
  raise_coercion_error(:/, other)
47
55
  end
48
56
 
57
+ # @private
49
58
  # Only zero is dimensionless and comparable to Money.
50
59
  # e.g. 0 < price is meaningful; 0.5 < price is not (what currency is 0.5?).
51
60
  def <=>(other)
@@ -56,6 +65,7 @@ module Mint
56
65
 
57
66
  private
58
67
 
68
+ # Raises a TypeError with a descriptive message for unsupported coercions.
59
69
  def raise_coercion_error(operation, operand)
60
70
  raise TypeError, "#{@value} #{operation} #{operand} : incompatible operands"
61
71
  end
@@ -14,6 +14,10 @@ module Mint
14
14
  end
15
15
  end
16
16
 
17
+ # Strict equality — both amount and currency must match exactly.
18
+ # Unlike ==, does not treat zero as equivalent across currencies.
19
+ #
20
+ # @return [Boolean]
17
21
  def eql?(other)
18
22
  other.is_a?(Mint::Money) &&
19
23
  amount == other.amount &&
@@ -37,8 +41,10 @@ module Mint
37
41
  end
38
42
  end
39
43
 
44
+ # @return [self, nil] self if amount is non-zero, nil otherwise
40
45
  def nonzero? = amount.nonzero?
41
46
 
47
+ # @return [Boolean] true if amount is zero
42
48
  def zero? = amount.zero?
43
49
  end
44
50
  end
@@ -10,10 +10,10 @@ module Mint
10
10
  def self.create(amount, currency)
11
11
  raise ArgumentError, 'amount must be Numeric' unless amount.is_a?(Numeric)
12
12
 
13
- checked_currency = Mint.currency(currency)
14
- raise ArgumentError, "Currency not found (#{currency})" unless checked_currency
13
+ currency = Currency.resolve!(currency)
14
+ amount = currency.normalize_amount(amount)
15
15
 
16
- new(checked_currency.normalize_amount(amount), checked_currency)
16
+ amount.zero? ? Mint.zero(currency) : new(amount, currency)
17
17
  end
18
18
 
19
19
  # Builds a Money from a fractional (smallest-unit) Integer amount.
@@ -37,11 +37,9 @@ module Mint
37
37
  def self.from_fractional(fractional, currency)
38
38
  raise ArgumentError, 'fractional must be an Integer' unless fractional.is_a?(Integer)
39
39
 
40
- checked_currency = Mint.currency(currency)
41
- raise ArgumentError, "Currency not found (#{currency})" unless checked_currency
42
-
43
- amount = Rational(fractional, checked_currency.fractional_multiplier)
44
- new(amount, checked_currency)
40
+ currency = Currency.resolve!(currency)
41
+ amount = Rational(fractional, currency.fractional_multiplier)
42
+ amount.zero? ? Mint.zero(currency) : new(amount, currency)
45
43
  end
46
44
 
47
45
  # Returns a new Money object with the specified amount, or self if unchanged.
@@ -56,7 +54,14 @@ module Mint
56
54
  # price.mint(10.00) #=> [USD 10.00] (returns self)
57
55
  def mint(new_amount)
58
56
  new_amount = currency.normalize_amount(new_amount)
59
- new_amount == amount ? self : Money.new(new_amount, currency)
57
+
58
+ if new_amount == amount
59
+ self
60
+ elsif new_amount.zero?
61
+ Mint.zero(currency)
62
+ else
63
+ Money.new(new_amount, currency)
64
+ end
60
65
  end
61
66
 
62
67
  private
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mint
4
+ # Formatting functionality for Money objects
5
+ class Money
6
+ private
7
+
8
+ # Resolves format/decimal/thousand from locale_backend when not explicitly given.
9
+ # @private
10
+ def resolve_locale_for(format, decimal, thousand)
11
+ locale = locale_backend
12
+ [format || locale[:format] || '%<symbol>s%<amount>f',
13
+ decimal || locale[:decimal] || '.',
14
+ thousand || locale[:thousand] || ',']
15
+ end
16
+
17
+ def locale_backend
18
+ bk = Mint.locale_backend
19
+ return {} unless bk.respond_to?(:call)
20
+
21
+ bk.call
22
+ end
23
+
24
+ # Selects the appropriate format template and value based on the amount's sign.
25
+ # @private
26
+ def select_format(format)
27
+ negative_format = format[:negative]
28
+ zero_format = format[:zero]
29
+
30
+ if amount.negative? && negative_format
31
+ [negative_format, -amount]
32
+ elsif amount.zero? && zero_format
33
+ [zero_format, amount]
34
+ else
35
+ [format[:positive], amount]
36
+ end
37
+ end
38
+
39
+ # Validates that format hash contains only known keys.
40
+ # @private
41
+ def validate_format_hash(format)
42
+ unknown = format.keys - %i[positive negative zero]
43
+
44
+ raise ArgumentError, "Unknown format parameter(s): #{unknown.inspect}. " unless unknown.empty?
45
+ end
46
+
47
+ # Applies a format template to produce a formatted string representation.
48
+ # @private
49
+ def format_amount(format)
50
+ format, value = select_format(format)
51
+ format ||= '%<symbol>s%<amount>f'
52
+ # Automatically adjust decimal places based on currency subunit if missing
53
+ format = format.gsub(/%<amount>(\s*\+?\d*)f/, "%<amount>\\1.#{currency.subunit}f")
54
+
55
+ refs = format.scan(/%<(\w+)>/).flatten.map(&:to_sym)
56
+ all_args = { amount: value, currency: currency_code, symbol: currency.symbol }
57
+ Kernel.format(format, **all_args.slice(*refs))
58
+ end
59
+ end
60
+ end
@@ -5,7 +5,7 @@ module Mint
5
5
  class Money
6
6
  # Formats money as a string with customizable format, thousand delimiter, and decimal
7
7
  #
8
- # @param format [String, Hash] Either a Format string with placeholders
8
+ # @param format [String, Hash, nil] Either a Format string with placeholders
9
9
  # (%<symbol>s, %<amount>f, %<currency>s), or a Hash with per-sign keys
10
10
  # (:positive, :negative, :zero) each holding a format string. A Hash
11
11
  # is convenient for sign-aware formats such as accounting parentheses:
@@ -15,8 +15,12 @@ module Mint
15
15
  # Missing keys fall back to the module default, so a Hash with only
16
16
  # :negative will still format positives sensibly. The valid keys are
17
17
  # :positive, :negative, :zero; anything else raises ArgumentError.
18
- # @param thousand [String, false] Thousands delimiter (e.g., ',' for 1,000)
19
- # @param decimal [String] Decimal separator (e.g., '.' or ',')
18
+ # When +nil+, falls back to +Mint.locale_backend+ if set, otherwise
19
+ # +"%<symbol>s%<amount>f"+.
20
+ # @param thousand [String, false, nil] Thousands delimiter (e.g., ',' for 1,000).
21
+ # When +nil+, falls back to +Mint.locale_backend+ if set, otherwise +","+.
22
+ # @param decimal [String, nil] Decimal separator (e.g., '.' or ',').
23
+ # When +nil+, falls back to +Mint.locale_backend+ if set, otherwise +"."+.
20
24
  # @return [String] Formatted money string
21
25
  #
22
26
  # @raise [ArgumentError] if +format+ is not a String or Hash, the Hash
@@ -43,7 +47,12 @@ module Mint
43
47
  # money.to_s(format: '%<amount>10.2f') #=> " 1234.56"
44
48
  # money.to_s(format: '%<symbol>s%<amount>010.2f') #=> "$0001234.56"
45
49
  #
46
- def to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil)
50
+ # @example Locale-aware formatting (with Mint.locale_backend set)
51
+ # money.to_s # decimal and thousand come from locale_backend
52
+ #
53
+ def to_s(format: nil, decimal: nil, thousand: nil, width: nil)
54
+ format, decimal, thousand = resolve_locale_for(format, decimal, thousand)
55
+
47
56
  case format
48
57
  when {}, '' then raise ArgumentError, 'format must not be empty'
49
58
  when Hash then validate_format_hash(format)
@@ -63,37 +72,5 @@ module Mint
63
72
 
64
73
  width ? formatted.rjust(width) : formatted
65
74
  end
66
-
67
- private
68
-
69
- def select_format(format)
70
- negative_format = format[:negative]
71
- zero_format = format[:zero]
72
-
73
- if amount.negative? && negative_format
74
- [negative_format, -amount]
75
- elsif amount.zero? && zero_format
76
- [zero_format, amount]
77
- else
78
- [format[:positive], amount]
79
- end
80
- end
81
-
82
- def validate_format_hash(format)
83
- unknown = format.keys - %i[positive negative zero]
84
-
85
- raise ArgumentError, "Unknown format parameter(s): #{unknown.inspect}. " unless unknown.empty?
86
- end
87
-
88
- def format_amount(format)
89
- format, value = select_format(format)
90
- format ||= '%<symbol>s%<amount>f'
91
- # Automatically adjust decimal places based on currency subunit if missing
92
- format = format.gsub(/%<amount>(\s*\+?\d*)f/, "%<amount>\\1.#{currency.subunit}f")
93
-
94
- refs = format.scan(/%<(\w+)>/).flatten.map(&:to_sym)
95
- all_args = { amount: value, currency: currency_code, symbol: currency.symbol }
96
- Kernel.format(format, **all_args.slice(*refs))
97
- end
98
75
  end
99
76
  end
@@ -1,5 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'allocation/allocation'
4
+ require_relative 'allocation/split'
5
+ require_relative 'arithmetics/methods'
6
+ require_relative 'arithmetics/operators'
7
+ require_relative 'clamp'
8
+ require_relative 'coercion'
9
+ require_relative 'comparable'
10
+ require_relative 'constructors'
11
+ require_relative 'conversion'
12
+ require_relative 'format/formatting'
13
+ require_relative 'format/to_s'
14
+
3
15
  module Mint
4
16
  # Money constructors
5
17
  class Money
@@ -43,63 +55,5 @@ module Mint
43
55
  # @param other [Currency] the target currency to compare
44
56
  # @return [Boolean] true if currencies match, false otherwise
45
57
  def same_currency?(other) = other.currency == currency
46
-
47
- # Constrains +self+ to the inclusive range [+min+, +max+].
48
- #
49
- # Bounds may be:
50
- # - nil meaning no boundary
51
- # - same-currency {Money} or Range
52
- # - Numeric amount, or Range
53
- #
54
- # Numeric is interpreted as an amount in +self+'s currency, so the common
55
- # pricing idiom +price.clamp(0, 100)+ reads as "0 to 100 in the same
56
- # currency as +price+".
57
- #
58
- # When +self+ is already in range the receiver is returned (no new object
59
- # allocated). When out of range, the nearest bound is returned as a new
60
- # frozen {Money} in +self+'s currency.
61
- #
62
- # @param min_or_range [Money, Numeric, Range, nil] lower bound (inclusive), or range
63
- # @param max [Money, Numeric, nil] upper bound (inclusive)
64
- # @return [Money] +self+ if in range, otherwise the nearer bound
65
- # @raise [ArgumentError] if +min+ or +max+ is not a Money, Numeric or nil; if
66
- # a Money operand has a different currency; if +min+ > +max+;
67
- # if min is a Range, and max is not nil
68
- #
69
- # @example In range
70
- # Mint.money(5, 'USD').clamp(0, 10) #=> [USD 5.00] (returns self)
71
- #
72
- # @example Out of range, with Numeric bounds
73
- # Mint.money(50, 'USD').clamp(0, 10) #=> [USD 10.00]
74
- #
75
- # @example Out of range, with Money bounds
76
- # loss = Mint.money(-5, 'USD')
77
- # floor = Mint.money(0, 'USD')
78
- # ceil = Mint.money(10, 'USD')
79
- # loss.clamp(floor, ceil) #=> [USD 0.00]
80
- #
81
- # @example Subunit-0 currency (JPY)
82
- # Mint.money(500, 'JPY').clamp(0, 100) #=> [JPY 100]
83
- def clamp(min_or_range, max = nil)
84
- if min_or_range.is_a?(Range)
85
- raise(ArgumentError, "Either amount range alone or two amounts accepted: #{max}") if max
86
-
87
- min, max = min_or_range.minmax
88
- else
89
- min = min_or_range
90
- end
91
- mint(amount.clamp(normalize_boundary(min), normalize_boundary(max)))
92
- end
93
-
94
- private
95
-
96
- def normalize_boundary(boundary)
97
- case boundary
98
- in NilClass | Numeric then boundary
99
- in Money if same_currency?(boundary) then boundary.amount
100
- in Money then raise ArgumentError, "oundary currency must be: #{currency_code}"
101
- else raise ArgumentError, "Boundary must be Numeric or Money #{boundary}"
102
- end
103
- end
104
58
  end
105
59
  end
@@ -3,5 +3,5 @@
3
3
  # Root namespace for the Minting library.
4
4
  module Minting
5
5
  # Current version of the Minting gem.
6
- VERSION = '1.7.0'
6
+ VERSION = '1.7.3'
7
7
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minting
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gilson Ferraz
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-06-12 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bigdecimal
@@ -33,6 +32,7 @@ files:
33
32
  - LICENSE
34
33
  - README.md
35
34
  - Rakefile
35
+ - bin/bench_check
36
36
  - bin/check-currencies
37
37
  - bin/console
38
38
  - bin/setup
@@ -40,14 +40,17 @@ files:
40
40
  - doc/Mint/Currency.html
41
41
  - doc/Mint/CurrencyRegistry.html
42
42
  - doc/Mint/Money.html
43
+ - doc/Mint/RangeStepPatch.html
44
+ - doc/Mint/Registry.html
43
45
  - doc/Mint/UnknownCurrency.html
44
46
  - doc/Minting.html
45
47
  - doc/_index.html
46
- - doc/agents/AGENTS.md
47
- - doc/agents/copilot-instructions.md
48
- - doc/agents/gemini_gem_evaluation.md
49
- - doc/agents/recommendations.md
50
- - doc/agents/rubocop-issues.md
48
+ - doc/agents/api_review-2026-06-15.md
49
+ - doc/agents/expired/AGENTS.md
50
+ - doc/agents/expired/copilot-instructions.md
51
+ - doc/agents/expired/gemini_gem_evaluation.md
52
+ - doc/agents/expired/recommendations.md
53
+ - doc/agents/expired/rubocop-issues.md
51
54
  - doc/class_list.html
52
55
  - doc/css/common.css
53
56
  - doc/css/full_list.css
@@ -62,24 +65,33 @@ files:
62
65
  - doc/method_list.html
63
66
  - doc/top-level-namespace.html
64
67
  - lib/minting.rb
68
+ - lib/minting/currency/currency.rb
65
69
  - lib/minting/data/world-currencies.yaml
66
70
  - lib/minting/mint.rb
67
71
  - lib/minting/mint/aliases.rb
68
- - lib/minting/mint/currency/currency.rb
69
- - lib/minting/mint/currency/currency_registry.rb
70
- - lib/minting/mint/currency/world_currencies.rb
72
+ - lib/minting/mint/dsl/numeric.rb
71
73
  - lib/minting/mint/dsl/range.rb
72
- - lib/minting/mint/dsl/refinements.rb
74
+ - lib/minting/mint/dsl/string.rb
73
75
  - lib/minting/mint/dsl/top_level.rb
76
+ - lib/minting/mint/locale_backend.rb
74
77
  - lib/minting/mint/mint.rb
75
- - lib/minting/mint/parser.rb
76
- - lib/minting/money/allocation.rb
77
- - lib/minting/money/arithmetics.rb
78
+ - lib/minting/mint/parser/parser.rb
79
+ - lib/minting/mint/parser/separators.rb
80
+ - lib/minting/mint/registry/registration.rb
81
+ - lib/minting/mint/registry/registry.rb
82
+ - lib/minting/mint/registry/symbols.rb
83
+ - lib/minting/mint/registry/zeros.rb
84
+ - lib/minting/money/allocation/allocation.rb
85
+ - lib/minting/money/allocation/split.rb
86
+ - lib/minting/money/arithmetics/methods.rb
87
+ - lib/minting/money/arithmetics/operators.rb
88
+ - lib/minting/money/clamp.rb
78
89
  - lib/minting/money/coercion.rb
79
90
  - lib/minting/money/comparable.rb
80
91
  - lib/minting/money/constructors.rb
81
92
  - lib/minting/money/conversion.rb
82
- - lib/minting/money/formatting.rb
93
+ - lib/minting/money/format/formatting.rb
94
+ - lib/minting/money/format/to_s.rb
83
95
  - lib/minting/money/money.rb
84
96
  - lib/minting/version.rb
85
97
  - minting.gemspec
@@ -94,7 +106,6 @@ metadata:
94
106
  source_code_uri: https://github.com/gferraz/minting
95
107
  allowed_push_host: https://rubygems.org
96
108
  rubygems_mfa_required: 'true'
97
- post_install_message:
98
109
  rdoc_options: []
99
110
  require_paths:
100
111
  - lib
@@ -109,8 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
120
  - !ruby/object:Gem::Version
110
121
  version: '0'
111
122
  requirements: []
112
- rubygems_version: 3.5.22
113
- signing_key:
123
+ rubygems_version: 4.0.10
114
124
  specification_version: 4
115
125
  summary: Library to manipulate currency values
116
126
  test_files: []
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mint
4
- # Represents a specific currency unit, identified by ISO 4217 alphabetic code.
5
- # Currency objects are immutable and define the properties of a monetary unit
6
- # including its subunit precision, display symbol, and formatting rules.
7
- #
8
- # @see https://www.iso.org/iso-4217-currency-codes.html
9
- # @attr_reader code [String] ISO 4217 currency code (e.g., "USD", "EUR")
10
- # @attr_reader subunit [Integer] Number of decimal places (0 for JPY, 2 for USD, 3 for IQD)
11
- # @attr_reader symbol [String] Display symbol (e.g., "$", "€", "R$")
12
- # @attr_reader priority [Integer] Parser precedence for symbol detection
13
- # @attr_reader country [String, nil] Associated country code
14
- # @attr_reader name [String, nil] Currency name
15
- # @attr_reader fractional_multiplier [Integer] 10^subunit, used for fractional conversions
16
- # @attr_reader minimum_amount [Rational] Smallest representable amount (1/fractional_multiplier)
17
- Currency = Data.define(:code, :subunit, :symbol, :priority, :country, :name,
18
- :fractional_multiplier) do
19
- def initialize(code:, symbol:, subunit: 0, priority: 0, country: nil, name: nil)
20
- subunit = subunit.to_i
21
- priority = priority.to_i
22
- fractional_multiplier = 10**subunit
23
- super(code:, subunit:, symbol:, priority:, country:, name:,
24
- fractional_multiplier:)
25
- end
26
-
27
- def inspect = "<Currency:(#{code} #{symbol} #{subunit} #{name})>"
28
-
29
- def minimum_amount = Rational(1, fractional_multiplier)
30
-
31
- # Normalizes numeric amounts for this currency
32
- # 1. Converts to Rational
33
- # 2. Rounds to respect currency subunit
34
- def normalize_amount(amount) = amount.to_r.round(subunit)
35
- end
36
- end