plutonium 0.33.0 → 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 (145) 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 +36 -0
  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/controllers/crud_actions.rb +21 -18
  102. data/lib/plutonium/resource/controllers/interactive_actions.rb +21 -25
  103. data/lib/plutonium/resource/query_object.rb +38 -8
  104. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  105. data/lib/plutonium/version.rb +1 -1
  106. data/lib/tasks/release.rake +19 -4
  107. data/package.json +1 -1
  108. metadata +76 -39
  109. data/brakeman.ignore +0 -28
  110. data/docs/api-examples.md +0 -49
  111. data/docs/guide/claude-code-guide.md +0 -74
  112. data/docs/guide/deep-dive/authorization.md +0 -189
  113. data/docs/guide/deep-dive/multitenancy.md +0 -256
  114. data/docs/guide/deep-dive/resources.md +0 -390
  115. data/docs/guide/getting-started/01-installation.md +0 -165
  116. data/docs/guide/index.md +0 -28
  117. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  118. data/docs/guide/introduction/02-core-concepts.md +0 -440
  119. data/docs/guide/tutorial/01-project-setup.md +0 -75
  120. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  121. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  122. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  123. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  124. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  125. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  126. data/docs/markdown-examples.md +0 -85
  127. data/docs/modules/action.md +0 -244
  128. data/docs/modules/authentication.md +0 -236
  129. data/docs/modules/configuration.md +0 -599
  130. data/docs/modules/controller.md +0 -443
  131. data/docs/modules/core.md +0 -316
  132. data/docs/modules/definition.md +0 -1308
  133. data/docs/modules/display.md +0 -759
  134. data/docs/modules/form.md +0 -495
  135. data/docs/modules/generator.md +0 -400
  136. data/docs/modules/index.md +0 -167
  137. data/docs/modules/interaction.md +0 -642
  138. data/docs/modules/package.md +0 -151
  139. data/docs/modules/policy.md +0 -176
  140. data/docs/modules/portal.md +0 -710
  141. data/docs/modules/query.md +0 -297
  142. data/docs/modules/resource_record.md +0 -618
  143. data/docs/modules/routing.md +0 -690
  144. data/docs/modules/table.md +0 -301
  145. data/docs/modules/ui.md +0 -631
@@ -1,211 +0,0 @@
1
- # What is Plutonium?
2
-
3
- Plutonium is a Rapid Application Development (RAD) toolkit that extends Rails with powerful conventions, patterns, and tools to accelerate application development while maintaining flexibility and maintainability.
4
-
5
- It acts as a higher-level abstraction on top of Rails, providing ready-to-use solutions for common application needs while preserving Rails' elegance.
6
-
7
- ## Core Architecture
8
-
9
- ### Rails-Native Design
10
-
11
- Plutonium integrates seamlessly with Rails, extending its conventions rather than replacing them:
12
-
13
- ```ruby
14
- # Plutonium controllers inherit from your ApplicationController
15
- class ResourceController < ApplicationController
16
- include Plutonium::Resource::Controller
17
- end
18
-
19
- # Plutonium controllers are Rails controllers
20
- class ProductsController < ResourceController
21
- # Enhanced with resource capabilities
22
- def custom_action
23
- # Regular Rails code works just fine
24
- respond_to do |format|
25
- format.html
26
- format.json
27
- end
28
- end
29
- end
30
- ```
31
-
32
- ### Resource-Oriented Architecture
33
-
34
- Resources are the building blocks of Plutonium applications:
35
-
36
- ```ruby
37
- class ProductDefinition < ResourceDefinition
38
- # Declarative field definitions
39
- field :name, as: :string
40
- field :description, as: :markdown
41
- field :price_cents, as: :money
42
-
43
- # Resource-level search
44
- search do |scope, query|
45
- scope.where("name LIKE ?", "%#{query}%")
46
- end
47
-
48
- # Business actions
49
- action :publish, interaction: PublishProduct
50
- action :archive, interaction: ArchiveProduct
51
- end
52
- ```
53
-
54
- ### Modular by Design
55
-
56
- Plutonium's packaging system helps organize code into focused, reusable modules:
57
-
58
- ```ruby
59
- # Generate different types of packages
60
- rails generate pu:pkg:portal admin
61
- rails generate pu:pkg:package inventory
62
-
63
- # Packages are isolated and focused
64
- module AdminPortal
65
- class Engine < Rails::Engine
66
- include Plutonium::Portal::Engine
67
- end
68
- end
69
-
70
- AdminPortal::Engine.routes.draw do
71
- register_resource ::Product
72
- end
73
- ```
74
-
75
- ### Progressive Enhancement
76
-
77
- Built on modern web technologies like Hotwire, delivering rich interactivity without sacrificing simplicity:
78
-
79
- ```ruby
80
- class PublishProduct < ResourceInteraction
81
- attribute :schedule_for, :datetime
82
- attribute :notify_users, :boolean
83
-
84
- def execute
85
- resource.publish!(schedule_for:)
86
- notify_users! if notify_users
87
-
88
- success(resource)
89
- .with_message("Product scheduled for publishing")
90
- .with_render_response(NotificationComponent.new)
91
- end
92
- end
93
- ```
94
-
95
- ## Use Cases
96
-
97
- ### Business Applications
98
-
99
- ::: details Enterprise Resource Planning (ERP)
100
- ```ruby
101
- class InvoiceDefinition < ResourceDefinition
102
- # Rich field handling
103
- field :line_items, as: :nested, limit: 20
104
- field :attachments, as: :document, multiple: true
105
-
106
- # Business actions
107
- action :submit_for_approval, interaction: SubmitForApproval
108
- action :approve, interaction: ApproveInvoice
109
- action :reject, interaction: RejectInvoice
110
-
111
- # Workflow states
112
- scope :draft
113
- scope :pending_approval
114
- scope :approved
115
- end
116
- ```
117
- :::
118
-
119
- ::: details Multi-tenant SaaS
120
- ```ruby
121
- module CustomerPortal
122
- class Engine < Rails::Engine
123
- include Plutonium::Package::Engine
124
- # Automatic tenant isolation
125
- scope_to_entity Organization
126
- end
127
- end
128
- ```
129
- :::
130
-
131
- ### Administrative Systems
132
-
133
- ::: details Back-office Applications
134
- ```ruby
135
- class OrderDefinition < ResourceDefinition
136
- # Advanced filtering
137
- filter :status, with: SelectFilter, choices: Order.statuses
138
- filter :created_at, with: DateRangeFilter
139
-
140
- # Bulk actions
141
- action :process_batch, interaction: ProcessPendingOrders
142
- action :export_to_csv, interaction: ExportOrders
143
-
144
- # Complex displays
145
- display :summary do |field|
146
- OrderSummaryComponent.new(field)
147
- end
148
- end
149
- ```
150
- :::
151
-
152
- ::: details Content Management
153
- ```ruby
154
- class ArticleDefinition < ResourceDefinition
155
- field :content, as: :markdown
156
- field :featured_image, as: :image
157
-
158
- # Publishing workflow
159
- action :publish, interaction: PublishArticle
160
- action :schedule, interaction: ScheduleArticle
161
-
162
- # Content organization
163
- scope :draft
164
- scope :published
165
- scope :scheduled
166
- end
167
- ```
168
- :::
169
-
170
- ## Key Benefits
171
-
172
- ### Accelerated Development
173
- - Pre-built components for common functionality
174
- - Smart generators for boilerplate code
175
- - Convention-based resource handling
176
- - Integrated authentication and authorization
177
-
178
- ### Maintainable Architecture
179
- - Clear separation of concerns through packages
180
- - Consistent patterns across the application
181
- - Deep Rails integration
182
- - Progressive enhancement
183
-
184
- ### Enterprise Ready
185
- - Flexible and robust access control
186
- - Multi-tenancy support
187
- - Extensible component system
188
- - Mobile-friendly by default
189
-
190
- ## Best For
191
-
192
- ::: tip IDEAL USE CASES
193
- - Complex business applications
194
- - Multi-tenant SaaS platforms
195
- - Administrative systems
196
- - Content management systems
197
- - Resource management systems
198
- :::
199
-
200
- ::: warning MIGHT NOT BE THE BEST FIT
201
- - Simple blogs or brochure sites
202
- - Basic CRUD applications
203
- - Pure API-only services
204
- :::
205
-
206
- ## Prerequisites
207
-
208
- - Ruby 3.2.2 or higher
209
- - Rails 7.1 or higher
210
- - Node.js and Yarn
211
- - Basic understanding of Ruby on Rails
@@ -1,440 +0,0 @@
1
- # Core Concepts
2
-
3
- ::: tip What you'll learn
4
- - Plutonium's architecture and how its core components work together
5
- - The role of Resources as your application's building blocks
6
- - How Packages help organize and modularize your code
7
- - Entity Scoping for building multi-tenant applications
8
- - Best practices for maintainable, scalable applications
9
- :::
10
-
11
- ## Understanding Resources
12
-
13
- Think of Resources as the DNA of your Plutonium application. Every meaningful piece of data or functionality in your app—whether it's a User, Product, or Blog Post—is represented as a Resource. But unlike traditional Rails models, Plutonium Resources are rich, self-contained units that know how to display themselves, authorize access, and handle user interactions.
14
-
15
- ### The Anatomy of a Resource
16
-
17
- Each Resource is composed of four key components that work together seamlessly:
18
-
19
- ```mermaid
20
- graph TD
21
- A[Resource] --> B[Model]
22
- A --> C[Definition]
23
- A --> D[Policy]
24
- A --> G[Actions]
25
-
26
- B --> H[Database Schema]
27
- C --> I[Fields & UI Logic]
28
- C --> F[QueryObject]
29
- D --> J[Authorization]
30
- F --> L[Querying & Filtering]
31
- G --> M[User Operations]
32
- ```
33
-
34
- - **Model**: Your familiar Active Record model with validations, associations, and business logic
35
- - **Definition**: Describes how the Resource appears and behaves in the UI
36
- - **Policy**: Controls who can access what and when
37
- - **Actions**: Custom operations users can perform on the Resource
38
-
39
- ::: details Complete Resource Example
40
-
41
- Let's see how all these pieces fit together in a real User resource:
42
-
43
- ::: code-group
44
- ```ruby [Model]
45
- # app/models/user.rb
46
- class User < ApplicationRecord
47
- include Plutonium::Resource::Record
48
-
49
- # Standard Rails associations
50
- has_many :posts
51
- has_many :comments
52
- belongs_to :organization
53
-
54
- # Helpful scopes for filtering
55
- scope :active, -> { where(status: :active) }
56
-
57
- # Standard validations
58
- validates :name, presence: true
59
- validates :email, presence: true, uniqueness: true
60
- validates :role, presence: true, inclusion: {in: %w[admin user]}
61
-
62
- def admin? = role == "admin"
63
- end
64
- ```
65
-
66
- ```ruby [Definition]
67
- # app/definitions/user_definition.rb
68
- class UserDefinition < Plutonium::Resource::Definition
69
- # How fields appear in forms and tables
70
- field :name, as: :string
71
- field :email, as: :email
72
-
73
- # Enable search functionality
74
- search do |scope, query|
75
- scope.where("name LIKE :q OR email LIKE :q", q: "%#{query}%")
76
- end
77
-
78
- # Add filters for easy data browsing
79
- filter :role, with: SelectFilter, choices: %w[admin user guest]
80
- filter :status, with: SelectFilter, choices: %w[active inactive]
81
-
82
- # Expose useful scopes
83
- scope :active
84
-
85
- scope :admins do
86
- where(role: :admin)
87
- end
88
-
89
- # Custom actions users can perform
90
- action :deactivate,
91
- interaction: DeactivateUser,
92
- color: :warning,
93
- icon: Phlex::TablerIcons::UserOff
94
-
95
- # Customize the detail page
96
- show_page_title "User Details"
97
- show_page_description "View and manage user information"
98
- end
99
- ```
100
-
101
- ```ruby [Policy]
102
- # app/policies/user_policy.rb
103
- class UserPolicy < Plutonium::Resource::Policy
104
- # Basic CRUD permissions
105
- def read?
106
- true # Everyone can view users
107
- end
108
-
109
- def create?
110
- user.admin? # Only admins can create users
111
- end
112
-
113
- def update?
114
- user.admin? || record.id == user.id # Admins or self
115
- end
116
-
117
- def destroy?
118
- user.admin? && record.id != user.id # Admins, but not themselves
119
- end
120
-
121
- # Custom action permissions
122
- def deactivate?
123
- user.admin? && record.status == :active && record.id != user.id
124
- end
125
-
126
- # Control which fields are visible/editable
127
- def permitted_attributes_for_read
128
- if user.admin?
129
- %i[name email role status created_at updated_at]
130
- else
131
- %i[name email status]
132
- end
133
- end
134
-
135
- def permitted_attributes_for_create
136
- if user.admin?
137
- %i[name email role status password]
138
- else
139
- %i[name email password]
140
- end
141
- end
142
-
143
- def permitted_attributes_for_update
144
- if user.admin?
145
- %i[name email role]
146
- else
147
- %i[name email]
148
- end
149
- end
150
-
151
- # Control access to related resources
152
- def permitted_associations
153
- %i[posts comments]
154
- end
155
- end
156
- ```
157
-
158
- ```ruby [Deactivate Interaction]
159
- # app/interactions/user_interactions/deactivate.rb
160
- module UserInteractions
161
- class Deactivate < Plutonium::Resource::Interaction
162
- # How this action appears in the UI
163
- presents label: "Deactivate User",
164
- icon: Phlex::TablerIcons::UserOff,
165
- description: "Deactivate user account"
166
-
167
- # What data this action needs
168
- attribute :resource, class: User
169
- attribute :reason, :string
170
-
171
- # Validation rules
172
- validates :resource, presence: true
173
- validates :reason, presence: true
174
-
175
- # The actual business logic
176
- def execute
177
- resource.transaction do
178
- resource.status = :inactive
179
- resource.deactivated_at = Time.current
180
- resource.deactivation_reason = reason
181
-
182
- if resource.save
183
- succeed(resource)
184
- .with_message("User was successfully deactivated")
185
- .with_redirect_response(resource)
186
- else
187
- failed(resource.errors)
188
- end
189
- end
190
- end
191
- end
192
- end
193
- ```
194
- :::
195
-
196
- ## Organizing with Packages
197
-
198
- As your application grows, you need a way to organize your code that doesn't turn into a tangled mess. That's where Packages come in. Think of them as specialized containers that help you group related functionality and keep your application modular.
199
-
200
- Plutonium uses two types of packages, each with a specific purpose:
201
-
202
- ### Feature Packages: Your Business Logic Containers
203
-
204
- Feature packages are where your core business logic lives. They're self-contained modules that focus on a specific domain area of your application—like blogging, e-commerce, or user management.
205
-
206
- ::: tip What Makes a Good Feature Package
207
- - **Domain-focused**: Centers around a specific business area
208
- - **Self-contained**: Has everything it needs to function
209
- - **No web interface**: Pure business logic, no controllers or views
210
- - **Reusable**: Can be used across different parts of your app
211
- :::
212
-
213
- ::: code-group
214
- ```ruby [Directory Structure]
215
- packages/
216
- └── blogging/
217
- ├── app/
218
- │ ├── models/
219
- │ │ └── blogging/
220
- │ │ ├── post.rb
221
- │ │ └── comment.rb
222
- │ ├── definitions/
223
- │ │ └── blogging/
224
- │ │ ├── post_definition.rb
225
- │ │ └── comment_definition.rb
226
- │ ├── policies/
227
- │ │ └── blogging/
228
- │ │ ├── post_policy.rb
229
- │ │ └── comment_policy.rb
230
- │ └── interactions/
231
- │ └── blogging/
232
- │ └── post_interactions/
233
- │ ├── publish.rb
234
- │ └── archive.rb
235
- ├── config/
236
- │ └── routes.rb
237
- └── lib/
238
- └── engine.rb
239
- ```
240
-
241
- ```ruby [Engine Configuration]
242
- # packages/blogging/lib/engine.rb
243
- module Blogging
244
- class Engine < ::Rails::Engine
245
- include Plutonium::Package::Engine
246
- end
247
- end
248
- ```
249
- :::
250
-
251
- ### Portal Packages: Your User Interface Gateways
252
-
253
- Portal packages are the web-facing part of your application. They take the business logic from Feature packages and present it to users through a web interface. Each portal typically serves a specific audience—like administrators, customers, or partners.
254
-
255
- ::: tip What Makes a Good Portal Package
256
- - **User-focused**: Designed for a specific type of user
257
- - **Interface-heavy**: Controllers, views, and authentication
258
- - **Feature composition**: Combines multiple feature packages
259
- - **Access control**: Manages who can see and do what
260
- :::
261
-
262
- ::: code-group
263
- ```ruby [Directory Structure]
264
- packages/
265
- └── admin_portal/
266
- ├── app/
267
- │ ├── controllers/
268
- │ │ └── admin_portal/
269
- │ │ ├── concerns/
270
- │ │ │ └── controller.rb
271
- │ │ ├── plutonium_controller.rb
272
- │ │ └── resource_controller.rb
273
- │ └── views/
274
- │ └── layouts/
275
- │ └── admin_portal.html.erb
276
- ├── config/
277
- │ └── routes.rb
278
- └── lib/
279
- └── engine.rb
280
- ```
281
-
282
- ```ruby [Engine Configuration]
283
- # packages/admin_portal/lib/engine.rb
284
- module AdminPortal
285
- class Engine < ::Rails::Engine
286
- include Plutonium::Portal::Engine
287
-
288
- # All resources in this portal are scoped to an organization
289
- scope_to_entity Organization, strategy: :path
290
- end
291
- end
292
- ```
293
-
294
- ```ruby [Routes Configuration]
295
- # packages/admin_portal/config/routes.rb
296
- AdminPortal::Engine.routes.draw do
297
- root to: "dashboard#index"
298
-
299
- # Make resources from feature packages available in this portal
300
- register_resource Blogging::Post
301
- register_resource Blogging::Comment
302
- end
303
- ```
304
-
305
- ```ruby [Controller Configuration]
306
- # packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
307
- module AdminPortal
308
- module Concerns
309
- module Controller
310
- extend ActiveSupport::Concern
311
- include Plutonium::Portal::Controller
312
-
313
- # Add authentication to this portal
314
- include Plutonium::Auth::Rodauth(:admin)
315
- end
316
- end
317
- end
318
- ```
319
- :::
320
-
321
- ## Entity Scoping: Building Multi-Tenant Applications
322
-
323
- One of Plutonium's most powerful features is Entity Scoping—a clean way to build multi-tenant applications where data is automatically partitioned based on a parent entity like Organization, Account, or Team.
324
-
325
- Here's the magic: when you properly set up entity scoping, Plutonium automatically ensures users only see and can access data that belongs to their organization. No manual filtering required!
326
-
327
- ### How It Works
328
-
329
- ```ruby
330
- # 1. Configure scoping in your portal engine
331
- module AdminPortal
332
- class Engine < ::Rails::Engine
333
- include Plutonium::Portal::Engine
334
-
335
- # Path-based scoping creates URLs like /org_123/posts
336
- scope_to_entity Organization, strategy: :path
337
-
338
- # Or use a custom strategy for more control
339
- scope_to_entity Organization, strategy: :current_organization
340
- end
341
- end
342
-
343
- # 2. Set up your models with proper associations
344
- class Post < ApplicationRecord
345
- include Plutonium::Resource::Record
346
-
347
- # Direct relationship to the scoping entity
348
- belongs_to :user
349
- belongs_to :organization, through: :user
350
-
351
- # Alternative: custom scoping for complex relationships
352
- scope :associated_with_organization, ->(organization) do
353
- joins(:user).where(users: { organization_id: organization.id })
354
- end
355
- end
356
-
357
- # 3. Implement the scoping strategy in your controller
358
- module AdminPortal::Concerns::Controller
359
- private
360
-
361
- # This method name must match your strategy name
362
- def current_organization
363
- # Extract tenant from subdomain, path, or any other source
364
- @current_organization ||= Organization.find_by!(subdomain: request.subdomain)
365
- end
366
- end
367
- ```
368
-
369
- ### The Result
370
-
371
- With this setup, when a user visits `/posts`, they automatically see only posts from their organization. When they create a new post, it's automatically associated with their organization. All of this happens transparently—no extra code needed in your controllers or views.
372
-
373
- ## Best Practices for Success
374
-
375
- ### Organizing Your Packages
376
-
377
- ::: tip Feature Packages
378
- - **Keep domains separate**: Each package should focus on one business area
379
- - **Minimize dependencies**: Packages should be as independent as possible
380
- - **Clear interfaces**: Use well-defined APIs for package interactions
381
- - **Single responsibility**: One clear purpose per package
382
- :::
383
-
384
- ::: tip Portal Packages
385
- - **User-centric design**: Each portal serves a specific user type
386
- - **Consistent authentication**: Use the same auth strategy throughout a portal
387
- - **Clear scoping rules**: Be explicit about data access boundaries
388
- - **Compose, don't duplicate**: Reuse feature packages rather than rebuilding
389
- :::
390
-
391
- ### Designing Robust Resources
392
-
393
- ::: tip Model Layer
394
- - **Clear validations**: Make invalid states impossible
395
- - **Meaningful associations**: Reflect real business relationships
396
- - **Useful scopes**: Provide common query patterns
397
- - **Business methods**: Encode domain logic in the model
398
- :::
399
-
400
- ::: tip Definition Layer
401
- - **Appropriate field types**: Match the UI to the data
402
- - **Efficient search**: Index searchable fields properly
403
- - **Logical grouping**: Organize fields and actions intuitively
404
- - **Clear labeling**: Use human-friendly names and descriptions
405
- :::
406
-
407
- ::: tip Policy Layer
408
- - **Granular permissions**: Control access at the field level when needed
409
- - **Explicit rules**: Make authorization logic clear and testable
410
- - **Action-specific controls**: Different actions may need different rules
411
- - **Association security**: Control access to related data
412
- :::
413
-
414
- ### Security First
415
-
416
- ::: warning Critical Security Practices
417
- - **Always implement policies**: Never rely on "security through obscurity"
418
- - **Use entity scoping consistently**: Don't mix scoped and unscoped access
419
- - **Validate all inputs**: Trust nothing from the user
420
- - **Control associations**: Prevent unauthorized data access through relationships
421
- - **Audit sensitive actions**: Log important operations for compliance
422
- :::
423
-
424
- ## Generator Support
425
-
426
- Plutonium provides generators to quickly scaffold components:
427
-
428
- ```bash
429
- # Create a new feature package
430
- rails generate pu:pkg:package blogging
431
-
432
- # Create a new portal package
433
- rails generate pu:pkg:portal admin
434
-
435
- # Create a new resource
436
- rails generate pu:res:scaffold post title:string content:text
437
-
438
- # Connect a resource to a portal
439
- rails generate pu:res:conn
440
- ```