plutonium 0.34.1 → 0.35.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/.claude/skills/plutonium/skill.md +53 -0
- data/.claude/skills/{assets → plutonium-assets}/SKILL.md +13 -8
- data/.claude/skills/{connect-resource → plutonium-connect-resource}/SKILL.md +1 -1
- data/.claude/skills/{controller → plutonium-controller}/SKILL.md +27 -13
- data/.claude/skills/{create-resource → plutonium-create-resource}/SKILL.md +1 -1
- data/.claude/skills/{definition → plutonium-definition}/SKILL.md +10 -10
- data/.claude/skills/{definition-actions → plutonium-definition-actions}/SKILL.md +34 -9
- data/.claude/skills/{definition-fields → plutonium-definition-fields}/SKILL.md +38 -10
- data/.claude/skills/plutonium-definition-query/SKILL.md +356 -0
- data/.claude/skills/{forms → plutonium-forms}/SKILL.md +6 -6
- data/.claude/skills/{installation → plutonium-installation}/SKILL.md +9 -9
- data/.claude/skills/{interaction → plutonium-interaction}/SKILL.md +20 -19
- data/.claude/skills/{model → plutonium-model}/SKILL.md +3 -3
- data/.claude/skills/{model-features → plutonium-model-features}/SKILL.md +3 -3
- data/.claude/skills/{nested-resources → plutonium-nested-resources}/SKILL.md +5 -5
- data/.claude/skills/{package → plutonium-package}/SKILL.md +7 -8
- data/.claude/skills/{policy → plutonium-policy}/SKILL.md +26 -4
- data/.claude/skills/{portal → plutonium-portal}/SKILL.md +33 -31
- data/.claude/skills/{resource → plutonium-resource}/SKILL.md +27 -27
- data/.claude/skills/{rodauth → plutonium-rodauth}/SKILL.md +5 -5
- data/.claude/skills/plutonium-theming/SKILL.md +424 -0
- data/.claude/skills/{views → plutonium-views}/SKILL.md +7 -7
- data/CHANGELOG.md +52 -0
- data/CLAUDE.md +215 -0
- data/CONTRIBUTING.md +72 -18
- data/README.md +100 -19
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1685 -1146
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +70 -70
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/resource/interactive_bulk_action.html.erb +1 -5
- data/app/views/rodauth/_email_auth_request_form.html.erb +1 -1
- data/app/views/rodauth/_login_form.html.erb +15 -55
- data/app/views/rodauth/_login_form_footer.html.erb +2 -2
- data/app/views/rodauth/_password_visibility.html.erb +2 -8
- data/app/views/rodauth/add_recovery_codes.html.erb +2 -2
- data/app/views/rodauth/change_login.html.erb +36 -19
- data/app/views/rodauth/change_password.html.erb +34 -10
- data/app/views/rodauth/close_account.html.erb +12 -4
- data/app/views/rodauth/confirm_password.html.erb +19 -17
- data/app/views/rodauth/create_account.html.erb +30 -109
- data/app/views/rodauth/email_auth.html.erb +1 -1
- data/app/views/rodauth/logout.html.erb +4 -4
- data/app/views/rodauth/otp_auth.html.erb +13 -4
- data/app/views/rodauth/otp_disable.html.erb +12 -4
- data/app/views/rodauth/otp_setup.html.erb +29 -12
- data/app/views/rodauth/otp_unlock.html.erb +19 -10
- data/app/views/rodauth/otp_unlock_not_available.html.erb +7 -7
- data/app/views/rodauth/recovery_auth.html.erb +12 -4
- data/app/views/rodauth/recovery_codes.html.erb +12 -4
- data/app/views/rodauth/remember.html.erb +7 -7
- data/app/views/rodauth/reset_password.html.erb +23 -7
- data/app/views/rodauth/reset_password_request.html.erb +14 -10
- data/app/views/rodauth/sms_auth.html.erb +13 -4
- data/app/views/rodauth/sms_confirm.html.erb +13 -4
- data/app/views/rodauth/sms_disable.html.erb +12 -4
- data/app/views/rodauth/sms_request.html.erb +1 -1
- data/app/views/rodauth/sms_setup.html.erb +23 -7
- data/app/views/rodauth/two_factor_auth.html.erb +2 -2
- data/app/views/rodauth/two_factor_disable.html.erb +12 -4
- data/app/views/rodauth/two_factor_manage.html.erb +7 -7
- data/app/views/rodauth/unlock_account.html.erb +13 -5
- data/app/views/rodauth/unlock_account_request.html.erb +2 -2
- data/app/views/rodauth/verify_account.html.erb +25 -7
- data/app/views/rodauth/verify_account_resend.html.erb +14 -10
- data/app/views/rodauth/verify_login_change.html.erb +1 -1
- data/app/views/rodauth/webauthn_auth.html.erb +1 -1
- data/app/views/rodauth/webauthn_remove.html.erb +18 -8
- data/app/views/rodauth/webauthn_setup.html.erb +12 -4
- data/docs/.vitepress/config.ts +15 -26
- data/docs/.vitepress/theme/custom.css +388 -29
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/tutorial/02-first-resource.md +9 -0
- data/docs/getting-started/tutorial/06-nested-resources.md +2 -2
- data/docs/getting-started/tutorial/07-author-portal.md +191 -0
- data/docs/getting-started/tutorial/{07-customizing-ui.md → 08-customizing-ui.md} +7 -7
- data/docs/getting-started/tutorial/index.md +5 -2
- data/docs/guides/authorization.md +33 -0
- data/docs/guides/creating-packages.md +12 -16
- data/docs/guides/custom-actions.md +36 -0
- data/docs/guides/search-filtering.md +121 -42
- data/docs/guides/theming.md +232 -36
- data/docs/index.md +203 -57
- data/docs/public/og-image.png +0 -0
- data/docs/reference/controller/index.md +14 -16
- data/docs/reference/definition/actions.md +38 -3
- data/docs/reference/definition/fields.md +3 -3
- data/docs/reference/definition/index.md +2 -2
- data/docs/reference/generators/index.md +0 -1
- data/docs/reference/interaction/index.md +14 -10
- data/docs/reference/model/index.md +0 -1
- data/docs/reference/portal/index.md +13 -27
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/pkg/portal/portal_generator.rb +0 -2
- data/lib/generators/pu/pkg/portal/templates/app/views/package/dashboard/index.html.erb +28 -72
- data/lib/plutonium/action/interactive.rb +2 -2
- data/lib/plutonium/core/controller.rb +2 -1
- data/lib/plutonium/definition/actions.rb +2 -2
- data/lib/plutonium/lib/deep_freezer.rb +3 -7
- data/lib/plutonium/query/filter.rb +14 -0
- data/lib/plutonium/query/filters/association.rb +49 -0
- data/lib/plutonium/query/filters/boolean.rb +35 -0
- data/lib/plutonium/query/filters/date.rb +97 -0
- data/lib/plutonium/query/filters/date_range.rb +58 -0
- data/lib/plutonium/query/filters/select.rb +55 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +24 -6
- data/lib/plutonium/resource/controllers/interactive_actions.rb +76 -58
- data/lib/plutonium/resource/controllers/queryable.rb +4 -2
- data/lib/plutonium/resource/query_object.rb +1 -1
- data/lib/plutonium/ui/action_button.rb +23 -65
- data/lib/plutonium/ui/actions_dropdown.rb +103 -0
- data/lib/plutonium/ui/block.rb +1 -1
- data/lib/plutonium/ui/breadcrumbs.rb +12 -19
- data/lib/plutonium/ui/color_mode_selector.rb +1 -1
- data/lib/plutonium/ui/component/kit.rb +6 -0
- data/lib/plutonium/ui/component_classes.rb +102 -0
- data/lib/plutonium/ui/display/base.rb +15 -0
- data/lib/plutonium/ui/display/components/attachment.rb +6 -5
- data/lib/plutonium/ui/display/components/boolean.rb +23 -0
- data/lib/plutonium/ui/display/components/color.rb +23 -0
- data/lib/plutonium/ui/display/resource.rb +1 -1
- data/lib/plutonium/ui/display/theme.rb +29 -15
- data/lib/plutonium/ui/empty_card.rb +3 -3
- data/lib/plutonium/ui/form/base.rb +20 -0
- data/lib/plutonium/ui/form/components/key_value_store.rb +11 -11
- data/lib/plutonium/ui/form/components/resource_select.rb +31 -0
- data/lib/plutonium/ui/form/components/secure_association.rb +1 -2
- data/lib/plutonium/ui/form/components/uppy.rb +5 -4
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +4 -4
- data/lib/plutonium/ui/form/interaction.rb +17 -1
- data/lib/plutonium/ui/form/query.rb +133 -80
- data/lib/plutonium/ui/form/theme.rb +50 -35
- data/lib/plutonium/ui/frame_navigator_panel.rb +2 -2
- data/lib/plutonium/ui/layout/base.rb +1 -1
- data/lib/plutonium/ui/layout/header.rb +4 -7
- data/lib/plutonium/ui/layout/rodauth_layout.rb +7 -7
- data/lib/plutonium/ui/layout/sidebar.rb +1 -1
- data/lib/plutonium/ui/nav_grid_menu.rb +7 -6
- data/lib/plutonium/ui/nav_user.rb +9 -8
- data/lib/plutonium/ui/page/interactive_action.rb +5 -5
- data/lib/plutonium/ui/page_header.rb +29 -10
- data/lib/plutonium/ui/panel.rb +4 -4
- data/lib/plutonium/ui/sidebar_menu.rb +8 -8
- data/lib/plutonium/ui/skeleton_table.rb +7 -8
- data/lib/plutonium/ui/tab_list.rb +5 -5
- data/lib/plutonium/ui/table/base.rb +3 -0
- data/lib/plutonium/ui/table/components/attachment.rb +4 -3
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +82 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +2 -2
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +13 -8
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +101 -0
- data/lib/plutonium/ui/table/components/scopes_bar.rb +2 -2
- data/lib/plutonium/ui/table/components/selection_column.rb +100 -0
- data/lib/plutonium/ui/table/display_theme.rb +6 -6
- data/lib/plutonium/ui/table/resource.rb +93 -52
- data/lib/plutonium/ui/table/theme.rb +28 -15
- data/lib/plutonium/version.rb +1 -1
- data/package.json +2 -2
- data/plutonium.gemspec +5 -4
- data/src/css/components.css +471 -0
- data/src/css/intl_tel_input.css +2 -2
- data/src/css/plutonium.css +2 -0
- data/src/css/tokens.css +149 -0
- data/src/js/controllers/bulk_actions_controller.js +109 -0
- data/src/js/controllers/filter_panel_controller.js +35 -0
- data/src/js/controllers/register_controllers.js +5 -1
- data/src/js/controllers/resource_drop_down_controller.js +25 -1
- data/src/js/controllers/slim_select_controller.js +6 -2
- data/src/js/turbo/turbo_actions.js +1 -1
- metadata +52 -39
- data/.claude/skills/definition-query/SKILL.md +0 -334
- data/docs/concepts/architecture.md +0 -226
- data/docs/concepts/auto-detection.md +0 -254
- data/docs/concepts/index.md +0 -61
- data/docs/concepts/packages-portals.md +0 -304
- data/docs/concepts/resources.md +0 -224
- data/docs/cookbook/blog.md +0 -411
- data/docs/cookbook/index.md +0 -289
- data/docs/cookbook/saas.md +0 -481
- data/docs/public/CLAUDE.md +0 -578
- data/lib/generators/pu/pkg/portal/templates/app/controllers/resource_controller.rb.tt +0 -5
|
@@ -1,254 +0,0 @@
|
|
|
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
|
data/docs/concepts/index.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
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.
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
# Packages and Portals
|
|
2
|
-
|
|
3
|
-
Plutonium organizes applications using two types of packages: **Feature Packages** for business logic and **Portal Packages** for web interfaces.
|
|
4
|
-
|
|
5
|
-
## Why Packages?
|
|
6
|
-
|
|
7
|
-
Packages provide:
|
|
8
|
-
- **Modularity** - Features are isolated and self-contained
|
|
9
|
-
- **Reusability** - Share features across multiple interfaces
|
|
10
|
-
- **Scalability** - Large apps stay organized
|
|
11
|
-
- **Team collaboration** - Teams can own specific packages
|
|
12
|
-
|
|
13
|
-
## Feature Packages
|
|
14
|
-
|
|
15
|
-
Feature packages contain your business logic: models, definitions, policies, interactions, and controllers.
|
|
16
|
-
|
|
17
|
-
### Creating a Feature Package
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
rails generate pu:pkg:package blogging
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
This creates:
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
packages/blogging/
|
|
27
|
-
├── app/
|
|
28
|
-
│ ├── controllers/blogging/
|
|
29
|
-
│ ├── definitions/blogging/
|
|
30
|
-
│ ├── interactions/blogging/
|
|
31
|
-
│ ├── models/blogging/
|
|
32
|
-
│ ├── policies/blogging/
|
|
33
|
-
│ └── views/blogging/
|
|
34
|
-
└── lib/
|
|
35
|
-
└── engine.rb
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Feature Package Structure
|
|
39
|
-
|
|
40
|
-
```ruby
|
|
41
|
-
# packages/blogging/lib/engine.rb
|
|
42
|
-
module Blogging
|
|
43
|
-
class Engine < Rails::Engine
|
|
44
|
-
include Plutonium::Package::Engine
|
|
45
|
-
|
|
46
|
-
# Package configuration here
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Adding Resources to a Feature Package
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
rails generate pu:res:scaffold Post title:string body:text --dest=blogging
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Resources are namespaced under the package:
|
|
58
|
-
|
|
59
|
-
```ruby
|
|
60
|
-
# packages/blogging/app/models/blogging/post.rb
|
|
61
|
-
module Blogging
|
|
62
|
-
class Post < Blogging::ResourceRecord
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Portal Packages
|
|
68
|
-
|
|
69
|
-
Portal packages are web interfaces that expose resources to users. Each portal can have its own authentication, authorization, and UI customizations.
|
|
70
|
-
|
|
71
|
-
### Creating a Portal Package
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
rails generate pu:pkg:portal admin
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This creates:
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
packages/admin_portal/
|
|
81
|
-
├── app/
|
|
82
|
-
│ ├── controllers/admin_portal/
|
|
83
|
-
│ └── views/admin_portal/
|
|
84
|
-
├── config/
|
|
85
|
-
│ └── routes.rb
|
|
86
|
-
├── lib/
|
|
87
|
-
│ └── admin_portal/
|
|
88
|
-
│ └── engine.rb
|
|
89
|
-
├── admin_portal.gemspec
|
|
90
|
-
└── Gemfile
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Portal Engine
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
# packages/admin_portal/lib/admin_portal/engine.rb
|
|
97
|
-
module AdminPortal
|
|
98
|
-
class Engine < Rails::Engine
|
|
99
|
-
include Plutonium::Portal::Engine
|
|
100
|
-
|
|
101
|
-
config.after_initialize do
|
|
102
|
-
# Optional: Scope to an entity (multi-tenancy)
|
|
103
|
-
scope_to_entity Organization
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Portal Authentication
|
|
110
|
-
|
|
111
|
-
Authentication is configured in the controller concern:
|
|
112
|
-
|
|
113
|
-
```ruby
|
|
114
|
-
# packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
|
|
115
|
-
module AdminPortal
|
|
116
|
-
module Concerns
|
|
117
|
-
module Controller
|
|
118
|
-
extend ActiveSupport::Concern
|
|
119
|
-
include Plutonium::Portal::Controller
|
|
120
|
-
include Plutonium::Auth::Rodauth(:admin)
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Connecting Resources to Portals
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
rails generate pu:res:conn Blogging::Post --dest=admin_portal
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
This:
|
|
133
|
-
1. Registers the resource in portal routes
|
|
134
|
-
2. Creates portal-specific controller
|
|
135
|
-
3. Creates portal-specific policy with attribute permissions
|
|
136
|
-
|
|
137
|
-
## Multiple Portals
|
|
138
|
-
|
|
139
|
-
A common pattern is having different portals for different user types:
|
|
140
|
-
|
|
141
|
-
```
|
|
142
|
-
packages/
|
|
143
|
-
├── admin_portal/ # Full access for administrators
|
|
144
|
-
├── author_portal/ # Content management for authors
|
|
145
|
-
└── customer_portal/ # Public-facing interface
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Each portal can:
|
|
149
|
-
- Use different authentication
|
|
150
|
-
- Show different fields
|
|
151
|
-
- Apply different policies
|
|
152
|
-
- Have unique UI customization
|
|
153
|
-
|
|
154
|
-
### Example: Same Resource, Different Portals
|
|
155
|
-
|
|
156
|
-
```ruby
|
|
157
|
-
# Admin sees everything
|
|
158
|
-
# packages/admin_portal/app/policies/admin_portal/blogging/post_policy.rb
|
|
159
|
-
module AdminPortal
|
|
160
|
-
module Blogging
|
|
161
|
-
class PostPolicy < ::Blogging::PostPolicy
|
|
162
|
-
def read?
|
|
163
|
-
true # Admins see all posts
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Authors see only their posts
|
|
170
|
-
# packages/author_portal/app/policies/author_portal/blogging/post_policy.rb
|
|
171
|
-
module AuthorPortal
|
|
172
|
-
module Blogging
|
|
173
|
-
class PostPolicy < ::Blogging::PostPolicy
|
|
174
|
-
def read?
|
|
175
|
-
record.user_id == user.id
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Package Dependencies
|
|
183
|
-
|
|
184
|
-
Feature packages can depend on each other:
|
|
185
|
-
|
|
186
|
-
```ruby
|
|
187
|
-
# packages/blogging/blogging.gemspec
|
|
188
|
-
Gem::Specification.new do |spec|
|
|
189
|
-
spec.add_dependency "users" # Depends on users package
|
|
190
|
-
end
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Mounting Packages
|
|
194
|
-
|
|
195
|
-
Packages are mounted in the main application routes:
|
|
196
|
-
|
|
197
|
-
```ruby
|
|
198
|
-
# config/routes.rb
|
|
199
|
-
Rails.application.routes.draw do
|
|
200
|
-
mount AdminPortal::Engine, at: "/admin"
|
|
201
|
-
mount AuthorPortal::Engine, at: "/author"
|
|
202
|
-
mount CustomerPortal::Engine, at: "/"
|
|
203
|
-
end
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Authentication per Portal
|
|
207
|
-
|
|
208
|
-
Each portal can use different authentication via its controller concern:
|
|
209
|
-
|
|
210
|
-
```ruby
|
|
211
|
-
# packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
|
|
212
|
-
module AdminPortal
|
|
213
|
-
module Concerns
|
|
214
|
-
module Controller
|
|
215
|
-
extend ActiveSupport::Concern
|
|
216
|
-
include Plutonium::Portal::Controller
|
|
217
|
-
include Plutonium::Auth::Rodauth(:admin)
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# packages/customer_portal/app/controllers/customer_portal/concerns/controller.rb
|
|
223
|
-
module CustomerPortal
|
|
224
|
-
module Concerns
|
|
225
|
-
module Controller
|
|
226
|
-
extend ActiveSupport::Concern
|
|
227
|
-
include Plutonium::Portal::Controller
|
|
228
|
-
include Plutonium::Auth::Rodauth(:customer)
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## Entity Scoping (Multi-tenancy)
|
|
235
|
-
|
|
236
|
-
Portals can be scoped to an entity:
|
|
237
|
-
|
|
238
|
-
```ruby
|
|
239
|
-
module CustomerPortal
|
|
240
|
-
class Engine < Rails::Engine
|
|
241
|
-
include Plutonium::Portal::Engine
|
|
242
|
-
|
|
243
|
-
config.after_initialize do
|
|
244
|
-
# All resources scoped to current organization
|
|
245
|
-
scope_to_entity Organization
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
With entity scoping:
|
|
252
|
-
- All queries automatically filter by entity
|
|
253
|
-
- New records automatically belong to entity
|
|
254
|
-
- Users can only access their entity's data
|
|
255
|
-
|
|
256
|
-
## Portal Customization
|
|
257
|
-
|
|
258
|
-
### Custom Layouts
|
|
259
|
-
|
|
260
|
-
```ruby
|
|
261
|
-
# packages/admin_portal/app/views/layouts/admin_portal/application.rb
|
|
262
|
-
module AdminPortal
|
|
263
|
-
class ApplicationLayout < Plutonium::UI::Layout::Application
|
|
264
|
-
def render_logo
|
|
265
|
-
img(src: asset_path("admin-logo.svg"))
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### Portal-Specific Definitions
|
|
272
|
-
|
|
273
|
-
```ruby
|
|
274
|
-
# packages/admin_portal/app/definitions/admin_portal/blogging/post_definition.rb
|
|
275
|
-
module AdminPortal
|
|
276
|
-
module Blogging
|
|
277
|
-
class PostDefinition < ::Blogging::PostDefinition
|
|
278
|
-
# Add admin-specific fields
|
|
279
|
-
field :internal_notes, as: :text
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## Best Practices
|
|
286
|
-
|
|
287
|
-
### 1. One Feature, One Package
|
|
288
|
-
Keep packages focused. A "blogging" package shouldn't handle user management.
|
|
289
|
-
|
|
290
|
-
### 2. Portal-Specific Overrides
|
|
291
|
-
Put customizations in the portal package, not the feature package.
|
|
292
|
-
|
|
293
|
-
### 3. Shared Logic in Features
|
|
294
|
-
Business logic goes in feature packages, UI customization in portals.
|
|
295
|
-
|
|
296
|
-
### 4. Clear Naming
|
|
297
|
-
- Feature packages: noun (blogging, inventory, billing)
|
|
298
|
-
- Portal packages: role + portal (admin_portal, customer_portal)
|
|
299
|
-
|
|
300
|
-
## Related Topics
|
|
301
|
-
|
|
302
|
-
- [Architecture](./architecture) - How layers work together
|
|
303
|
-
- [Resources](./resources) - Understanding resources
|
|
304
|
-
- [Portal Reference](/reference/portal/) - Portal configuration
|