plutonium 0.14.0 → 0.15.0.pre.rc1
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 copy.md +1 -1
- data/README.md +1 -1
- data/app/assets/plutonium.css +1 -1
- data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
- data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
- data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
- data/app/views/resource/_resource_details.html.erb +1 -36
- data/app/views/resource/_resource_form.html.erb +1 -5
- data/app/views/resource/_resource_table.html.erb +315 -85
- data/app/views/resource/edit.html.erb +1 -5
- data/app/views/resource/index.html.erb +1 -5
- data/app/views/resource/new.html.erb +1 -5
- data/app/views/resource/show.html.erb +1 -5
- data/config/initializers/pagy.rb +1 -0
- data/config/initializers/rabl.rb +27 -20
- data/gemfiles/rails_7.gemfile.lock +5 -1
- data/lib/generators/pu/core/assets/assets_generator.rb +2 -2
- data/lib/generators/pu/core/install/install_generator.rb +0 -3
- data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
- data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +5 -2
- data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +19 -0
- data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
- data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
- data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
- data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
- data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
- data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
- data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
- data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
- data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
- data/lib/generators/pu/res/model/model_generator.rb +0 -7
- data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
- data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
- data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
- data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
- data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
- data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
- data/lib/plutonium/action/README.md +0 -0
- data/lib/plutonium/action/base.rb +103 -0
- data/lib/plutonium/action/interactive.rb +117 -0
- data/lib/plutonium/action/route_options.rb +65 -0
- data/lib/plutonium/action/simple.rb +8 -0
- data/lib/plutonium/auth.rb +1 -1
- data/lib/plutonium/configuration.rb +130 -0
- data/lib/plutonium/core/actions/collection.rb +1 -1
- data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
- data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/controller.rb +110 -0
- data/lib/plutonium/core/controllers/authorizable.rb +12 -35
- data/lib/plutonium/core/controllers/bootable.rb +38 -7
- data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
- data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
- data/lib/plutonium/core/ui/collection.rb +1 -1
- data/lib/plutonium/core/ui/detail.rb +1 -1
- data/lib/plutonium/core/ui/form.rb +1 -1
- data/lib/plutonium/definition/actions.rb +50 -0
- data/lib/plutonium/definition/base.rb +92 -0
- data/lib/plutonium/definition/config_attr.rb +30 -0
- data/lib/plutonium/definition/defineable_props.rb +96 -0
- data/lib/plutonium/definition/search.rb +21 -0
- data/lib/plutonium/engine/validator.rb +30 -0
- data/lib/plutonium/engine.rb +25 -0
- data/lib/plutonium/helpers/assets_helper.rb +73 -20
- data/lib/plutonium/helpers/form_helper.rb +1 -3
- data/lib/plutonium/interaction/README.md +369 -0
- data/lib/plutonium/interaction/base.rb +75 -0
- data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
- data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
- data/lib/plutonium/interaction/outcome.rb +129 -0
- data/lib/plutonium/interaction/response/base.rb +63 -0
- data/lib/plutonium/interaction/response/null.rb +33 -0
- data/lib/plutonium/interaction/response/redirect.rb +30 -0
- data/lib/plutonium/interaction/response/render.rb +28 -0
- data/lib/plutonium/lib/bit_flags.rb +70 -9
- data/lib/plutonium/lib/overlayed_hash.rb +86 -0
- data/lib/plutonium/lib/smart_cache.rb +171 -0
- data/lib/plutonium/models/has_cents.rb +170 -0
- data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
- data/lib/plutonium/{application → portal}/controller.rb +3 -11
- data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
- data/lib/plutonium/portal/engine.rb +15 -0
- data/lib/plutonium/railtie.rb +35 -15
- data/lib/plutonium/reloader.rb +71 -29
- data/lib/plutonium/resource/controller.rb +51 -34
- data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
- data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
- data/lib/plutonium/resource/controllers/defineable.rb +26 -0
- data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
- data/lib/plutonium/resource/controllers/presentable.rb +41 -0
- data/lib/plutonium/resource/controllers/queryable.rb +44 -0
- data/lib/plutonium/resource/definition.rb +6 -0
- data/lib/plutonium/resource/policy.rb +25 -13
- data/lib/plutonium/resource/query_object.rb +50 -51
- data/lib/plutonium/resource/record.rb +6 -89
- data/lib/plutonium/resource/register.rb +82 -0
- data/lib/plutonium/routing/mapper_extensions.rb +1 -1
- data/lib/plutonium/routing/resource_registration.rb +1 -1
- data/lib/plutonium/routing/route_set_extensions.rb +6 -18
- data/lib/plutonium/ui/action_button.rb +125 -0
- data/lib/plutonium/ui/breadcrumbs.rb +163 -0
- data/lib/plutonium/ui/component/base.rb +13 -0
- data/lib/plutonium/ui/component/behaviour.rb +38 -0
- data/lib/plutonium/ui/component/kit.rb +31 -0
- data/lib/plutonium/ui/component/methods.rb +54 -0
- data/lib/plutonium/ui/display/base.rb +25 -0
- data/lib/plutonium/ui/display/component/association.rb +26 -0
- data/lib/plutonium/ui/display/resource.rb +77 -0
- data/lib/plutonium/ui/display/theme.rb +27 -0
- data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
- data/lib/plutonium/ui/empty_card.rb +20 -0
- data/lib/plutonium/ui/form/base.rb +37 -0
- data/lib/plutonium/ui/form/resource.rb +75 -0
- data/lib/plutonium/ui/form/theme.rb +42 -0
- data/lib/plutonium/ui/page/base.rb +112 -0
- data/lib/plutonium/ui/page/edit.rb +23 -0
- data/lib/plutonium/ui/page/index.rb +27 -0
- data/lib/plutonium/ui/page/new.rb +23 -0
- data/lib/plutonium/ui/page/show.rb +27 -0
- data/lib/plutonium/ui/page_header.rb +49 -0
- data/lib/plutonium/ui/table/base.rb +13 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
- data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
- data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
- data/lib/plutonium/ui/table/display_theme.rb +21 -0
- data/lib/plutonium/ui/table/resource.rb +98 -0
- data/lib/plutonium/ui/table/theme.rb +35 -0
- data/lib/plutonium/ui.rb +9 -0
- data/lib/plutonium/version.rb +5 -1
- data/lib/plutonium.rb +53 -26
- data/package-lock.json +19 -22
- data/package.json +4 -4
- data/sig/.keep +0 -0
- data/src/css/plutonium.css +15 -0
- data/tailwind.options.js +11 -3
- metadata +220 -81
- data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
- data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
- data/lib/plutonium/concerns/resource_validatable.rb +0 -34
- data/lib/plutonium/config.rb +0 -9
- data/lib/plutonium/core/controllers/base.rb +0 -101
- data/lib/plutonium/core/controllers/presentable.rb +0 -65
- data/lib/plutonium/core/controllers/queryable.rb +0 -28
- data/lib/plutonium/pkg/app.rb +0 -35
- data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
- data/lib/plutonium/pkg/feature.rb +0 -18
- data/lib/plutonium/policy/initializer.rb +0 -22
- data/lib/plutonium/policy/scope.rb +0 -19
- data/lib/plutonium/pundit/context.rb +0 -18
- data/lib/plutonium/pundit/policy_finder.rb +0 -25
- data/lib/plutonium/resource/policy_context.rb +0 -5
- data/lib/plutonium/resource_register.rb +0 -83
- data/lib/plutonium/smart_cache.rb +0 -151
- data/sig/plutonium.rbs +0 -12
- /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
- /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Lib
|
|
5
|
+
# The SmartCache module provides flexible caching mechanisms for classes and objects,
|
|
6
|
+
# allowing for both inline caching and method-level memoization.
|
|
7
|
+
#
|
|
8
|
+
# This module is designed to optimize performance by caching results
|
|
9
|
+
# when class caching is enabled (typically in production),
|
|
10
|
+
# while ensuring fresh results when caching is disabled (typically in development).
|
|
11
|
+
#
|
|
12
|
+
# This implementation is thread-safe.
|
|
13
|
+
#
|
|
14
|
+
# @example Including SmartCache in a class
|
|
15
|
+
# class MyClass
|
|
16
|
+
# include Plutonium::Lib::SmartCache
|
|
17
|
+
#
|
|
18
|
+
# def my_method(arg)
|
|
19
|
+
# cache_unless_reloading("my_method_#{arg}") { expensive_operation(arg) }
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# def another_method(arg)
|
|
23
|
+
# # Method implementation
|
|
24
|
+
# end
|
|
25
|
+
# memoize_unless_reloading :another_method
|
|
26
|
+
# end
|
|
27
|
+
module SmartCache
|
|
28
|
+
extend ::ActiveSupport::Concern
|
|
29
|
+
|
|
30
|
+
# Class-level thread-local storage for force caching (useful for testing)
|
|
31
|
+
thread_mattr_accessor :force_caching
|
|
32
|
+
|
|
33
|
+
included do
|
|
34
|
+
mattr_accessor :_smart_cache, instance_writer: false, default: Concurrent::Map.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Caches the result of the given block unless class caching is disabled.
|
|
40
|
+
#
|
|
41
|
+
# @param cache_key [String] A unique key to identify the cached result
|
|
42
|
+
# @yield The block whose result will be cached
|
|
43
|
+
# @return [Object] The result of the block, either freshly computed or from cache
|
|
44
|
+
#
|
|
45
|
+
# @example Using cache_unless_reloading inline
|
|
46
|
+
# def fetch_user_data(user_id)
|
|
47
|
+
# cache_unless_reloading("user_data_#{user_id}") do
|
|
48
|
+
# UserDataService.fetch(user_id)
|
|
49
|
+
# end
|
|
50
|
+
# end
|
|
51
|
+
def cache_unless_reloading(cache_key, &block)
|
|
52
|
+
return yield unless should_smart_cache?
|
|
53
|
+
|
|
54
|
+
self.class._smart_cache.compute_if_absent(cache_key.to_sym) { yield }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Flushes the smart cache for the specified keys or all keys if none are specified.
|
|
58
|
+
#
|
|
59
|
+
# @param keys [Array<Symbol, String>, Symbol, String] The cache key(s) to flush
|
|
60
|
+
# @return [void]
|
|
61
|
+
#
|
|
62
|
+
# @example Flushing specific cache keys
|
|
63
|
+
# flush_smart_cache([:user_data, :product_list])
|
|
64
|
+
#
|
|
65
|
+
# @example Flushing all cache keys
|
|
66
|
+
# flush_smart_cache
|
|
67
|
+
def flush_smart_cache(keys = nil)
|
|
68
|
+
self.class.flush_smart_cache(keys)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Determines whether caching should be performed based on the current Rails configuration
|
|
72
|
+
# or the force_caching flag.
|
|
73
|
+
#
|
|
74
|
+
# @return [Boolean] true if caching should be performed, false otherwise
|
|
75
|
+
def should_smart_cache?
|
|
76
|
+
Plutonium::Lib::SmartCache.force_caching.nil? ? Rails.application.config.cache_classes : Plutonium::Lib::SmartCache.force_caching
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class_methods do
|
|
80
|
+
# Memoizes the result of the specified method unless class caching is disabled.
|
|
81
|
+
#
|
|
82
|
+
# @param method_name [Symbol] The name of the method to memoize
|
|
83
|
+
# @return [void]
|
|
84
|
+
#
|
|
85
|
+
# @example Memoizing a method
|
|
86
|
+
# class User
|
|
87
|
+
# include Plutonium::Lib::SmartCache
|
|
88
|
+
#
|
|
89
|
+
# def expensive_full_name_calculation
|
|
90
|
+
# # Complex name calculation
|
|
91
|
+
# end
|
|
92
|
+
# memoize_unless_reloading :expensive_full_name_calculation
|
|
93
|
+
# end
|
|
94
|
+
def memoize_unless_reloading(method_name)
|
|
95
|
+
return if method_defined?(:"#{method_name}_without_memoization")
|
|
96
|
+
|
|
97
|
+
original_method = instance_method(method_name)
|
|
98
|
+
alias_method :"#{method_name}_without_memoization", method_name
|
|
99
|
+
|
|
100
|
+
define_method(method_name) do |*args|
|
|
101
|
+
if should_smart_cache?
|
|
102
|
+
cache = self.class._smart_cache.compute_if_absent(method_name.to_sym) { Concurrent::Map.new }
|
|
103
|
+
if args.empty?
|
|
104
|
+
cache.compute_if_absent(:no_args) { original_method.bind_call(self) }
|
|
105
|
+
else
|
|
106
|
+
cache.compute_if_absent(args.hash.to_s) { original_method.bind_call(self, *args) }
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
original_method.bind_call(self, *args)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Flushes the smart cache for the specified keys or all keys if none are specified.
|
|
115
|
+
#
|
|
116
|
+
# @param keys [Array<Symbol, String>, Symbol, String] The cache key(s) to flush
|
|
117
|
+
# @return [void]
|
|
118
|
+
#
|
|
119
|
+
# @example Flushing specific cache keys
|
|
120
|
+
# flush_smart_cache([:user_data, :product_list])
|
|
121
|
+
#
|
|
122
|
+
# @example Flushing all cache keys
|
|
123
|
+
# flush_smart_cache
|
|
124
|
+
def flush_smart_cache(keys = nil)
|
|
125
|
+
keys = Array(keys).map(&:to_sym)
|
|
126
|
+
if keys.present?
|
|
127
|
+
keys.each { |key| _smart_cache.delete(key) }
|
|
128
|
+
else
|
|
129
|
+
_smart_cache.clear
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Configuration:
|
|
136
|
+
# The caching behavior is controlled by the Rails configuration option config.cache_classes
|
|
137
|
+
# or can be overridden using the SmartCache.force_caching flag:
|
|
138
|
+
#
|
|
139
|
+
# - When false (typical in development):
|
|
140
|
+
# - Classes are reloaded on each request.
|
|
141
|
+
# - cache_unless_reloading always yields fresh results.
|
|
142
|
+
# - memoize_unless_reloading always calls the original method.
|
|
143
|
+
#
|
|
144
|
+
# - When true (typical in production):
|
|
145
|
+
# - Classes are cached.
|
|
146
|
+
# - cache_unless_reloading uses cached results.
|
|
147
|
+
# - memoize_unless_reloading uses memoized results, caching for each unique set of arguments.
|
|
148
|
+
#
|
|
149
|
+
# Best Practices:
|
|
150
|
+
# - Use meaningful and unique cache keys to avoid collisions.
|
|
151
|
+
# - Be mindful of memory usage, especially with large cached results.
|
|
152
|
+
# - Consider cache expiration strategies for long-running processes.
|
|
153
|
+
# - Use cache_unless_reloading for fine-grained control within methods.
|
|
154
|
+
# - Use memoize_unless_reloading for entire methods, especially those with expensive computations.
|
|
155
|
+
#
|
|
156
|
+
# Thread Safety:
|
|
157
|
+
# - This implementation is thread-safe.
|
|
158
|
+
# - It uses Concurrent::Map from the concurrent-ruby gem for thread-safe caching.
|
|
159
|
+
#
|
|
160
|
+
# Testing:
|
|
161
|
+
# - In your test environment, you can control caching behavior explicitly using SmartCache.force_caching.
|
|
162
|
+
# - Example:
|
|
163
|
+
# describe "MyClass" do
|
|
164
|
+
# before { Plutonium::Lib::SmartCache.force_caching = true }
|
|
165
|
+
# after { Plutonium::Lib::SmartCache.force_caching = nil }
|
|
166
|
+
# it "caches results" do
|
|
167
|
+
# # Test caching behavior
|
|
168
|
+
# end
|
|
169
|
+
# end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Models
|
|
5
|
+
# HasCents module provides functionality to handle monetary values stored as cents
|
|
6
|
+
# and expose them as decimal values. It also ensures that validations applied to
|
|
7
|
+
# the cents attribute are inherited by the decimal attribute.
|
|
8
|
+
#
|
|
9
|
+
# @example Usage
|
|
10
|
+
# class Product < ApplicationRecord
|
|
11
|
+
# include Plutonium::Models::HasCents
|
|
12
|
+
#
|
|
13
|
+
# has_cents :price_cents
|
|
14
|
+
# has_cents :cost_cents, name: :wholesale_price, rate: 1000
|
|
15
|
+
# has_cents :quantity_cents, name: :quantity, rate: 1
|
|
16
|
+
# has_cents :total_cents, suffix: "value"
|
|
17
|
+
#
|
|
18
|
+
# validates :price_cents, numericality: { greater_than_or_equal_to: 0 }
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Basic Usage
|
|
22
|
+
# product = Product.new(price: 10.99)
|
|
23
|
+
#
|
|
24
|
+
# product.price_cents #=> 1099
|
|
25
|
+
# product.price #=> 10.99
|
|
26
|
+
#
|
|
27
|
+
# product.wholesale_price = 5.5
|
|
28
|
+
# product.cost_cents #=> 5500
|
|
29
|
+
#
|
|
30
|
+
# product.quantity = 3
|
|
31
|
+
# product.quantity_cents #=> 3
|
|
32
|
+
#
|
|
33
|
+
# @example Truncation
|
|
34
|
+
# product.price = 10.991
|
|
35
|
+
# product.price_cents #=> 1099
|
|
36
|
+
#
|
|
37
|
+
# product.price = 10.995
|
|
38
|
+
# product.price_cents #=> 1099
|
|
39
|
+
#
|
|
40
|
+
# product.price = 10.999
|
|
41
|
+
# product.price_cents #=> 1099
|
|
42
|
+
#
|
|
43
|
+
# product.total_value = 100.50
|
|
44
|
+
# product.total_cents #=> 10050
|
|
45
|
+
#
|
|
46
|
+
# @example Validation Inheritance
|
|
47
|
+
# product = Product.new(price: -10.99)
|
|
48
|
+
# product.valid? #=> false
|
|
49
|
+
# product.errors[:price_cents] #=> ["must be greater than or equal to 0"]
|
|
50
|
+
# product.errors[:price] #=> ["is invalid"]
|
|
51
|
+
#
|
|
52
|
+
# @example Reflection
|
|
53
|
+
# Product.has_cents_attributes
|
|
54
|
+
# #=> {
|
|
55
|
+
# # price_cents: { name: :price, rate: 100 },
|
|
56
|
+
# # cost_cents: { name: :wholesale_price, rate: 1000 },
|
|
57
|
+
# # quantity_cents: { name: :quantity, rate: 1 },
|
|
58
|
+
# # total_cents: { name: :total_value, rate: 100 }
|
|
59
|
+
# # }
|
|
60
|
+
#
|
|
61
|
+
# Product.has_cents_attribute?(:price_cents) #=> true
|
|
62
|
+
# Product.has_cents_attribute?(:name) #=> false
|
|
63
|
+
# Product.has_cents_attributes[:cost_cents] #=> {name: :wholesale_price, rate: 1000}
|
|
64
|
+
#
|
|
65
|
+
# @note This module automatically handles validation propagation. If a validation error
|
|
66
|
+
# is applied to the cents attribute, the decimal attribute will be marked as invalid.
|
|
67
|
+
#
|
|
68
|
+
# @note The module uses BigDecimal for internal calculations to ensure precision
|
|
69
|
+
# in monetary operations.
|
|
70
|
+
#
|
|
71
|
+
# @see ClassMethods#has_cents for details on setting up attributes
|
|
72
|
+
module HasCents
|
|
73
|
+
extend ActiveSupport::Concern
|
|
74
|
+
|
|
75
|
+
included do
|
|
76
|
+
class_attribute :has_cents_attributes, instance_writer: false, default: {}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
module ClassMethods
|
|
80
|
+
# # Inherit validations from cents attribute to decimal attribute
|
|
81
|
+
# def validate(*args, &block)
|
|
82
|
+
# options = args.extract_options!
|
|
83
|
+
# Array(options[:attributes]).each do |attribute|
|
|
84
|
+
# attribute = attribute.to_sym
|
|
85
|
+
# if has_cents_attribute?(attribute)
|
|
86
|
+
# decimal_attribute = has_cents_attributes[attribute][:name]
|
|
87
|
+
# options[:attributes] += [decimal_attribute]
|
|
88
|
+
# args = args.map do |validator|
|
|
89
|
+
# if validator.respond_to?(:attributes)
|
|
90
|
+
# validator.instance_variable_set(:@attributes, validator.attributes + [decimal_attribute])
|
|
91
|
+
# _validators[decimal_attribute] << validator
|
|
92
|
+
# validator
|
|
93
|
+
# end
|
|
94
|
+
# end
|
|
95
|
+
# end
|
|
96
|
+
# end
|
|
97
|
+
|
|
98
|
+
# super(*args, options, &block)
|
|
99
|
+
# end
|
|
100
|
+
# Defines getter and setter methods for a monetary value stored as cents,
|
|
101
|
+
# and ensures validations are applied to both cents and decimal attributes.
|
|
102
|
+
#
|
|
103
|
+
# @param cents_name [Symbol] The name of the attribute storing the cents value.
|
|
104
|
+
# @param name [Symbol, nil] The name for the generated methods. If nil, it's derived from cents_name.
|
|
105
|
+
# @param rate [Integer] The conversion rate from the decimal value to cents (default: 100).
|
|
106
|
+
# This represents how many cents are in one unit of the decimal value.
|
|
107
|
+
# For example:
|
|
108
|
+
# - rate: 100 for dollars/cents (1 dollar = 100 cents)
|
|
109
|
+
# - rate: 1000 for dollars/mils (1 dollar = 1000 mils)
|
|
110
|
+
# - rate: 1 for a whole number representation
|
|
111
|
+
# @param suffix [String] The suffix to append to the cents_name if name is not provided (default: "amount").
|
|
112
|
+
#
|
|
113
|
+
# @example Standard currency (dollars and cents)
|
|
114
|
+
# has_cents :price_cents
|
|
115
|
+
#
|
|
116
|
+
# @example Custom rate for a different currency division
|
|
117
|
+
# has_cents :amount_cents, name: :cost, rate: 1000
|
|
118
|
+
#
|
|
119
|
+
# @example Whole number storage without decimal places
|
|
120
|
+
# has_cents :quantity_cents, name: :quantity, rate: 1
|
|
121
|
+
#
|
|
122
|
+
# @example Using custom suffix
|
|
123
|
+
# has_cents :total_cents, suffix: "value"
|
|
124
|
+
def has_cents(cents_name, name: nil, rate: 100, suffix: "amount")
|
|
125
|
+
cents_name = cents_name.to_sym
|
|
126
|
+
name ||= cents_name.to_s.gsub(/_cents$/, "")
|
|
127
|
+
name = name.to_sym
|
|
128
|
+
name = (name == cents_name) ? :"#{cents_name}_#{suffix}" : name
|
|
129
|
+
|
|
130
|
+
self.has_cents_attributes = has_cents_attributes.merge(
|
|
131
|
+
cents_name => {name: name, rate: rate}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
135
|
+
# Getter method for the decimal representation of the cents value.
|
|
136
|
+
#
|
|
137
|
+
# @return [BigDecimal, nil] The decimal value or nil if cents_name is not present.
|
|
138
|
+
def #{name}
|
|
139
|
+
#{cents_name}.to_d / #{rate} if #{cents_name}.present?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Setter method for the decimal representation of the cents value.
|
|
143
|
+
#
|
|
144
|
+
# @param value [Numeric, nil] The decimal value to be set.
|
|
145
|
+
def #{name}=(value)
|
|
146
|
+
self.#{cents_name} = if value.present?
|
|
147
|
+
(BigDecimal(value.to_s) * #{rate}).to_i
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Mark decimal field as invalid if cents field is not valid
|
|
152
|
+
after_validation do
|
|
153
|
+
next unless errors[#{cents_name.inspect}].present?
|
|
154
|
+
|
|
155
|
+
errors.add(#{name.inspect}, :invalid)
|
|
156
|
+
end
|
|
157
|
+
RUBY
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Checks if a given attribute is defined with has_cents
|
|
161
|
+
#
|
|
162
|
+
# @param attribute [Symbol] The attribute to check
|
|
163
|
+
# @return [Boolean] true if the attribute is defined with has_cents, false otherwise
|
|
164
|
+
def has_cents_attribute?(attribute)
|
|
165
|
+
has_cents_attributes.key?(attribute.to_sym)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Plutonium
|
|
2
|
-
module
|
|
3
|
-
module
|
|
2
|
+
module Package
|
|
3
|
+
module Engine
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
6
|
included do
|
|
@@ -16,6 +16,14 @@ module Plutonium
|
|
|
16
16
|
end
|
|
17
17
|
add_view_paths_initializer.instance_variable_set(:@block, ->(app) {})
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
initializer :append_migrations do |app|
|
|
21
|
+
unless app.root.to_s.match root.to_s
|
|
22
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
23
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
19
27
|
end
|
|
20
28
|
end
|
|
21
29
|
end
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
module Plutonium
|
|
2
|
-
module
|
|
2
|
+
module Portal
|
|
3
3
|
module Controller
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
|
-
include Plutonium::Core::
|
|
5
|
+
include Plutonium::Core::Controller
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
helper_method :registered_resources
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
private
|
|
7
|
+
# private
|
|
12
8
|
|
|
13
9
|
# # Menu Builder
|
|
14
10
|
# def build_namespace_node(namespaces, resource, parent)
|
|
@@ -34,10 +30,6 @@ module Plutonium
|
|
|
34
30
|
# def build_sidebar_menu
|
|
35
31
|
# build_namespace_tree(current_engine.resource_register)
|
|
36
32
|
# end
|
|
37
|
-
|
|
38
|
-
def registered_resources
|
|
39
|
-
current_engine.resource_register.resources
|
|
40
|
-
end
|
|
41
33
|
end
|
|
42
34
|
end
|
|
43
35
|
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Plutonium
|
|
4
|
-
module
|
|
4
|
+
module Portal
|
|
5
5
|
# DynamicControllers module provides functionality for dynamically creating controller classes
|
|
6
6
|
# when they are missing in the current module's namespace.
|
|
7
7
|
#
|
|
8
8
|
# @example Usage
|
|
9
9
|
# module MyApp
|
|
10
|
-
# include Plutonium::
|
|
10
|
+
# include Plutonium::Portal::DynamicControllers
|
|
11
11
|
# end
|
|
12
12
|
#
|
|
13
13
|
# # Now, MyApp::SomeController will be dynamically created if it doesn't exist,
|
|
@@ -74,7 +74,7 @@ module Plutonium
|
|
|
74
74
|
log_controller_creation(const_full_name, parent_controller)
|
|
75
75
|
const_full_name.constantize
|
|
76
76
|
rescue => e
|
|
77
|
-
Plutonium.logger.error "[plutonium] Failed to create dynamic controller: #{e.message}"
|
|
77
|
+
Plutonium.logger.error { "[plutonium] Failed to create dynamic controller: #{e.message}" }
|
|
78
78
|
raise
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -100,7 +100,7 @@ module Plutonium
|
|
|
100
100
|
# @param const_full_name [String] The full name of the created controller
|
|
101
101
|
# @param parent_controller [Class] The parent controller class
|
|
102
102
|
def log_controller_creation(const_full_name, parent_controller)
|
|
103
|
-
Plutonium.logger.info "[plutonium] Dynamically created #{const_full_name} < #{parent_controller}"
|
|
103
|
+
Plutonium.logger.info { "[plutonium] Dynamically created #{const_full_name} < #{parent_controller}" }
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Portal
|
|
5
|
+
module Engine
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
include Plutonium::Engine
|
|
8
|
+
include Plutonium::Package::Engine
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
isolate_namespace to_s.deconstantize.constantize
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/plutonium/railtie.rb
CHANGED
|
@@ -8,18 +8,6 @@ module Plutonium
|
|
|
8
8
|
# This Railtie sets up configurations, initializers, and tasks for Plutonium
|
|
9
9
|
# to work seamlessly within a Rails environment.
|
|
10
10
|
class Railtie < Rails::Railtie
|
|
11
|
-
# Configuration options for Plutonium
|
|
12
|
-
config.plutonium = ActiveSupport::OrderedOptions.new
|
|
13
|
-
config.plutonium.cache_discovery = !Rails.env.development?
|
|
14
|
-
config.plutonium.enable_hotreload = Rails.env.development?
|
|
15
|
-
|
|
16
|
-
# Asset configuration
|
|
17
|
-
config.plutonium.assets = ActiveSupport::OrderedOptions.new
|
|
18
|
-
config.plutonium.assets.logo = "plutonium.png"
|
|
19
|
-
config.plutonium.assets.favicon = "plutonium.ico"
|
|
20
|
-
config.plutonium.assets.stylesheet = "plutonium.css"
|
|
21
|
-
config.plutonium.assets.script = "plutonium.min.js"
|
|
22
|
-
|
|
23
11
|
# Assets to be precompiled
|
|
24
12
|
#
|
|
25
13
|
# If you don't want to precompile Plutonium's assets (eg. because you're using webpack),
|
|
@@ -33,6 +21,14 @@ module Plutonium
|
|
|
33
21
|
plutonium.css plutonium.png plutonium.ico
|
|
34
22
|
].freeze
|
|
35
23
|
|
|
24
|
+
initializer "plutonium.base" do
|
|
25
|
+
Rails.application.class.include Plutonium::Engine
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
initializer "plutonium.deprecator" do |app|
|
|
29
|
+
app.deprecators[:plutonium] = Plutonium.deprecator
|
|
30
|
+
end
|
|
31
|
+
|
|
36
32
|
initializer "plutonium.assets" do
|
|
37
33
|
setup_asset_pipeline if Rails.application.config.respond_to?(:assets)
|
|
38
34
|
end
|
|
@@ -46,7 +42,7 @@ module Plutonium
|
|
|
46
42
|
end
|
|
47
43
|
|
|
48
44
|
initializer "plutonium.asset_server" do
|
|
49
|
-
setup_development_asset_server if Plutonium.development?
|
|
45
|
+
setup_development_asset_server if Plutonium.configuration.development?
|
|
50
46
|
end
|
|
51
47
|
|
|
52
48
|
initializer "plutonium.view_components_capture_compat" do
|
|
@@ -57,13 +53,22 @@ module Plutonium
|
|
|
57
53
|
extend_action_dispatch
|
|
58
54
|
end
|
|
59
55
|
|
|
56
|
+
initializer "plutonium.active_record_extensions" do
|
|
57
|
+
extend_active_record
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
initializer "plutonium.phlexi_themes" do
|
|
61
|
+
setup_phlexi_themes
|
|
62
|
+
end
|
|
63
|
+
|
|
60
64
|
rake_tasks do
|
|
61
65
|
load "tasks/create_rodauth_admin.rake"
|
|
62
66
|
end
|
|
63
67
|
|
|
64
68
|
config.after_initialize do
|
|
65
|
-
Plutonium::Reloader.start! if
|
|
66
|
-
Plutonium::
|
|
69
|
+
Plutonium::Reloader.start! if Plutonium.configuration.enable_hotreload
|
|
70
|
+
Plutonium::Loader.eager_load if Rails.env.production?
|
|
71
|
+
ActionPolicy::PerThreadCache.enabled = !Rails.env.local?
|
|
67
72
|
end
|
|
68
73
|
|
|
69
74
|
private
|
|
@@ -95,10 +100,25 @@ module Plutonium
|
|
|
95
100
|
)
|
|
96
101
|
end
|
|
97
102
|
|
|
103
|
+
def setup_phlexi_themes
|
|
104
|
+
Rails.application.config.to_prepare do
|
|
105
|
+
Phlexi::Form::Theme.instance = Plutonium::UI::Form::Theme.instance
|
|
106
|
+
Phlexi::Display::Theme.instance = Plutonium::UI::Display::Theme.instance
|
|
107
|
+
Phlexi::Table::Theme.instance = Plutonium::UI::Table::Theme.instance
|
|
108
|
+
Phlexi::Table::DisplayTheme.instance = Plutonium::UI::Table::DisplayTheme.instance
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
98
112
|
def extend_action_dispatch
|
|
99
113
|
ActionDispatch::Routing::Mapper.prepend Plutonium::Routing::MapperExtensions
|
|
100
114
|
ActionDispatch::Routing::RouteSet.prepend Plutonium::Routing::RouteSetExtensions
|
|
101
115
|
Rails::Engine.include Plutonium::Routing::ResourceRegistration
|
|
102
116
|
end
|
|
117
|
+
|
|
118
|
+
def extend_active_record
|
|
119
|
+
ActiveSupport.on_load(:active_record) do
|
|
120
|
+
include Plutonium::Resource::Record
|
|
121
|
+
end
|
|
122
|
+
end
|
|
103
123
|
end
|
|
104
124
|
end
|