whittaker_tech-midas 0.1.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/README.md +418 -0
- data/Rakefile +8 -0
- data/app/assets/config/whittaker_tech_midas_manifest.js +1 -0
- data/app/assets/stylesheets/whittaker_tech/midas/application.css +15 -0
- data/app/controllers/whittaker_tech/midas/application_controller.rb +6 -0
- data/app/helpers/whittaker_tech/midas/application_helper.rb +6 -0
- data/app/helpers/whittaker_tech/midas/form_helper.rb +78 -0
- data/app/javascript/controllers/index.js +1 -0
- data/app/javascript/controllers/midas_currency_controller.js +60 -0
- data/app/jobs/whittaker_tech/midas/application_job.rb +6 -0
- data/app/mailers/whittaker_tech/midas/application_mailer.rb +8 -0
- data/app/models/concerns/whittaker_tech/midas/bankable.rb +184 -0
- data/app/models/whittaker_tech/midas/application_record.rb +7 -0
- data/app/models/whittaker_tech/midas/coin.rb +81 -0
- data/app/views/layouts/whittaker_tech/midas/application.html.erb +15 -0
- data/app/views/layouts/whittaker_tech/midas/shared/_currency_field.html.erb +49 -0
- data/config/locales/midas.en.yml +58 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20251015160523_create_wt_midas_coins.rb +16 -0
- data/lib/tasks/whittaker_tech/midas_tasks.rake +4 -0
- data/lib/whittaker_tech/midas/engine.rb +117 -0
- data/lib/whittaker_tech/midas/version.rb +5 -0
- data/lib/whittaker_tech/midas.rb +11 -0
- metadata +212 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WhittakerTech
|
|
4
|
+
module Midas
|
|
5
|
+
# The Bankable module provides currency and monetary value management functionality
|
|
6
|
+
# for Active Record models. It allows models to have associated monetary values
|
|
7
|
+
# (coins) with different currencies and provides convenient methods for setting,
|
|
8
|
+
# retrieving, and formatting these values.
|
|
9
|
+
#
|
|
10
|
+
# When included in a model, Bankable automatically sets up a polymorphic association
|
|
11
|
+
# to the Midas::Coin model and provides class methods to define specific monetary
|
|
12
|
+
# attributes.
|
|
13
|
+
#
|
|
14
|
+
# @example Basic usage
|
|
15
|
+
# class Product < ApplicationRecord
|
|
16
|
+
# include WhittakerTech::Midas::Bankable
|
|
17
|
+
#
|
|
18
|
+
# has_coin :price
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # Create and set a price
|
|
22
|
+
# product = Product.create!
|
|
23
|
+
# product.set_price(amount: 29.99, currency_code: 'USD')
|
|
24
|
+
#
|
|
25
|
+
# # Access the price
|
|
26
|
+
# product.price # => Coin object
|
|
27
|
+
# product.price_amount # => Money object
|
|
28
|
+
# product.price_format # => "$29.99"
|
|
29
|
+
# product.price_in(:eur) # => "€26.85" (if exchange rates available)
|
|
30
|
+
#
|
|
31
|
+
# @example Multiple coins
|
|
32
|
+
# class Invoice < ApplicationRecord
|
|
33
|
+
# include WhittakerTech::Midas::Bankable
|
|
34
|
+
#
|
|
35
|
+
# has_coins :subtotal, :tax, :total
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# @example Custom dependency handling
|
|
39
|
+
# class Order < ApplicationRecord
|
|
40
|
+
# include WhittakerTech::Midas::Bankable
|
|
41
|
+
#
|
|
42
|
+
# has_coin :deposit, dependent: :nullify
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# == Associations Created
|
|
46
|
+
#
|
|
47
|
+
# When included, the module automatically creates:
|
|
48
|
+
# - `midas_coins`: A polymorphic has_many association to all Coin records
|
|
49
|
+
# associated with this model instance
|
|
50
|
+
#
|
|
51
|
+
# == Methods Created by has_coin
|
|
52
|
+
#
|
|
53
|
+
# For each coin defined with `has_coin :name`, the following methods are created:
|
|
54
|
+
#
|
|
55
|
+
# - `name`: Returns the associated Coin object
|
|
56
|
+
# - `name_amount`: Returns the Money object representing the amount
|
|
57
|
+
# - `name_format`: Returns a formatted string representation of the amount
|
|
58
|
+
# - `name_in(currency)`: Returns the amount converted to the specified currency
|
|
59
|
+
# - `set_name(amount:, currency_code:)`: Sets the coin value with the given amount and currency
|
|
60
|
+
#
|
|
61
|
+
# == Supported Amount Types
|
|
62
|
+
#
|
|
63
|
+
# The `set_*` methods accept amounts in various formats:
|
|
64
|
+
# - Money objects: Used directly for cents value
|
|
65
|
+
# - Integer: Treated as cents/minor currency units
|
|
66
|
+
# - Numeric: Converted to cents using currency-specific decimal places
|
|
67
|
+
#
|
|
68
|
+
# == Currency Configuration
|
|
69
|
+
#
|
|
70
|
+
# The module uses I18n for currency-specific configuration:
|
|
71
|
+
# - `midas.ui.currencies.{ISO_CODE}.decimal_count`: Decimal places for specific currency
|
|
72
|
+
# - `midas.ui.defaults.decimal_count`: Default decimal places (defaults to 2)
|
|
73
|
+
#
|
|
74
|
+
# == Thread Safety
|
|
75
|
+
#
|
|
76
|
+
# This module is designed to be thread-safe when used with Rails' standard
|
|
77
|
+
# Active Record patterns.
|
|
78
|
+
#
|
|
79
|
+
# @see WhittakerTech::Midas::Coin
|
|
80
|
+
# @since 0.1.0
|
|
81
|
+
module Bankable
|
|
82
|
+
extend ActiveSupport::Concern
|
|
83
|
+
|
|
84
|
+
included do
|
|
85
|
+
has_many :midas_coins,
|
|
86
|
+
as: :resource,
|
|
87
|
+
class_name: 'WhittakerTech::Midas::Coin',
|
|
88
|
+
dependent: :destroy
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class_methods do
|
|
92
|
+
# Defines multiple coin attributes at once.
|
|
93
|
+
#
|
|
94
|
+
# @param names [Array<Symbol>] The names of the coin attributes to define
|
|
95
|
+
# @param dependent [Symbol] The dependency behavior when the parent record is destroyed
|
|
96
|
+
# (:destroy, :delete_all, :nullify, :restrict_with_exception, :restrict_with_error)
|
|
97
|
+
#
|
|
98
|
+
# @example
|
|
99
|
+
# has_coins :price, :cost, :tax
|
|
100
|
+
# has_coins :deposit, :refund, dependent: :nullify
|
|
101
|
+
def has_coins(*names, dependent: :destroy)
|
|
102
|
+
names.each { |name| has_coin(name, dependent:) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Defines a single coin attribute with associated methods and database relationship.
|
|
106
|
+
#
|
|
107
|
+
# This method creates:
|
|
108
|
+
# - A has_one association to the Coin model
|
|
109
|
+
# - Getter and setter methods for the coin
|
|
110
|
+
# - Helper methods for amount access and formatting
|
|
111
|
+
#
|
|
112
|
+
# @param name [Symbol] The name of the coin attribute
|
|
113
|
+
# @param dependent [Symbol] The dependency behavior when the parent record is destroyed
|
|
114
|
+
#
|
|
115
|
+
# @example
|
|
116
|
+
# has_coin :price
|
|
117
|
+
# has_coin :refundable_deposit, dependent: :nullify
|
|
118
|
+
def has_coin(name, dependent: :destroy)
|
|
119
|
+
label = name.to_s
|
|
120
|
+
assoc_name = :"#{name}_coin"
|
|
121
|
+
|
|
122
|
+
has_one assoc_name,
|
|
123
|
+
-> { where(resource_label: label) },
|
|
124
|
+
as: :resource,
|
|
125
|
+
class_name: 'WhittakerTech::Midas::Coin',
|
|
126
|
+
dependent: dependent
|
|
127
|
+
|
|
128
|
+
define_methods(name, label, assoc_name)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def define_methods(name, label, assoc_name)
|
|
132
|
+
define_method(name) { public_send(assoc_name) }
|
|
133
|
+
|
|
134
|
+
define_method("#{name}_amount") { public_send(name)&.amount }
|
|
135
|
+
define_method("#{name}_format") { public_send(name)&.amount&.format }
|
|
136
|
+
define_method("#{name}_in") { |to| public_send(name)&.exchange_to(to)&.format }
|
|
137
|
+
|
|
138
|
+
# Sets the coin value with the specified amount and currency.
|
|
139
|
+
#
|
|
140
|
+
# @param amount [Money, Integer, Numeric] The amount to set
|
|
141
|
+
# @param currency_code [String, Symbol] The ISO currency code (e.g., 'USD', 'EUR')
|
|
142
|
+
# @return [Coin] The created or updated Coin object
|
|
143
|
+
# @raise [ArgumentError] If the amount type is not supported
|
|
144
|
+
#
|
|
145
|
+
# @example
|
|
146
|
+
# product.set_price(amount: 29.99, currency_code: 'USD')
|
|
147
|
+
# product.set_price(amount: Money.new(2999, 'USD'), currency_code: 'USD')
|
|
148
|
+
# product.set_price(amount: 2999, currency_code: 'USD') # 2999 cents
|
|
149
|
+
define_method("set_#{name}") do |amount:, currency_code:|
|
|
150
|
+
iso = currency_code.to_s.upcase
|
|
151
|
+
cents =
|
|
152
|
+
case amount
|
|
153
|
+
when Money then amount.cents
|
|
154
|
+
when Integer then amount
|
|
155
|
+
when Numeric then (BigDecimal(amount.to_s) * (10**decimals_for(iso))).round.to_i
|
|
156
|
+
else raise ArgumentError, "Invalid value for #{name}: #{amount.inspect}"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
coin = public_send(name) || public_send("build_#{assoc_name}", resource_label: label)
|
|
160
|
+
coin.currency_code = iso
|
|
161
|
+
coin.currency_minor = cents
|
|
162
|
+
coin.resource = self
|
|
163
|
+
coin.save!
|
|
164
|
+
|
|
165
|
+
coin
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
# Determines the number of decimal places for a given currency.
|
|
173
|
+
#
|
|
174
|
+
# @param iso [String] The ISO currency code
|
|
175
|
+
# @return [Integer] Number of decimal places (0-12)
|
|
176
|
+
def decimals_for(iso)
|
|
177
|
+
scope = 'midas.ui'
|
|
178
|
+
per = I18n.t("#{scope}.currencies.#{iso}", default: {})
|
|
179
|
+
default = I18n.t("#{scope}.defaults.decimal_count", default: 2)
|
|
180
|
+
(per['decimal_count'] || default).to_i.clamp(0, 12)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WhittakerTech
|
|
4
|
+
module Midas
|
|
5
|
+
class Coin < ApplicationRecord
|
|
6
|
+
self.table_name = 'wt_midas_coins'
|
|
7
|
+
|
|
8
|
+
belongs_to :resource, polymorphic: true
|
|
9
|
+
|
|
10
|
+
before_validation :normalize_fields
|
|
11
|
+
|
|
12
|
+
validates :resource_label,
|
|
13
|
+
presence: true,
|
|
14
|
+
format: { with: /\A[a-z0-9_]+\z/ },
|
|
15
|
+
length: { maximum: 64 },
|
|
16
|
+
uniqueness: { scope: %i[resource_type resource_id], case_sensitive: false }
|
|
17
|
+
|
|
18
|
+
validates :currency_code, presence: true, length: { is: 3 }
|
|
19
|
+
validates :currency_minor, presence: true, numericality: { only_integer: true }
|
|
20
|
+
|
|
21
|
+
# Returns a Money object representing the stored monetary value.
|
|
22
|
+
# Memoized for performance in hot paths.
|
|
23
|
+
def amount
|
|
24
|
+
@amount ||= Money.new(currency_minor, currency_code)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Sets the coin's monetary value from various input types.
|
|
28
|
+
def amount=(value)
|
|
29
|
+
case value
|
|
30
|
+
when Money
|
|
31
|
+
self.currency_minor = value.cents
|
|
32
|
+
self.currency_code = value.currency.iso_code
|
|
33
|
+
when Numeric
|
|
34
|
+
raise ArgumentError, 'currency_code required before setting numeric amount' if currency_code.blank?
|
|
35
|
+
|
|
36
|
+
self.currency_minor = Integer(value)
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError, "Invalid value for Coin#amount: #{value.inspect}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
delegate :exchange_to, to: :amount
|
|
43
|
+
|
|
44
|
+
def format(to: nil)
|
|
45
|
+
(to ? exchange_to(to) : amount).format
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Convenient aliases for form helpers
|
|
49
|
+
def minor
|
|
50
|
+
currency_minor
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def currency
|
|
54
|
+
currency_code
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def fractional
|
|
58
|
+
currency_minor
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Override setters to clear memoization
|
|
62
|
+
def currency_minor=(value)
|
|
63
|
+
@amount = nil
|
|
64
|
+
super
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def currency_code=(value)
|
|
68
|
+
@amount = nil
|
|
69
|
+
super
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Normalize inputs for consistency and case-insensitive uniqueness
|
|
75
|
+
def normalize_fields
|
|
76
|
+
self.resource_label = resource_label.to_s.strip.downcase.presence if resource_label
|
|
77
|
+
self.currency_code = currency_code.to_s.strip.upcase.presence if currency_code
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Whittaker tech midas</title>
|
|
5
|
+
<%= csrf_meta_tags %>
|
|
6
|
+
<%= csp_meta_tag %>
|
|
7
|
+
|
|
8
|
+
<%= stylesheet_link_tag "whittaker_tech/midas/application", media: "all" %>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
|
|
12
|
+
<%= yield %>
|
|
13
|
+
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<%
|
|
2
|
+
# Headless currency input field for Money-like objects.
|
|
3
|
+
#
|
|
4
|
+
# Locals:
|
|
5
|
+
# form: FormBuilder
|
|
6
|
+
# attribute: Symbol, e.g. :price
|
|
7
|
+
# value: Money object (responds to #fractional and #currency.iso_code)
|
|
8
|
+
# decimals: Integer (default: 2)
|
|
9
|
+
# input_html: Hash (optional)
|
|
10
|
+
# wrapper_html: Hash (optional)
|
|
11
|
+
# label_text: String (optional, defaults to humanized attribute)
|
|
12
|
+
# show_label: Boolean (default: false)
|
|
13
|
+
#
|
|
14
|
+
input_id = "#{form.object_name}_#{attribute}_display"
|
|
15
|
+
money_value = value || Money.new(0, "USD")
|
|
16
|
+
current_minor = money_value.fractional
|
|
17
|
+
currency_code = money_value.currency.iso_code
|
|
18
|
+
decimals ||= 2
|
|
19
|
+
input_html ||= {}
|
|
20
|
+
wrapper_html ||= {}
|
|
21
|
+
show_label ||= false
|
|
22
|
+
label_text = label_text.presence || attribute.to_s.humanize
|
|
23
|
+
%>
|
|
24
|
+
|
|
25
|
+
<div <%= tag.attributes(wrapper_html) %>
|
|
26
|
+
data-controller="midas-currency"
|
|
27
|
+
data-midas-currency-currency-value="<%= currency_code %>"
|
|
28
|
+
data-midas-currency-decimals-value="<%= decimals %>">
|
|
29
|
+
|
|
30
|
+
<%= form.label "#{attribute}_display",
|
|
31
|
+
label_text,
|
|
32
|
+
for: input_id,
|
|
33
|
+
class: (show_label ? nil : "sr-only") %>
|
|
34
|
+
|
|
35
|
+
<input type="text"
|
|
36
|
+
id="<%= input_id %>"
|
|
37
|
+
name="<%= form.object_name %>[<%= attribute %>_display]"
|
|
38
|
+
data-midas-currency-target="display"
|
|
39
|
+
data-action="beforeinput->midas-currency#input keydown->midas-currency#backspace keydown->midas-currency#preventDefault"
|
|
40
|
+
inputmode="decimal"
|
|
41
|
+
autocomplete="off"
|
|
42
|
+
<%= tag.attributes(input_html) %>>
|
|
43
|
+
|
|
44
|
+
<%= form.hidden_field "#{attribute}_minor",
|
|
45
|
+
value: current_minor,
|
|
46
|
+
data: { midas_currency_target: "hidden" } %>
|
|
47
|
+
|
|
48
|
+
<%= form.hidden_field "#{attribute}_currency", value: currency_code %>
|
|
49
|
+
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# WhittakerTech::Midas I18n Configuration
|
|
2
|
+
#
|
|
3
|
+
# This file defines currency-specific settings used by the Bankable concern
|
|
4
|
+
# for decimal precision when converting numeric values to minor units.
|
|
5
|
+
#
|
|
6
|
+
# Add more currencies as needed following the ISO 4217 standard.
|
|
7
|
+
|
|
8
|
+
en:
|
|
9
|
+
midas:
|
|
10
|
+
ui:
|
|
11
|
+
defaults:
|
|
12
|
+
decimal_count: 2
|
|
13
|
+
currencies:
|
|
14
|
+
# Major currencies
|
|
15
|
+
USD:
|
|
16
|
+
decimal_count: 2
|
|
17
|
+
symbol: "$"
|
|
18
|
+
symbol_position: left
|
|
19
|
+
EUR:
|
|
20
|
+
decimal_count: 2
|
|
21
|
+
symbol: "€"
|
|
22
|
+
symbol_position: left
|
|
23
|
+
GBP:
|
|
24
|
+
decimal_count: 2
|
|
25
|
+
symbol: "£"
|
|
26
|
+
symbol_position: left
|
|
27
|
+
JPY:
|
|
28
|
+
decimal_count: 0
|
|
29
|
+
symbol: "¥"
|
|
30
|
+
symbol_position: left
|
|
31
|
+
CNY:
|
|
32
|
+
decimal_count: 2
|
|
33
|
+
symbol: "¥"
|
|
34
|
+
symbol_position: left
|
|
35
|
+
|
|
36
|
+
# Additional currencies
|
|
37
|
+
CAD:
|
|
38
|
+
decimal_count: 2
|
|
39
|
+
symbol: "$"
|
|
40
|
+
symbol_position: left
|
|
41
|
+
AUD:
|
|
42
|
+
decimal_count: 2
|
|
43
|
+
symbol: "$"
|
|
44
|
+
symbol_position: left
|
|
45
|
+
CHF:
|
|
46
|
+
decimal_count: 2
|
|
47
|
+
symbol: "Fr"
|
|
48
|
+
symbol_position: left
|
|
49
|
+
INR:
|
|
50
|
+
decimal_count: 2
|
|
51
|
+
symbol: "₹"
|
|
52
|
+
symbol_position: left
|
|
53
|
+
|
|
54
|
+
# Cryptocurrency (if needed)
|
|
55
|
+
BTC:
|
|
56
|
+
decimal_count: 8
|
|
57
|
+
symbol: "₿"
|
|
58
|
+
symbol_position: left
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateWtMidasCoins < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :wt_midas_coins do |t|
|
|
4
|
+
t.references :resource, polymorphic: true, null: false, index: true
|
|
5
|
+
t.string :resource_label, null: false, limit: 64
|
|
6
|
+
t.string :currency_code, null: false, limit: 3
|
|
7
|
+
t.bigint :currency_minor, null: false
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
|
|
11
|
+
t.index %i[resource_id resource_type resource_label],
|
|
12
|
+
unique: true,
|
|
13
|
+
name: 'index_wt_midas_coins_on_owner_and_label'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WhittakerTech
|
|
4
|
+
module Midas
|
|
5
|
+
# The Midas Engine provides monetary value management capabilities as a Rails Engine.
|
|
6
|
+
# It integrates seamlessly with Rails applications to add currency handling, coin management,
|
|
7
|
+
# and monetary formatting functionality through the Bankable concern and related components.
|
|
8
|
+
#
|
|
9
|
+
# This engine follows Rails Engine conventions and can be mounted in any Rails application
|
|
10
|
+
# to provide comprehensive monetary value handling capabilities. It includes models, helpers,
|
|
11
|
+
# and utilities for working with currencies and monetary amounts.
|
|
12
|
+
#
|
|
13
|
+
# == Features
|
|
14
|
+
#
|
|
15
|
+
# - **Bankable Concern**: Allows any Active Record model to have monetary attributes
|
|
16
|
+
# - **Coin Model**: Stores monetary values with currency information
|
|
17
|
+
# - **Form Helpers**: View helpers for monetary input and display
|
|
18
|
+
# - **Currency Management**: Integration with the Money gem for currency operations
|
|
19
|
+
# - **Namespace Isolation**: Clean separation from host application code
|
|
20
|
+
#
|
|
21
|
+
# == Installation and Setup
|
|
22
|
+
#
|
|
23
|
+
# Add the engine to your Rails application's Gemfile and run bundle install.
|
|
24
|
+
# The engine will automatically configure itself when Rails boots.
|
|
25
|
+
#
|
|
26
|
+
# @example Adding to a Rails application
|
|
27
|
+
# # In your Gemfile
|
|
28
|
+
# gem 'whittaker_tech-midas'
|
|
29
|
+
#
|
|
30
|
+
# # The engine auto-configures on Rails boot
|
|
31
|
+
# # No additional setup required
|
|
32
|
+
#
|
|
33
|
+
# == Usage in Host Applications
|
|
34
|
+
#
|
|
35
|
+
# Once installed, the engine's functionality becomes available throughout
|
|
36
|
+
# your Rails application:
|
|
37
|
+
#
|
|
38
|
+
# @example Using Bankable in models
|
|
39
|
+
# class Product < ApplicationRecord
|
|
40
|
+
# include WhittakerTech::Midas::Bankable
|
|
41
|
+
# has_coin :price
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# @example Using form helpers in views
|
|
45
|
+
# <%= form_with model: @product do |form| %>
|
|
46
|
+
# <%= form.money_field :price %>
|
|
47
|
+
# <% end %>
|
|
48
|
+
#
|
|
49
|
+
# == Configuration
|
|
50
|
+
#
|
|
51
|
+
# The engine provides several configuration points:
|
|
52
|
+
#
|
|
53
|
+
# - **Eager Loading**: Automatically configures model loading paths
|
|
54
|
+
# - **Helper Integration**: Injects form helpers into ActionView
|
|
55
|
+
# - **Namespace Isolation**: Keeps engine code separate from host application
|
|
56
|
+
#
|
|
57
|
+
# == Directory Structure
|
|
58
|
+
#
|
|
59
|
+
# The engine follows standard Rails Engine structure:
|
|
60
|
+
# - `app/models/`: Coin model and concerns
|
|
61
|
+
# - `app/helpers/`: Form helpers for monetary inputs
|
|
62
|
+
# - `db/migrate/`: Database migrations for coin storage
|
|
63
|
+
# - `lib/`: Engine configuration and initialization
|
|
64
|
+
#
|
|
65
|
+
# == Database Integration
|
|
66
|
+
#
|
|
67
|
+
# The engine provides database migrations that create the necessary tables
|
|
68
|
+
# for storing monetary values. Run migrations after installation:
|
|
69
|
+
#
|
|
70
|
+
# @example Running migrations
|
|
71
|
+
# rails db:migrate
|
|
72
|
+
#
|
|
73
|
+
# == Namespace Isolation
|
|
74
|
+
#
|
|
75
|
+
# The engine uses `isolate_namespace` to prevent conflicts with host
|
|
76
|
+
# application code. All engine components are properly namespaced under
|
|
77
|
+
# `WhittakerTech::Midas`.
|
|
78
|
+
#
|
|
79
|
+
# == Helper Integration
|
|
80
|
+
#
|
|
81
|
+
# Form helpers are automatically made available in all views through
|
|
82
|
+
# an initializer that extends ActionView::Base when the view layer loads.
|
|
83
|
+
# This provides seamless integration without requiring manual includes.
|
|
84
|
+
#
|
|
85
|
+
# == Eager Loading
|
|
86
|
+
#
|
|
87
|
+
# The engine configures additional eager load paths to ensure all models
|
|
88
|
+
# are properly loaded in production environments. This includes the models
|
|
89
|
+
# directory which contains the Coin model and Bankable concern.
|
|
90
|
+
#
|
|
91
|
+
# == Thread Safety
|
|
92
|
+
#
|
|
93
|
+
# The engine follows Rails conventions for thread safety and can be safely
|
|
94
|
+
# used in multi-threaded environments like Puma or Falcon.
|
|
95
|
+
#
|
|
96
|
+
# == Development and Testing
|
|
97
|
+
#
|
|
98
|
+
# The engine can be developed and tested independently using its own
|
|
99
|
+
# test suite, or integrated into a host application for testing.
|
|
100
|
+
#
|
|
101
|
+
# @see WhittakerTech::Midas::Bankable
|
|
102
|
+
# @see WhittakerTech::Midas::Coin
|
|
103
|
+
# @see WhittakerTech::Midas::FormHelper
|
|
104
|
+
# @since 0.1.0
|
|
105
|
+
class Engine < ::Rails::Engine
|
|
106
|
+
isolate_namespace WhittakerTech::Midas
|
|
107
|
+
|
|
108
|
+
config.eager_load_paths += Dir["#{config.root}/app/models"]
|
|
109
|
+
|
|
110
|
+
initializer 'midas.helpers' do
|
|
111
|
+
ActiveSupport.on_load(:action_view) do
|
|
112
|
+
include WhittakerTech::Midas::FormHelper
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|