minting-rails 0.4.5 → 0.7.0
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 +4 -4
- data/README.md +79 -24
- data/lib/generators/templates/minting.rb +0 -16
- data/lib/minting/money_attribute/configuration.rb +4 -9
- data/lib/minting/money_attribute/money_attribute.rb +19 -23
- data/lib/minting/money_attribute/money_type.rb +19 -7
- data/lib/minting/money_attribute/parser.rb +27 -0
- data/lib/minting/money_attribute/version.rb +1 -1
- data/lib/minting/rails.rb +2 -1
- data/lib/minting/railties.rb +1 -1
- metadata +5 -4
- /data/lib/minting/{money_attribute/core_ext.rb → core_ext.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 205d9eb9b1d27048756b4f65f2e65781ae71f131d1d838a1834ea733349d3bf4
|
|
4
|
+
data.tar.gz: ea2dfd05351ae59717737a0770480c1eeb6cf484264c9daea064c87ac80dd4a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3e434d199296b8d9a22db937b2b40af35eec147a7fdc1d4a1c1a387a3a0300b806381f2e98cb3a3dfc40371b1087f1529a3a81b399b91dff4d4f454034f39c4
|
|
7
|
+
data.tar.gz: 8552a6d4d6c8f6567a4300c6540b04c00ca220b52b3deb0f0ba00af1fb40d0c974110eb97f04d3d0acb8d037f32852d61cd4a55a6b8d2dc17cbc398c9e58c6d4
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Minting::Rails brings [Minting](https://github.com/gferraz/minting) money objects to Active Record models.
|
|
4
4
|
|
|
5
|
-
It adds a `money_attribute` model helper, registers a `:mint_money` Active Record type, and includes small convenience methods such as `12.to_money(
|
|
5
|
+
It adds a `money_attribute` model helper, registers a `:mint_money` Active Record type, and includes small convenience methods such as `12.to_money('USD')`, `12.dollars`, and `'12.00'.mint('BRL')`.
|
|
6
6
|
|
|
7
7
|
## What it does
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ It adds a `money_attribute` model helper, registers a `:mint_money` Active Recor
|
|
|
15
15
|
|
|
16
16
|
- Ruby 3.3 or newer.
|
|
17
17
|
- Rails 7.1.3.2 or newer.
|
|
18
|
-
- Minting 1.
|
|
18
|
+
- Minting 1.6.0 or newer.
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
@@ -45,6 +45,7 @@ Configure Minting in `config/initializers/minting.rb`:
|
|
|
45
45
|
Mint.configure do |config|
|
|
46
46
|
config.enabled_currencies = :all
|
|
47
47
|
config.default_currency = 'USD'
|
|
48
|
+
config.default_format = '%<symbol>s%<amount>f'
|
|
48
49
|
end
|
|
49
50
|
```
|
|
50
51
|
|
|
@@ -63,7 +64,7 @@ You can also register custom currencies before enabling or using them:
|
|
|
63
64
|
Mint.configure do |config|
|
|
64
65
|
config.added_currencies = [
|
|
65
66
|
{ currency: 'CRC', subunit: 2, symbol: 'CRC' },
|
|
66
|
-
{ currency: 'NGN', subunit: 2, symbol: 'NGN' }
|
|
67
|
+
{ currency: 'NGN', subunit: 2, symbol: 'NGN' } # subunit is the number of digits after the decimal; USD has 2, JPY has 0, BHD has 3
|
|
67
68
|
]
|
|
68
69
|
|
|
69
70
|
config.enabled_currencies = :all
|
|
@@ -120,7 +121,7 @@ product.discount
|
|
|
120
121
|
Assigning a `Mint::Money` with a different currency raises `ArgumentError`:
|
|
121
122
|
|
|
122
123
|
```ruby
|
|
123
|
-
Product.new(price: 12.to_money(
|
|
124
|
+
Product.new(price: 12.to_money('EUR'))
|
|
124
125
|
# raises ArgumentError because the attribute only accepts USD
|
|
125
126
|
```
|
|
126
127
|
|
|
@@ -154,7 +155,7 @@ end
|
|
|
154
155
|
The attribute is composed from `price_amount` and `price_currency`:
|
|
155
156
|
|
|
156
157
|
```ruby
|
|
157
|
-
offer = Offer.new(price: 15.to_money(
|
|
158
|
+
offer = Offer.new(price: 15.to_money('EUR'))
|
|
158
159
|
|
|
159
160
|
offer.price
|
|
160
161
|
# => #<Mint::Money ... EUR 15.00>
|
|
@@ -166,6 +167,68 @@ offer.price_currency
|
|
|
166
167
|
# => "EUR"
|
|
167
168
|
```
|
|
168
169
|
|
|
170
|
+
### Integer storage
|
|
171
|
+
|
|
172
|
+
By default, money attributes are stored as `decimal` columns. If you prefer to store amounts as integer subunits (cents, pence, etc.), use a `bigint` or `integer` column instead. Minting::Rails detects the column type automatically and adapts serialization accordingly.
|
|
173
|
+
|
|
174
|
+
Migration:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
class CreateOrders < ActiveRecord::Migration[7.1]
|
|
178
|
+
def change
|
|
179
|
+
create_table :orders do |t|
|
|
180
|
+
t.bigint :total_amount
|
|
181
|
+
t.string :total_currency
|
|
182
|
+
|
|
183
|
+
t.timestamps
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Model:
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
class Order < ApplicationRecord
|
|
193
|
+
money_attribute :total
|
|
194
|
+
end
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The amount is stored and retrieved in subunits:
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
order = Order.new(total: 19.99.to_money(:USD))
|
|
201
|
+
|
|
202
|
+
order.total
|
|
203
|
+
# => #<Mint::Money ... USD 19.99>
|
|
204
|
+
|
|
205
|
+
order.total_amount
|
|
206
|
+
# => 1999
|
|
207
|
+
|
|
208
|
+
order.total_currency
|
|
209
|
+
# => "USD"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The same applies to single-column fixed-currency attributes:
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
class Ticket < ApplicationRecord
|
|
216
|
+
money_attribute :price, currency: 'USD'
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Migration:
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
t.bigint :price
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
No model change is needed. The column type drives the behavior.
|
|
227
|
+
|
|
228
|
+
> **Note:** Integer storage is more efficient for large tables. Use Decimal when you need to stay close to SQL standards for interoperability with other systems
|
|
229
|
+
|
|
230
|
+
### Default Currency
|
|
231
|
+
|
|
169
232
|
When you assign a plain number or string, Minting::Rails uses `Mint.default_currency`:
|
|
170
233
|
|
|
171
234
|
```ruby
|
|
@@ -182,26 +245,26 @@ If your amount and currency columns do not follow the `<name>_amount` and `<name
|
|
|
182
245
|
```ruby
|
|
183
246
|
class Invoice < ApplicationRecord
|
|
184
247
|
money_attribute :total, mapping: {
|
|
185
|
-
|
|
186
|
-
|
|
248
|
+
amount: :total_amount,
|
|
249
|
+
currency: :currency_code
|
|
187
250
|
}
|
|
188
251
|
end
|
|
189
252
|
```
|
|
190
253
|
|
|
191
|
-
The mapping keys are
|
|
254
|
+
The mapping keys are `:amount` and `:currency`. The values are your database columns.
|
|
192
255
|
|
|
193
256
|
## Querying
|
|
194
257
|
|
|
195
258
|
Fixed-currency attributes can be queried with `Mint::Money` values:
|
|
196
259
|
|
|
197
260
|
```ruby
|
|
198
|
-
Product.where(price: 15.to_money(
|
|
261
|
+
Product.where(price: 15.to_money('USD'))
|
|
199
262
|
```
|
|
200
263
|
|
|
201
264
|
Composed attributes can also be queried with a money object:
|
|
202
265
|
|
|
203
266
|
```ruby
|
|
204
|
-
Offer.where(price: 15.to_money(
|
|
267
|
+
Offer.where(price: 15.to_money('EUR'))
|
|
205
268
|
```
|
|
206
269
|
|
|
207
270
|
You can still query the backing columns directly when that is clearer:
|
|
@@ -215,12 +278,14 @@ Offer.where(price_amount: 15, price_currency: 'EUR')
|
|
|
215
278
|
Minting::Rails adds a few small helpers:
|
|
216
279
|
|
|
217
280
|
```ruby
|
|
218
|
-
12.to_money(
|
|
219
|
-
12.mint(
|
|
281
|
+
12.to_money('USD')
|
|
282
|
+
12.mint('BRL')
|
|
220
283
|
12.dollars
|
|
284
|
+
1.dollar
|
|
285
|
+
1.euro
|
|
221
286
|
12.euros
|
|
222
|
-
'12.50'.to_money(
|
|
223
|
-
'12.50'.mint(
|
|
287
|
+
'12.50'.to_money('USD')
|
|
288
|
+
'12.50'.mint('BRL')
|
|
224
289
|
```
|
|
225
290
|
|
|
226
291
|
These return `Mint::Money` instances.
|
|
@@ -241,16 +306,6 @@ bundle exec rake test
|
|
|
241
306
|
|
|
242
307
|
The repository includes a dummy Rails application under `test/dummy` for exercising the engine in a Rails environment.
|
|
243
308
|
|
|
244
|
-
## Releasing
|
|
245
|
-
|
|
246
|
-
Update the version in `lib/minting/money_attribute/version.rb`, update release notes, and build the gem:
|
|
247
|
-
|
|
248
|
-
```sh
|
|
249
|
-
gem build minting-rails.gemspec
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
Publishing is configured for RubyGems.org.
|
|
253
|
-
|
|
254
309
|
## Contributing
|
|
255
310
|
|
|
256
311
|
Bug reports and pull requests are welcome on GitHub at [gferraz/minting-rails](https://github.com/gferraz/minting-rails).
|
|
@@ -27,22 +27,6 @@ Mint.configure do |config|
|
|
|
27
27
|
#
|
|
28
28
|
config.default_currency = 'BRL'
|
|
29
29
|
|
|
30
|
-
# Specify a rounding mode (not yet implemented)
|
|
31
|
-
# Any one of:
|
|
32
|
-
#
|
|
33
|
-
# BigDecimal::ROUND_UP,
|
|
34
|
-
# BigDecimal::ROUND_DOWN,
|
|
35
|
-
# BigDecimal::ROUND_HALF_UP,
|
|
36
|
-
# BigDecimal::ROUND_HALF_DOWN,
|
|
37
|
-
# BigDecimal::ROUND_HALF_EVEN,
|
|
38
|
-
# BigDecimal::ROUND_CEILING,
|
|
39
|
-
# BigDecimal::ROUND_FLOOR
|
|
40
|
-
#
|
|
41
|
-
# set to BigDecimal::ROUND_HALF_EVEN by default
|
|
42
|
-
#
|
|
43
|
-
# config.rounding_mode = BigDecimal::ROUND_HALF_UP
|
|
44
|
-
|
|
45
|
-
# Set default money format globally.
|
|
46
30
|
# Default value is nil meaning "ignore this option".
|
|
47
31
|
# Example:
|
|
48
32
|
#
|
|
@@ -27,11 +27,10 @@ module Mint
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def self.assert_valid_currency!(currency)
|
|
30
|
-
|
|
31
|
-
currency = Mint.currency(code)
|
|
30
|
+
currency = Mint.currency(currency)
|
|
32
31
|
return currency if Mint.valid_currency?(currency)
|
|
33
32
|
|
|
34
|
-
raise ArgumentError, "Invalid currency '#{
|
|
33
|
+
raise ArgumentError, "Invalid currency '#{currency}'. Please select a registered currency"
|
|
35
34
|
end
|
|
36
35
|
|
|
37
36
|
def self.default_currency
|
|
@@ -39,11 +38,7 @@ module Mint
|
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
def self.valid_currency?(currency)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
code = currency.is_a?(Mint::Currency) ? currency.code : currency.to_s
|
|
45
|
-
currencies = config.enabled_currencies == :all ? Mint.currencies.keys : config.enabled_currencies
|
|
46
|
-
|
|
47
|
-
currencies.map(&:to_s).include?(code)
|
|
41
|
+
enabled = config.enabled_currencies
|
|
42
|
+
currency && (enabled == :all || enabled.include?(currency.code))
|
|
48
43
|
end
|
|
49
44
|
end
|
|
@@ -9,7 +9,7 @@ module Mint
|
|
|
9
9
|
# Money attribute
|
|
10
10
|
def money_attribute(name, currency: Mint.default_currency, mapping: nil)
|
|
11
11
|
currency = Mint.assert_valid_currency!(currency)
|
|
12
|
-
parser =
|
|
12
|
+
parser = Parser.new(currency)
|
|
13
13
|
if attribute_names.include? name.to_s
|
|
14
14
|
attribute(name, :mint_money, currency:)
|
|
15
15
|
normalizes(name, with: parser)
|
|
@@ -18,15 +18,31 @@ module Mint
|
|
|
18
18
|
options = {
|
|
19
19
|
allow_nil: true, class_name: 'Mint::Money',
|
|
20
20
|
constructor: parser, converter: parser,
|
|
21
|
-
mapping: {
|
|
21
|
+
mapping: {
|
|
22
|
+
aggregated[:amount] => amount_extractor_for(aggregated[:amount]),
|
|
23
|
+
aggregated[:currency] => :currency_code
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
26
|
composed_of(name, options)
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
|
|
30
|
+
def amount_extractor_for(column_name)
|
|
31
|
+
col = columns.find { |c| c.name == column_name.to_s }
|
|
32
|
+
|
|
33
|
+
case col&.type
|
|
34
|
+
when :bigint, :integer
|
|
35
|
+
:fractional
|
|
36
|
+
when :decimal, :numeric
|
|
37
|
+
:to_d
|
|
38
|
+
else
|
|
39
|
+
:to_d # :decimal, :numeric, unknown
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
27
43
|
def find_money_attributes(name, mapping:)
|
|
28
44
|
composite = if mapping.present?
|
|
29
|
-
{ amount: mapping
|
|
45
|
+
{ amount: mapping[:amount].to_s, currency: mapping[:currency].to_s }
|
|
30
46
|
else
|
|
31
47
|
{ amount: "#{name}_amount", currency: "#{name}_currency" }
|
|
32
48
|
end
|
|
@@ -37,25 +53,5 @@ module Mint
|
|
|
37
53
|
composite
|
|
38
54
|
end
|
|
39
55
|
end
|
|
40
|
-
|
|
41
|
-
def self.parse(amount, currency)
|
|
42
|
-
money = case amount
|
|
43
|
-
when NilClass
|
|
44
|
-
nil
|
|
45
|
-
when Mint::Money
|
|
46
|
-
amount
|
|
47
|
-
when Numeric
|
|
48
|
-
Mint.money(amount, currency)
|
|
49
|
-
else
|
|
50
|
-
if amount.respond_to? :to_money
|
|
51
|
-
amount.to_money(currency)
|
|
52
|
-
else
|
|
53
|
-
Mint.money(amount.to_s.split[0].to_r, currency)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
# puts "parse(#{amount}, #{currency.inspect}) => #{money.inspect}"
|
|
57
|
-
Mint.assert_valid_currency!(currency)
|
|
58
|
-
money
|
|
59
|
-
end
|
|
60
56
|
end
|
|
61
57
|
end
|
|
@@ -3,31 +3,43 @@
|
|
|
3
3
|
module Mint
|
|
4
4
|
# MintMoneyType
|
|
5
5
|
class MintMoneyType < ActiveRecord::Type::Value
|
|
6
|
-
def initialize(currency:)
|
|
7
|
-
@currency =
|
|
6
|
+
def initialize(currency:, column_type: ActiveRecord::Type::Decimal.new)
|
|
7
|
+
@currency = currency
|
|
8
|
+
@column_type = column_type
|
|
8
9
|
super()
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def assert_valid_value(value)
|
|
12
13
|
case value
|
|
13
|
-
when NilClass, Numeric, String
|
|
14
|
-
return
|
|
14
|
+
when NilClass, Numeric, String then return
|
|
15
15
|
when Mint::Money
|
|
16
16
|
return if value.currency == @currency
|
|
17
17
|
|
|
18
18
|
message = "'#{value.inspect}' has different currency. Only #{@currency.code} allowed."
|
|
19
19
|
else
|
|
20
|
-
message = "'#{value}' is not a valid type for the attribute."
|
|
20
|
+
message = "'#{value.inspect}' is not a valid type for the attribute."
|
|
21
21
|
end
|
|
22
22
|
raise ArgumentError, message
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def deserialize(value)
|
|
26
|
-
|
|
26
|
+
return nil unless value
|
|
27
|
+
|
|
28
|
+
if @column_type.is_a?(ActiveRecord::Type::Integer)
|
|
29
|
+
Mint.money(value * @currency.multiplier, @currency)
|
|
30
|
+
else
|
|
31
|
+
Mint.money(value, @currency)
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def serialize(value)
|
|
30
|
-
value
|
|
36
|
+
return nil unless value
|
|
37
|
+
|
|
38
|
+
if @column_type.is_a?(ActiveRecord::Type::Integer)
|
|
39
|
+
value.fractional
|
|
40
|
+
else
|
|
41
|
+
value.to_d
|
|
42
|
+
end
|
|
31
43
|
end
|
|
32
44
|
|
|
33
45
|
def self.type
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mint
|
|
4
|
+
# MoneyAttribute
|
|
5
|
+
module MoneyAttribute
|
|
6
|
+
class Parser
|
|
7
|
+
def initialize(currency = Mint.default_currency)
|
|
8
|
+
@default_currency = currency
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse(amount, currency = @default_currency)
|
|
12
|
+
currency = Mint.assert_valid_currency!(currency)
|
|
13
|
+
case amount
|
|
14
|
+
when NilClass then nil
|
|
15
|
+
when Numeric then Mint::Money.create(amount, currency)
|
|
16
|
+
when String then Mint::Money.create(amount.to_r, currency)
|
|
17
|
+
when Mint::Money
|
|
18
|
+
return amount if amount.currency == currency
|
|
19
|
+
raise TypeError, "Cannot automatically convert #{amount} to #{currency.code}"
|
|
20
|
+
else
|
|
21
|
+
Mint.parse(amount, currency)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
alias_method :call, :parse
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/minting/rails.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'minting'
|
|
4
|
-
require 'minting/
|
|
4
|
+
require 'minting/core_ext'
|
|
5
5
|
require 'minting/money_attribute/configuration'
|
|
6
6
|
require 'minting/money_attribute/money_attribute'
|
|
7
7
|
require 'minting/money_attribute/money_type'
|
|
8
|
+
require 'minting/money_attribute/parser'
|
|
8
9
|
require 'minting/money_attribute/version'
|
|
9
10
|
require 'minting/railties'
|
data/lib/minting/railties.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minting-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gilson Ferraz
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 1.
|
|
18
|
+
version: 1.6.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 1.
|
|
25
|
+
version: 1.6.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: rails
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -49,10 +49,11 @@ files:
|
|
|
49
49
|
- Rakefile
|
|
50
50
|
- lib/generators/minting/initializer_generator.rb
|
|
51
51
|
- lib/generators/templates/minting.rb
|
|
52
|
+
- lib/minting/core_ext.rb
|
|
52
53
|
- lib/minting/money_attribute/configuration.rb
|
|
53
|
-
- lib/minting/money_attribute/core_ext.rb
|
|
54
54
|
- lib/minting/money_attribute/money_attribute.rb
|
|
55
55
|
- lib/minting/money_attribute/money_type.rb
|
|
56
|
+
- lib/minting/money_attribute/parser.rb
|
|
56
57
|
- lib/minting/money_attribute/version.rb
|
|
57
58
|
- lib/minting/rails.rb
|
|
58
59
|
- lib/minting/railties.rb
|
|
File without changes
|