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.
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MoneyAttribute
4
- # :nodoc:
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
- def extract_pick_value(row, spec, cursor)
25
- return [row[cursor], cursor + 1] if spec.single?
26
-
27
- [spec.build_money(row[cursor], row[cursor + 1]), cursor + 2]
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
- # :nodoc:
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
- cursor = 0
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
- # :nodoc:
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.amount_col) if spec.single?
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
- specs.map do |spec|
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
- # :nodoc:
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.currency_col).sum(spec.amount_col)
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.amount_col)
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 +Mint::Money+ objects, numeric values (decimal columns), Ranges, or Arrays.
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
- # Offer.where_amount(price: 10.dollars..100.dollars)
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 the attribute is not a registered money attribute
46
- def where_amount(conditions)
47
- scope = all
48
- conditions.each { |attr, value| scope = scope.resolve_amount_condition(attr, value) }
49
- scope
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
- # :nodoc:
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)
@@ -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 && Money.from(value, MoneyAttribute.default_currency)
53
+ def deserialize(value) = value&.to_money(MoneyAttribute.default_currency)
54
54
 
55
55
  # Serializes a +Mint::Money+ value into a decimal.
56
56
  #
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MoneyAttribute
4
- VERSION = '1.2.0'
4
+ # The current gem version, following semantic versioning.
5
+ VERSION = '1.2.1'
5
6
  end
@@ -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.0
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.0'
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.0'
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.17
103
+ rubygems_version: 4.0.16
103
104
  specification_version: 4
104
105
  summary: Money attributes for ActiveRecord
105
106
  test_files: []