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,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
# Configuration class for Plutonium module
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Plutonium.configure do |config|
|
|
8
|
+
# config.load_defaults 1.0
|
|
9
|
+
# config.development = true
|
|
10
|
+
# config.cache_discovery = false
|
|
11
|
+
# config.enable_hotreload = true
|
|
12
|
+
# config.assets.logo = "custom_logo.png"
|
|
13
|
+
# end
|
|
14
|
+
class Configuration
|
|
15
|
+
# @return [Boolean] whether Plutonium is in development mode
|
|
16
|
+
attr_accessor :development
|
|
17
|
+
|
|
18
|
+
# @return [Boolean] whether to cache discovery
|
|
19
|
+
attr_accessor :cache_discovery
|
|
20
|
+
|
|
21
|
+
# @return [Boolean] whether to enable hot reloading
|
|
22
|
+
attr_accessor :enable_hotreload
|
|
23
|
+
|
|
24
|
+
# @return [AssetConfiguration] asset configuration
|
|
25
|
+
attr_reader :assets
|
|
26
|
+
|
|
27
|
+
# @return [Float] the current defaults version
|
|
28
|
+
attr_reader :defaults_version
|
|
29
|
+
|
|
30
|
+
# Map of version numbers to their default configurations
|
|
31
|
+
VERSION_DEFAULTS = {
|
|
32
|
+
1.0 => proc do |config|
|
|
33
|
+
# No changes for 1.0 yet as it's the current base configuration
|
|
34
|
+
end
|
|
35
|
+
# Add more version configurations here as needed
|
|
36
|
+
# 1.1 => proc do |config|
|
|
37
|
+
# config.some_new_setting = true
|
|
38
|
+
# end
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# Initialize a new Configuration instance
|
|
42
|
+
#
|
|
43
|
+
# @note This method sets initial values
|
|
44
|
+
def initialize
|
|
45
|
+
@defaults_version = nil
|
|
46
|
+
@assets = AssetConfiguration.new
|
|
47
|
+
|
|
48
|
+
@development = parse_boolean_env("PLUTONIUM_DEV")
|
|
49
|
+
@cache_discovery = !Rails.env.development?
|
|
50
|
+
@enable_hotreload = Rails.env.development?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Load default configuration for a specific version
|
|
54
|
+
#
|
|
55
|
+
# @param version [Float] the version to load defaults for
|
|
56
|
+
# @return [void]
|
|
57
|
+
def load_defaults(version)
|
|
58
|
+
available_versions = VERSION_DEFAULTS.keys.sort
|
|
59
|
+
applicable_versions = available_versions.select { |v| v <= version }
|
|
60
|
+
|
|
61
|
+
if applicable_versions.empty?
|
|
62
|
+
raise "No applicable defaults found for version #{version}."
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
applicable_versions.each do |v|
|
|
66
|
+
VERSION_DEFAULTS[v].call(self)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
@defaults_version = applicable_versions.last
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# whether Plutonium is in development mode
|
|
73
|
+
#
|
|
74
|
+
# @return [Boolean]
|
|
75
|
+
def development?
|
|
76
|
+
@development
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
# Parse boolean environment variable
|
|
82
|
+
#
|
|
83
|
+
# @param env_var [String] name of the environment variable
|
|
84
|
+
# @return [Boolean] parsed boolean value
|
|
85
|
+
def parse_boolean_env(env_var)
|
|
86
|
+
ActiveModel::Type::Boolean.new.cast(ENV[env_var]).present?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Asset configuration for Plutonium
|
|
90
|
+
class AssetConfiguration
|
|
91
|
+
# @return [String] path to logo file
|
|
92
|
+
attr_accessor :logo
|
|
93
|
+
|
|
94
|
+
# @return [String] path to favicon file
|
|
95
|
+
attr_accessor :favicon
|
|
96
|
+
|
|
97
|
+
# @return [String] path to stylesheet file
|
|
98
|
+
attr_accessor :stylesheet
|
|
99
|
+
|
|
100
|
+
# @return [String] path to JavaScript file
|
|
101
|
+
attr_accessor :script
|
|
102
|
+
|
|
103
|
+
# Initialize a new AssetConfiguration instance with default values
|
|
104
|
+
def initialize
|
|
105
|
+
@logo = "plutonium.png"
|
|
106
|
+
@favicon = "plutonium.ico"
|
|
107
|
+
@stylesheet = "plutonium.css"
|
|
108
|
+
@script = "plutonium.min.js"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class << self
|
|
114
|
+
# Get the current configuration
|
|
115
|
+
#
|
|
116
|
+
# @return [Configuration] current configuration instance
|
|
117
|
+
def configuration
|
|
118
|
+
@configuration ||= Configuration.new
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Configure Plutonium
|
|
122
|
+
#
|
|
123
|
+
# @yield [config] Configuration instance
|
|
124
|
+
# @yieldparam config [Configuration] current configuration instance
|
|
125
|
+
# @return [void]
|
|
126
|
+
def configure
|
|
127
|
+
yield(configuration)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -9,7 +9,7 @@ module Plutonium
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def permitted_for(policy)
|
|
12
|
-
Collection.new(@collection.select { |name, action| policy.
|
|
12
|
+
Collection.new(@collection.select { |name, action| policy.allowed_to? :"#{action.name}?" })
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def collection_actions
|
|
@@ -19,7 +19,9 @@ module Plutonium
|
|
|
19
19
|
def self.for_resource_association(resource_class, attr_name, **options)
|
|
20
20
|
association = resource_class.try(:reflect_on_association, attr_name)
|
|
21
21
|
raise ArgumentError, "#{attr_name} is not a valid association of #{resource_class}" unless association.present?
|
|
22
|
-
|
|
22
|
+
# # TODO: fix constant being out of sync after reload during development
|
|
23
|
+
# valid_resource_record = Plutonium.configuration.development? ? association.klass.respond_to?(:resource_field_names) : association.klass.include?(Plutonium: :Resource: :Record)
|
|
24
|
+
# raise ArgumentError, "#{association.klass} is not a resource record" unless valid_resource_record
|
|
23
25
|
|
|
24
26
|
type = association.macro
|
|
25
27
|
raise NotImplementedError, "#{macro} associations are currently not supported." unless type == :has_many
|
|
@@ -14,7 +14,7 @@ module Plutonium
|
|
|
14
14
|
# If cache_discovery is enabled, use the class level cache that persists
|
|
15
15
|
# between requests, otherwise use the instance one.
|
|
16
16
|
def autodiscovery_association_renderer_cache
|
|
17
|
-
if
|
|
17
|
+
if Plutonium.configuration.cache_discovery
|
|
18
18
|
self.class.autodiscovery_association_renderer_cache
|
|
19
19
|
else
|
|
20
20
|
@autodiscovery_association_renderer_cache ||= {}
|
|
@@ -14,7 +14,7 @@ module Plutonium
|
|
|
14
14
|
# If cache_discovery is enabled, use the class level cache that persists
|
|
15
15
|
# between requests, otherwise use the instance one.
|
|
16
16
|
def autodiscovery_input_cache
|
|
17
|
-
if
|
|
17
|
+
if Plutonium.configuration.cache_discovery
|
|
18
18
|
self.class.autodiscovery_input_cache
|
|
19
19
|
else
|
|
20
20
|
@autodiscovery_input_cache ||= {}
|
|
@@ -14,7 +14,7 @@ module Plutonium
|
|
|
14
14
|
# If cache_discovery is enabled, use the class level cache that persists
|
|
15
15
|
# between requests, otherwise use the instance one.
|
|
16
16
|
def autodiscovery_renderer_cache
|
|
17
|
-
if
|
|
17
|
+
if Plutonium.configuration.cache_discovery
|
|
18
18
|
self.class.autodiscovery_renderer_cache
|
|
19
19
|
else
|
|
20
20
|
@autodiscovery_renderer_cache ||= {}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Plutonium
|
|
2
|
+
module Core
|
|
3
|
+
module Controller
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include Plutonium::Core::Controllers::Bootable
|
|
6
|
+
include Plutonium::Core::Controllers::EntityScoping
|
|
7
|
+
include Plutonium::Core::Controllers::Authorizable
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
add_flash_types :success, :warning, :error
|
|
11
|
+
|
|
12
|
+
before_action do
|
|
13
|
+
next unless defined?(ActiveStorage)
|
|
14
|
+
|
|
15
|
+
ActiveStorage::Current.url_options = {protocol: request.protocol, host: request.host, port: request.port}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
helper Plutonium::Helpers
|
|
19
|
+
helper_method :make_page_title, :resource_url_for, :resource_url_args_for, :root_path
|
|
20
|
+
|
|
21
|
+
append_view_path File.expand_path("app/views", Plutonium.root)
|
|
22
|
+
layout -> { turbo_frame_request? ? false : "resource" }
|
|
23
|
+
helper_method :registered_resources
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def set_page_title(page_title)
|
|
29
|
+
@page_title = page_title
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def make_page_title(title)
|
|
33
|
+
[title.presence, helpers.application_name].compact.join(" | ")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Returns a dynamic list of args to be used with `url_for`, which considers the route namespace and nesting.
|
|
38
|
+
# The current entity and parent record (for nested routes) are inserted appropriately, ensuring that generated URLs
|
|
39
|
+
# obey the current routing.
|
|
40
|
+
#
|
|
41
|
+
# e.g., of route helpers that will be invoked given the output of this method
|
|
42
|
+
#
|
|
43
|
+
# - when invoked in a root route (/acme/dashboard/users)
|
|
44
|
+
#
|
|
45
|
+
# `resource_url_args_for User` => `entity_users_*`
|
|
46
|
+
# `resource_url_args_for @user` => `entity_user_*`
|
|
47
|
+
# `resource_url_args_for @user, action: :edit` => `edit_entity_user_*`
|
|
48
|
+
# `resource_url_args_for @user, Post` => `entity_user_posts_*`
|
|
49
|
+
#
|
|
50
|
+
# - when invoked in a nested route (/acme/dashboard/users/1/post/1)
|
|
51
|
+
#
|
|
52
|
+
# `resource_url_args_for Post` => `entity_user_posts_*`
|
|
53
|
+
# `resource_url_args_for @post` => `entity_user_post_*`
|
|
54
|
+
# `resource_url_args_for @post, action: :edit` => `edit_entity_user_post_*`
|
|
55
|
+
#
|
|
56
|
+
# @param [Class, ApplicationRecord] *args arguments you would normally pass to `url_for`
|
|
57
|
+
# @param [Symbol] action optional action to invoke, e.g., :new, :edit
|
|
58
|
+
# @param [ApplicationRecord] parent the parent record for nested routes, if any
|
|
59
|
+
# @param [Hash] kwargs additional keyword arguments to pass to `url_for`
|
|
60
|
+
#
|
|
61
|
+
# @return [Hash] args to pass to `url_for`
|
|
62
|
+
#
|
|
63
|
+
def resource_url_args_for(*args, action: nil, parent: nil, **kwargs)
|
|
64
|
+
url_args = {**kwargs, action: action}.compact
|
|
65
|
+
|
|
66
|
+
controller_chain = [current_package&.to_s].compact
|
|
67
|
+
[*args].compact.each_with_index do |element, index|
|
|
68
|
+
if element.is_a?(Class)
|
|
69
|
+
controller_chain << element.to_s.pluralize
|
|
70
|
+
else
|
|
71
|
+
controller_chain << element.class.to_s.pluralize
|
|
72
|
+
if index == args.length - 1
|
|
73
|
+
url_args[:id] = element.to_param
|
|
74
|
+
url_args[:action] ||= :show
|
|
75
|
+
else
|
|
76
|
+
url_args[element.model_name.singular_route_key.to_sym] = element.to_param
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
url_args[:controller] = "/#{controller_chain.join("::").underscore}"
|
|
81
|
+
|
|
82
|
+
url_args[:"#{parent.model_name.singular_route_key}_id"] = parent.to_param if parent.present?
|
|
83
|
+
if scoped_to_entity? && scoped_entity_strategy == :path
|
|
84
|
+
url_args[scoped_entity_param_key] = current_scoped_entity
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
url_args
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def resource_url_for(...)
|
|
91
|
+
args = resource_url_args_for(...)
|
|
92
|
+
if current_package.present?
|
|
93
|
+
send(current_package.name.underscore.to_sym).url_for(args)
|
|
94
|
+
else
|
|
95
|
+
url_for(args)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def root_path(*)
|
|
100
|
+
return send(:"#{scoped_entity_param_key}_root_path", *) if scoped_to_entity? && scoped_entity_strategy == :path
|
|
101
|
+
|
|
102
|
+
super(*)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def registered_resources
|
|
106
|
+
current_engine.resource_register.resources
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -1,56 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Plutonium
|
|
4
4
|
module Core
|
|
5
5
|
module Controllers
|
|
6
6
|
module Authorizable
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
|
-
include ::
|
|
8
|
+
include ActionPolicy::Controller
|
|
9
9
|
|
|
10
10
|
included do
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
authorize :user, through: :current_user
|
|
12
|
+
authorize :scope, through: :entity_scope_for_authorize
|
|
13
13
|
|
|
14
|
-
helper_method :
|
|
14
|
+
helper_method :policy_for, :authorized_resource_scope
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
|
-
def
|
|
20
|
-
raise
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def pundit_user
|
|
24
|
-
policy_context
|
|
25
|
-
end
|
|
19
|
+
def authorized_resource_scope(resource, **options)
|
|
20
|
+
raise ArgumentError("Expected resource to be a class inheriting ActiveRecord::Base") unless resource.instance_of?(Class) && resource < ActiveRecord::Base
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pundit ||= Plutonium::Pundit::Context.new(
|
|
30
|
-
package: current_package.to_s.underscore.to_sym,
|
|
31
|
-
user: pundit_user,
|
|
32
|
-
policy_cache: ::Pundit::CacheStore::LegacyStore.new(policies)
|
|
33
|
-
)
|
|
34
|
-
end
|
|
22
|
+
options[:with] ||= ActionPolicy.lookup(resource, namespace: authorization_namespace)
|
|
23
|
+
resource = resource.all
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
@permitted_attributes ||= current_policy.send_with_report :"permitted_attributes_for_#{action_name}"
|
|
25
|
+
authorized_scope(resource, **options)
|
|
38
26
|
end
|
|
39
27
|
|
|
40
|
-
def
|
|
41
|
-
|
|
28
|
+
def entity_scope_for_authorize
|
|
29
|
+
scoped_to_entity? ? current_scoped_entity : nil
|
|
42
30
|
end
|
|
43
|
-
|
|
44
|
-
def current_policy
|
|
45
|
-
@current_policy ||= begin
|
|
46
|
-
policy_subject = resource_record || resource_class
|
|
47
|
-
policy(policy_subject)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# def parent_policy
|
|
52
|
-
# @parent_policy ||= policy(current_parent) if current_parent.present?
|
|
53
|
-
# end
|
|
54
31
|
end
|
|
55
32
|
end
|
|
56
33
|
end
|
|
@@ -3,21 +3,52 @@ module Plutonium
|
|
|
3
3
|
module Controllers
|
|
4
4
|
module Bootable
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
|
+
include Plutonium::Engine::Validator
|
|
6
7
|
|
|
7
8
|
included do
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
helper_method :current_package, :current_engine
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def current_package
|
|
13
|
+
self.class.current_package
|
|
14
|
+
end
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
def current_engine
|
|
17
|
+
self.class.current_engine
|
|
12
18
|
end
|
|
13
19
|
|
|
14
20
|
class_methods do
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
include Plutonium::Lib::SmartCache
|
|
22
|
+
|
|
23
|
+
def inherited(subclass)
|
|
24
|
+
super
|
|
25
|
+
|
|
26
|
+
subclass.boot
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def boot(package = nil)
|
|
30
|
+
if package.present?
|
|
31
|
+
Plutonium.deprecator.warn(
|
|
32
|
+
"Calling boot with an argument is deprecated and no longer has an effect.",
|
|
33
|
+
caller_locations(1)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if current_engine != Rails.application.class
|
|
38
|
+
prepend_view_path current_engine.paths["app/views"].first
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def current_package
|
|
43
|
+
(current_engine == Rails.application.class) ? nil : current_engine.module_parent
|
|
44
|
+
end
|
|
45
|
+
memoize_unless_reloading :current_package
|
|
18
46
|
|
|
19
|
-
|
|
47
|
+
def current_engine
|
|
48
|
+
potential_package = module_parents[-2]
|
|
49
|
+
potential_package.nil? ? Rails.application.class : ("#{potential_package}::Engine".safe_constantize || Rails.application.class)
|
|
20
50
|
end
|
|
51
|
+
memoize_unless_reloading :current_engine
|
|
21
52
|
end
|
|
22
53
|
end
|
|
23
54
|
end
|
|
@@ -60,7 +60,7 @@ module Plutonium
|
|
|
60
60
|
# @raise [NotImplementedError] if not scoped to an entity
|
|
61
61
|
def scoped_entity_session_key
|
|
62
62
|
ensure_legal_entity_scoping_method_access!(__method__)
|
|
63
|
-
|
|
63
|
+
[current_package&.name&.underscore, "scoped_entity_id"].compact.join("__").to_sym
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Returns the current scoped entity for the request.
|
|
@@ -69,6 +69,8 @@ module Plutonium
|
|
|
69
69
|
# @raise [NotImplementedError] if not scoped to an entity or strategy is unknown
|
|
70
70
|
def current_scoped_entity
|
|
71
71
|
ensure_legal_entity_scoping_method_access!(__method__)
|
|
72
|
+
# this method might be invoked even when not authenticated.
|
|
73
|
+
# so let's guard against that.
|
|
72
74
|
return unless current_user.present?
|
|
73
75
|
|
|
74
76
|
@current_scoped_entity ||= fetch_current_scoped_entity
|
|
@@ -79,7 +81,7 @@ module Plutonium
|
|
|
79
81
|
# @return [ActiveRecord::Base, nil] the current scoped entity or nil if not found
|
|
80
82
|
# @raise [NotImplementedError] if the scoping strategy is unknown
|
|
81
83
|
def fetch_current_scoped_entity
|
|
82
|
-
case scoped_entity_strategy
|
|
84
|
+
scoped_entity = case scoped_entity_strategy
|
|
83
85
|
when :path
|
|
84
86
|
fetch_entity_from_path
|
|
85
87
|
when Symbol
|
|
@@ -87,6 +89,8 @@ module Plutonium
|
|
|
87
89
|
else
|
|
88
90
|
raise NotImplementedError, "Unknown scoped entity strategy: #{scoped_entity_strategy.inspect}"
|
|
89
91
|
end
|
|
92
|
+
authorize! scoped_entity, to: :read?
|
|
93
|
+
scoped_entity
|
|
90
94
|
end
|
|
91
95
|
|
|
92
96
|
# Fetches the scoped entity from the path parameters.
|
|
@@ -18,7 +18,7 @@ module Plutonium
|
|
|
18
18
|
|
|
19
19
|
def renderer_options
|
|
20
20
|
{
|
|
21
|
-
helper: value.class.include?(Plutonium
|
|
21
|
+
# helper: value.class.include?(Plutonium: :Resource: :Record) ? :display_association_value : :display_name_of
|
|
22
22
|
}
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Plutonium
|
|
2
|
+
module Definition
|
|
3
|
+
module Actions
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
defineable_prop :action
|
|
8
|
+
|
|
9
|
+
def self.action(name, interaction: nil, **)
|
|
10
|
+
defined_actions[name] = if interaction
|
|
11
|
+
Plutonium::Action::Interactive::Factory.create(name, interaction:, **)
|
|
12
|
+
else
|
|
13
|
+
Plutonium::Action::Simple.new(name, **)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def action(name, **)
|
|
18
|
+
instance_defined_actions[name] = Plutonium::Action::Simple.new(name, **)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def defined_actions
|
|
22
|
+
@merged_defined_actions ||= begin
|
|
23
|
+
customize_actions
|
|
24
|
+
merged = self.class.defined_actions.merge(instance_defined_actions)
|
|
25
|
+
merged.sort_by { |k, v| v.position }.to_h
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# standard CRUD actions
|
|
30
|
+
|
|
31
|
+
action(:new, route_options: {action: :new},
|
|
32
|
+
resource_action: true, category: :primary,
|
|
33
|
+
icon: Phlex::TablerIcons::Plus, position: 10)
|
|
34
|
+
|
|
35
|
+
action(:show, route_options: {action: :show},
|
|
36
|
+
collection_record_action: true,
|
|
37
|
+
icon: Phlex::TablerIcons::Eye, position: 10)
|
|
38
|
+
|
|
39
|
+
action(:edit, route_options: {action: :edit},
|
|
40
|
+
record_action: true, collection_record_action: true,
|
|
41
|
+
icon: Phlex::TablerIcons::Edit, position: 20)
|
|
42
|
+
|
|
43
|
+
action(:destroy, route_options: {method: :delete},
|
|
44
|
+
record_action: true, collection_record_action: true, category: :danger,
|
|
45
|
+
icon: Phlex::TablerIcons::Trash, position: 100,
|
|
46
|
+
confirmation: "Are you sure?", turbo_frame: "_top")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Definition
|
|
5
|
+
# Base class for Plutonium definitions
|
|
6
|
+
#
|
|
7
|
+
# @abstract Subclass and override {#customize_fields}, {#customize_inputs},
|
|
8
|
+
# {#customize_filters}, {#customize_scopes}, and {#customize_sorters}
|
|
9
|
+
# to implement custom behavior.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# class MyDefinition < Plutonium::Definition::Base
|
|
13
|
+
# field :name, as: :string
|
|
14
|
+
# input :email, as: :email
|
|
15
|
+
# filter :status, type: :select, collection: %w[active inactive]
|
|
16
|
+
# scope :active, default: true
|
|
17
|
+
# sorter :created_at
|
|
18
|
+
#
|
|
19
|
+
# def customize_fields
|
|
20
|
+
# field :custom_field, as: :integer
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @note This class is not thread-safe. Ensure proper synchronization
|
|
25
|
+
# if used in a multi-threaded environment.
|
|
26
|
+
class Base
|
|
27
|
+
include DefineableProps
|
|
28
|
+
include ConfigAttr
|
|
29
|
+
include Actions
|
|
30
|
+
include Search
|
|
31
|
+
|
|
32
|
+
class IndexPage < Plutonium::UI::Page::Index; end
|
|
33
|
+
|
|
34
|
+
class NewPage < Plutonium::UI::Page::New; end
|
|
35
|
+
|
|
36
|
+
class ShowPage < Plutonium::UI::Page::Show; end
|
|
37
|
+
|
|
38
|
+
class EditPage < Plutonium::UI::Page::Edit; end
|
|
39
|
+
|
|
40
|
+
class Form < Plutonium::UI::Form::Resource; end
|
|
41
|
+
|
|
42
|
+
class Table < Plutonium::UI::Table::Resource; end
|
|
43
|
+
|
|
44
|
+
class Display < Plutonium::UI::Display::Resource; end
|
|
45
|
+
|
|
46
|
+
# fields
|
|
47
|
+
defineable_props :field, :input, :display, :column
|
|
48
|
+
|
|
49
|
+
# queries
|
|
50
|
+
defineable_props :filter, :scope, :sort
|
|
51
|
+
|
|
52
|
+
# pages
|
|
53
|
+
config_attr \
|
|
54
|
+
:index_page_title, :index_page_description,
|
|
55
|
+
:show_page_title, :show_page_description,
|
|
56
|
+
:new_page_title, :new_page_description,
|
|
57
|
+
:edit_page_title, :edit_page_description
|
|
58
|
+
|
|
59
|
+
def initialize
|
|
60
|
+
super
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def index_page_class
|
|
64
|
+
self.class::IndexPage
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def new_page_class
|
|
68
|
+
self.class::NewPage
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def show_page_class
|
|
72
|
+
self.class::ShowPage
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def edit_page_class
|
|
76
|
+
self.class::EditPage
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def form_class
|
|
80
|
+
self.class::Form
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def collection_class
|
|
84
|
+
self.class::Table
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def detail_class
|
|
88
|
+
self.class::Display
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Plutonium
|
|
2
|
+
module Definition
|
|
3
|
+
module ConfigAttr
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
class_methods do
|
|
7
|
+
def config_attr(*names)
|
|
8
|
+
names.each do |name|
|
|
9
|
+
define_singleton_method(name) do |value = :__this_is_so_nilly__|
|
|
10
|
+
if value == :__this_is_so_nilly__
|
|
11
|
+
# Getter behavior
|
|
12
|
+
# if singleton_class.instance_variable_defined?(:"@#{name}")
|
|
13
|
+
singleton_class.instance_variable_get(:"@#{name}")
|
|
14
|
+
# end
|
|
15
|
+
else
|
|
16
|
+
# Setter behavior
|
|
17
|
+
singleton_class.instance_variable_set(:"@#{name}", value)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Instance-level method
|
|
22
|
+
define_method(name) do
|
|
23
|
+
self.class.send(name)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|