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.
- checksums.yaml +4 -4
- data/# Plutonium: The pre-alpha demo.md +4 -2
- data/.claude/skills/assets/SKILL.md +416 -0
- data/.claude/skills/connect-resource/SKILL.md +112 -0
- data/.claude/skills/controller/SKILL.md +302 -0
- data/.claude/skills/create-resource/SKILL.md +240 -0
- data/.claude/skills/definition/SKILL.md +218 -0
- data/.claude/skills/definition-actions/SKILL.md +386 -0
- data/.claude/skills/definition-fields/SKILL.md +474 -0
- data/.claude/skills/definition-query/SKILL.md +334 -0
- data/.claude/skills/forms/SKILL.md +439 -0
- data/.claude/skills/installation/SKILL.md +300 -0
- data/.claude/skills/interaction/SKILL.md +382 -0
- data/.claude/skills/model/SKILL.md +267 -0
- data/.claude/skills/model-features/SKILL.md +286 -0
- data/.claude/skills/nested-resources/SKILL.md +274 -0
- data/.claude/skills/package/SKILL.md +191 -0
- data/.claude/skills/policy/SKILL.md +352 -0
- data/.claude/skills/portal/SKILL.md +400 -0
- data/.claude/skills/resource/SKILL.md +281 -0
- data/.claude/skills/rodauth/SKILL.md +452 -0
- data/.claude/skills/views/SKILL.md +563 -0
- data/Appraisals +46 -4
- data/CHANGELOG.md +36 -0
- data/app/assets/plutonium.css +2 -2
- data/config/brakeman.ignore +239 -0
- data/config/initializers/action_policy.rb +1 -1
- data/docs/.vitepress/config.ts +132 -47
- data/docs/concepts/architecture.md +226 -0
- data/docs/concepts/auto-detection.md +254 -0
- data/docs/concepts/index.md +61 -0
- data/docs/concepts/packages-portals.md +304 -0
- data/docs/concepts/resources.md +224 -0
- data/docs/cookbook/blog.md +412 -0
- data/docs/cookbook/index.md +289 -0
- data/docs/cookbook/saas.md +481 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +146 -0
- data/docs/getting-started/tutorial/01-setup.md +118 -0
- data/docs/getting-started/tutorial/02-first-resource.md +180 -0
- data/docs/getting-started/tutorial/03-authentication.md +246 -0
- data/docs/getting-started/tutorial/04-authorization.md +170 -0
- data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
- data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
- data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
- data/docs/getting-started/tutorial/index.md +64 -0
- data/docs/guides/adding-resources.md +420 -0
- data/docs/guides/authentication.md +551 -0
- data/docs/guides/authorization.md +468 -0
- data/docs/guides/creating-packages.md +380 -0
- data/docs/guides/custom-actions.md +523 -0
- data/docs/guides/index.md +45 -0
- data/docs/guides/multi-tenancy.md +302 -0
- data/docs/guides/nested-resources.md +411 -0
- data/docs/guides/search-filtering.md +266 -0
- data/docs/guides/theming.md +321 -0
- data/docs/index.md +67 -26
- data/docs/public/CLAUDE.md +64 -21
- data/docs/reference/assets/index.md +496 -0
- data/docs/reference/controller/index.md +363 -0
- data/docs/reference/definition/actions.md +400 -0
- data/docs/reference/definition/fields.md +350 -0
- data/docs/reference/definition/index.md +252 -0
- data/docs/reference/definition/query.md +342 -0
- data/docs/reference/generators/index.md +469 -0
- data/docs/reference/index.md +49 -0
- data/docs/reference/interaction/index.md +445 -0
- data/docs/reference/model/features.md +248 -0
- data/docs/reference/model/index.md +219 -0
- data/docs/reference/policy/index.md +385 -0
- data/docs/reference/portal/index.md +382 -0
- data/docs/reference/views/forms.md +396 -0
- data/docs/reference/views/index.md +479 -0
- data/gemfiles/rails_7.gemfile +9 -2
- data/gemfiles/rails_7.gemfile.lock +146 -111
- data/gemfiles/rails_8.0.gemfile +20 -0
- data/gemfiles/rails_8.0.gemfile.lock +417 -0
- data/gemfiles/rails_8.1.gemfile +20 -0
- data/gemfiles/rails_8.1.gemfile.lock +419 -0
- data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
- data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
- data/lib/generators/pu/pkg/portal/USAGE +65 -0
- data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
- data/lib/generators/pu/res/conn/USAGE +71 -0
- data/lib/generators/pu/res/model/USAGE +106 -110
- data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
- data/lib/generators/pu/res/scaffold/USAGE +85 -0
- data/lib/generators/pu/rodauth/install_generator.rb +2 -6
- data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
- data/lib/generators/pu/skills/sync/USAGE +14 -0
- data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
- data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
- data/lib/plutonium/core/controller.rb +2 -2
- data/lib/plutonium/interaction/base.rb +1 -0
- data/lib/plutonium/package/engine.rb +2 -2
- data/lib/plutonium/query/adhoc_block.rb +6 -2
- data/lib/plutonium/query/model_scope.rb +1 -1
- data/lib/plutonium/railtie.rb +4 -0
- data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
- data/lib/plutonium/resource/controllers/crud_actions.rb +21 -18
- data/lib/plutonium/resource/controllers/interactive_actions.rb +21 -25
- data/lib/plutonium/resource/query_object.rb +38 -8
- data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
- data/lib/plutonium/version.rb +1 -1
- data/lib/tasks/release.rake +19 -4
- data/package.json +1 -1
- metadata +76 -39
- data/brakeman.ignore +0 -28
- data/docs/api-examples.md +0 -49
- data/docs/guide/claude-code-guide.md +0 -74
- data/docs/guide/deep-dive/authorization.md +0 -189
- data/docs/guide/deep-dive/multitenancy.md +0 -256
- data/docs/guide/deep-dive/resources.md +0 -390
- data/docs/guide/getting-started/01-installation.md +0 -165
- data/docs/guide/index.md +0 -28
- data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
- data/docs/guide/introduction/02-core-concepts.md +0 -440
- data/docs/guide/tutorial/01-project-setup.md +0 -75
- data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
- data/docs/guide/tutorial/03-defining-resources.md +0 -90
- data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
- data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
- data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
- data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
- data/docs/markdown-examples.md +0 -85
- data/docs/modules/action.md +0 -244
- data/docs/modules/authentication.md +0 -236
- data/docs/modules/configuration.md +0 -599
- data/docs/modules/controller.md +0 -443
- data/docs/modules/core.md +0 -316
- data/docs/modules/definition.md +0 -1308
- data/docs/modules/display.md +0 -759
- data/docs/modules/form.md +0 -495
- data/docs/modules/generator.md +0 -400
- data/docs/modules/index.md +0 -167
- data/docs/modules/interaction.md +0 -642
- data/docs/modules/package.md +0 -151
- data/docs/modules/policy.md +0 -176
- data/docs/modules/portal.md +0 -710
- data/docs/modules/query.md +0 -297
- data/docs/modules/resource_record.md +0 -618
- data/docs/modules/routing.md +0 -690
- data/docs/modules/table.md +0 -301
- 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
|
-
```
|