money_attribute 1.2.0 → 1.2.1
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 +29 -26
- data/Rakefile +19 -6
- data/lib/generators/money_attribute/initializer_generator.rb +10 -0
- data/lib/money_attribute/attribute_spec.rb +9 -3
- data/lib/money_attribute/attribute_spec_registry.rb +33 -5
- data/lib/money_attribute/column_type_validations.rb +47 -0
- data/lib/money_attribute/converter.rb +17 -2
- data/lib/money_attribute/core_ext/numeric.rb +10 -1
- data/lib/money_attribute/core_ext/string.rb +11 -1
- data/lib/money_attribute/form_builder_extension.rb +48 -3
- data/lib/money_attribute/macro.rb +63 -6
- data/lib/money_attribute/migration_extensions/helper.rb +61 -1
- data/lib/money_attribute/migration_extensions/schema_statements.rb +89 -9
- data/lib/money_attribute/migration_extensions/table_definition.rb +68 -11
- data/lib/money_attribute/money_amount.rb +30 -2
- data/lib/money_attribute/query/amount_condition.rb +161 -2
- data/lib/money_attribute/query/amount_order.rb +9 -3
- data/lib/money_attribute/query/currency_condition.rb +8 -2
- data/lib/money_attribute/query/helpers.rb +28 -5
- data/lib/money_attribute/query/pick.rb +11 -8
- data/lib/money_attribute/query/pluck.rb +11 -15
- data/lib/money_attribute/query/sum.rb +15 -3
- data/lib/money_attribute/query.rb +16 -11
- data/lib/money_attribute/railtie.rb +40 -0
- data/lib/money_attribute/type.rb +1 -1
- data/lib/money_attribute/version.rb +2 -1
- data/lib/money_attribute.rb +1 -0
- metadata +5 -4
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MoneyAttribute
|
|
4
|
-
#
|
|
4
|
+
# Internal helpers shared by the query sub-modules.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
5
7
|
module QueryHelpers
|
|
6
8
|
# Returns the registered money attribute spec or raises when missing.
|
|
7
9
|
#
|
|
8
10
|
# @param attr [Symbol, String] the money attribute name
|
|
9
11
|
# @return [AttributeSpec]
|
|
10
12
|
# @raise [ArgumentError] if the attribute is not registered
|
|
13
|
+
# @api private
|
|
11
14
|
def money_attribute_spec!(attr)
|
|
12
15
|
spec = klass.money_attribute_spec(attr)
|
|
13
16
|
raise ArgumentError, "#{attr} is not a money attribute on #{klass.name}" unless spec
|
|
@@ -15,16 +18,36 @@ module MoneyAttribute
|
|
|
15
18
|
spec
|
|
16
19
|
end
|
|
17
20
|
|
|
21
|
+
# Extracts money values for all specs from a single result row.
|
|
22
|
+
#
|
|
23
|
+
# @param row [Array] the flat row from +pluck+ or +pick+
|
|
24
|
+
# @param specs [Array<AttributeSpec>] the money attribute specs
|
|
25
|
+
# @return [Array] the extracted money values
|
|
26
|
+
# @api private
|
|
27
|
+
def extract_money_row(row, specs)
|
|
28
|
+
cursor = 0
|
|
29
|
+
|
|
30
|
+
specs.map do |spec|
|
|
31
|
+
value, cursor = extract_attribute_value(row, spec, cursor)
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
18
38
|
# Extracts a single value from a flat row at the given cursor position.
|
|
19
39
|
#
|
|
20
40
|
# @param row [Array] the flat row from +pluck+ or +pick+
|
|
21
41
|
# @param spec [AttributeSpec] the money attribute spec
|
|
22
42
|
# @param cursor [Integer] current position in the row array
|
|
23
43
|
# @return [Array(Object, Integer)] the extracted value and updated cursor
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
# @api private
|
|
45
|
+
def extract_attribute_value(row, spec, cursor)
|
|
46
|
+
if spec.single?
|
|
47
|
+
[row[cursor], cursor + 1]
|
|
48
|
+
else
|
|
49
|
+
[spec.build_money(row[cursor], row[cursor + 1]), cursor + 2]
|
|
50
|
+
end
|
|
28
51
|
end
|
|
29
52
|
end
|
|
30
53
|
end
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MoneyAttribute
|
|
4
|
-
#
|
|
4
|
+
# Internal pick resolution for the +pick_amount+ query helper.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
5
7
|
module PickAmount
|
|
6
8
|
# Picks money-aware amounts for one or more attributes.
|
|
7
9
|
#
|
|
8
10
|
# @param attrs [Array<Symbol>] one or more registered money attribute names
|
|
9
11
|
# @return [Mint::Money, Array, nil] Money for a single attribute, row array for multiple, nil if empty
|
|
10
12
|
# @raise [ArgumentError] if any attribute is not a registered money attribute
|
|
13
|
+
# @api private
|
|
11
14
|
def pick_amount(*attrs)
|
|
12
15
|
raise ArgumentError, 'No attribute specified' if attrs.empty?
|
|
13
16
|
|
|
@@ -17,22 +20,22 @@ module MoneyAttribute
|
|
|
17
20
|
raw = pick(*specs.flat_map(&:columns))
|
|
18
21
|
return unless raw
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
specs.map do |spec|
|
|
22
|
-
value, cursor = extract_pick_value(raw, spec, cursor)
|
|
23
|
-
value
|
|
24
|
-
end
|
|
23
|
+
extract_money_row(raw, specs)
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
private
|
|
28
27
|
|
|
29
28
|
# Picks a single money-aware attribute and returns a single value.
|
|
29
|
+
#
|
|
30
|
+
# @param spec [AttributeSpec] the money attribute spec
|
|
31
|
+
# @return [Mint::Money, Object, nil] the composed Money, the raw value for
|
|
32
|
+
# single-column attributes, or nil when the relation is empty
|
|
33
|
+
# @api private
|
|
30
34
|
def pick_single_amount(spec)
|
|
31
35
|
raw = pick(*spec.columns)
|
|
32
36
|
return unless raw
|
|
33
|
-
return raw if spec.single?
|
|
34
37
|
|
|
35
|
-
spec.build_money(raw[0], raw[1])
|
|
38
|
+
spec.single? ? raw : spec.build_money(raw[0], raw[1])
|
|
36
39
|
end
|
|
37
40
|
end
|
|
38
41
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MoneyAttribute
|
|
4
|
-
#
|
|
4
|
+
# Internal pluck resolution for the +pluck_amount+ query helper.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
5
7
|
module PluckAmount
|
|
6
8
|
# Plucks money-aware amounts for one or more attributes.
|
|
7
9
|
#
|
|
@@ -9,6 +11,7 @@ module MoneyAttribute
|
|
|
9
11
|
# @return [Array<Mint::Money>] for a single attribute
|
|
10
12
|
# @return [Array<Array>] for multiple attributes, one row array per attribute
|
|
11
13
|
# @raise [ArgumentError] if any attribute is not a registered money attribute
|
|
14
|
+
# @api private
|
|
12
15
|
def pluck_amount(*attrs)
|
|
13
16
|
raise ArgumentError, 'No attribute specified' if attrs.empty?
|
|
14
17
|
|
|
@@ -22,22 +25,15 @@ module MoneyAttribute
|
|
|
22
25
|
private
|
|
23
26
|
|
|
24
27
|
# Plucks a single money-aware attribute and returns money values.
|
|
28
|
+
#
|
|
29
|
+
# @param spec [AttributeSpec] the money attribute spec
|
|
30
|
+
# @return [Array] raw values for single-column attributes, composed
|
|
31
|
+
# +Mint::Money+ values for composite attributes
|
|
32
|
+
# @api private
|
|
25
33
|
def pluck_single_amount(spec)
|
|
26
|
-
return pluck(spec.
|
|
27
|
-
|
|
28
|
-
pluck(spec.amount_col, spec.currency_col).map do |amount, currency|
|
|
29
|
-
spec.build_money(amount, currency)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Rebuilds a result row for multi-attribute plucks.
|
|
34
|
-
def extract_money_row(row, specs)
|
|
35
|
-
cursor = 0
|
|
34
|
+
return pluck(spec.amount_column) if spec.single?
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
value, cursor = extract_pick_value(row, spec, cursor)
|
|
39
|
-
value
|
|
40
|
-
end
|
|
36
|
+
pluck(spec.amount_column, spec.currency_column).map { |amount, currency| spec.build_money(amount, currency) }
|
|
41
37
|
end
|
|
42
38
|
end
|
|
43
39
|
end
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MoneyAttribute
|
|
4
|
-
#
|
|
4
|
+
# Internal sum resolution for the +sum_amount+ query helper.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
5
7
|
module SumAmount
|
|
6
8
|
# Sums money-aware amounts for a single attribute.
|
|
7
9
|
#
|
|
8
10
|
# @param attr [Symbol] a registered money attribute name
|
|
9
11
|
# @return [Array<Mint::Money>] one Money per currency (or one for single-column attributes)
|
|
10
12
|
# @raise [ArgumentError] if the attribute is not a registered money attribute
|
|
13
|
+
# @api private
|
|
11
14
|
def sum_amount(attr)
|
|
12
15
|
raise ArgumentError, 'No attribute specified' if attr.nil?
|
|
13
16
|
|
|
@@ -22,8 +25,13 @@ module MoneyAttribute
|
|
|
22
25
|
private
|
|
23
26
|
|
|
24
27
|
# Sums a composite attribute grouped by currency.
|
|
28
|
+
#
|
|
29
|
+
# @param spec [AttributeSpec] the money attribute spec
|
|
30
|
+
# @return [Array<Mint::Money>] one +Mint::Money+ per currency, sorted by
|
|
31
|
+
# currency code, or a single zero-value Money when no rows match
|
|
32
|
+
# @api private
|
|
25
33
|
def resolve_composite_sum(spec)
|
|
26
|
-
totals = group(spec.
|
|
34
|
+
totals = group(spec.currency_column).sum(spec.amount_column)
|
|
27
35
|
return [spec.build_money(0, MoneyAttribute.default_currency)] if totals.empty?
|
|
28
36
|
|
|
29
37
|
totals.map { |code, amount| spec.build_money(amount, code) }
|
|
@@ -31,8 +39,12 @@ module MoneyAttribute
|
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
# Sums a fixed-currency single-column attribute.
|
|
42
|
+
#
|
|
43
|
+
# @param spec [AttributeSpec] the money attribute spec
|
|
44
|
+
# @return [Array<Mint::Money>] a single +Mint::Money+ in the default currency
|
|
45
|
+
# @api private
|
|
34
46
|
def resolve_single_sum(spec)
|
|
35
|
-
total = sum(spec.
|
|
47
|
+
total = sum(spec.amount_column)
|
|
36
48
|
|
|
37
49
|
[spec.build_money(total, MoneyAttribute.default_currency)]
|
|
38
50
|
end
|
|
@@ -35,18 +35,21 @@ module MoneyAttribute
|
|
|
35
35
|
|
|
36
36
|
# Filters by amount for one or more money attributes.
|
|
37
37
|
#
|
|
38
|
-
# Accepts
|
|
38
|
+
# Accepts a hash of conditions or a SQL string with +?+ placeholders.
|
|
39
|
+
# Hash: +{ attr: value }+, supports +Mint::Money+, +Range+, +Array+.
|
|
40
|
+
# SQL: only money attribute names, +and+, +or+, +not+, +is+, +null+.
|
|
39
41
|
#
|
|
40
|
-
#
|
|
41
|
-
# Offer.where_amount(total: [10, 20, 30])
|
|
42
|
-
#
|
|
43
|
-
# @param conditions [Hash{Symbol => Numeric, Range, Array}] attribute name to filter value
|
|
42
|
+
# @param args [Array] a condition hash, or a SQL string with bind values
|
|
44
43
|
# @return [ActiveRecord::Relation]
|
|
45
|
-
# @raise [ArgumentError] if
|
|
46
|
-
def where_amount(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
# @raise [ArgumentError] if an identifier is not a registered money attribute
|
|
45
|
+
def where_amount(*args)
|
|
46
|
+
if args.first.is_a?(Hash)
|
|
47
|
+
scope = all
|
|
48
|
+
args.first.each { |attr, value| scope = scope.resolve_amount_condition(attr, value) }
|
|
49
|
+
scope
|
|
50
|
+
else
|
|
51
|
+
all.resolve_amount_condition_from_sql(*args)
|
|
52
|
+
end
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
# Orders by amount for one or more money attributes.
|
|
@@ -101,7 +104,9 @@ module MoneyAttribute
|
|
|
101
104
|
end
|
|
102
105
|
end
|
|
103
106
|
|
|
104
|
-
#
|
|
107
|
+
# Internal mixin combining all query sub-modules onto relations.
|
|
108
|
+
#
|
|
109
|
+
# @api private
|
|
105
110
|
module QueryMethods
|
|
106
111
|
include QueryHelpers
|
|
107
112
|
include CurrencyCondition
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MoneyAttribute
|
|
4
|
+
# Rails engine integration.
|
|
5
|
+
#
|
|
6
|
+
# On boot, includes the migration and form-builder extensions into the
|
|
7
|
+
# corresponding Rails classes, wires Mint's locale backend to Rails i18n,
|
|
8
|
+
# and registers custom currencies declared in the initializer.
|
|
9
|
+
#
|
|
10
|
+
# @example Generated initializer
|
|
11
|
+
# MoneyAttribute.configure do |config|
|
|
12
|
+
# config.default_currency = 'BRL'
|
|
13
|
+
# config.added_currencies = [
|
|
14
|
+
# { currency: 'BTB', subunit: 8, symbol: '₿' }
|
|
15
|
+
# ]
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
4
19
|
class Railtie < ::Rails::Railtie
|
|
5
20
|
generators do
|
|
6
21
|
require 'generators/money_attribute/initializer_generator'
|
|
@@ -21,17 +36,27 @@ module MoneyAttribute
|
|
|
21
36
|
end
|
|
22
37
|
|
|
23
38
|
# Configures Mint to use the Rails locale currency format.
|
|
39
|
+
#
|
|
40
|
+
# @return [void]
|
|
41
|
+
# @api private
|
|
24
42
|
def self.setup_locale_backend!
|
|
25
43
|
::Mint.locale_backend = method(:build_locale_format).to_proc
|
|
26
44
|
end
|
|
27
45
|
|
|
28
46
|
# Builds the locale-aware currency formatting hash.
|
|
47
|
+
#
|
|
48
|
+
# @return [Hash] the +:decimal+, +:thousand+, and +:format+ keys for Mint
|
|
49
|
+
# @api private
|
|
29
50
|
def self.build_locale_format
|
|
30
51
|
fmt = I18n.t('number.currency.format', default: {})
|
|
31
52
|
{ decimal: fmt[:separator], thousand: fmt[:delimiter], format: build_format(fmt) }
|
|
32
53
|
end
|
|
33
54
|
|
|
34
55
|
# Builds the final currency format string or hash for Mint.
|
|
56
|
+
#
|
|
57
|
+
# @param fmt [Hash] the Rails currency format settings
|
|
58
|
+
# @return [String, Hash] a single format string or a per-sign format hash
|
|
59
|
+
# @api private
|
|
35
60
|
def self.build_format(fmt)
|
|
36
61
|
if %i[positive negative zero].any? { |k| fmt.key?(k) }
|
|
37
62
|
build_hash_format(fmt)
|
|
@@ -41,6 +66,10 @@ module MoneyAttribute
|
|
|
41
66
|
end
|
|
42
67
|
|
|
43
68
|
# Builds a per-sign currency format hash.
|
|
69
|
+
#
|
|
70
|
+
# @param fmt [Hash] the Rails currency format settings
|
|
71
|
+
# @return [Hash] the +:positive+, +:negative+, and +:zero+ format strings
|
|
72
|
+
# @api private
|
|
44
73
|
def self.build_hash_format(fmt)
|
|
45
74
|
{
|
|
46
75
|
positive: translate_format(fmt[:positive] || fmt[:format]),
|
|
@@ -50,11 +79,22 @@ module MoneyAttribute
|
|
|
50
79
|
end
|
|
51
80
|
|
|
52
81
|
# Translates Rails currency placeholders into Mint placeholders.
|
|
82
|
+
#
|
|
83
|
+
# @param str [String, nil] the Rails format string
|
|
84
|
+
# @return [String] the translated format string
|
|
85
|
+
# @api private
|
|
53
86
|
def self.translate_format(str)
|
|
54
87
|
str.to_s.gsub('%n', '%<amount>f').gsub('%u', '%<symbol>s')
|
|
55
88
|
end
|
|
56
89
|
|
|
57
90
|
# Registers custom currencies configured by the application.
|
|
91
|
+
#
|
|
92
|
+
# Accepts hashes with +:currency+, +:subunit+, +:symbol+ keys or the
|
|
93
|
+
# matching positional array form. Already-registered currencies are skipped.
|
|
94
|
+
#
|
|
95
|
+
# @return [void]
|
|
96
|
+
# @raise [ArgumentError] if a currency hash is missing a required key
|
|
97
|
+
# @api private
|
|
58
98
|
def self.register_custom_currencies!
|
|
59
99
|
Array(MoneyAttribute.config.added_currencies).each do |currency_data|
|
|
60
100
|
if currency_data.respond_to?(:values_at)
|
data/lib/money_attribute/type.rb
CHANGED
|
@@ -50,7 +50,7 @@ module MoneyAttribute
|
|
|
50
50
|
#
|
|
51
51
|
# @param value [BigDecimal, nil] the raw database value
|
|
52
52
|
# @return [Mint::Money, nil]
|
|
53
|
-
def deserialize(value) = value
|
|
53
|
+
def deserialize(value) = value&.to_money(MoneyAttribute.default_currency)
|
|
54
54
|
|
|
55
55
|
# Serializes a +Mint::Money+ value into a decimal.
|
|
56
56
|
#
|
data/lib/money_attribute.rb
CHANGED
|
@@ -15,6 +15,7 @@ require 'money_attribute/core_ext/numeric'
|
|
|
15
15
|
require 'money_attribute/core_ext/string'
|
|
16
16
|
require 'money_attribute/configuration'
|
|
17
17
|
require 'money_attribute/current'
|
|
18
|
+
require 'money_attribute/column_type_validations'
|
|
18
19
|
require 'money_attribute/attribute_spec'
|
|
19
20
|
require 'money_attribute/attribute_spec_registry'
|
|
20
21
|
require 'money_attribute/macro'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: money_attribute
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.1
|
|
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: '2.
|
|
18
|
+
version: '2.1'
|
|
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: '2.
|
|
25
|
+
version: '2.1'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: rails
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +52,7 @@ files:
|
|
|
52
52
|
- lib/money_attribute.rb
|
|
53
53
|
- lib/money_attribute/attribute_spec.rb
|
|
54
54
|
- lib/money_attribute/attribute_spec_registry.rb
|
|
55
|
+
- lib/money_attribute/column_type_validations.rb
|
|
55
56
|
- lib/money_attribute/configuration.rb
|
|
56
57
|
- lib/money_attribute/converter.rb
|
|
57
58
|
- lib/money_attribute/core_ext/numeric.rb
|
|
@@ -99,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
99
100
|
- !ruby/object:Gem::Version
|
|
100
101
|
version: '0'
|
|
101
102
|
requirements: []
|
|
102
|
-
rubygems_version: 4.0.
|
|
103
|
+
rubygems_version: 4.0.16
|
|
103
104
|
specification_version: 4
|
|
104
105
|
summary: Money attributes for ActiveRecord
|
|
105
106
|
test_files: []
|