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.
- 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 +32 -1
- 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/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
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Plutonium follows a layered architecture where each layer has a specific responsibility. This separation makes applications easier to understand, test, and maintain.
|
|
4
|
+
|
|
5
|
+
## The Four Layers
|
|
6
|
+
|
|
7
|
+
### 1. Model Layer
|
|
8
|
+
|
|
9
|
+
The Model layer handles **data and business rules**.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class Post < ResourceRecord
|
|
13
|
+
belongs_to :user
|
|
14
|
+
has_many :comments
|
|
15
|
+
|
|
16
|
+
validates :title, presence: true
|
|
17
|
+
validates :body, presence: true
|
|
18
|
+
|
|
19
|
+
scope :published, -> { where(published: true) }
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Responsibilities:
|
|
24
|
+
- Database schema and migrations
|
|
25
|
+
- Validations
|
|
26
|
+
- Associations
|
|
27
|
+
- Scopes and queries
|
|
28
|
+
- Core business logic
|
|
29
|
+
|
|
30
|
+
### 2. Definition Layer
|
|
31
|
+
|
|
32
|
+
The Definition layer controls **how resources render**.
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
class PostDefinition < Plutonium::Resource::Definition
|
|
36
|
+
# Fields shown in forms
|
|
37
|
+
field :title
|
|
38
|
+
field :body, as: :rich_text
|
|
39
|
+
field :published, as: :switch
|
|
40
|
+
|
|
41
|
+
# Columns shown in tables
|
|
42
|
+
column :title, sortable: true
|
|
43
|
+
column :published
|
|
44
|
+
column :created_at
|
|
45
|
+
|
|
46
|
+
# Custom actions
|
|
47
|
+
action :publish, interaction: PublishPost
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Responsibilities:
|
|
52
|
+
- Which fields appear in forms
|
|
53
|
+
- How fields are rendered (input types)
|
|
54
|
+
- Table column configuration
|
|
55
|
+
- Search and filtering
|
|
56
|
+
- Custom actions
|
|
57
|
+
- Field groups and layout
|
|
58
|
+
|
|
59
|
+
### 3. Policy Layer
|
|
60
|
+
|
|
61
|
+
The Policy layer controls **authorization**.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
class PostPolicy < Plutonium::Resource::Policy
|
|
65
|
+
def read?
|
|
66
|
+
record.published? || owner?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def update?
|
|
70
|
+
owner?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def permitted_attributes_for_update
|
|
74
|
+
[:title, :body, :published]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def relation_scope(relation)
|
|
78
|
+
relation.where(published: true).or(relation.where(user: user))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Responsibilities:
|
|
84
|
+
- Action permissions (can user perform action?)
|
|
85
|
+
- Attribute permissions (which fields can user see/modify?)
|
|
86
|
+
- Collection scoping (which records can user access?)
|
|
87
|
+
- Multi-tenancy isolation
|
|
88
|
+
|
|
89
|
+
### 4. Controller Layer
|
|
90
|
+
|
|
91
|
+
The Controller layer handles **HTTP requests**.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
class PostsController < Plutonium::Resource::Controller
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def build_resource
|
|
98
|
+
super.tap do |post|
|
|
99
|
+
post.user = current_user
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def after_create_success
|
|
104
|
+
notify_subscribers(@resource)
|
|
105
|
+
super
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Responsibilities:
|
|
111
|
+
- CRUD actions
|
|
112
|
+
- Request/response handling
|
|
113
|
+
- Resource building hooks
|
|
114
|
+
- Redirects and rendering
|
|
115
|
+
- Format handling (HTML, JSON, etc.)
|
|
116
|
+
|
|
117
|
+
## Request Flow
|
|
118
|
+
|
|
119
|
+
When a request comes in, it flows through the layers:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
1. REQUEST arrives at Portal
|
|
123
|
+
↓
|
|
124
|
+
2. CONTROLLER receives request
|
|
125
|
+
↓
|
|
126
|
+
3. POLICY checks authorization
|
|
127
|
+
↓
|
|
128
|
+
4. DEFINITION determines rendering
|
|
129
|
+
↓
|
|
130
|
+
5. MODEL provides data
|
|
131
|
+
↓
|
|
132
|
+
6. RESPONSE rendered and returned
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Example: Viewing a Post
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
GET /admin/posts/1
|
|
139
|
+
|
|
140
|
+
1. AdminPortal routes to PostsController#show
|
|
141
|
+
2. PostsController loads Post.find(1)
|
|
142
|
+
3. PostPolicy#read? checks if user can view
|
|
143
|
+
4. PostDefinition provides field configuration
|
|
144
|
+
5. UI renders the post display
|
|
145
|
+
6. HTML response returned
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Example: Creating a Post
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
POST /admin/posts
|
|
152
|
+
|
|
153
|
+
1. AdminPortal routes to PostsController#create
|
|
154
|
+
2. PostsController builds new Post
|
|
155
|
+
3. PostPolicy#create? checks if user can create
|
|
156
|
+
4. PostPolicy#permitted_attributes_for_create filters params
|
|
157
|
+
5. Post validates and saves
|
|
158
|
+
6. Redirect to show page
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Layer Interaction
|
|
162
|
+
|
|
163
|
+
Layers communicate through well-defined interfaces:
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
# Controller asks Policy
|
|
167
|
+
policy = policy_for(resource)
|
|
168
|
+
policy.authorize!(:update)
|
|
169
|
+
|
|
170
|
+
# Controller uses Definition
|
|
171
|
+
definition = definition_for(resource)
|
|
172
|
+
definition.fields_for(:form)
|
|
173
|
+
|
|
174
|
+
# Policy uses Model
|
|
175
|
+
def owner?
|
|
176
|
+
record.user_id == user.id
|
|
177
|
+
end
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Customization Points
|
|
181
|
+
|
|
182
|
+
Each layer provides hooks for customization:
|
|
183
|
+
|
|
184
|
+
### Model Hooks
|
|
185
|
+
- Callbacks (before_save, after_create, etc.)
|
|
186
|
+
- Custom methods
|
|
187
|
+
- Scopes
|
|
188
|
+
|
|
189
|
+
### Definition Hooks
|
|
190
|
+
- Field configuration
|
|
191
|
+
- Custom renderers
|
|
192
|
+
- Conditional display
|
|
193
|
+
|
|
194
|
+
### Policy Hooks
|
|
195
|
+
- Custom permission methods
|
|
196
|
+
- Attribute filtering
|
|
197
|
+
- Scope customization
|
|
198
|
+
|
|
199
|
+
### Controller Hooks
|
|
200
|
+
- `build_resource` - Customize resource initialization
|
|
201
|
+
- `before_action` - Standard Rails callbacks
|
|
202
|
+
- `after_*_success/failure` - Action result hooks
|
|
203
|
+
|
|
204
|
+
## Why This Architecture?
|
|
205
|
+
|
|
206
|
+
### 1. Separation of Concerns
|
|
207
|
+
Each layer has one job. Forms don't know about authorization. Policies don't know about rendering.
|
|
208
|
+
|
|
209
|
+
### 2. Testability
|
|
210
|
+
Each layer can be tested in isolation:
|
|
211
|
+
- Model specs test validations and queries
|
|
212
|
+
- Policy specs test authorization
|
|
213
|
+
- Definition specs test field configuration
|
|
214
|
+
- Controller specs test request handling
|
|
215
|
+
|
|
216
|
+
### 3. Reusability
|
|
217
|
+
Definitions and policies can be shared across portals. Models are independent of the UI.
|
|
218
|
+
|
|
219
|
+
### 4. Maintainability
|
|
220
|
+
Changes to authorization don't affect forms. UI changes don't affect data logic.
|
|
221
|
+
|
|
222
|
+
## Related Topics
|
|
223
|
+
|
|
224
|
+
- [Resources](./resources) - Understanding resource classes
|
|
225
|
+
- [Packages and Portals](./packages-portals) - Organizing your application
|
|
226
|
+
- [Auto-Detection](./auto-detection) - How defaults are determined
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Auto-Detection
|
|
2
|
+
|
|
3
|
+
Plutonium automatically detects configuration from your models, reducing boilerplate code. You only write configuration when you need to override the defaults.
|
|
4
|
+
|
|
5
|
+
## How Auto-Detection Works
|
|
6
|
+
|
|
7
|
+
When Plutonium renders a resource, it examines:
|
|
8
|
+
1. Database columns (types, constraints)
|
|
9
|
+
2. Model validations
|
|
10
|
+
3. Model associations
|
|
11
|
+
4. Existing configuration
|
|
12
|
+
|
|
13
|
+
From this, it determines sensible defaults for forms, tables, and displays.
|
|
14
|
+
|
|
15
|
+
## Field Detection
|
|
16
|
+
|
|
17
|
+
### From Column Types
|
|
18
|
+
|
|
19
|
+
| Column Type | Default Input | Default Display |
|
|
20
|
+
|-------------|---------------|-----------------|
|
|
21
|
+
| `string` | Text input | Text |
|
|
22
|
+
| `text` | Textarea | Formatted text |
|
|
23
|
+
| `integer` | Number input | Number |
|
|
24
|
+
| `float`/`decimal` | Decimal input | Formatted number |
|
|
25
|
+
| `boolean` | Checkbox | Yes/No badge |
|
|
26
|
+
| `date` | Date picker | Formatted date |
|
|
27
|
+
| `datetime` | Datetime picker | Formatted datetime |
|
|
28
|
+
| `time` | Time picker | Formatted time |
|
|
29
|
+
| `json`/`jsonb` | JSON editor | JSON display |
|
|
30
|
+
|
|
31
|
+
### From Constraints
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
# Schema
|
|
35
|
+
t.string :email, null: false
|
|
36
|
+
|
|
37
|
+
# Auto-detects: required field
|
|
38
|
+
field :email # Marked as required in form
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### From Validations
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# Model
|
|
45
|
+
validates :title, presence: true, length: { maximum: 100 }
|
|
46
|
+
|
|
47
|
+
# Auto-detects:
|
|
48
|
+
# - Required field
|
|
49
|
+
# - Max length constraint
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Association Detection
|
|
53
|
+
|
|
54
|
+
### belongs_to
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# Model
|
|
58
|
+
belongs_to :user
|
|
59
|
+
|
|
60
|
+
# Auto-detects:
|
|
61
|
+
# - Select input with user options
|
|
62
|
+
# - Link to user in display
|
|
63
|
+
# - Automatic eager loading in queries
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### has_many
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
# Model
|
|
70
|
+
has_many :comments
|
|
71
|
+
|
|
72
|
+
# Auto-detects:
|
|
73
|
+
# - Association panel on detail page
|
|
74
|
+
# - Count display in tables (optional)
|
|
75
|
+
# - Nested forms support (if configured)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### has_one
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
# Model
|
|
82
|
+
has_one :profile
|
|
83
|
+
|
|
84
|
+
# Auto-detects:
|
|
85
|
+
# - Inline display on detail page
|
|
86
|
+
# - Nested form support
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Validation Detection
|
|
90
|
+
|
|
91
|
+
| Validation | Effect |
|
|
92
|
+
|------------|--------|
|
|
93
|
+
| `presence` | Field marked required |
|
|
94
|
+
| `length` | Min/max constraints |
|
|
95
|
+
| `numericality` | Number input with constraints |
|
|
96
|
+
| `inclusion` | Select input with options |
|
|
97
|
+
| `format` | Pattern attribute on input |
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
# Model
|
|
101
|
+
validates :status, inclusion: { in: %w[draft published archived] }
|
|
102
|
+
|
|
103
|
+
# Auto-detects: Select with options
|
|
104
|
+
field :status # Renders as select with draft/published/archived options
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Overriding Defaults
|
|
108
|
+
|
|
109
|
+
Auto-detection provides a starting point. Override when needed:
|
|
110
|
+
|
|
111
|
+
### Field Type Override
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
# Auto-detected: text input
|
|
115
|
+
# Override: rich text editor
|
|
116
|
+
field :body, as: :rich_text
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Field Options
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
# Auto-detected: select with model options
|
|
123
|
+
# Override: custom collection
|
|
124
|
+
field :user, collection: -> { User.active.pluck(:name, :id) }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Hiding Fields
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# Exclude from forms
|
|
131
|
+
exclude_from_form :created_at, :updated_at
|
|
132
|
+
|
|
133
|
+
# Or per-field
|
|
134
|
+
field :user, as: :hidden
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Required Override
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
# Model allows null, but form requires it
|
|
141
|
+
field :nickname, required: true
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Table Column Detection
|
|
145
|
+
|
|
146
|
+
By default, tables show a subset of fields. Plutonium prioritizes:
|
|
147
|
+
1. Non-association fields
|
|
148
|
+
2. Non-text fields (text is too long for tables)
|
|
149
|
+
3. Fields with meaningful data
|
|
150
|
+
|
|
151
|
+
### Override Columns
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
# Explicitly set columns
|
|
155
|
+
column :title
|
|
156
|
+
column :published
|
|
157
|
+
column :user
|
|
158
|
+
column :created_at
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Search Detection
|
|
162
|
+
|
|
163
|
+
Plutonium can auto-detect searchable fields:
|
|
164
|
+
- String columns are searchable by default
|
|
165
|
+
- Text columns can be included
|
|
166
|
+
|
|
167
|
+
### Override Search
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# Custom search implementation
|
|
171
|
+
search do |scope, query|
|
|
172
|
+
scope.where("title ILIKE ?", "%#{query}%")
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Filter Detection
|
|
177
|
+
|
|
178
|
+
For associations, Plutonium can auto-generate filters:
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
# Auto-detected from belongs_to :user
|
|
182
|
+
filter :user # Select filter with user options
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Override Filters
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
filter :status, as: :select, collection: %w[draft published archived]
|
|
189
|
+
filter :created_at, as: :date_range
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## When Auto-Detection Runs
|
|
193
|
+
|
|
194
|
+
Auto-detection happens at render time, not definition time. This means:
|
|
195
|
+
|
|
196
|
+
1. Changes to models are reflected immediately
|
|
197
|
+
2. New columns appear in forms automatically
|
|
198
|
+
3. Removed columns disappear from forms
|
|
199
|
+
|
|
200
|
+
## Caching
|
|
201
|
+
|
|
202
|
+
In production, auto-detection results are cached for performance. Clear the cache after schema changes:
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
Rails.cache.clear
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Best Practices
|
|
209
|
+
|
|
210
|
+
### 1. Start with Defaults
|
|
211
|
+
Let auto-detection do its job. Only configure what you need to change.
|
|
212
|
+
|
|
213
|
+
### 2. Be Explicit When It Matters
|
|
214
|
+
For important forms, explicitly declare fields to prevent surprise changes:
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
# Explicit field list
|
|
218
|
+
field :title
|
|
219
|
+
field :body
|
|
220
|
+
field :published
|
|
221
|
+
|
|
222
|
+
# vs relying on auto-detection
|
|
223
|
+
# (new columns would appear automatically)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 3. Use Models as Documentation
|
|
227
|
+
Your model's validations and associations document the expected UI:
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
class Post < ResourceRecord
|
|
231
|
+
belongs_to :user # Select input
|
|
232
|
+
validates :title, presence: true # Required field
|
|
233
|
+
validates :status, inclusion: {...} # Select options
|
|
234
|
+
end
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 4. Test After Schema Changes
|
|
238
|
+
After adding/removing columns, verify forms still work correctly.
|
|
239
|
+
|
|
240
|
+
## Debugging Auto-Detection
|
|
241
|
+
|
|
242
|
+
To see what Plutonium detected:
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
definition = PostDefinition.new
|
|
246
|
+
puts definition.detected_fields.inspect
|
|
247
|
+
puts definition.detected_columns.inspect
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Related Topics
|
|
251
|
+
|
|
252
|
+
- [Fields Reference](/reference/definition/fields) - All field options
|
|
253
|
+
- [Model Reference](/reference/model/) - Model configuration
|
|
254
|
+
- [Resources](./resources) - Understanding resources
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Core Concepts
|
|
2
|
+
|
|
3
|
+
This section explains the fundamental concepts behind Plutonium's architecture.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Plutonium is built around a few key principles:
|
|
8
|
+
|
|
9
|
+
1. **Separation of Concerns** - Each layer has a single responsibility
|
|
10
|
+
2. **Convention over Configuration** - Smart defaults, override when needed
|
|
11
|
+
3. **Modularity** - Features organized into independent packages
|
|
12
|
+
4. **Full Customization** - Every layer can be overridden
|
|
13
|
+
|
|
14
|
+
## Key Concepts
|
|
15
|
+
|
|
16
|
+
### [Architecture](./architecture)
|
|
17
|
+
How the Model → Definition → Policy → Controller layers work together to create a resource.
|
|
18
|
+
|
|
19
|
+
### [Resources](./resources)
|
|
20
|
+
What resources are and how they differ from plain Rails models.
|
|
21
|
+
|
|
22
|
+
### [Packages and Portals](./packages-portals)
|
|
23
|
+
How to organize your application using Feature Packages and Portal Packages.
|
|
24
|
+
|
|
25
|
+
### [Auto-Detection](./auto-detection)
|
|
26
|
+
How Plutonium automatically discovers fields, associations, and validations.
|
|
27
|
+
|
|
28
|
+
## The Big Picture
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
32
|
+
│ PORTAL │
|
|
33
|
+
│ (Web Interface - routes, authentication, UI customization) │
|
|
34
|
+
└─────────────────────────────────────────────────────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
38
|
+
│ CONTROLLER │
|
|
39
|
+
│ (HTTP handling - request/response, rendering) │
|
|
40
|
+
└─────────────────────────────────────────────────────────────┘
|
|
41
|
+
│
|
|
42
|
+
▼
|
|
43
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
44
|
+
│ POLICY │
|
|
45
|
+
│ (Authorization - who can do what) │
|
|
46
|
+
└─────────────────────────────────────────────────────────────┘
|
|
47
|
+
│
|
|
48
|
+
▼
|
|
49
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
50
|
+
│ DEFINITION │
|
|
51
|
+
│ (Presentation - how resources render) │
|
|
52
|
+
└─────────────────────────────────────────────────────────────┘
|
|
53
|
+
│
|
|
54
|
+
▼
|
|
55
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
56
|
+
│ MODEL │
|
|
57
|
+
│ (Data - structure, validations, business rules) │
|
|
58
|
+
└─────────────────────────────────────────────────────────────┘
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Each layer builds on the one below it, and each can be customized independently.
|