money_attribute 0.10.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +381 -0
- data/Rakefile +30 -0
- data/lib/generators/money_attribute/initializer_generator.rb +15 -0
- data/lib/generators/templates/money_attribute.rb +22 -0
- data/lib/money_attribute/configuration.rb +26 -0
- data/lib/money_attribute/core_ext.rb +26 -0
- data/lib/money_attribute/macro.rb +91 -0
- data/lib/money_attribute/migration_extensions/helper.rb +46 -0
- data/lib/money_attribute/migration_extensions/schema_statements.rb +25 -0
- data/lib/money_attribute/migration_extensions/table_definition.rb +25 -0
- data/lib/money_attribute/parser.rb +25 -0
- data/lib/money_attribute/railtie.rb +55 -0
- data/lib/money_attribute/type.rb +55 -0
- data/lib/money_attribute/version.rb +5 -0
- data/lib/money_attribute.rb +10 -0
- data/lib/tasks/money_attribute.rake +1 -0
- metadata +91 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8058c06f7d8fd8f12abbe12c979efc9802cd56dcd788bf4f83e100838275d0fa
|
|
4
|
+
data.tar.gz: '09ade456fbca1582b9f600858177bf20f049d24d35f8539967fce3989d445534'
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1623898044fb45adb289c256683e7843365b078ecb840d5bc7e178995f7a9580aa6df92baa65a5ead5d45cd00ae8aa496062f9579ece5e9f960b9b64a3814fda
|
|
7
|
+
data.tar.gz: 48a8712ec075f95c446265c14813067f4c3ff7dd8f76bad2dba5887685235481ac95a57c0c24542ec913a0b3b4153eb9ed7187ca1cfcad74d73ec2cfbbf190ea
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Gilson Ferraz
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# MoneyAttribute
|
|
2
|
+
|
|
3
|
+
[](https://github.com/gferraz/money-attribute/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/money_attribute)
|
|
5
|
+
|
|
6
|
+
Store and read Active Record attributes as `Mint::Money` objects with a single `money_attribute` declaration. No manual serialization, no boilerplate.
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
class Product < ApplicationRecord
|
|
10
|
+
money_attribute :price, currency: 'USD' # fixed currency, single column
|
|
11
|
+
money_attribute :total # multi-currency, two columns
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Product.new(price: 12).price # => [USD 12.00]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
bundle add money_attribute
|
|
21
|
+
bin/rails g money_attribute:initializer
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# db/migrate/20260620000000_create_products.rb
|
|
26
|
+
class CreateProducts < ActiveRecord::Migration[8.1]
|
|
27
|
+
def change
|
|
28
|
+
create_table :products do |t|
|
|
29
|
+
t.string :name
|
|
30
|
+
t.money :price
|
|
31
|
+
t.timestamps
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
# app/models/product.rb
|
|
39
|
+
class Product < ApplicationRecord
|
|
40
|
+
money_attribute :price, currency: 'USD'
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
That's it. `Product.new(price: 12).price` is a `Mint::Money`.
|
|
45
|
+
|
|
46
|
+
## Why MoneyAttribute?
|
|
47
|
+
|
|
48
|
+
- **No serialization boilerplate** — declare once, read/write `Mint::Money` everywhere.
|
|
49
|
+
- **Two storage modes** — single column for fixed-currency apps (simpler), amount+currency columns for multi-currency records (more flexible).
|
|
50
|
+
- **Integer or decimal columns** — auto-detects the column type and adjusts serialization (e.g. integer stores cents, decimal stores unit value).
|
|
51
|
+
- **Normalizes everything** — pass a number, string, or `Mint::Money`; always get a `Mint::Money` back.
|
|
52
|
+
- **Currency enforcement** — fixed-currency attributes reject wrong currencies at assignment time.
|
|
53
|
+
- **Built on Rails primitives** — uses `ActiveRecord::Type`, `composed_of`, and `normalizes` under the hood. No monkey-patching of core classes.
|
|
54
|
+
|
|
55
|
+
### At a glance — vs money-rails
|
|
56
|
+
|
|
57
|
+
| Feature | MoneyAttribute | money-rails |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| **Declaration** | `money_attribute :price` | `monetize :price_cents` |
|
|
60
|
+
| **Column types** | `integer`, `decimal`, `bigint` — auto-detected | `integer` cents only |
|
|
61
|
+
| **Storage modes** | Single column, composite (amount+currency)| Single cents column, composite (cents+currency) |
|
|
62
|
+
| **Decimal columns** | Native — `t.decimal :price` | Not supported — must convert to cents manually |
|
|
63
|
+
| **Multi-currency** | `money_attribute :price` (convention: `<name>_amount` + `<name>_currency`) | `monetize :price_cents, with_currency: :price_currency` |
|
|
64
|
+
| **Rails integration** | `ActiveRecord::Type` + `composed_of` — no monkey-patches | `monetize` overrides reader/writer methods |
|
|
65
|
+
| **Query (fixed)** | `Model.where(price: money)` — `=`, `IN`, `BETWEEN`, `ORDER`, `SUM` | Through cents column (`price_cents`) |
|
|
66
|
+
| **Query (multi)** | `Model.where(price: money)` | `Model.where(price_cents:, price_currency:)` |
|
|
67
|
+
| **Internal amount** | `Rational` | `BigDecimal` |
|
|
68
|
+
| **Performance** | See [BENCHMARKS.md](BENCHMARKS.md) — wins 9/11 cells | |
|
|
69
|
+
|
|
70
|
+
For a detailed side-by-side comparison, see [COMPARISON.md](COMPARISON.md).
|
|
71
|
+
|
|
72
|
+
## Requirements
|
|
73
|
+
|
|
74
|
+
- Ruby 3.3+
|
|
75
|
+
- Rails 7.1.3.2+
|
|
76
|
+
- [Minting](https://github.com/gferraz/minting) 1.8.0+
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
# Gemfile
|
|
82
|
+
gem 'money_attribute'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
bundle install
|
|
87
|
+
bin/rails g money_attribute:initializer
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The generator creates `config/initializers/money_attribute.rb`.
|
|
91
|
+
|
|
92
|
+
## Migration helpers
|
|
93
|
+
|
|
94
|
+
MoneyAttribute adds `add_money` / `remove_money` for existing tables and `t.money` / `t.remove_money` for `create_table` / `change_table` blocks:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
class CreateProducts < ActiveRecord::Migration[8.1]
|
|
98
|
+
def change
|
|
99
|
+
create_table :products do |t|
|
|
100
|
+
t.string :name
|
|
101
|
+
t.money :price # price (decimal) + price_currency (string)
|
|
102
|
+
t.money :price_amount # price_amount + price_currency (strips _amount suffix)
|
|
103
|
+
t.money :fee, currency: false # single column, no currency
|
|
104
|
+
t.money :tax, type: :bigint # bigint amount + currency
|
|
105
|
+
t.timestamps
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class AddPriceToProducts < ActiveRecord::Migration[8.1]
|
|
111
|
+
def change
|
|
112
|
+
add_money :products, :price # add price + price_currency
|
|
113
|
+
add_money :products, :discount, type: :integer
|
|
114
|
+
remove_money :products, :obsolete_fee # reversible in change
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Naming
|
|
120
|
+
|
|
121
|
+
| Migration call | Columns created | Model declaration |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `t.money :price` | `price` decimal + `price_currency` string | `money_attribute :price` |
|
|
124
|
+
| `t.money :price_amount` | `price_amount` decimal + `price_currency` string | `money_attribute :price` |
|
|
125
|
+
| `t.money :price, currency: false` | `price` decimal | `money_attribute :price` |
|
|
126
|
+
| `t.money :price, type: :integer` | `price` integer + `price_currency` string | `money_attribute :price` |
|
|
127
|
+
| `t.money :price, amount: :a, currency: :c` | `a` + `c` | `money_attribute :price, mapping: { amount: :a, currency: :c }` |
|
|
128
|
+
|
|
129
|
+
## Configuration
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
# config/initializers/money_attribute.rb
|
|
133
|
+
MoneyAttribute.configure do |config|
|
|
134
|
+
config.default_currency = 'USD'
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See the [Minting gem](https://github.com/gferraz/minting) for full configuration options (custom currencies, formatting, rounding).
|
|
139
|
+
|
|
140
|
+
### I18n / Locale-aware formatting
|
|
141
|
+
|
|
142
|
+
MoneyAttribute integrates with Rails I18n to automatically format money amounts according to the current locale.
|
|
143
|
+
|
|
144
|
+
With `I18n.locale` set to `:en`:
|
|
145
|
+
```ruby
|
|
146
|
+
Mint.money(1234.56, 'USD').to_s # => "$1,234.56"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Switch to `:'pt-BR'` and the separators change automatically (requires [`rails-i18n`](https://github.com/svenfuchs/rails-i18n) or your own locale file):
|
|
150
|
+
```ruby
|
|
151
|
+
I18n.locale = :'pt-BR'
|
|
152
|
+
Mint.money(1234.56, 'USD').to_s # => "$1.234,56"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The locale backend reads `number.currency.format` from your I18n translations and maps Rails format syntax (`%n` for amount, `%u` for unit) to `Mint::Money#to_s`. If the translation key is missing (no locale file for that language), it falls back to hardcoded defaults (`.` decimal, `,` thousand, `%<symbol>s%<amount>f` format).
|
|
156
|
+
|
|
157
|
+
You can configure per-sign formatting by adding `positive`, `negative`, and `zero` keys to your locale:
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
# config/locales/money_attribute.en.yml
|
|
161
|
+
en:
|
|
162
|
+
number:
|
|
163
|
+
currency:
|
|
164
|
+
format:
|
|
165
|
+
format: "%u%n" # fallback when no per-sign key matches
|
|
166
|
+
positive: "%u%n" # "$1,234.56"
|
|
167
|
+
negative: "(%u%n)" # "($1,234.56)"
|
|
168
|
+
zero: "--" # "--"
|
|
169
|
+
separator: "."
|
|
170
|
+
delimiter: ","
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
When any of `positive`, `negative`, or `zero` is present, a Hash format is built. Missing keys fall back to `format`:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
Mint.money(1234.56, 'USD').to_s # => "$1,234.56"
|
|
177
|
+
Mint.money(-1234.56, 'USD').to_s # => "($1,234.56)"
|
|
178
|
+
Mint.money(0, 'USD').to_s # => "--"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
If none of those keys are set, `format` is used as a plain string (simple formatting).
|
|
182
|
+
|
|
183
|
+
> Formatting respects the currency's own `subunit` for decimal precision — `I18n` locale settings for `precision` are ignored since that is a currency property, not a locale one.
|
|
184
|
+
|
|
185
|
+
## Usage — Two modes
|
|
186
|
+
|
|
187
|
+
### Decision table
|
|
188
|
+
|
|
189
|
+
| | Fixed currency (single column) | Multi-currency (amount + currency) |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| **Migration** | `t.money :price` (or `t.decimal :price`) | `t.money :price` (or `t.decimal :price_amount` + `t.string :price_currency`) |
|
|
192
|
+
| **Model** | `money_attribute :price, currency: 'USD'` | `money_attribute :price` |
|
|
193
|
+
| **When to use** | Column always holds the same currency | Each row can hold a different currency |
|
|
194
|
+
| **Column type** | `decimal`, `integer`, or `bigint` | `decimal`, `integer`, or `bigint` for amount; `string` for currency |
|
|
195
|
+
| **Query** | `Product.where(price: 10.to_money('USD'))` — full type support | `Offer.where(price: 10.to_money('EUR'))` — equality only |
|
|
196
|
+
|
|
197
|
+
### Fixed currency
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
class Product < ApplicationRecord
|
|
201
|
+
money_attribute :price, currency: 'USD'
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
product = Product.new(price: 12)
|
|
205
|
+
product.price # => [USD 12.00]
|
|
206
|
+
|
|
207
|
+
Product.new(price: 12.to_money('EUR'))
|
|
208
|
+
# => ArgumentError: ... has different currency. Only USD allowed.
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Multi-currency
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
class Offer < ApplicationRecord
|
|
215
|
+
money_attribute :price
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
offer = Offer.new(price: 15.to_money('EUR'))
|
|
219
|
+
offer.price # => [EUR 15.00]
|
|
220
|
+
offer.price_amount # => 15.0
|
|
221
|
+
offer.price_currency # => "EUR"
|
|
222
|
+
|
|
223
|
+
offer = Offer.new(price: '12')
|
|
224
|
+
offer.price.currency.code # => "USD"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Column type detection
|
|
228
|
+
|
|
229
|
+
Declare the column as `decimal`, `integer`, or `bigint` — the gem adapts:
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
# Migration
|
|
233
|
+
create_table :orders do |t|
|
|
234
|
+
t.bigint :total_amount # stored as cents (subunits)
|
|
235
|
+
t.string :total_currency
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Model
|
|
239
|
+
class Order < ApplicationRecord
|
|
240
|
+
money_attribute :total
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
Order.new(total: 19.99.to_money('USD')).total_amount # => 1999
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Same for fixed-currency attributes:
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
# Migration
|
|
250
|
+
t.bigint :price
|
|
251
|
+
|
|
252
|
+
# Model (no change needed)
|
|
253
|
+
money_attribute :price, currency: 'USD'
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
> Use `integer`/`bigint` for large tables (faster, smaller). Use `decimal` when SQL-level readability matters.
|
|
257
|
+
|
|
258
|
+
## Custom column names
|
|
259
|
+
|
|
260
|
+
If your columns don't follow the `<name>_amount` / `<name>_currency` convention:
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
class Invoice < ApplicationRecord
|
|
264
|
+
money_attribute :total, mapping: {
|
|
265
|
+
amount: :total_amount,
|
|
266
|
+
currency: :currency_code
|
|
267
|
+
}
|
|
268
|
+
end
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The mapping keys are `:amount` and `:currency`; values are your database column names.
|
|
272
|
+
|
|
273
|
+
## Column resolution
|
|
274
|
+
|
|
275
|
+
When you declare `money_attribute :name`, the gem resolves which database columns to use by checking the table schema in this order:
|
|
276
|
+
|
|
277
|
+
| Step | Condition | Columns used | Mode |
|
|
278
|
+
|---|---|---|---|
|
|
279
|
+
| 1 | `mapping:` provided | As specified | Explicit composite |
|
|
280
|
+
| 2 | `name_currency` column exists | `name` + `name_currency` | Composite (multi-currency) |
|
|
281
|
+
| 3 | `name == 'amount'` AND `currency` column exists | `amount` + `currency` | Composite (multi-currency) |
|
|
282
|
+
| 4 | `name_amount` + `name_currency` columns exist | `name_amount` + `name_currency` | Composite (multi-currency) |
|
|
283
|
+
| 5 | `name` column exists (no currency partner) | `name` alone | Single-column (fixed-currency) |
|
|
284
|
+
|
|
285
|
+
**Example**
|
|
286
|
+
|
|
287
|
+
```ruby
|
|
288
|
+
create_table :financial_transactions do |t|
|
|
289
|
+
t.integer :amount
|
|
290
|
+
t.string :currency, limit: 3
|
|
291
|
+
t.integer :discount
|
|
292
|
+
t.string :discount_currency, limit: 3
|
|
293
|
+
t.decimal :price_amount
|
|
294
|
+
t.string :price_currency, limit: 3
|
|
295
|
+
t.bigint :surplus
|
|
296
|
+
t.bigint :tax
|
|
297
|
+
t.decimal :total_amount
|
|
298
|
+
t.string :currency_code, limit: 3
|
|
299
|
+
end
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
class FinancialTransaction < ApplicationRecord
|
|
304
|
+
money_attribute :amount # step 3: amount(int) + currency
|
|
305
|
+
money_attribute :discount # step 2: discount(int) + discount_currency
|
|
306
|
+
money_attribute :price # step 4: price_amount(dec) + price_currency
|
|
307
|
+
money_attribute :surplus, currency: 'EUR' # step 5: surplus(int) (single-column, will use EUR)
|
|
308
|
+
money_attribute :tax # step 5: tax(int) (single-column, will use default currency)
|
|
309
|
+
money_attribute :total, mapping: { amount: :total_amount, currency: :currency_code } # step 1: explicit
|
|
310
|
+
end
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Querying
|
|
314
|
+
|
|
315
|
+
Fixed-currency attributes support Rails-native querying through the custom type:
|
|
316
|
+
|
|
317
|
+
```ruby
|
|
318
|
+
# Equality
|
|
319
|
+
Product.where(price: 10.to_money('USD'))
|
|
320
|
+
|
|
321
|
+
# IN clause
|
|
322
|
+
Product.where(price: [10.to_money('USD'), 20.to_money('USD')])
|
|
323
|
+
|
|
324
|
+
# BETWEEN
|
|
325
|
+
Product.where(price: 10.to_money('USD')..20.to_money('USD'))
|
|
326
|
+
|
|
327
|
+
# Ordering
|
|
328
|
+
Product.order(price: :desc)
|
|
329
|
+
|
|
330
|
+
# Aggregation
|
|
331
|
+
Product.where(price: 10.to_money('USD')).sum(:price)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Multi-currency attributes support equality queries via `composed_of`:
|
|
335
|
+
|
|
336
|
+
```ruby
|
|
337
|
+
Offer.where(price: 10.to_money('EUR'))
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
For comparisons on multi-currency attributes, use the backing columns directly:
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
Offer.where(price_amount: 10..20, price_currency: 'EUR')
|
|
344
|
+
Offer.where('price_amount > ? AND price_currency = ?', 10, 'EUR')
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Convenience methods
|
|
348
|
+
|
|
349
|
+
MoneyAttribute adds small helpers on `Numeric` and `String`:
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
12.to_money('USD') # => [USD 12.00]
|
|
353
|
+
12.dollars # => [USD 12.00]
|
|
354
|
+
12.euros # => [EUR 12.00]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
> If you prefer not to extend core classes, use `Mint.money(12, 'USD')` instead.
|
|
358
|
+
|
|
359
|
+
## Roadmap
|
|
360
|
+
|
|
361
|
+
1. **Method-level currency** — lambda-based currency resolution for multi-tenant and instance-level scenarios
|
|
362
|
+
2. Prepare to official 1.0 launh
|
|
363
|
+
|
|
364
|
+
Contributions and suggestions are welcome — open an issue or PR at [gferraz/money-attribute](https://github.com/gferraz/money-attribute).
|
|
365
|
+
|
|
366
|
+
## Development
|
|
367
|
+
|
|
368
|
+
```sh
|
|
369
|
+
bundle install
|
|
370
|
+
bundle exec rake test
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
The dummy Rails app under `test/dummy` exercises the engine in a full Rails environment.
|
|
374
|
+
|
|
375
|
+
## Contributing
|
|
376
|
+
|
|
377
|
+
Bug reports and pull requests welcome at [gferraz/money-attribute](https://github.com/gferraz/money-attribute).
|
|
378
|
+
|
|
379
|
+
## License
|
|
380
|
+
|
|
381
|
+
[MIT](MIT-LICENSE)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'bundler/gem_tasks'
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
|
|
7
|
+
task default: :test_run
|
|
8
|
+
|
|
9
|
+
desc 'Run tests'
|
|
10
|
+
Rake::TestTask.new(:test_run) do |t|
|
|
11
|
+
t.libs << 'test'
|
|
12
|
+
t.libs << 'lib'
|
|
13
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
14
|
+
t.ruby_opts << '-rtest_helper.rb'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'Migrate test database'
|
|
18
|
+
task :test_db_migrate do
|
|
19
|
+
Dir.chdir('test/dummy') do
|
|
20
|
+
sh({ 'RAILS_ENV' => 'test' }, 'bin/rails', 'db:migrate')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'Run tests (migrates test DB first)'
|
|
25
|
+
task test: %i[test_db_migrate test_run]
|
|
26
|
+
|
|
27
|
+
desc 'Run money_attribute vs money-rails benchmark'
|
|
28
|
+
task :bench do
|
|
29
|
+
sh({ 'RAILS_ENV' => 'test' }, 'bundle', 'exec', 'ruby', 'benchmark/comparison.rb')
|
|
30
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
module Generators
|
|
5
|
+
class InitializerGenerator < ::Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path('../templates', __dir__)
|
|
7
|
+
|
|
8
|
+
desc 'Creates MoneyAttribute initializer.'
|
|
9
|
+
|
|
10
|
+
def copy_initializer
|
|
11
|
+
copy_file 'money_attribute.rb', 'config/initializers/money_attribute.rb'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# encoding : utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
MoneyAttribute.configure do |config|
|
|
5
|
+
# Register a custom currency
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# config.added_currencies = [
|
|
9
|
+
# {currency: 'CRC', subunit: 2, symbol: '₡'},
|
|
10
|
+
# {currency: 'NGN', subunit: 3, symbol: '₦'}
|
|
11
|
+
# ]
|
|
12
|
+
config.added_currencies = [
|
|
13
|
+
{ currency: 'XCRC', subunit: 2, symbol: '₡' },
|
|
14
|
+
{ currency: 'XNGN', subunit: 3, symbol: '₦' }
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# To set the default currency
|
|
18
|
+
#
|
|
19
|
+
# It must be a registered currency
|
|
20
|
+
#
|
|
21
|
+
config.default_currency = 'BRL'
|
|
22
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :added_currencies, :default_currency
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@added_currencies = []
|
|
9
|
+
@default_currency = 'USD'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.config
|
|
14
|
+
@config ||= Configuration.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.configure
|
|
18
|
+
yield config if block_given?
|
|
19
|
+
@default_currency = nil
|
|
20
|
+
config
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.default_currency
|
|
24
|
+
@default_currency ||= ::Mint::Currency.resolve!(config.default_currency)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :nodoc
|
|
4
|
+
class Numeric
|
|
5
|
+
def to_money(currency = MoneyAttribute.default_currency)
|
|
6
|
+
::Mint.money(self, currency)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def dollars
|
|
10
|
+
::Mint.money(self, 'USD')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def euros
|
|
14
|
+
::Mint.money(self, 'EUR')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
alias dollar dollars
|
|
18
|
+
alias euro euros
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# :nodoc
|
|
22
|
+
class String
|
|
23
|
+
def to_money(currency = MoneyAttribute.default_currency)
|
|
24
|
+
::Mint.money(to_r, currency)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
module Macro
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
class_methods do
|
|
8
|
+
def money_attribute(name, currency: MoneyAttribute.default_currency, mapping: nil)
|
|
9
|
+
columns = attribute_names
|
|
10
|
+
currency = ::Mint::Currency.resolve!(currency)
|
|
11
|
+
name = name.to_s
|
|
12
|
+
parser = Parser.new(currency)
|
|
13
|
+
resolved_mapping = mapping || resolve_mapping(name, columns)
|
|
14
|
+
|
|
15
|
+
if columns.include?(name) && resolved_mapping.nil?
|
|
16
|
+
define_single_column_money_attribute(name, currency, parser)
|
|
17
|
+
else
|
|
18
|
+
define_composite_money_attribute(name, resolved_mapping, parser)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# --- Preparation (no side effects) ---
|
|
25
|
+
|
|
26
|
+
def resolve_mapping(name, columns)
|
|
27
|
+
return nil unless columns.include?(name)
|
|
28
|
+
|
|
29
|
+
if columns.include?("#{name}_currency")
|
|
30
|
+
{ amount: name, currency: :"#{name}_currency" }
|
|
31
|
+
elsif columns.include?('currency') && name == 'amount'
|
|
32
|
+
{ amount: name, currency: :currency }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def resolve_composite_for(name, mapping:)
|
|
37
|
+
composite = { amount: "#{name}_amount", currency: "#{name}_currency" }
|
|
38
|
+
|
|
39
|
+
composite[:amount] = mapping[:amount].to_s if mapping&.key?(:amount)
|
|
40
|
+
composite[:currency] = mapping[:currency].to_s if mapping&.key?(:currency)
|
|
41
|
+
|
|
42
|
+
assert_columns_exist!(name, composite)
|
|
43
|
+
composite
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def assert_columns_exist!(name, composite)
|
|
47
|
+
missing = composite.values - attribute_names
|
|
48
|
+
return if missing.empty?
|
|
49
|
+
|
|
50
|
+
raise ArgumentError,
|
|
51
|
+
"Could not find columns for :#{name} money attribute. " \
|
|
52
|
+
"Expected: #{composite.values.join(', ')}, " \
|
|
53
|
+
"Found: #{attribute_names.join(', ')}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def amount_extractor_for(column_name)
|
|
57
|
+
integer_column?(column_name) ? :fractional : :to_d
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def money_constructor_for(amount_column) = integer_column?(amount_column) ? :from_fractional : :from
|
|
61
|
+
|
|
62
|
+
def integer_column?(column_name)
|
|
63
|
+
col = columns.find { |c| c.name == column_name }
|
|
64
|
+
%i[integer bigint].include?(col&.type)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# --- Configuration (registers types, normalizers, composed_of) ---
|
|
68
|
+
|
|
69
|
+
def define_single_column_money_attribute(name, currency, parser)
|
|
70
|
+
column_type = integer_column?(name) ? ActiveRecord::Type::Integer.new : ActiveRecord::Type::Decimal.new
|
|
71
|
+
attribute(name.to_sym, :money, currency:, column_type: column_type)
|
|
72
|
+
normalizes(name.to_sym, with: parser)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def define_composite_money_attribute(name, mapping, parser)
|
|
76
|
+
aggregated = resolve_composite_for(name, mapping:)
|
|
77
|
+
|
|
78
|
+
composed_of(name.to_sym, {
|
|
79
|
+
allow_nil: true,
|
|
80
|
+
class_name: 'Mint::Money',
|
|
81
|
+
constructor: money_constructor_for(aggregated[:amount]),
|
|
82
|
+
converter: parser,
|
|
83
|
+
mapping: {
|
|
84
|
+
aggregated[:amount] => amount_extractor_for(aggregated[:amount]),
|
|
85
|
+
aggregated[:currency] => :currency_code
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
module MigrationExtensions
|
|
5
|
+
module Helper
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def parse_money_args(accessor, options = {})
|
|
9
|
+
name = accessor.to_s
|
|
10
|
+
|
|
11
|
+
amount_col = options.key?(:amount) ? options[:amount].to_s : name
|
|
12
|
+
|
|
13
|
+
if options.key?(:currency)
|
|
14
|
+
currency_col = if options[:currency] == false
|
|
15
|
+
nil
|
|
16
|
+
else
|
|
17
|
+
options[:currency].to_s
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
stripped = name.end_with?('_amount') ? name.sub(/_amount$/, '') : name
|
|
21
|
+
currency_col = "#{stripped}_currency"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
col_type = options[:type] || :decimal
|
|
25
|
+
|
|
26
|
+
amount_opts = {}
|
|
27
|
+
amount_opts[:type] = col_type
|
|
28
|
+
|
|
29
|
+
if options.key?(:amount) && options[:amount].is_a?(Hash)
|
|
30
|
+
amount_opts[:null] = options[:amount][:null] if options[:amount].key?(:null)
|
|
31
|
+
amount_opts[:default] = options[:amount][:default] if options[:amount].key?(:default)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
currency_opts = {}
|
|
35
|
+
currency_opts[:limit] = options[:currency_limit] if options[:currency_limit]
|
|
36
|
+
|
|
37
|
+
if options[:currency].is_a?(Hash)
|
|
38
|
+
currency_opts[:null] = options[:currency][:null] if options[:currency].key?(:null)
|
|
39
|
+
currency_opts[:default] = options[:currency][:default] if options[:currency].key?(:default)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
[amount_col, currency_col, amount_opts, currency_opts]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helper'
|
|
4
|
+
|
|
5
|
+
module MoneyAttribute
|
|
6
|
+
module MigrationExtensions
|
|
7
|
+
module SchemaStatements
|
|
8
|
+
include Helper
|
|
9
|
+
|
|
10
|
+
def add_money(table_name, accessor, options = {})
|
|
11
|
+
amount_col, currency_col, amount_opts, currency_opts = parse_money_args(accessor, options)
|
|
12
|
+
|
|
13
|
+
add_column(table_name, amount_col, amount_opts[:type], **amount_opts.except(:type))
|
|
14
|
+
add_column(table_name, currency_col, :string, **currency_opts) if currency_col
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_money(table_name, accessor, options = {})
|
|
18
|
+
amount_col, currency_col, = parse_money_args(accessor, options)
|
|
19
|
+
|
|
20
|
+
remove_column(table_name, amount_col)
|
|
21
|
+
remove_column(table_name, currency_col) if currency_col
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helper'
|
|
4
|
+
|
|
5
|
+
module MoneyAttribute
|
|
6
|
+
module MigrationExtensions
|
|
7
|
+
module TableDefinition
|
|
8
|
+
include Helper
|
|
9
|
+
|
|
10
|
+
def money(accessor, options = {})
|
|
11
|
+
amount_col, currency_col, amount_opts, currency_opts = parse_money_args(accessor, options)
|
|
12
|
+
|
|
13
|
+
column(amount_col, amount_opts[:type], **amount_opts.except(:type))
|
|
14
|
+
column(currency_col, :string, **currency_opts) if currency_col
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_money(accessor, options = {})
|
|
18
|
+
amount_col, currency_col, = parse_money_args(accessor, options)
|
|
19
|
+
|
|
20
|
+
remove_column(amount_col)
|
|
21
|
+
remove_column(currency_col) if currency_col
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
class Parser
|
|
5
|
+
def initialize(currency = MoneyAttribute.default_currency)
|
|
6
|
+
@default_currency = currency
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def parse(amount, currency = @default_currency)
|
|
10
|
+
currency = ::Mint::Currency.resolve!(currency)
|
|
11
|
+
case amount
|
|
12
|
+
when NilClass then nil
|
|
13
|
+
when Numeric then ::Mint::Money.from(amount, currency)
|
|
14
|
+
when String then ::Mint::Money.from(amount.to_r, currency)
|
|
15
|
+
when ::Mint::Money
|
|
16
|
+
return amount if amount.currency == currency
|
|
17
|
+
|
|
18
|
+
raise TypeError, "Cannot automatically convert #{amount} to #{currency.code}"
|
|
19
|
+
else
|
|
20
|
+
::Mint.parse(amount, currency)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
alias call parse
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
class Railtie < ::Rails::Railtie
|
|
5
|
+
generators do
|
|
6
|
+
require 'generators/money_attribute/initializer_generator'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
config.after_initialize do
|
|
10
|
+
require 'money_attribute/migration_extensions/schema_statements'
|
|
11
|
+
require 'money_attribute/migration_extensions/table_definition'
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Migration.include(MoneyAttribute::MigrationExtensions::SchemaStatements)
|
|
14
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.include(MoneyAttribute::MigrationExtensions::TableDefinition)
|
|
15
|
+
ActiveRecord::ConnectionAdapters::Table.include(MoneyAttribute::MigrationExtensions::TableDefinition)
|
|
16
|
+
|
|
17
|
+
setup_locale_backend!
|
|
18
|
+
register_custom_currencies!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.setup_locale_backend!
|
|
22
|
+
::Mint.locale_backend = lambda {
|
|
23
|
+
fmt = I18n.t('number.currency.format', default: {})
|
|
24
|
+
translator = ->(s) { s&.gsub('%n', '%<amount>f')&.gsub('%u', '%<symbol>s') }
|
|
25
|
+
|
|
26
|
+
format = if fmt.key?(:positive) || fmt.key?(:negative) || fmt.key?(:zero)
|
|
27
|
+
{
|
|
28
|
+
positive: translator.call(fmt[:positive] || fmt[:format]),
|
|
29
|
+
negative: translator.call(fmt[:negative] || fmt[:format]),
|
|
30
|
+
zero: translator.call(fmt[:zero] || fmt[:format])
|
|
31
|
+
}
|
|
32
|
+
else
|
|
33
|
+
translator.call(fmt[:format])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
{ decimal: fmt[:separator], thousand: fmt[:delimiter], format: format }
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.register_custom_currencies!
|
|
41
|
+
Array(MoneyAttribute.config.added_currencies).each do |currency_data|
|
|
42
|
+
if currency_data.respond_to?(:values_at)
|
|
43
|
+
code = currency_data[:currency]
|
|
44
|
+
subunit = currency_data[:subunit]
|
|
45
|
+
symbol = currency_data[:symbol]
|
|
46
|
+
else
|
|
47
|
+
code, subunit, symbol = *currency_data
|
|
48
|
+
end
|
|
49
|
+
::Mint::Currency.register(code:, subunit:, symbol:)
|
|
50
|
+
rescue KeyError
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MoneyAttribute
|
|
4
|
+
# Type
|
|
5
|
+
class Type < ActiveRecord::Type::Value
|
|
6
|
+
def initialize(currency:, column_type: ActiveRecord::Type::Decimal.new)
|
|
7
|
+
@currency = currency
|
|
8
|
+
@column_type = column_type
|
|
9
|
+
super()
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def assert_valid_value(value)
|
|
13
|
+
case value
|
|
14
|
+
when NilClass, Numeric, String then return
|
|
15
|
+
when ::Mint::Money
|
|
16
|
+
return if value.currency == @currency
|
|
17
|
+
|
|
18
|
+
message = "'#{value.inspect}' has different currency. Only #{@currency.code} allowed."
|
|
19
|
+
else
|
|
20
|
+
message = "'#{value.inspect}' is not a valid type for the attribute."
|
|
21
|
+
end
|
|
22
|
+
raise ArgumentError, message
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def deserialize(value)
|
|
26
|
+
return nil unless value
|
|
27
|
+
|
|
28
|
+
if @column_type.is_a?(ActiveRecord::Type::Integer)
|
|
29
|
+
::Mint::Money.from_fractional(value, @currency)
|
|
30
|
+
else
|
|
31
|
+
::Mint::Money.from(value, @currency)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def serialize(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
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.type
|
|
46
|
+
:money
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
ActiveSupport.on_load(:active_record) do
|
|
52
|
+
include MoneyAttribute::Macro
|
|
53
|
+
|
|
54
|
+
ActiveRecord::Type.register(:money, MoneyAttribute::Type)
|
|
55
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'minting'
|
|
4
|
+
require 'money_attribute/core_ext'
|
|
5
|
+
require 'money_attribute/configuration'
|
|
6
|
+
require 'money_attribute/macro'
|
|
7
|
+
require 'money_attribute/parser'
|
|
8
|
+
require 'money_attribute/type'
|
|
9
|
+
require 'money_attribute/railtie'
|
|
10
|
+
require 'money_attribute/version'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
metadata
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: money_attribute
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.10.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Gilson Ferraz
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minting
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 1.8.1
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 1.8.1
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rails
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 7.1.3.2
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 7.1.3.2
|
|
40
|
+
description: ''
|
|
41
|
+
email:
|
|
42
|
+
- gilson@cesar.etc.br
|
|
43
|
+
executables: []
|
|
44
|
+
extensions: []
|
|
45
|
+
extra_rdoc_files: []
|
|
46
|
+
files:
|
|
47
|
+
- MIT-LICENSE
|
|
48
|
+
- README.md
|
|
49
|
+
- Rakefile
|
|
50
|
+
- lib/generators/money_attribute/initializer_generator.rb
|
|
51
|
+
- lib/generators/templates/money_attribute.rb
|
|
52
|
+
- lib/money_attribute.rb
|
|
53
|
+
- lib/money_attribute/configuration.rb
|
|
54
|
+
- lib/money_attribute/core_ext.rb
|
|
55
|
+
- lib/money_attribute/macro.rb
|
|
56
|
+
- lib/money_attribute/migration_extensions/helper.rb
|
|
57
|
+
- lib/money_attribute/migration_extensions/schema_statements.rb
|
|
58
|
+
- lib/money_attribute/migration_extensions/table_definition.rb
|
|
59
|
+
- lib/money_attribute/parser.rb
|
|
60
|
+
- lib/money_attribute/railtie.rb
|
|
61
|
+
- lib/money_attribute/type.rb
|
|
62
|
+
- lib/money_attribute/version.rb
|
|
63
|
+
- lib/tasks/money_attribute.rake
|
|
64
|
+
homepage: https://github.com/gferraz/money-attribute
|
|
65
|
+
licenses:
|
|
66
|
+
- MIT
|
|
67
|
+
metadata:
|
|
68
|
+
allowed_push_host: https://rubygems.org
|
|
69
|
+
homepage_uri: https://github.com/gferraz/money-attribute
|
|
70
|
+
source_code_uri: https://github.com/gferraz/money-attribute
|
|
71
|
+
changelog_uri: https://github.com/gferraz/money-attribute/releases
|
|
72
|
+
bug_tracker_uri: https://github.com/gferraz/money-attribute/issues
|
|
73
|
+
rubygems_mfa_required: 'true'
|
|
74
|
+
rdoc_options: []
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.3'
|
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
requirements: []
|
|
88
|
+
rubygems_version: 4.0.10
|
|
89
|
+
specification_version: 4
|
|
90
|
+
summary: Money attributes for ActiveRecord
|
|
91
|
+
test_files: []
|