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
data/lib/plutonium/reloader.rb
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "active_support/notifications"
|
|
4
4
|
|
|
5
5
|
module Plutonium
|
|
6
|
+
# Reloader class for Plutonium
|
|
7
|
+
#
|
|
8
|
+
# This class is responsible for managing the reloading of Plutonium components
|
|
9
|
+
# and related files during development.
|
|
6
10
|
class Reloader
|
|
7
11
|
class << self
|
|
12
|
+
# Start the reloader
|
|
13
|
+
#
|
|
14
|
+
# @return [void]
|
|
8
15
|
def start!
|
|
9
16
|
puts "=> [plutonium] starting reloader"
|
|
10
17
|
|
|
11
18
|
ActiveSupport::Notifications.instrument("plutonium.reloader.start") do
|
|
12
|
-
# Task code here
|
|
13
19
|
@listener&.stop
|
|
14
20
|
@listener = initialize_listener
|
|
15
21
|
end
|
|
@@ -17,37 +23,49 @@ module Plutonium
|
|
|
17
23
|
|
|
18
24
|
private
|
|
19
25
|
|
|
26
|
+
# Initialize the file listener
|
|
27
|
+
#
|
|
28
|
+
# @return [Listen::Listener, nil] the initialized listener or nil if no paths to watch
|
|
20
29
|
def initialize_listener
|
|
21
30
|
require "listen"
|
|
22
31
|
|
|
23
32
|
reload_paths = gather_reload_paths
|
|
24
|
-
return
|
|
33
|
+
return if reload_paths.empty?
|
|
25
34
|
|
|
26
|
-
|
|
35
|
+
Listen.to(*reload_paths, only: /\.rb$/) { |modified, added, removed|
|
|
27
36
|
handle_file_changes(modified, added, removed)
|
|
28
|
-
|
|
29
|
-
listener.start
|
|
30
|
-
listener
|
|
37
|
+
}.tap(&:start)
|
|
31
38
|
end
|
|
32
39
|
|
|
40
|
+
# Gather paths to be watched for changes
|
|
41
|
+
#
|
|
42
|
+
# @return [Array<String>] list of paths to watch
|
|
33
43
|
def gather_reload_paths
|
|
34
44
|
reload_paths = []
|
|
35
45
|
|
|
36
|
-
if Plutonium.development?
|
|
37
|
-
reload_paths
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
if Plutonium.configuration.development?
|
|
47
|
+
reload_paths.concat([
|
|
48
|
+
Plutonium.lib_root.to_s,
|
|
49
|
+
Plutonium.root.join("app", "views", "components").to_s,
|
|
50
|
+
Plutonium.root.join("config", "initializers").to_s
|
|
51
|
+
])
|
|
40
52
|
end
|
|
41
53
|
|
|
42
|
-
packages_dir = Rails.root.join("packages
|
|
54
|
+
packages_dir = Rails.root.join("packages").to_s
|
|
43
55
|
reload_paths << packages_dir if File.directory?(packages_dir)
|
|
44
56
|
|
|
45
57
|
reload_paths
|
|
46
58
|
end
|
|
47
59
|
|
|
60
|
+
# Handle file changes detected by the listener
|
|
61
|
+
#
|
|
62
|
+
# @param modified [Array<String>] list of modified files
|
|
63
|
+
# @param added [Array<String>] list of added files
|
|
64
|
+
# @param removed [Array<String>] list of removed files
|
|
65
|
+
# @return [void]
|
|
48
66
|
def handle_file_changes(modified, added, removed)
|
|
49
67
|
(modified + added).each do |file|
|
|
50
|
-
Plutonium.logger.debug "[plutonium] change detected: #{file}"
|
|
68
|
+
Plutonium.logger.debug { "[plutonium] change detected: #{file}" }
|
|
51
69
|
|
|
52
70
|
if file == __FILE__
|
|
53
71
|
reload_file(file)
|
|
@@ -62,48 +80,72 @@ module Plutonium
|
|
|
62
80
|
end
|
|
63
81
|
end
|
|
64
82
|
|
|
83
|
+
# Check if the file is within the packages directory
|
|
84
|
+
#
|
|
85
|
+
# @param file [String] path to the file
|
|
86
|
+
# @return [Boolean] true if the file is within the packages directory
|
|
65
87
|
def file_starts_with_packages_dir?(file)
|
|
66
|
-
|
|
67
|
-
file.starts_with?(packages_dir)
|
|
88
|
+
file.start_with?(Rails.root.join("packages").to_s)
|
|
68
89
|
end
|
|
69
90
|
|
|
91
|
+
# Handle changes to files within the packages directory
|
|
92
|
+
#
|
|
93
|
+
# @param file [String] path to the changed file
|
|
94
|
+
# @param added [Array<String>] list of added files
|
|
95
|
+
# @return [void]
|
|
70
96
|
def handle_package_file_changes(file, added)
|
|
71
97
|
return if added.include?(file)
|
|
72
98
|
|
|
73
99
|
case File.basename(file)
|
|
74
100
|
when "engine.rb"
|
|
75
101
|
reload_engine_and_routes(file)
|
|
76
|
-
else
|
|
77
|
-
# Non-engine package files are reloaded by Rails automatically
|
|
78
102
|
end
|
|
79
103
|
end
|
|
80
104
|
|
|
105
|
+
# Reload engine and routes
|
|
106
|
+
#
|
|
107
|
+
# @param file [String] path to the engine file
|
|
108
|
+
# @return [void]
|
|
81
109
|
def reload_engine_and_routes(file)
|
|
82
|
-
Plutonium.logger.debug "[plutonium] reloading: engine+routes"
|
|
110
|
+
Plutonium.logger.debug { "[plutonium] reloading: engine+routes" }
|
|
83
111
|
load file
|
|
84
112
|
Rails.application.reload_routes!
|
|
85
113
|
end
|
|
86
114
|
|
|
115
|
+
# Reload the framework and file
|
|
116
|
+
#
|
|
117
|
+
# @param file [String] path to the file
|
|
118
|
+
# @return [void]
|
|
87
119
|
def reload_framework_and_file(file)
|
|
88
|
-
|
|
89
|
-
# load file
|
|
90
|
-
|
|
91
|
-
Plutonium.logger.debug "[plutonium] reloading: app+framework"
|
|
120
|
+
Plutonium.logger.debug { "[plutonium] reloading: app+framework" }
|
|
92
121
|
Rails.application.reloader.reload!
|
|
93
|
-
Plutonium::
|
|
94
|
-
|
|
122
|
+
Plutonium::Loader.reload
|
|
123
|
+
reload_components
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Reload components
|
|
127
|
+
#
|
|
128
|
+
# @return [void]
|
|
129
|
+
def reload_components
|
|
95
130
|
Object.send(:remove_const, "PlutoniumUi")
|
|
96
131
|
load Plutonium.root.join("app", "views", "components", "base.rb")
|
|
97
|
-
# # Ensure files that do not contain constants are loaded again e.g. initializers
|
|
98
|
-
# load file
|
|
99
132
|
end
|
|
100
133
|
|
|
134
|
+
# Reload a single file
|
|
135
|
+
#
|
|
136
|
+
# @param file [String] path to the file
|
|
137
|
+
# @return [Boolean] true if the file was successfully loaded
|
|
101
138
|
def reload_file(file)
|
|
102
|
-
load
|
|
139
|
+
load(file)
|
|
103
140
|
end
|
|
104
141
|
|
|
142
|
+
# Log reload failure
|
|
143
|
+
#
|
|
144
|
+
# @param file [String] path to the file that failed to reload
|
|
145
|
+
# @param error [StandardError] the error that occurred during reloading
|
|
146
|
+
# @return [void]
|
|
105
147
|
def log_reload_failure(file, error)
|
|
106
|
-
Plutonium.logger.error "\n[plutonium] reloading failed\n\n#{error.message}\n"
|
|
148
|
+
Plutonium.logger.error { "\n[plutonium] reloading failed\n\n#{error.message}\n" }
|
|
107
149
|
end
|
|
108
150
|
end
|
|
109
151
|
end
|
|
@@ -10,16 +10,15 @@ module Plutonium
|
|
|
10
10
|
module Controller
|
|
11
11
|
extend ActiveSupport::Concern
|
|
12
12
|
include Pagy::Backend
|
|
13
|
-
include Plutonium::Core::
|
|
14
|
-
include Plutonium::
|
|
15
|
-
include Plutonium::
|
|
16
|
-
include Plutonium::
|
|
17
|
-
include Plutonium::
|
|
18
|
-
include Plutonium::
|
|
13
|
+
include Plutonium::Core::Controller
|
|
14
|
+
include Plutonium::Resource::Controllers::Defineable
|
|
15
|
+
include Plutonium::Resource::Controllers::Authorizable
|
|
16
|
+
include Plutonium::Resource::Controllers::Presentable
|
|
17
|
+
include Plutonium::Resource::Controllers::Queryable
|
|
18
|
+
include Plutonium::Resource::Controllers::CrudActions
|
|
19
|
+
include Plutonium::Resource::Controllers::InteractiveActions
|
|
19
20
|
|
|
20
21
|
included do
|
|
21
|
-
class_attribute :resource_class, instance_writer: false, instance_predicate: false
|
|
22
|
-
|
|
23
22
|
# https://github.com/ddnexus/pagy/blob/master/docs/extras/headers.md#headers
|
|
24
23
|
after_action { pagy_headers_merge(@pagy) if @pagy }
|
|
25
24
|
|
|
@@ -27,49 +26,55 @@ module Plutonium
|
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
class_methods do
|
|
29
|
+
include Plutonium::Lib::SmartCache
|
|
30
|
+
|
|
30
31
|
# Sets the resource class for the controller
|
|
31
|
-
# @param [
|
|
32
|
+
# @param [ActiveRecord::Base] resource_class The resource class
|
|
32
33
|
def controller_for(resource_class)
|
|
33
|
-
|
|
34
|
+
@resource_class = resource_class
|
|
34
35
|
end
|
|
36
|
+
|
|
37
|
+
# Gets the resource class for the controller
|
|
38
|
+
# @return [ActiveRecord::Base] The resource class
|
|
39
|
+
def resource_class
|
|
40
|
+
return @resource_class if @resource_class.present?
|
|
41
|
+
|
|
42
|
+
name.to_s.gsub(/^#{current_package}::/, "").gsub(/Controller$/, "").classify.constantize
|
|
43
|
+
rescue NameError
|
|
44
|
+
raise NameError, "Failed to determine the resource class. Please call `controller_for(MyResource)` in #{name}."
|
|
45
|
+
end
|
|
46
|
+
memoize_unless_reloading :resource_class
|
|
35
47
|
end
|
|
36
48
|
|
|
37
49
|
private
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def policy_context
|
|
42
|
-
Plutonium::Resource::PolicyContext.new(
|
|
43
|
-
user: current_user,
|
|
44
|
-
resource_context: resource_context
|
|
45
|
-
)
|
|
51
|
+
def resource_class
|
|
52
|
+
self.class.resource_class
|
|
46
53
|
end
|
|
47
54
|
|
|
48
55
|
# Returns the resource record based on path parameters
|
|
49
56
|
# @return [ActiveRecord::Base, nil] The resource record
|
|
50
57
|
def resource_record
|
|
51
|
-
@resource_record ||=
|
|
58
|
+
@resource_record ||= current_authorized_scope.from_path_param(params[:id]).first! if params[:id].present?
|
|
52
59
|
@resource_record
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
# Returns the submitted resource parameters
|
|
56
63
|
# @return [Hash] The submitted resource parameters
|
|
57
64
|
def submitted_resource_params
|
|
58
|
-
@submitted_resource_params ||=
|
|
59
|
-
strong_parameters = resource_class.strong_parameters_for(*permitted_attributes)
|
|
60
|
-
params.require(resource_param_key).permit(*strong_parameters).nilify.to_h
|
|
61
|
-
end
|
|
65
|
+
@submitted_resource_params ||= build_form(resource_class.new).extract_input(params)[resource_param_key.to_sym]
|
|
62
66
|
end
|
|
63
67
|
|
|
64
68
|
# Returns the resource parameters, including scoped and parent parameters
|
|
65
69
|
# @return [Hash] The resource parameters
|
|
66
70
|
def resource_params
|
|
67
|
-
|
|
71
|
+
@resource_params ||= begin
|
|
72
|
+
input_params = submitted_resource_params.dup
|
|
73
|
+
override_entity_scoping_params(input_params)
|
|
74
|
+
override_parent_params(input_params)
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
current_presenter.defined_field_inputs_for(*permitted_attributes).collect_all(input_params)
|
|
76
|
+
input_params
|
|
77
|
+
end
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
# Returns the resource parameter key
|
|
@@ -93,10 +98,20 @@ module Plutonium
|
|
|
93
98
|
# @param [ActiveRecord::Base] resource_record The resource record
|
|
94
99
|
# @return [Object] The resource presenter
|
|
95
100
|
def resource_presenter(resource_class, resource_record)
|
|
96
|
-
presenter_class = "#{
|
|
101
|
+
presenter_class = [current_package, "#{resource_class}Presenter"].compact.join("::").constantize
|
|
97
102
|
presenter_class.new resource_context, resource_record
|
|
98
103
|
rescue NameError
|
|
99
|
-
super
|
|
104
|
+
super
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Creates a resource definition
|
|
108
|
+
# @param [Class] resource_class The resource class
|
|
109
|
+
# @return [Object] The resource definition
|
|
110
|
+
def resource_definition(resource_class)
|
|
111
|
+
definition_class = [current_package, "#{resource_class}Definition"].compact.join("::").constantize
|
|
112
|
+
definition_class.new
|
|
113
|
+
rescue NameError
|
|
114
|
+
super
|
|
100
115
|
end
|
|
101
116
|
|
|
102
117
|
# Creates a resource query object
|
|
@@ -104,10 +119,10 @@ module Plutonium
|
|
|
104
119
|
# @param [ActionController::Parameters] params The request parameters
|
|
105
120
|
# @return [Object] The resource query object
|
|
106
121
|
def resource_query_object(resource_class, params)
|
|
107
|
-
query_object_class = "#{
|
|
122
|
+
query_object_class = [current_package, "#{resource_class}QueryObject"].compact.join("::").constantize
|
|
108
123
|
query_object_class.new resource_context, params
|
|
109
124
|
rescue NameError
|
|
110
|
-
super
|
|
125
|
+
super
|
|
111
126
|
end
|
|
112
127
|
|
|
113
128
|
# Applies submitted resource params if they have been passed
|
|
@@ -126,7 +141,9 @@ module Plutonium
|
|
|
126
141
|
parent_class = current_engine.resource_register.route_key_lookup[parent_route_key]
|
|
127
142
|
parent_scope = parent_class.from_path_param(params[parent_route_param])
|
|
128
143
|
parent_scope = parent_scope.associated_with(current_scoped_entity) if scoped_to_entity?
|
|
129
|
-
parent_scope.first!
|
|
144
|
+
current_parent = parent_scope.first!
|
|
145
|
+
authorize! current_parent, to: :read?
|
|
146
|
+
current_parent
|
|
130
147
|
end
|
|
131
148
|
end
|
|
132
149
|
|
|
@@ -173,9 +190,9 @@ module Plutonium
|
|
|
173
190
|
# @param [Array] args The URL arguments
|
|
174
191
|
# @param [Hash] kwargs The keyword arguments
|
|
175
192
|
# @return [Array] The URL arguments
|
|
176
|
-
def resource_url_args_for(
|
|
193
|
+
def resource_url_args_for(*, **kwargs)
|
|
177
194
|
kwargs[:parent] = current_parent unless kwargs.key?(:parent)
|
|
178
|
-
super
|
|
195
|
+
super
|
|
179
196
|
end
|
|
180
197
|
end
|
|
181
198
|
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Resource
|
|
5
|
+
module Controllers
|
|
6
|
+
# The Authorizable module provides authorization functionality for controllers,
|
|
7
|
+
# specifically for the current resource being handled by the controller.
|
|
8
|
+
# It integrates with ActionPolicy to enforce authorization checks and scoping.
|
|
9
|
+
#
|
|
10
|
+
# @example Including the module in a controller
|
|
11
|
+
# class MyController < ApplicationController
|
|
12
|
+
# include Plutonium::Resource::Controllers::Authorizable
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# @note This module assumes the existence of methods like `resource_record`,
|
|
16
|
+
# `resource_class`, `current_parent`, and `entity_scope_for_authorize`.
|
|
17
|
+
#
|
|
18
|
+
# @see ActionPolicy
|
|
19
|
+
module Authorizable
|
|
20
|
+
extend ActiveSupport::Concern
|
|
21
|
+
|
|
22
|
+
# Custom exception for missing authorize_current call
|
|
23
|
+
class ActionMissingAuthorizeCurrent < ActionPolicy::UnauthorizedAction; end
|
|
24
|
+
|
|
25
|
+
# Custom exception for missing current_authorized_scope call
|
|
26
|
+
class ActionMissingCurrentAuthorizedScope < ActionPolicy::UnauthorizedAction; end
|
|
27
|
+
|
|
28
|
+
included do
|
|
29
|
+
verify_authorized
|
|
30
|
+
after_action :verify_authorize_current
|
|
31
|
+
after_action :verify_current_authorized_scope, except: %i[new create]
|
|
32
|
+
|
|
33
|
+
helper_method :current_policy, :permitted_attributes
|
|
34
|
+
|
|
35
|
+
attr_writer :authorize_current_count
|
|
36
|
+
attr_writer :current_authorized_scope_count
|
|
37
|
+
|
|
38
|
+
protected :authorize_current_count=, :authorize_current_count
|
|
39
|
+
protected :current_authorized_scope_count=, :current_authorized_scope_count
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# Verifies that authorize_current has been called
|
|
45
|
+
#
|
|
46
|
+
# @raise [ActionMissingAuthorizeCurrent] if authorize_current hasn't been called
|
|
47
|
+
def verify_authorize_current
|
|
48
|
+
return if verify_authorized_skipped
|
|
49
|
+
|
|
50
|
+
raise ActionMissingAuthorizeCurrent.new(controller_path, action_name) if authorize_current_count.zero?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Verifies that current_authorized_scope has been called
|
|
54
|
+
#
|
|
55
|
+
# @raise [ActionMissingCurrentAuthorizedScope] if current_authorized_scope hasn't been called
|
|
56
|
+
def verify_current_authorized_scope
|
|
57
|
+
return if verify_authorized_skipped
|
|
58
|
+
|
|
59
|
+
raise ActionMissingCurrentAuthorizedScope.new(controller_path, action_name) if current_authorized_scope_count.zero?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Integer] the number of times authorize_current has been called
|
|
63
|
+
def authorize_current_count
|
|
64
|
+
@authorize_current_count ||= 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Integer] the number of times current_authorized_scope has been called
|
|
68
|
+
def current_authorized_scope_count
|
|
69
|
+
@current_authorized_scope_count ||= 0
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns the policy for the current resource
|
|
73
|
+
#
|
|
74
|
+
# @return [ActionPolicy::Base] the policy for the current resource
|
|
75
|
+
def current_policy
|
|
76
|
+
@current_policy ||= policy_for(record: current_policy_subject, context: current_policy_context)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns the authorized scope for the current resource
|
|
80
|
+
#
|
|
81
|
+
# @return [ActiveRecord::Relation] the authorized scope for the current resource
|
|
82
|
+
def current_authorized_scope
|
|
83
|
+
self.current_authorized_scope_count += 1
|
|
84
|
+
authorized_scope(resource_class.all, context: current_policy_context)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Sets the policy context scope value to the current parent if available
|
|
88
|
+
#
|
|
89
|
+
# @return [Hash] default context for the current resource's policy
|
|
90
|
+
def current_policy_context
|
|
91
|
+
{scope: current_parent || entity_scope_for_authorize}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Authorizes the current action for the given record of the current resource
|
|
95
|
+
#
|
|
96
|
+
# @param record [Object] the record to authorize
|
|
97
|
+
# @param options [Hash] additional options for authorization
|
|
98
|
+
# @raise [ActionPolicy::Unauthorized] if the action is not authorized
|
|
99
|
+
def authorize_current!(record, **options)
|
|
100
|
+
options[:context] = (options[:context] || {}).deep_merge(current_policy_context)
|
|
101
|
+
authorize!(record, **options)
|
|
102
|
+
self.authorize_current_count += 1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns the list of permitted attributes for the current action on the current resource
|
|
106
|
+
#
|
|
107
|
+
# @return [Array<Symbol>] the list of permitted attributes for the current action
|
|
108
|
+
def permitted_attributes
|
|
109
|
+
@permitted_attributes ||= current_policy.send_with_report(:"permitted_attributes_for_#{action_name}")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns the list of permitted associations for the current resource
|
|
113
|
+
#
|
|
114
|
+
# @return [Array<Symbol>] the list of permitted associations
|
|
115
|
+
def permitted_associations
|
|
116
|
+
@permitted_associations ||= current_policy.send_with_report(:permitted_associations)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Returns the subject for the current resource's policy
|
|
120
|
+
#
|
|
121
|
+
# @return [Object] the subject for the policy (either resource_record or resource_class)
|
|
122
|
+
def current_policy_subject
|
|
123
|
+
resource_record || resource_class
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Plutonium
|
|
2
|
-
module
|
|
2
|
+
module Resource
|
|
3
3
|
module Controllers
|
|
4
4
|
module CrudActions
|
|
5
5
|
extend ActiveSupport::Concern
|
|
@@ -10,44 +10,40 @@ module Plutonium
|
|
|
10
10
|
|
|
11
11
|
# GET /resources(.{format})
|
|
12
12
|
def index
|
|
13
|
-
|
|
13
|
+
authorize_current! resource_class
|
|
14
14
|
set_page_title resource_class.model_name.human.pluralize.titleize
|
|
15
15
|
|
|
16
16
|
@search_object = current_query_object
|
|
17
|
-
base_query =
|
|
17
|
+
base_query = current_authorized_scope
|
|
18
18
|
base_query = @search_object.apply(base_query)
|
|
19
|
-
base_query = base_query.public_send(params[:scope].to_sym) if params[:scope].present?
|
|
19
|
+
# base_query = base_query.public_send(params[:scope].to_sym) if params[:scope].present?
|
|
20
20
|
@pagy, @resource_records = pagy base_query
|
|
21
|
-
@collection = build_collection
|
|
22
21
|
|
|
23
22
|
render :index
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
# GET /resources/1(.{format})
|
|
27
26
|
def show
|
|
28
|
-
|
|
27
|
+
authorize_current! resource_record
|
|
29
28
|
set_page_title resource_record.to_label.titleize
|
|
30
29
|
|
|
31
|
-
@detail = build_detail
|
|
32
|
-
|
|
33
30
|
render :show
|
|
34
31
|
end
|
|
35
32
|
|
|
36
33
|
# GET /resources/new
|
|
37
34
|
def new
|
|
38
|
-
|
|
35
|
+
authorize_current! resource_class
|
|
39
36
|
set_page_title "Create #{resource_class.model_name.human.titleize}"
|
|
40
37
|
|
|
41
38
|
@resource_record = resource_class.new
|
|
42
39
|
maybe_apply_submitted_resource_params!
|
|
43
|
-
@form = build_form
|
|
44
40
|
|
|
45
41
|
render :new
|
|
46
42
|
end
|
|
47
43
|
|
|
48
44
|
# POST /resources(.{format})
|
|
49
45
|
def create
|
|
50
|
-
|
|
46
|
+
authorize_current! resource_class
|
|
51
47
|
set_page_title "Create #{resource_class.model_name.human.titleize}"
|
|
52
48
|
|
|
53
49
|
@resource_record = resource_class.new resource_params
|
|
@@ -61,7 +57,6 @@ module Plutonium
|
|
|
61
57
|
format.any { render :show, status: :created, location: redirect_url_after_submit }
|
|
62
58
|
else
|
|
63
59
|
format.html do
|
|
64
|
-
@form = build_form
|
|
65
60
|
render :new, status: :unprocessable_entity
|
|
66
61
|
end
|
|
67
62
|
format.any do
|
|
@@ -74,18 +69,17 @@ module Plutonium
|
|
|
74
69
|
|
|
75
70
|
# GET /resources/1/edit
|
|
76
71
|
def edit
|
|
77
|
-
|
|
72
|
+
authorize_current! resource_record
|
|
78
73
|
set_page_title "Update #{resource_record.to_label.titleize}"
|
|
79
74
|
|
|
80
75
|
maybe_apply_submitted_resource_params!
|
|
81
|
-
@form = build_form
|
|
82
76
|
|
|
83
77
|
render :edit
|
|
84
78
|
end
|
|
85
79
|
|
|
86
80
|
# PATCH/PUT /resources/1(.{format})
|
|
87
81
|
def update
|
|
88
|
-
|
|
82
|
+
authorize_current! resource_record
|
|
89
83
|
set_page_title "Update #{resource_record.to_label.titleize}"
|
|
90
84
|
|
|
91
85
|
respond_to do |format|
|
|
@@ -97,7 +91,6 @@ module Plutonium
|
|
|
97
91
|
format.any { render :show, status: :ok, location: redirect_url_after_submit }
|
|
98
92
|
else
|
|
99
93
|
format.html do
|
|
100
|
-
@form = build_form
|
|
101
94
|
render :edit, status: :unprocessable_entity
|
|
102
95
|
end
|
|
103
96
|
format.any do
|
|
@@ -110,13 +103,13 @@ module Plutonium
|
|
|
110
103
|
|
|
111
104
|
# DELETE /resources/1(.{format})
|
|
112
105
|
def destroy
|
|
113
|
-
|
|
106
|
+
authorize_current! resource_record
|
|
114
107
|
|
|
115
108
|
respond_to do |format|
|
|
116
109
|
resource_record.destroy
|
|
117
110
|
|
|
118
111
|
format.html do
|
|
119
|
-
redirect_to
|
|
112
|
+
redirect_to redirect_url_after_destroy,
|
|
120
113
|
notice: "#{resource_class.model_name.human} was successfully deleted."
|
|
121
114
|
end
|
|
122
115
|
format.json { head :no_content }
|
|
@@ -143,13 +136,13 @@ module Plutonium
|
|
|
143
136
|
|
|
144
137
|
url = case preferred_action_after_submit
|
|
145
138
|
when "show"
|
|
146
|
-
resource_url_for(resource_record) if current_policy.show?
|
|
139
|
+
resource_url_for(resource_record) if current_policy.allowed_to? :show?
|
|
147
140
|
when "edit"
|
|
148
|
-
resource_url_for(resource_record, action: :edit) if current_policy.edit?
|
|
141
|
+
resource_url_for(resource_record, action: :edit) if current_policy.allowed_to? :edit?
|
|
149
142
|
when "new"
|
|
150
|
-
resource_url_for(resource_class, action: :new) if current_policy.new?
|
|
143
|
+
resource_url_for(resource_class, action: :new) if current_policy.allowed_to? :new?
|
|
151
144
|
when "index"
|
|
152
|
-
resource_url_for(resource_class) if current_policy.index?
|
|
145
|
+
resource_url_for(resource_class) if current_policy.allowed_to? :index?
|
|
153
146
|
else
|
|
154
147
|
# ensure we have a valid value
|
|
155
148
|
session[:action_after_submit_preference] = "show"
|
|
@@ -157,6 +150,14 @@ module Plutonium
|
|
|
157
150
|
url || resource_url_for(resource_record)
|
|
158
151
|
end
|
|
159
152
|
|
|
153
|
+
def redirect_url_after_destroy
|
|
154
|
+
if (return_to = url_from(params[:return_to]))
|
|
155
|
+
return return_to
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
resource_url_for(resource_class)
|
|
159
|
+
end
|
|
160
|
+
|
|
160
161
|
def preferred_action_after_submit
|
|
161
162
|
@preferred_action_after_submit = begin
|
|
162
163
|
if %w[new edit show index].include? params[:commit]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
using Plutonium::Refinements::ParameterRefinements
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Resource
|
|
5
|
+
module Controllers
|
|
6
|
+
module Defineable
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
helper_method :current_definition, :resource_definition
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def resource_definition(resource_class)
|
|
16
|
+
definition_class = "#{resource_class}Definition".constantize
|
|
17
|
+
definition_class.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def current_definition
|
|
21
|
+
@current_definition ||= resource_definition resource_class
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|