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
data/docs/public/CLAUDE.md
DELETED
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
# Plutonium Framework Development Guide
|
|
2
|
-
|
|
3
|
-
This guide helps AI agents understand and build Plutonium applications effectively. Plutonium is a Rails RAD framework that extends Rails conventions with application-level concepts.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
### New Application
|
|
8
|
-
```bash
|
|
9
|
-
rails new app_name -a propshaft -j esbuild -c tailwind \
|
|
10
|
-
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### Essential Commands
|
|
14
|
-
```bash
|
|
15
|
-
# Setup auth
|
|
16
|
-
rails generate pu:rodauth:install
|
|
17
|
-
rails generate pu:rodauth:account user
|
|
18
|
-
|
|
19
|
-
# Create feature + portal
|
|
20
|
-
rails generate pu:pkg:package blog_management
|
|
21
|
-
rails generate pu:pkg:portal admin_portal
|
|
22
|
-
|
|
23
|
-
# Create complete resource (always specify --dest to avoid prompts)
|
|
24
|
-
rails generate pu:res:scaffold Post user:belongs_to title:string content:text --dest=blog_management
|
|
25
|
-
rails generate pu:res:scaffold User name:string email:string:uniq --dest=main_app # main app resource
|
|
26
|
-
|
|
27
|
-
# Connect to portal
|
|
28
|
-
rails generate pu:res:conn BlogManagement::Post --dest=admin_portal
|
|
29
|
-
rails generate pu:res:conn User --dest=admin_portal # main app resource (no namespace)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Start Building
|
|
33
|
-
```bash
|
|
34
|
-
rails db:migrate
|
|
35
|
-
bin/dev # Visit http://localhost:3000
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Core Concepts
|
|
39
|
-
|
|
40
|
-
### 1. Architecture
|
|
41
|
-
- **Packages**: Modular organization using Rails engines
|
|
42
|
-
- **Feature Packages**: Contain business logic (models, interactions, policies)
|
|
43
|
-
- **Portal Packages**: Provide web interfaces with authentication
|
|
44
|
-
- **Resources**: Complete CRUD entities with models, definitions, policies, controllers
|
|
45
|
-
- **Entity Scoping**: Built-in multi-tenancy support
|
|
46
|
-
|
|
47
|
-
### 2. Key Components
|
|
48
|
-
- **Models**: ActiveRecord with `ResourceRecord` base class for enhanced functionality
|
|
49
|
-
- **Definitions**: Declarative UI configuration (how fields render)
|
|
50
|
-
- **Policies**: Authorization control (what users can access)
|
|
51
|
-
- **Interactions**: Business logic encapsulation (what actions do)
|
|
52
|
-
- **Controllers**: Auto-generated CRUD with customization points
|
|
53
|
-
|
|
54
|
-
## Essential Generators
|
|
55
|
-
|
|
56
|
-
### Project Setup
|
|
57
|
-
```bash
|
|
58
|
-
# New app with Plutonium template
|
|
59
|
-
rails new app_name -a propshaft -j esbuild -c tailwind \
|
|
60
|
-
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
61
|
-
|
|
62
|
-
# Add to existing app
|
|
63
|
-
rails generate pu:core:install
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Authentication Setup (Optional)
|
|
67
|
-
|
|
68
|
-
Authentication is optional in Plutonium. You can build apps without auth using `Plutonium::Auth::Public`. When you do need authentication:
|
|
69
|
-
|
|
70
|
-
#### Basic Setup Workflow
|
|
71
|
-
```bash
|
|
72
|
-
# 1. Install Rodauth infrastructure
|
|
73
|
-
rails generate pu:rodauth:install
|
|
74
|
-
|
|
75
|
-
# 2. Create account types as needed
|
|
76
|
-
rails generate pu:rodauth:account user
|
|
77
|
-
|
|
78
|
-
# 3. Run migrations
|
|
79
|
-
rails db:migrate
|
|
80
|
-
|
|
81
|
-
# 4. Connect auth to portals (see Portal Integration below)
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
#### Account Type Options
|
|
85
|
-
```bash
|
|
86
|
-
# Standard user accounts
|
|
87
|
-
rails generate pu:rodauth:account user
|
|
88
|
-
|
|
89
|
-
# Enhanced admin accounts with MFA and security features
|
|
90
|
-
rails generate pu:rodauth:admin admin
|
|
91
|
-
|
|
92
|
-
# Multi-tenant customer accounts with entity scoping
|
|
93
|
-
rails generate pu:rodauth:customer Customer --entity=Organization
|
|
94
|
-
|
|
95
|
-
# Custom feature set (specify exact features needed)
|
|
96
|
-
rails generate pu:rodauth:account api_user \
|
|
97
|
-
--no-defaults --login --logout --jwt --verify-account
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
#### Portal Integration
|
|
101
|
-
```ruby
|
|
102
|
-
# For authenticated portals - add to controller concerns:
|
|
103
|
-
include Plutonium::Auth::Rodauth(:user) # Links to user auth
|
|
104
|
-
include Plutonium::Auth::Rodauth(:admin) # Links to admin auth
|
|
105
|
-
|
|
106
|
-
# For public portals - no authentication required:
|
|
107
|
-
include Plutonium::Auth::Public
|
|
108
|
-
|
|
109
|
-
# This provides: current_user, logout_url helpers in controllers/views
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
#### Available Auth Features
|
|
113
|
-
- **Core**: `login`, `logout`, `create_account`, `verify_account`, `reset_password`, `change_password`
|
|
114
|
-
- **Security**: `remember`, `lockout`, `active_sessions`, `audit_logging`
|
|
115
|
-
- **MFA**: `otp`, `recovery_codes`, `sms_codes`, `webauthn`
|
|
116
|
-
- **Advanced**: `password_grace_period`, `single_session`, `jwt`, `internal_request`
|
|
117
|
-
|
|
118
|
-
### Package Generators
|
|
119
|
-
```bash
|
|
120
|
-
# Create feature package for business logic
|
|
121
|
-
rails generate pu:pkg:package blog_management
|
|
122
|
-
|
|
123
|
-
# Create portal package for web interface
|
|
124
|
-
rails generate pu:pkg:portal admin_dashboard
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Resource Generators (Most Important)
|
|
128
|
-
|
|
129
|
-
**Always specify `--dest`** to avoid interactive prompts:
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
# Main app resource (not in a package)
|
|
133
|
-
rails generate pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
|
|
134
|
-
|
|
135
|
-
# Package resource
|
|
136
|
-
rails generate pu:res:scaffold Post user:belongs_to title:string content:text 'published_at:datetime?' --dest=blogging
|
|
137
|
-
|
|
138
|
-
# Referencing namespaced models in associations
|
|
139
|
-
rails generate pu:res:scaffold Comment user:belongs_to blogging/post:belongs_to body:text --dest=comments
|
|
140
|
-
rails generate pu:res:scaffold Order customer:belongs_to inventory/product:belongs_to quantity:integer --dest=commerce
|
|
141
|
-
|
|
142
|
-
# Model only
|
|
143
|
-
rails generate pu:res:model Article title:string body:text author:belongs_to --dest=blogging
|
|
144
|
-
rails generate pu:res:model Review user:belongs_to inventory/product:belongs_to rating:integer --dest=reviews
|
|
145
|
-
|
|
146
|
-
# CRITICAL: Use connection generator to expose resources in portals
|
|
147
|
-
# Always use the generator - NEVER manually connect resources
|
|
148
|
-
|
|
149
|
-
# Use full class name for namespaced resources
|
|
150
|
-
rails generate pu:res:conn BlogManagement::Post BlogManagement::Comment --dest=admin_portal
|
|
151
|
-
|
|
152
|
-
# Main app resources (not namespaced)
|
|
153
|
-
rails generate pu:res:conn Post Comment --dest=admin_portal
|
|
154
|
-
|
|
155
|
-
# The generator handles all routing, controller, and policy connections automatically
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Other Useful Generators
|
|
159
|
-
```bash
|
|
160
|
-
# Entity for multi-tenancy
|
|
161
|
-
rails generate pu:res:entity Organization --auth-account=Customer
|
|
162
|
-
|
|
163
|
-
# Eject components for customization
|
|
164
|
-
rails generate pu:eject:layout --dest=admin_portal
|
|
165
|
-
rails generate pu:eject:shell --dest=admin_portal
|
|
166
|
-
|
|
167
|
-
# Development tools
|
|
168
|
-
rails generate pu:gem:annotated # Model annotations
|
|
169
|
-
rails generate pu:gem:standard # Ruby Standard linter
|
|
170
|
-
rails generate pu:gem:dotenv # Environment variables
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## File Structure Patterns
|
|
174
|
-
|
|
175
|
-
### Feature Package Structure
|
|
176
|
-
```
|
|
177
|
-
packages/blog_management/
|
|
178
|
-
├── lib/
|
|
179
|
-
│ └── engine.rb # Package engine
|
|
180
|
-
└── app/
|
|
181
|
-
├── models/blog_management/
|
|
182
|
-
│ ├── post.rb # Business models
|
|
183
|
-
│ └── resource_record.rb # Base class
|
|
184
|
-
├── policies/blog_management/
|
|
185
|
-
│ ├── post_policy.rb # Authorization rules
|
|
186
|
-
│ └── resource_policy.rb # Base policy
|
|
187
|
-
├── definitions/blog_management/
|
|
188
|
-
│ ├── post_definition.rb # UI configuration
|
|
189
|
-
│ └── resource_definition.rb # Base definition
|
|
190
|
-
├── interactions/blog_management/
|
|
191
|
-
│ └── post_interactions/
|
|
192
|
-
│ └── publish.rb # Business logic
|
|
193
|
-
└── controllers/blog_management/
|
|
194
|
-
└── posts_controller.rb # Optional custom controller
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Portal Package Structure
|
|
198
|
-
```
|
|
199
|
-
packages/admin_portal/
|
|
200
|
-
├── lib/
|
|
201
|
-
│ └── engine.rb # Portal engine
|
|
202
|
-
├── config/
|
|
203
|
-
│ └── routes.rb # Portal routes
|
|
204
|
-
└── app/
|
|
205
|
-
├── controllers/admin_portal/
|
|
206
|
-
│ ├── concerns/controller.rb # Auth integration
|
|
207
|
-
│ ├── dashboard_controller.rb # Dashboard
|
|
208
|
-
│ ├── plutonium_controller.rb # Base controller
|
|
209
|
-
│ └── resource_controller.rb # Resource base
|
|
210
|
-
├── policies/admin_portal/
|
|
211
|
-
│ └── resource_policy.rb # Portal-specific policies
|
|
212
|
-
└── definitions/admin_portal/
|
|
213
|
-
└── resource_definition.rb # Portal-specific definitions
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
## Development Patterns
|
|
217
|
-
|
|
218
|
-
### 1. Resource Definition (UI Configuration)
|
|
219
|
-
|
|
220
|
-
**CRITICAL**: Plutonium automatically detects all fields from your model - database columns, associations, and Active Storage attachments. Definitions should only contain customizations, not field declarations.
|
|
221
|
-
|
|
222
|
-
```ruby
|
|
223
|
-
class PostDefinition < Plutonium::Resource::Definition
|
|
224
|
-
# NO NEED to declare fields - they're auto-detected from the model
|
|
225
|
-
# Only add customizations below when you want to change default behavior
|
|
226
|
-
|
|
227
|
-
# Form inputs (only override when changing auto-detected behavior)
|
|
228
|
-
input :content, as: :rich_text
|
|
229
|
-
input :published_at, as: :date
|
|
230
|
-
input :category, as: :select, choices: %w[Tech Business]
|
|
231
|
-
|
|
232
|
-
# Display formatting (only override when changing auto-detected behavior)
|
|
233
|
-
display :content, as: :markdown
|
|
234
|
-
display :published_at, as: :datetime
|
|
235
|
-
|
|
236
|
-
# Table columns (only override when changing auto-detected behavior)
|
|
237
|
-
column :published_at, as: :datetime
|
|
238
|
-
|
|
239
|
-
# Search functionality
|
|
240
|
-
search do |scope, query|
|
|
241
|
-
scope.where("title ILIKE ? OR content ILIKE ?", "%#{query}%", "%#{query}%")
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Filters (currently only Text filter available)
|
|
245
|
-
filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
|
|
246
|
-
|
|
247
|
-
# Scopes (named scopes defined on the model. they that appear as filter buttons)
|
|
248
|
-
scope :published
|
|
249
|
-
scope :drafts
|
|
250
|
-
|
|
251
|
-
# Custom actions
|
|
252
|
-
action :publish, interaction: PostInteractions::Publish
|
|
253
|
-
action :archive, interaction: PostInteractions::Archive
|
|
254
|
-
end
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### 2. Policy Configuration (Authorization)
|
|
258
|
-
```ruby
|
|
259
|
-
class PostPolicy < Plutonium::Resource::Policy
|
|
260
|
-
# Basic permissions (required - secure by default)
|
|
261
|
-
def create?
|
|
262
|
-
user.present?
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
def update?
|
|
266
|
-
record.user_id == user.id || user.admin?
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def destroy?
|
|
270
|
-
user.admin?
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
# Custom action permissions
|
|
274
|
-
def publish?
|
|
275
|
-
update? && record.published_at.nil?
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
# Attribute permissions (what fields are visible/editable)
|
|
279
|
-
def permitted_attributes_for_read
|
|
280
|
-
attrs = [:title, :content, :published_at, :created_at]
|
|
281
|
-
attrs << :admin_notes if user.admin?
|
|
282
|
-
attrs
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def permitted_attributes_for_create
|
|
286
|
-
[:title, :content, :user_id]
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def permitted_attributes_for_update
|
|
290
|
-
permitted_attributes_for_create
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
# Data scoping (what records are visible)
|
|
294
|
-
relation_scope do |scope|
|
|
295
|
-
scope = super(scope) # Important: call super for entity scoping
|
|
296
|
-
|
|
297
|
-
if user.admin?
|
|
298
|
-
scope
|
|
299
|
-
else
|
|
300
|
-
scope.where(user: user).or(scope.where(published: true))
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### 3. Interaction Implementation (Business Logic)
|
|
307
|
-
```ruby
|
|
308
|
-
module PostInteractions
|
|
309
|
-
class Publish < Plutonium::Resource::Interaction
|
|
310
|
-
# Define what this interaction accepts
|
|
311
|
-
attribute :resource, class: "Post"
|
|
312
|
-
attribute :published_at, :datetime, default: -> { Time.current }
|
|
313
|
-
|
|
314
|
-
# UI presentation
|
|
315
|
-
presents label: "Publish Post",
|
|
316
|
-
icon: Phlex::TablerIcons::Send,
|
|
317
|
-
description: "Make this post public"
|
|
318
|
-
|
|
319
|
-
# Validations
|
|
320
|
-
validates :resource, presence: true
|
|
321
|
-
|
|
322
|
-
private
|
|
323
|
-
|
|
324
|
-
# Business logic
|
|
325
|
-
def execute
|
|
326
|
-
if resource.update(published_at: published_at, status: 'published')
|
|
327
|
-
succeed(resource)
|
|
328
|
-
.with_message("Post published successfully")
|
|
329
|
-
.with_redirect_response(resource_url_for(resource))
|
|
330
|
-
else
|
|
331
|
-
failed(resource.errors)
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### 4. Model Setup
|
|
339
|
-
```ruby
|
|
340
|
-
class Post < BlogManagement::ResourceRecord
|
|
341
|
-
# Associations
|
|
342
|
-
belongs_to :user
|
|
343
|
-
has_many :comments, dependent: :destroy
|
|
344
|
-
has_one_attached :featured_image
|
|
345
|
-
|
|
346
|
-
# Validations
|
|
347
|
-
validates :title, presence: true
|
|
348
|
-
validates :content, presence: true
|
|
349
|
-
|
|
350
|
-
# Enums
|
|
351
|
-
enum status: { draft: 0, published: 1, archived: 2 }
|
|
352
|
-
|
|
353
|
-
# Scopes
|
|
354
|
-
scope :published, -> { where.not(published_at: nil) }
|
|
355
|
-
scope :recent, -> { order(created_at: :desc) }
|
|
356
|
-
|
|
357
|
-
# Monetary fields (if needed)
|
|
358
|
-
has_cents :price_cents
|
|
359
|
-
|
|
360
|
-
# Custom path parameters (class methods, not instance methods)
|
|
361
|
-
# path_parameter :username # Uses username in URLs
|
|
362
|
-
# dynamic_path_parameter :title # Creates SEO URLs like "1-my-title"
|
|
363
|
-
|
|
364
|
-
# Custom labeling (optional)
|
|
365
|
-
def to_label
|
|
366
|
-
title.presence || "Post ##{id}"
|
|
367
|
-
end
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
# Example with cross-package associations
|
|
371
|
-
class Comment < Comments::ResourceRecord
|
|
372
|
-
belongs_to :user
|
|
373
|
-
belongs_to :post, class_name: "Blogging::Post" # Cross-package reference
|
|
374
|
-
|
|
375
|
-
validates :body, presence: true
|
|
376
|
-
end
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
## Best Practices
|
|
380
|
-
|
|
381
|
-
### 1. File Organization
|
|
382
|
-
- Use packages to organize related features
|
|
383
|
-
- Keep business logic in feature packages
|
|
384
|
-
- Use portal packages for different user interfaces
|
|
385
|
-
- Follow namespacing conventions strictly
|
|
386
|
-
|
|
387
|
-
### 2. Security First
|
|
388
|
-
- Always define explicit permissions in policies
|
|
389
|
-
- Use relation_scope for data access control
|
|
390
|
-
- Leverage entity scoping for multi-tenancy
|
|
391
|
-
- Test authorization thoroughly
|
|
392
|
-
|
|
393
|
-
### 3. Generator Usage
|
|
394
|
-
- Start with `pu:res:scaffold` for complete resources
|
|
395
|
-
- Use `--dest=package_name` to specify target package
|
|
396
|
-
- **CRITICAL**: Use `pu:res:conn` generator to connect resources to portals - never manually connect
|
|
397
|
-
- Definitions only need overrides - auto-detection handles defaults
|
|
398
|
-
|
|
399
|
-
### 4. UI Customization
|
|
400
|
-
- Policies control WHAT (authorization)
|
|
401
|
-
- Definitions control HOW (presentation) - **fields are auto-detected, only add customizations**
|
|
402
|
-
- Interactions control business logic
|
|
403
|
-
- Trust auto-detection, customize only when needed
|
|
404
|
-
|
|
405
|
-
### 5. Common Field Types
|
|
406
|
-
**Input Types**: `:string`, `:text`, `:rich_text`, `:email`, `:url`, `:tel`, `:password`, `:number`, `:boolean`, `:date`, `:datetime`, `:select`, `:file`, `:uppy`, `:association`
|
|
407
|
-
|
|
408
|
-
**Display Types**: `:string`, `:text`, `:markdown`, `:email`, `:url`, `:boolean`, `:date`, `:datetime`, `:association`, `:attachment`
|
|
409
|
-
|
|
410
|
-
**Action Options**: `category: :primary/:secondary/:danger`, `position: 10`, `record_action: true`, `collection_record_action: true`, `resource_action: true`, `bulk_action: true`, `confirmation: "message"`, `icon: Phlex::TablerIcons::IconName`
|
|
411
|
-
|
|
412
|
-
## Migration Tips
|
|
413
|
-
|
|
414
|
-
### Database Setup
|
|
415
|
-
- Use standard Rails migration conventions
|
|
416
|
-
- Always inline indexes and constraints in create_table blocks
|
|
417
|
-
- Leverage Rails associations (`belongs_to`, `has_many`, etc.)
|
|
418
|
-
|
|
419
|
-
### Generator Field Syntax
|
|
420
|
-
|
|
421
|
-
**IMPORTANT**: Quote fields containing `?` or `{}` to prevent shell expansion.
|
|
422
|
-
|
|
423
|
-
**IMPORTANT**: Always specify `--dest` to avoid interactive prompts:
|
|
424
|
-
- `--dest=main_app` for resources in the main application
|
|
425
|
-
- `--dest=package_name` for resources in a feature package
|
|
426
|
-
|
|
427
|
-
```bash
|
|
428
|
-
# Nullable fields
|
|
429
|
-
'name:string?' # null: true
|
|
430
|
-
'parent:belongs_to?' # null: true + optional: true in model
|
|
431
|
-
|
|
432
|
-
# Decimal precision (only works for decimal type)
|
|
433
|
-
'price:decimal{10,2}' # precision: 10, scale: 2
|
|
434
|
-
'amount:decimal?{10,2}' # nullable with precision
|
|
435
|
-
|
|
436
|
-
# For default values on other types (boolean, integer, etc.),
|
|
437
|
-
# edit the migration manually after generation
|
|
438
|
-
|
|
439
|
-
# Indexes
|
|
440
|
-
email:string:index # Regular index
|
|
441
|
-
email:string:uniq # Unique index
|
|
442
|
-
|
|
443
|
-
# Cross-package references
|
|
444
|
-
blogging/post:belongs_to # belongs_to :post, class_name: "Blogging::Post"
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Cross-Package Associations
|
|
448
|
-
```bash
|
|
449
|
-
# Reference models from other packages using package/model syntax
|
|
450
|
-
rails generate pu:res:scaffold Comment \
|
|
451
|
-
user:belongs_to \
|
|
452
|
-
blogging/post:belongs_to \
|
|
453
|
-
'parent:belongs_to?' \
|
|
454
|
-
body:text \
|
|
455
|
-
--dest=comments
|
|
456
|
-
|
|
457
|
-
# Generates:
|
|
458
|
-
# belongs_to :post, class_name: "Blogging::Post"
|
|
459
|
-
# belongs_to :parent, optional: true
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
### Entity Scoping (Multi-Tenancy) Setup
|
|
463
|
-
|
|
464
|
-
Plutonium provides powerful multi-tenancy through Entity Scoping, which automatically isolates data by tenant.
|
|
465
|
-
|
|
466
|
-
#### 1. Configure Portal Engine
|
|
467
|
-
```ruby
|
|
468
|
-
# In packages/admin_portal/lib/engine.rb
|
|
469
|
-
config.after_initialize do
|
|
470
|
-
scope_to_entity Organization, strategy: :path # URLs: /organizations/:organization_id/posts
|
|
471
|
-
|
|
472
|
-
# Custom strategy (subdomain-based)
|
|
473
|
-
scope_to_entity Organization, strategy: :current_organization # URLs: /posts on acme.app.com
|
|
474
|
-
|
|
475
|
-
# Custom parameter name
|
|
476
|
-
scope_to_entity Client, strategy: :path, param_key: :client_slug # URLs: /clients/:client_slug/posts
|
|
477
|
-
end
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
#### 2. Implement Custom Strategy Methods
|
|
481
|
-
```ruby
|
|
482
|
-
# In packages/customer_portal/app/controllers/customer_portal/concerns/controller.rb
|
|
483
|
-
private
|
|
484
|
-
|
|
485
|
-
def current_organization
|
|
486
|
-
@current_organization ||= begin
|
|
487
|
-
organization = Organization.find_by!(subdomain: request.subdomain)
|
|
488
|
-
|
|
489
|
-
# CRITICAL: Verify user has access to this organization
|
|
490
|
-
unless current_user.organizations.include?(organization)
|
|
491
|
-
raise ActionPolicy::Unauthorized, "Access denied to organization"
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
organization
|
|
495
|
-
end
|
|
496
|
-
rescue ActiveRecord::RecordNotFound
|
|
497
|
-
redirect_to root_path, error: "Invalid organization subdomain"
|
|
498
|
-
end
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
#### 3. Model Association Setup
|
|
502
|
-
```ruby
|
|
503
|
-
# Direct association (preferred)
|
|
504
|
-
class Post < ApplicationRecord
|
|
505
|
-
belongs_to :organization # Direct link
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
# Indirect association (automatic chain discovery)
|
|
509
|
-
class Comment < ApplicationRecord
|
|
510
|
-
belongs_to :post
|
|
511
|
-
has_one :organization, through: :post # Chain: Comment -> Post -> Organization
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
# Custom scope for complex relationships
|
|
515
|
-
class Invoice < ApplicationRecord
|
|
516
|
-
belongs_to :customer
|
|
517
|
-
|
|
518
|
-
scope :associated_with_organization, ->(organization) do
|
|
519
|
-
joins(customer: :organization_memberships)
|
|
520
|
-
.where(organization_memberships: { organization_id: organization.id })
|
|
521
|
-
end
|
|
522
|
-
end
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
#### 4. Policy Integration
|
|
526
|
-
```ruby
|
|
527
|
-
class PostPolicy < Plutonium::Resource::Policy
|
|
528
|
-
authorize :entity_scope, allow_nil: true # Access to current tenant
|
|
529
|
-
|
|
530
|
-
def update?
|
|
531
|
-
# Ensure record belongs to current tenant AND user can edit
|
|
532
|
-
record.organization == entity_scope && record.author == user
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
relation_scope do |relation|
|
|
536
|
-
relation = super(relation) # Apply entity scoping first
|
|
537
|
-
|
|
538
|
-
# Add additional tenant-aware filtering
|
|
539
|
-
user.admin? ? relation : relation.where(published: true)
|
|
540
|
-
end
|
|
541
|
-
end
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
## Quick Reference Commands
|
|
545
|
-
|
|
546
|
-
```bash
|
|
547
|
-
# Essential workflow (always use --dest to avoid prompts)
|
|
548
|
-
rails generate pu:pkg:package feature_name # Create feature package
|
|
549
|
-
rails generate pu:res:scaffold Resource --dest=main_app # Main app resource
|
|
550
|
-
rails generate pu:res:scaffold Resource --dest=feature_name # Package resource
|
|
551
|
-
rails generate pu:pkg:portal portal_name # Create portal
|
|
552
|
-
rails generate pu:res:conn Feature::Resource --dest=portal # CRITICAL: Always use generator to connect
|
|
553
|
-
|
|
554
|
-
# Authentication
|
|
555
|
-
rails generate pu:rodauth:install # Install auth system
|
|
556
|
-
rails generate pu:rodauth:account user # Create user account
|
|
557
|
-
rails generate pu:rodauth:admin admin # Create admin account
|
|
558
|
-
|
|
559
|
-
# Database
|
|
560
|
-
rails db:migrate # Run migrations
|
|
561
|
-
rails db:seed # Seed data
|
|
562
|
-
|
|
563
|
-
# Development
|
|
564
|
-
rails server # Start server
|
|
565
|
-
rails console # Rails console
|
|
566
|
-
rails runner "code" # Run code (prefer over console)
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
## Troubleshooting
|
|
570
|
-
|
|
571
|
-
### Common Issues
|
|
572
|
-
- **Missing permissions**: Check policy methods return true
|
|
573
|
-
- **Fields not showing**: Verify policy permits attributes
|
|
574
|
-
- **Actions not visible**: Ensure action policy method exists
|
|
575
|
-
- **Routing errors**: Check portal routes registration
|
|
576
|
-
- **Package not loading**: Verify engine is properly configured
|
|
577
|
-
|
|
578
|
-
This guide provides the foundation for building robust Plutonium applications with proper separation of concerns, security, and maintainability.
|