plutonium 0.33.1 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/# Plutonium: The pre-alpha demo.md +4 -2
  3. data/.claude/skills/assets/SKILL.md +416 -0
  4. data/.claude/skills/connect-resource/SKILL.md +112 -0
  5. data/.claude/skills/controller/SKILL.md +302 -0
  6. data/.claude/skills/create-resource/SKILL.md +240 -0
  7. data/.claude/skills/definition/SKILL.md +218 -0
  8. data/.claude/skills/definition-actions/SKILL.md +386 -0
  9. data/.claude/skills/definition-fields/SKILL.md +474 -0
  10. data/.claude/skills/definition-query/SKILL.md +334 -0
  11. data/.claude/skills/forms/SKILL.md +439 -0
  12. data/.claude/skills/installation/SKILL.md +300 -0
  13. data/.claude/skills/interaction/SKILL.md +382 -0
  14. data/.claude/skills/model/SKILL.md +267 -0
  15. data/.claude/skills/model-features/SKILL.md +286 -0
  16. data/.claude/skills/nested-resources/SKILL.md +274 -0
  17. data/.claude/skills/package/SKILL.md +191 -0
  18. data/.claude/skills/policy/SKILL.md +352 -0
  19. data/.claude/skills/portal/SKILL.md +400 -0
  20. data/.claude/skills/resource/SKILL.md +281 -0
  21. data/.claude/skills/rodauth/SKILL.md +452 -0
  22. data/.claude/skills/views/SKILL.md +563 -0
  23. data/Appraisals +46 -4
  24. data/CHANGELOG.md +32 -1
  25. data/app/assets/plutonium.css +2 -2
  26. data/config/brakeman.ignore +239 -0
  27. data/config/initializers/action_policy.rb +1 -1
  28. data/docs/.vitepress/config.ts +132 -47
  29. data/docs/concepts/architecture.md +226 -0
  30. data/docs/concepts/auto-detection.md +254 -0
  31. data/docs/concepts/index.md +61 -0
  32. data/docs/concepts/packages-portals.md +304 -0
  33. data/docs/concepts/resources.md +224 -0
  34. data/docs/cookbook/blog.md +412 -0
  35. data/docs/cookbook/index.md +289 -0
  36. data/docs/cookbook/saas.md +481 -0
  37. data/docs/getting-started/index.md +56 -0
  38. data/docs/getting-started/installation.md +146 -0
  39. data/docs/getting-started/tutorial/01-setup.md +118 -0
  40. data/docs/getting-started/tutorial/02-first-resource.md +180 -0
  41. data/docs/getting-started/tutorial/03-authentication.md +246 -0
  42. data/docs/getting-started/tutorial/04-authorization.md +170 -0
  43. data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
  44. data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
  45. data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
  46. data/docs/getting-started/tutorial/index.md +64 -0
  47. data/docs/guides/adding-resources.md +420 -0
  48. data/docs/guides/authentication.md +551 -0
  49. data/docs/guides/authorization.md +468 -0
  50. data/docs/guides/creating-packages.md +380 -0
  51. data/docs/guides/custom-actions.md +523 -0
  52. data/docs/guides/index.md +45 -0
  53. data/docs/guides/multi-tenancy.md +302 -0
  54. data/docs/guides/nested-resources.md +411 -0
  55. data/docs/guides/search-filtering.md +266 -0
  56. data/docs/guides/theming.md +321 -0
  57. data/docs/index.md +67 -26
  58. data/docs/public/CLAUDE.md +64 -21
  59. data/docs/reference/assets/index.md +496 -0
  60. data/docs/reference/controller/index.md +363 -0
  61. data/docs/reference/definition/actions.md +400 -0
  62. data/docs/reference/definition/fields.md +350 -0
  63. data/docs/reference/definition/index.md +252 -0
  64. data/docs/reference/definition/query.md +342 -0
  65. data/docs/reference/generators/index.md +469 -0
  66. data/docs/reference/index.md +49 -0
  67. data/docs/reference/interaction/index.md +445 -0
  68. data/docs/reference/model/features.md +248 -0
  69. data/docs/reference/model/index.md +219 -0
  70. data/docs/reference/policy/index.md +385 -0
  71. data/docs/reference/portal/index.md +382 -0
  72. data/docs/reference/views/forms.md +396 -0
  73. data/docs/reference/views/index.md +479 -0
  74. data/gemfiles/rails_7.gemfile +9 -2
  75. data/gemfiles/rails_7.gemfile.lock +146 -111
  76. data/gemfiles/rails_8.0.gemfile +20 -0
  77. data/gemfiles/rails_8.0.gemfile.lock +417 -0
  78. data/gemfiles/rails_8.1.gemfile +20 -0
  79. data/gemfiles/rails_8.1.gemfile.lock +419 -0
  80. data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
  81. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
  82. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
  83. data/lib/generators/pu/pkg/portal/USAGE +65 -0
  84. data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
  85. data/lib/generators/pu/res/conn/USAGE +71 -0
  86. data/lib/generators/pu/res/model/USAGE +106 -110
  87. data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
  88. data/lib/generators/pu/res/scaffold/USAGE +85 -0
  89. data/lib/generators/pu/rodauth/install_generator.rb +2 -6
  90. data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
  91. data/lib/generators/pu/skills/sync/USAGE +14 -0
  92. data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
  93. data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
  94. data/lib/plutonium/core/controller.rb +2 -2
  95. data/lib/plutonium/interaction/base.rb +1 -0
  96. data/lib/plutonium/package/engine.rb +2 -2
  97. data/lib/plutonium/query/adhoc_block.rb +6 -2
  98. data/lib/plutonium/query/model_scope.rb +1 -1
  99. data/lib/plutonium/railtie.rb +4 -0
  100. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
  101. data/lib/plutonium/resource/query_object.rb +38 -8
  102. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  103. data/lib/plutonium/version.rb +1 -1
  104. data/lib/tasks/release.rake +19 -4
  105. data/package.json +1 -1
  106. metadata +76 -39
  107. data/brakeman.ignore +0 -28
  108. data/docs/api-examples.md +0 -49
  109. data/docs/guide/claude-code-guide.md +0 -74
  110. data/docs/guide/deep-dive/authorization.md +0 -189
  111. data/docs/guide/deep-dive/multitenancy.md +0 -256
  112. data/docs/guide/deep-dive/resources.md +0 -390
  113. data/docs/guide/getting-started/01-installation.md +0 -165
  114. data/docs/guide/index.md +0 -28
  115. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  116. data/docs/guide/introduction/02-core-concepts.md +0 -440
  117. data/docs/guide/tutorial/01-project-setup.md +0 -75
  118. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  119. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  120. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  121. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  122. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  123. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  124. data/docs/markdown-examples.md +0 -85
  125. data/docs/modules/action.md +0 -244
  126. data/docs/modules/authentication.md +0 -236
  127. data/docs/modules/configuration.md +0 -599
  128. data/docs/modules/controller.md +0 -443
  129. data/docs/modules/core.md +0 -316
  130. data/docs/modules/definition.md +0 -1308
  131. data/docs/modules/display.md +0 -759
  132. data/docs/modules/form.md +0 -495
  133. data/docs/modules/generator.md +0 -400
  134. data/docs/modules/index.md +0 -167
  135. data/docs/modules/interaction.md +0 -642
  136. data/docs/modules/package.md +0 -151
  137. data/docs/modules/policy.md +0 -176
  138. data/docs/modules/portal.md +0 -710
  139. data/docs/modules/query.md +0 -297
  140. data/docs/modules/resource_record.md +0 -618
  141. data/docs/modules/routing.md +0 -690
  142. data/docs/modules/table.md +0 -301
  143. data/docs/modules/ui.md +0 -631
@@ -1,244 +0,0 @@
1
- ---
2
- title: Action Module
3
- ---
4
-
5
- # Action Module
6
-
7
- The Action module provides a comprehensive system for defining and managing custom actions in Plutonium applications. Actions represent operations that can be performed on resources, from simple navigation to complex business logic.
8
-
9
- ::: tip
10
- The Action module is located in `lib/plutonium/action/`. Actions are typically defined within a resource's Definition file.
11
- :::
12
-
13
- ## Overview
14
-
15
- - **Declarative Definition**: Define actions with metadata and behavior in your resource definition files.
16
- - **Multiple Action Types**: Support for actions on individual records, entire resources, or bulk collections.
17
- - **UI Integration**: Automatic button and form generation in the UI.
18
- - **Authorization Support**: Integrates with policies to control action visibility and execution.
19
-
20
- ## Defining Actions
21
-
22
- Actions are typically defined in a resource definition file using the `action` method.
23
-
24
- ```ruby
25
- # app/definitions/post_definition.rb
26
- class PostDefinition < Plutonium::Resource::Definition
27
- # An action that runs a complex operation via an Interaction
28
- action :publish,
29
- interaction: PublishPostInteraction,
30
- icon: Phlex::TablerIcons::Send,
31
- category: :primary
32
-
33
- # A simple navigation action
34
- action :export,
35
- route_options: { action: :export, format: :csv },
36
- icon: Phlex::TablerIcons::Download,
37
- resource_action: true # This is a resource-level action
38
-
39
- # A destructive action with a confirmation
40
- action :archive,
41
- interaction: ArchivePostInteraction,
42
- icon: Phlex::TablerIcons::Archive,
43
- category: :danger,
44
- confirmation: "Are you sure you want to archive this post?"
45
- end
46
- ```
47
-
48
- ### Action Types
49
-
50
- You can specify where an action should be available.
51
-
52
- ::: code-group
53
- ```ruby [Record Action]
54
- # Shows on the record's #show page and in the table row.
55
- action :edit,
56
- record_action: true,
57
- collection_record_action: true,
58
- icon: Phlex::TablerIcons::Edit
59
- ```
60
- ```ruby [Resource Action]
61
- # Shows on the resource's #index page (e.g., "Import").
62
- action :import,
63
- resource_action: true,
64
- icon: Phlex::TablerIcons::Upload
65
- ```
66
- ```ruby [Bulk Action]
67
- # Appears when records are selected on the #index page.
68
- action :bulk_delete,
69
- bulk_action: true,
70
- category: :danger,
71
- icon: Phlex::TablerIcons::Trash
72
- ```
73
- :::
74
-
75
- ### Action Categories & Positioning
76
-
77
- Categories and positioning control the visual appearance and order of action buttons.
78
-
79
- - **`category`**: Can be `:primary`, `:secondary`, or `:danger`.
80
- - **`position`**: A number used for sorting; lower numbers appear first (default: 50).
81
-
82
- ```ruby
83
- # A prominent primary action
84
- action :create, category: :primary, position: 10
85
-
86
- # A standard secondary action
87
- action :archive, category: :secondary, position: 50
88
-
89
- # A destructive action, shown last
90
- action :delete, category: :danger, position: 100
91
- ```
92
-
93
- ## Action Types
94
-
95
- ### Simple Actions
96
-
97
- Simple actions are for basic navigation or links. They are defined with `route_options`.
98
-
99
- ::: code-group
100
- ```ruby [Internal Link]
101
- # Navigates to the #reports action on the current controller
102
- action :view_reports,
103
- label: "View Reports",
104
- route_options: { action: :reports },
105
- icon: Phlex::TablerIcons::ChartBar
106
- ```
107
- ```ruby [External Link]
108
- # Links to an external URL
109
- action :documentation,
110
- label: "Documentation",
111
- route_options: { url: "https://docs.example.com" },
112
- icon: Phlex::TablerIcons::Book
113
- ```
114
- :::
115
-
116
- ### Dynamic Route Options
117
-
118
- For actions that need dynamic URL generation based on the current record or context, use the `RouteOptions` class with a custom `url_resolver`:
119
-
120
- ::: code-group
121
- ```ruby [Dynamic Parent-Child Navigation]
122
- # Navigate to create a child resource with the current record as parent
123
- action :create_deployment,
124
- label: "Create Deployment",
125
- icon: Phlex::TablerIcons::Rocket,
126
- record_action: true,
127
- route_options: Plutonium::Action::RouteOptions.new(
128
- url_resolver: ->(subject) {
129
- resource_url_for(UniversalFlow::Deployment, action: :new, parent: subject)
130
- }
131
- )
132
- ```
133
- ```ruby [Conditional Routing]
134
- # Different routes based on user permissions or record state
135
- action :manage_settings,
136
- label: "Manage Settings",
137
- resource_action: true,
138
- route_options: Plutonium::Action::RouteOptions.new(
139
- url_resolver: ->(subject) {
140
- if current_user.admin?
141
- admin_settings_path(subject)
142
- else
143
- basic_settings_path(subject)
144
- end
145
- }
146
- )
147
- ```
148
- ```ruby [External Integration]
149
- # Dynamic external URLs based on record attributes
150
- action :view_external,
151
- label: "View in External System",
152
- record_action: true,
153
- route_options: Plutonium::Action::RouteOptions.new(
154
- url_resolver: ->(subject) {
155
- "https://external-system.com/items/#{subject.external_id}"
156
- }
157
- )
158
- ```
159
- :::
160
-
161
- The `url_resolver` lambda receives the current record (for record actions) or resource class (for resource actions) as the `subject` parameter, allowing you to generate URLs dynamically based on the context.
162
-
163
- ### Interactive Actions
164
-
165
- Interactive actions are powered by an `Interaction` class and handle business logic. The action's properties (label, description, etc.) are often inferred from the interaction itself.
166
-
167
- ```ruby
168
- # The action is automatically configured based on the interaction
169
- action :publish,
170
- interaction: PublishPostInteraction,
171
- icon: Phlex::TablerIcons::Send
172
- ```
173
-
174
- ::: details Automatic Type Detection from Interaction
175
- The Action module inspects the interaction's attributes to determine its type (`record`, `resource`, or `bulk`).
176
- ```ruby
177
- # This will be a RECORD action because it has a `:resource` attribute.
178
- class PublishPostInteraction < Plutonium::Interaction::Base
179
- attribute :resource
180
- attribute :publish_date, :date
181
- end
182
-
183
- # This will be a BULK action because it has a `:resources` attribute.
184
- class BulkArchiveInteraction < Plutonium::Interaction::Base
185
- attribute :resources
186
- attribute :archive_reason, :string
187
- end
188
-
189
- # This will be a RESOURCE action because it has neither.
190
- class ImportPostsInteraction < Plutonium::Interaction::Base
191
- attribute :csv_file, :string
192
- end
193
- ```
194
- :::
195
-
196
- ::: details Immediate vs. Modal Actions
197
- - If an interaction has **only** a `resource` or `resources` attribute, the action will execute immediately on click.
198
- - If it has **any other attributes**, a modal form will be rendered to collect user input before execution.
199
- ```ruby
200
- # IMMEDIATE: No extra inputs, runs on click.
201
- class SimplePublishInteraction < Plutonium::Interaction::Base
202
- attribute :resource
203
- end
204
-
205
- # MODAL: Requires `publish_date`, so a form will be shown.
206
- class ScheduledPublishInteraction < Plutonium::Interaction::Base
207
- attribute :resource
208
- attribute :publish_date, :datetime
209
- end
210
- ```
211
- :::
212
-
213
- ## Best Practices
214
-
215
- ### Action Design
216
-
217
- 1. **Clear Intent**: Use descriptive action names that clearly indicate what they do
218
- 2. **Consistent Categories**: Group related actions using consistent categories
219
- 3. **Appropriate Icons**: Choose icons that clearly represent the action
220
- 4. **Meaningful Confirmations**: Use confirmation messages for destructive actions
221
- 5. **Logical Positioning**: Order actions by importance and frequency of use
222
-
223
- ### Dynamic Route Actions
224
-
225
- 1. **Context Awareness**: Use the subject parameter to make routing decisions based on the current record or resource
226
- 2. **Error Handling**: Handle cases where dynamic URLs might fail (e.g., missing external IDs)
227
- 3. **Performance**: Keep url_resolver lambdas simple to avoid performance issues
228
- 4. **Security**: Validate permissions within the lambda when generating sensitive URLs
229
-
230
- ### Interactive Actions
231
-
232
- 1. **Single Purpose**: Each action should have a single, well-defined purpose
233
- 2. **Input Validation**: Always validate user inputs in the interaction
234
- 3. **Error Messages**: Provide clear, actionable error messages
235
- 4. **Success Feedback**: Give users clear feedback when actions complete successfully
236
- 5. **Idempotency**: Design actions to be safely repeatable when possible
237
-
238
- ### Security
239
-
240
- 1. **Authorization**: Always check user permissions before executing actions
241
- 2. **Input Sanitization**: Sanitize all user inputs
242
- 3. **CSRF Protection**: Include CSRF tokens in all action forms
243
- 4. **Rate Limiting**: Implement rate limiting for resource-intensive actions
244
- 5. **Audit Logging**: Log important actions for security auditing
@@ -1,236 +0,0 @@
1
- ---
2
- title: Authentication Module
3
- ---
4
-
5
- # Authentication Module
6
-
7
- The Authentication module provides comprehensive authentication capabilities for Plutonium applications. It integrates seamlessly with Rodauth for authentication while offering flexibility for different application security needs.
8
-
9
- ::: tip
10
- The Authentication module is located in `lib/plutonium/auth/`.
11
- :::
12
-
13
- ## Overview
14
-
15
- - **Rodauth Integration**: Seamless integration with Rodauth authentication
16
- - **Public Access Support**: Optional public access for applications without authentication
17
- - **Multi-Account Support**: Support for multiple user types and authentication contexts
18
- - **Portal-Aware Security**: Authentication scoped to specific portals/packages
19
- - **Flexible Configuration**: Support for custom authentication systems
20
- - **Security Features**: Built-in security best practices and configurations
21
-
22
- ## Core Components
23
-
24
- ::: code-group
25
- ```ruby [Rodauth Integration]
26
- # lib/plutonium/auth/rodauth.rb
27
- # Basic Rodauth integration
28
- module MyApp
29
- module Concerns
30
- module Controller
31
- extend ActiveSupport::Concern
32
- include Plutonium::Auth::Rodauth(:main)
33
-
34
- # Automatically provides:
35
- # - current_user helper method
36
- # - logout_url helper method
37
- # - Proper URL options handling
38
- end
39
- end
40
- end
41
- ```
42
-
43
- ```ruby [Public Access]
44
- # For applications that don't require authentication
45
- module MyApp
46
- module Concerns
47
- module Controller
48
- extend ActiveSupport::Concern
49
- include Plutonium::Auth::Public
50
- end
51
- end
52
- end
53
- ```
54
-
55
- ```ruby [Custom Authentication]
56
- # For applications using custom authentication systems
57
- module MyApp
58
- module Concerns
59
- module Controller
60
- extend ActiveSupport::Concern
61
-
62
- included do
63
- helper_method :current_user
64
- end
65
-
66
- def current_user
67
- # Your custom authentication logic
68
- @current_user ||= User.find(session[:user_id]) if session[:user_id]
69
- end
70
- end
71
- end
72
- end
73
- ```
74
- :::
75
-
76
- ### Automatic Helper Methods
77
-
78
- When you include `Plutonium::Auth::Rodauth`, you automatically get:
79
-
80
- - `current_user`: Returns the authenticated user/account (available in controllers and views).
81
- - `logout_url`: Returns the logout URL for the current account type (available in controllers and views).
82
- - `rodauth`: Access to the Rodauth instance (available in controllers only).
83
-
84
- ## Rodauth Configuration
85
-
86
- ### Account Generation
87
-
88
- Plutonium provides generators for creating Rodauth accounts:
89
-
90
- ::: code-group
91
- ```bash [Basic User Account]
92
- rails generate pu:rodauth:account user
93
- ```
94
-
95
- ```bash [Admin Account]
96
- rails generate pu:rodauth:admin admin
97
- ```
98
-
99
- ```bash [Custom Features]
100
- rails generate pu:rodauth:account customer \
101
- --no-defaults \
102
- --login --logout --create-account --verify-account \
103
- --reset-password --change-password --remember
104
- ```
105
- :::
106
-
107
- ### Configuration Examples
108
-
109
- ::: details Standard Rodauth Plugin Configuration
110
- ```ruby
111
- # app/rodauth/user_rodauth_plugin.rb
112
- class UserRodauthPlugin < RodauthPlugin
113
- configure do
114
- # Enable features
115
- enable :login, :logout, :create_account, :verify_account,
116
- :reset_password, :change_password, :remember
117
-
118
- # Account model
119
- rails_account_model { User }
120
-
121
- # Controller for views and CSRF
122
- rails_controller { Rodauth::UserController }
123
-
124
- # Redirects
125
- login_redirect "/"
126
- logout_redirect "/"
127
- create_account_redirect "/"
128
-
129
- # Email configuration
130
- create_reset_password_email do
131
- UserMailer.reset_password(account_id, reset_password_key_value)
132
- end
133
-
134
- # Remember feature
135
- after_login { remember_login }
136
- extend_remember_deadline? true
137
-
138
- # Password requirements
139
- password_minimum_length 8
140
-
141
- # Custom validation
142
- before_create_account do
143
- throw_error_status(422, "name", "must be present") if param("name").empty?
144
- end
145
- end
146
- end
147
- ```
148
- :::
149
-
150
- ::: details Enhanced Admin Configuration with MFA
151
- ```ruby
152
- # app/rodauth/admin_rodauth_plugin.rb
153
- class AdminRodauthPlugin < RodauthPlugin
154
- configure do
155
- enable :login, :logout, :create_account, :verify_account,
156
- :reset_password, :change_password, :remember,
157
- :otp, :recovery_codes, :lockout, :active_sessions,
158
- :audit_logging, :password_grace_period, :internal_request
159
-
160
- # Account model
161
- rails_account_model { Admin }
162
-
163
- # Controller
164
- rails_controller { Rodauth::AdminController }
165
-
166
- # Prefix for admin routes
167
- prefix "/admin"
168
-
169
- # Require MFA setup
170
- two_factor_not_setup_error_flash "You need to setup two factor authentication"
171
- two_factor_auth_return_to_requested_location? true
172
-
173
- # Multi-phase login for enhanced security
174
- use_multi_phase_login? true
175
-
176
- # Prevent web signup for admin accounts
177
- before_create_account_route do
178
- request.halt unless internal_request?
179
- end
180
-
181
- # Enhanced security settings
182
- max_invalid_logins 3
183
- lockout_deadline_interval Hash[minutes: 60]
184
-
185
- # Session security
186
- session_key "_admin_session"
187
- remember_cookie_key "_admin_remember"
188
- end
189
- end
190
- ```
191
- :::
192
-
193
- ## Portal Integration
194
-
195
- Each portal can have its own authentication requirements, allowing you to secure different parts of your application with different user types.
196
-
197
- ::: code-group
198
- ```ruby [Admin Portal]
199
- # Admin portal with admin authentication
200
- module AdminPortal
201
- module Concerns
202
- module Controller
203
- extend ActiveSupport::Concern
204
- include Plutonium::Portal::Controller
205
- include Plutonium::Auth::Rodauth(:admin)
206
- end
207
- end
208
- end
209
- ```
210
-
211
- ```ruby [Customer Portal]
212
- # Customer portal with customer authentication
213
- module CustomerPortal
214
- module Concerns
215
- module Controller
216
- extend ActiveSupport::Concern
217
- include Plutonium::Portal::Controller
218
- include Plutonium::Auth::Rodauth(:customer)
219
- end
220
- end
221
- end
222
- ```
223
-
224
- ```ruby [Public Portal]
225
- # Public portal without authentication
226
- module PublicPortal
227
- module Concerns
228
- module Controller
229
- extend ActiveSupport::Concern
230
- include Plutonium::Portal::Controller
231
- include Plutonium::Auth::Public
232
- end
233
- end
234
- end
235
- ```
236
- :::