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,75 +0,0 @@
|
|
|
1
|
-
# 1. Project Setup
|
|
2
|
-
|
|
3
|
-
Welcome to the Plutonium tutorial! In this guide, we'll build a complete blog application from scratch.
|
|
4
|
-
|
|
5
|
-
First, let's get a new Rails application up and running with Plutonium installed.
|
|
6
|
-
|
|
7
|
-
## Prerequisites
|
|
8
|
-
|
|
9
|
-
Before you begin, make sure you have the following installed:
|
|
10
|
-
|
|
11
|
-
- Ruby 3.2.2 or higher
|
|
12
|
-
- Rails 7.1 or higher
|
|
13
|
-
- Node.js and Yarn
|
|
14
|
-
|
|
15
|
-
If you're new to Rails, we highly recommend the official [Rails Getting Started Guide](https://guides.rubyonrails.org/getting_started.html) first.
|
|
16
|
-
|
|
17
|
-
## Create a New Rails App
|
|
18
|
-
|
|
19
|
-
We'll use the Plutonium Rails template to create a new application. This is the fastest way to get started.
|
|
20
|
-
|
|
21
|
-
Open your terminal and run the following command:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
rails new plutonium_blog -a propshaft -j esbuild -c tailwind \
|
|
25
|
-
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
This command creates a new Rails app named `plutonium_blog` and configures it with:
|
|
29
|
-
- `propshaft` for assets
|
|
30
|
-
- `esbuild` for JavaScript bundling
|
|
31
|
-
- `TailwindCSS` for styling
|
|
32
|
-
- The base Plutonium gem and configurations
|
|
33
|
-
|
|
34
|
-
## Explore the Project
|
|
35
|
-
|
|
36
|
-
Once the command finishes, navigate into your new project's directory:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
cd plutonium_blog
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The installer has already set up the core files you need. Here are the most important ones for now:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
plutonium_blog/
|
|
46
|
-
├── app/
|
|
47
|
-
│ ├── controllers/
|
|
48
|
-
│ │ ├── plutonium_controller.rb # Base controller for Plutonium
|
|
49
|
-
│ │ └── resource_controller.rb # Base for all resource controllers
|
|
50
|
-
│ └── views/
|
|
51
|
-
│ └── layouts/
|
|
52
|
-
│ └── resource.html.erb # Default layout for Plutonium views
|
|
53
|
-
├── config/
|
|
54
|
-
│ ├── initializers/
|
|
55
|
-
│ │ └── plutonium.rb # Main Plutonium configuration
|
|
56
|
-
│ └── packages.rb # How custom packages are loaded
|
|
57
|
-
└── packages/ # Where you'll build your features
|
|
58
|
-
└── .keep
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Start the Server
|
|
62
|
-
|
|
63
|
-
Let's boot up the Rails server to make sure everything is working correctly.
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
bin/dev
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Open your web browser and navigate to [http://localhost:3000](http://localhost:3000). You should see the default Rails welcome page.
|
|
70
|
-
|
|
71
|
-
## Next Steps
|
|
72
|
-
|
|
73
|
-
Congratulations! You have a fresh Plutonium application ready to go.
|
|
74
|
-
|
|
75
|
-
In the next chapter, we'll create our first **Feature Package** to house all the logic for our blog.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# 2. Creating a Feature Package
|
|
2
|
-
|
|
3
|
-
Now that we have our Rails application, it's time to start organizing our code. In Plutonium, we do this using **Packages**.
|
|
4
|
-
|
|
5
|
-
## What are Packages?
|
|
6
|
-
|
|
7
|
-
Packages are like mini-Rails applications (Rails Engines) that live inside your main app. They help you group related code together, making your application more modular and easier to maintain.
|
|
8
|
-
|
|
9
|
-
There are two main types of packages, but for now, we'll focus on **Feature Packages**.
|
|
10
|
-
|
|
11
|
-
A **Feature Package** holds all the business logic for a specific feature. In our case, we'll create a `Blogging` package to hold everything related to blog posts and comments.
|
|
12
|
-
|
|
13
|
-
## Generate the Package
|
|
14
|
-
|
|
15
|
-
Plutonium comes with a generator to create feature packages. Run the following command in your terminal:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
rails generate pu:pkg:package blogging
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
This command creates a new directory at `packages/blogging` with the structure for your new package.
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
packages/
|
|
25
|
-
└── blogging/
|
|
26
|
-
├── app/
|
|
27
|
-
│ ├── controllers/
|
|
28
|
-
│ │ └── blogging/ # Controllers for this package
|
|
29
|
-
│ ├── definitions/
|
|
30
|
-
│ │ └── blogging/ # Resource definitions
|
|
31
|
-
│ ├── interactions/
|
|
32
|
-
│ │ └── blogging/ # Business logic actions
|
|
33
|
-
│ ├── models/
|
|
34
|
-
│ │ └── blogging/ # ActiveRecord models
|
|
35
|
-
│ └── policies/
|
|
36
|
-
│ └── blogging/ # Authorization policies
|
|
37
|
-
└── lib/
|
|
38
|
-
└── engine.rb # The engine configuration
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Every file and class inside this package will be automatically namespaced under the `Blogging` module to prevent conflicts with other parts of your application.
|
|
42
|
-
|
|
43
|
-
## Next Steps
|
|
44
|
-
|
|
45
|
-
With our `Blogging` package in place, we're ready to define the core data models for our application. In the next chapter, we'll create the `Post` and `Comment` resources.
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# 3. Defining Resources
|
|
2
|
-
|
|
3
|
-
With our `Blogging` package ready, we can now define the core **Resources** of our feature: `Post` and `Comment`. But first, we need a way to represent users, since posts and comments will belong to a user.
|
|
4
|
-
|
|
5
|
-
## Setting Up Users and Authentication
|
|
6
|
-
|
|
7
|
-
Plutonium provides generators to quickly set up user authentication using the popular [Rodauth](https://rodauth.jeremyevans.net/) library.
|
|
8
|
-
|
|
9
|
-
Run the following commands in your terminal:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# 1. Install Rodauth configuration
|
|
13
|
-
rails generate pu:rodauth:install
|
|
14
|
-
|
|
15
|
-
# 2. Generate a User model and authentication logic
|
|
16
|
-
rails generate pu:rodauth:account user
|
|
17
|
-
|
|
18
|
-
# 3. Run the database migrations
|
|
19
|
-
rails db:migrate
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
This is a huge time-saver! It creates:
|
|
23
|
-
- A `User` model (`app/models/user.rb`).
|
|
24
|
-
- Database migrations for the `users` table.
|
|
25
|
-
- All the necessary authentication logic (login, logout, sign up, password reset, etc.).
|
|
26
|
-
|
|
27
|
-
Now that we have a `User` model, we can create our blog-specific resources.
|
|
28
|
-
|
|
29
|
-
## Scaffolding the Post Resource
|
|
30
|
-
|
|
31
|
-
A **Resource** in Plutonium is more than just a model; it's a combination of the model, its definition (how it's displayed), and its policy (who can do what).
|
|
32
|
-
|
|
33
|
-
Let's scaffold our `Post` resource. It will have a title, some text content, and will belong to a `User`.
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
rails generate pu:res:scaffold Post user:belongs_to title:string content:text 'published_at:datetime?'
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
The generator will prompt you to choose which package this resource belongs to. **Select `blogging`**.
|
|
40
|
-
|
|
41
|
-
This command generates several important files inside your `packages/blogging` directory:
|
|
42
|
-
|
|
43
|
-
- `app/models/blogging/post.rb`: The ActiveRecord model.
|
|
44
|
-
- `app/definitions/blogging/post_definition.rb`: The resource definition for UI.
|
|
45
|
-
- `app/policies/blogging/post_policy.rb`: The authorization policy.
|
|
46
|
-
- A database migration to create the `blogging_posts` table.
|
|
47
|
-
|
|
48
|
-
Let's look at the generated model at `packages/blogging/app/models/blogging/post.rb`:
|
|
49
|
-
|
|
50
|
-
```ruby
|
|
51
|
-
# packages/blogging/app/models/blogging/post.rb
|
|
52
|
-
class Blogging::Post < Blogging::ResourceRecord
|
|
53
|
-
belongs_to :user
|
|
54
|
-
validates :title, presence: true
|
|
55
|
-
validates :content, presence: true
|
|
56
|
-
end
|
|
57
|
-
```
|
|
58
|
-
Notice it's correctly namespaced under `Blogging` and associated with the `User` model.
|
|
59
|
-
|
|
60
|
-
Before we continue, run the migration to update your database:
|
|
61
|
-
```bash
|
|
62
|
-
rails db:migrate
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Scaffolding the Comment Resource
|
|
66
|
-
|
|
67
|
-
Now, let's do the same for `Comment`. A comment will belong to a `User` and a `Post`.
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
rails generate pu:res:scaffold Comment user:belongs_to blogging/post:belongs_to body:text
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Again, select the **`blogging`** package when prompted.
|
|
74
|
-
|
|
75
|
-
::: tip Namespaced Associations
|
|
76
|
-
Notice that we used `blogging/post` to specify the association. Plutonium's generators understand this and will correctly create the namespaced `Blogging::Post` association.
|
|
77
|
-
:::
|
|
78
|
-
|
|
79
|
-
This generates the model, definition, policy, and migration for comments.
|
|
80
|
-
|
|
81
|
-
Run the migration for comments:
|
|
82
|
-
```bash
|
|
83
|
-
rails db:migrate
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Next Steps
|
|
87
|
-
|
|
88
|
-
We've now set up users, authentication, and the core `Post` and `Comment` resources for our blog. However, there's no way to interact with them yet.
|
|
89
|
-
|
|
90
|
-
In the next chapter, we'll create a **Portal** to provide a web interface for managing our new resources.
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# 4. Creating a Portal
|
|
2
|
-
|
|
3
|
-
We have our `Blogging` feature package with `Post` and `Comment` resources, but no user interface to manage them. Let's create a **Portal** to serve as a web interface for our blog.
|
|
4
|
-
|
|
5
|
-
## What are Portal Packages?
|
|
6
|
-
|
|
7
|
-
While Feature Packages hold business logic, **Portal Packages** provide the web interface. They are also Rails Engines, but they are responsible for things like:
|
|
8
|
-
|
|
9
|
-
- Dashboards and layouts.
|
|
10
|
-
- Routing requests to the correct resources.
|
|
11
|
-
- Handling user authentication for the interface.
|
|
12
|
-
|
|
13
|
-
We'll create a `Dashboard` portal for managing our blog.
|
|
14
|
-
|
|
15
|
-
## Generate the Portal
|
|
16
|
-
|
|
17
|
-
Plutonium has a generator for portals. In your terminal, run:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
rails generate pu:pkg:portal dashboard
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
The generator will ask which authentication account to use. We already set up the `user` account with Rodauth, so **select `user`**.
|
|
24
|
-
|
|
25
|
-
This creates a new package at `packages/dashboard_portal/` and wires it up for authentication.
|
|
26
|
-
|
|
27
|
-
## Connect Resources to the Portal
|
|
28
|
-
|
|
29
|
-
Now we need to tell our new `Dashboard` portal which resources it should manage. We can use the `pu:res:conn` (connect) generator for this.
|
|
30
|
-
|
|
31
|
-
Run the connect generator:
|
|
32
|
-
```bash
|
|
33
|
-
rails generate pu:res:conn
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
It will ask you a series of questions:
|
|
37
|
-
1. **Select source feature**: Choose **`blogging`**.
|
|
38
|
-
2. **Select resources**: Use the spacebar to select both **`Blogging::Post`** and **`Blogging::Comment`**, then press Enter.
|
|
39
|
-
3. **Select destination portal**: Choose **`dashboard_portal`**.
|
|
40
|
-
|
|
41
|
-
This command does the magic of connecting your feature logic to your UI. It updates the portal's routes file (`packages/dashboard_portal/config/routes.rb`) to register the resources:
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
# packages/dashboard_portal/config/routes.rb
|
|
45
|
-
DashboardPortal::Engine.routes.draw do
|
|
46
|
-
root to: "dashboard#index"
|
|
47
|
-
|
|
48
|
-
register_resource Blogging::Post
|
|
49
|
-
register_resource Blogging::Comment
|
|
50
|
-
end
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Configure Application Routes
|
|
54
|
-
|
|
55
|
-
There's one last step. We need to tell our main Rails application how to handle routing for authentication and the new dashboard.
|
|
56
|
-
|
|
57
|
-
1. **Update Rodauth Redirects**: We want users to be sent to the dashboard after they log in or sign up.
|
|
58
|
-
|
|
59
|
-
```ruby [app/rodauth/user_rodauth_plugin.rb]
|
|
60
|
-
# ==> Redirects
|
|
61
|
-
|
|
62
|
-
# Redirect to home after login.
|
|
63
|
-
create_account_redirect "/" # [!code --]
|
|
64
|
-
create_account_redirect "/dashboard" # [!code ++]
|
|
65
|
-
|
|
66
|
-
# Redirect to home after login.
|
|
67
|
-
login_redirect "/" # [!code --]
|
|
68
|
-
login_redirect "/dashboard" # [!code ++]
|
|
69
|
-
|
|
70
|
-
# Redirect to home page after logout.
|
|
71
|
-
logout_redirect "/" # [!code --]
|
|
72
|
-
logout_redirect "/dashboard" # [!code ++]
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
2. **Update Root Route**: We'll make the dashboard the root of our application.
|
|
76
|
-
|
|
77
|
-
```ruby [config/routes.rb]
|
|
78
|
-
Rails.application.routes.draw do
|
|
79
|
-
# ...
|
|
80
|
-
# Defines the root path route ("/")
|
|
81
|
-
# root "posts#index" # [!code --]
|
|
82
|
-
root to: redirect("/dashboard") # [!code ++]
|
|
83
|
-
end
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## See it in Action!
|
|
87
|
-
|
|
88
|
-
Let's check our progress. Start your Rails server (`bin/dev`) and navigate to [http://localhost:3000](http://localhost:3000).
|
|
89
|
-
|
|
90
|
-
You will be redirected to `/dashboard` and should see the login page.
|
|
91
|
-
|
|
92
|
-
- **Sign up** for a new account.
|
|
93
|
-
- After signing up, you'll be redirected to the dashboard. It will be empty for now, but this confirms the portal and authentication are working!
|
|
94
|
-
|
|
95
|
-

|
|
96
|
-
|
|
97
|
-
## Next Steps
|
|
98
|
-
|
|
99
|
-
We now have a functional dashboard! It's not very useful yet, though.
|
|
100
|
-
|
|
101
|
-
In the next chapter, we will customize the user interface to display our posts and comments, and add navigation to make them accessible.
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# 5. Customizing the UI
|
|
2
|
-
|
|
3
|
-
Our dashboard is functional, but it's not very user-friendly. In Plutonium, customizing the UI is a two-step process that respects the separation of concerns:
|
|
4
|
-
|
|
5
|
-
1. **Policy (`app/policies`)**: The policy file controls **what** a user is authorized to see or edit. This is a security layer.
|
|
6
|
-
2. **Definition (`app/definitions`)**: The definition file controls **how** those permitted fields are displayed and formatted. This is a presentation layer.
|
|
7
|
-
|
|
8
|
-
Let's customize our `Post` resource by following this two-step process for the table, detail page, and form.
|
|
9
|
-
|
|
10
|
-
## Customizing the Posts Table (Index View)
|
|
11
|
-
|
|
12
|
-
First, we'll define **what** columns should appear in the posts table. This is an authorization concern, so we'll use the policy file.
|
|
13
|
-
|
|
14
|
-
Open the post policy: `packages/blogging/app/policies/blogging/post_policy.rb`
|
|
15
|
-
|
|
16
|
-
```ruby
|
|
17
|
-
# packages/blogging/app/policies/blogging/post_policy.rb
|
|
18
|
-
class Blogging::PostPolicy < Blogging::ResourcePolicy
|
|
19
|
-
# ... (other methods)
|
|
20
|
-
|
|
21
|
-
# 1. Define WHAT columns are visible in the table.
|
|
22
|
-
def permitted_attributes_for_index
|
|
23
|
-
[:user, :title, :published_at, :created_at]
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Next, we'll configure **how** these columns are displayed in the definition file.
|
|
29
|
-
|
|
30
|
-
Open the post definition: `packages/blogging/app/definitions/blogging/post_definition.rb`
|
|
31
|
-
|
|
32
|
-
```ruby
|
|
33
|
-
# packages/blogging/app/definitions/blogging/post_definition.rb
|
|
34
|
-
class Blogging::PostDefinition < Blogging::ResourceDefinition
|
|
35
|
-
# 2. Define HOW the permitted columns are rendered.
|
|
36
|
-
column :published_at, as: :datetime
|
|
37
|
-
column :created_at, as: :datetime
|
|
38
|
-
end
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
The table now only shows the permitted columns, formatted as we specified.
|
|
42
|
-
|
|
43
|
-

|
|
44
|
-
|
|
45
|
-
## Customizing the Post Detail Page (Show View)
|
|
46
|
-
|
|
47
|
-
The same two-step process applies to the detail page.
|
|
48
|
-
|
|
49
|
-
1. **Policy**: Define what fields are visible with `permitted_attributes_for_show`.
|
|
50
|
-
2. **Definition**: Define how they are rendered with `display`.
|
|
51
|
-
|
|
52
|
-
Let's define the fields in the policy. For this view, we want to show the `:content`.
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
# packages/blogging/app/policies/blogging/post_policy.rb
|
|
56
|
-
class Blogging::PostPolicy < Blogging::ResourcePolicy
|
|
57
|
-
# ...
|
|
58
|
-
|
|
59
|
-
def permitted_attributes_for_index
|
|
60
|
-
[:user, :title, :published_at, :created_at]
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# 1. Define WHAT fields are visible on the show page.
|
|
64
|
-
def permitted_attributes_for_show
|
|
65
|
-
[:user, :title, :content, :published_at, :created_at]
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Now, let's configure the layout in the definition. We'll make each field take up the full width of the view.
|
|
71
|
-
|
|
72
|
-
```ruby
|
|
73
|
-
# packages/blogging/app/definitions/blogging/post_definition.rb
|
|
74
|
-
class Blogging::PostDefinition < Blogging::ResourceDefinition
|
|
75
|
-
display :user, wrapper: { class: "col-span-full" }
|
|
76
|
-
display :title, wrapper: { class: "col-span-full" }
|
|
77
|
-
display :content, wrapper: { class: "col-span-full" }
|
|
78
|
-
display :published_at, wrapper: { class: "col-span-full" }
|
|
79
|
-
display :created_at, wrapper: { class: "col-span-full" }
|
|
80
|
-
end
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-

|
|
84
|
-
|
|
85
|
-
## Customizing the Post Form
|
|
86
|
-
|
|
87
|
-
Finally, we'll customize the `new` and `edit` forms.
|
|
88
|
-
|
|
89
|
-
1. **Policy**: Use `permitted_attributes_for_create` and `_for_update` to control which fields can be submitted.
|
|
90
|
-
2. **Definition**: Use the `input` helper to control how the form inputs are rendered.
|
|
91
|
-
|
|
92
|
-
First, the policy. We don't want the user to be able to set the `published_at` date directly in the form.
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
# packages/blogging/app/policies/blogging/post_policy.rb
|
|
96
|
-
class Blogging::PostPolicy < Blogging::ResourcePolicy
|
|
97
|
-
# ...
|
|
98
|
-
|
|
99
|
-
# 1. Define WHAT fields can be submitted in the form.
|
|
100
|
-
def permitted_attributes_for_create
|
|
101
|
-
[:user_id, :title, :content]
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def permitted_attributes_for_update
|
|
105
|
-
permitted_attributes_for_create
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Now, the definition. We'll use the `input` helper to specify that `:content` should use a rich text editor.
|
|
111
|
-
|
|
112
|
-
```ruby
|
|
113
|
-
# packages/blogging/app/definitions/blogging/post_definition.rb
|
|
114
|
-
class Blogging::PostDefinition < Blogging::ResourceDefinition
|
|
115
|
-
# ... (display helpers from above)
|
|
116
|
-
|
|
117
|
-
# 2. Define HOW the form inputs are rendered.
|
|
118
|
-
input :content, as: :rich_text # Use a rich text editor
|
|
119
|
-
end
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
By separating what is permitted from how it is rendered, Plutonium gives you both security and flexibility.
|
|
123
|
-
|
|
124
|
-
## Next Steps
|
|
125
|
-
|
|
126
|
-
Our UI is now much more polished. We've used the **Policy** and **Definition** files together to customize our `Post` resource.
|
|
127
|
-
|
|
128
|
-
In the next chapter, we'll add custom business logic by creating an **Action** to publish a draft post.
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# 6. Adding Custom Actions
|
|
2
|
-
|
|
3
|
-
Our application now handles the basics of creating, reading, updating, and deleting posts and comments. But what about business logic that isn't simple CRUD? For this, Plutonium has **Actions**.
|
|
4
|
-
|
|
5
|
-
An Action is a piece of business logic you can execute on a resource. We'll create a "Publish" action for our posts.
|
|
6
|
-
|
|
7
|
-
Create a new file for our "interaction" at:
|
|
8
|
-
`packages/blogging/app/interactions/blogging/post_interactions/publish.rb`
|
|
9
|
-
|
|
10
|
-
## Implement the Interaction
|
|
11
|
-
|
|
12
|
-
The "Interaction" is where the business logic for an action lives. Open the newly created file and let's implement the logic to publish a post.
|
|
13
|
-
|
|
14
|
-
We want the action to set the `published_at` timestamp on the post.
|
|
15
|
-
|
|
16
|
-
```ruby
|
|
17
|
-
# packages/blogging/app/interactions/blogging/post_interactions/publish.rb
|
|
18
|
-
module Blogging
|
|
19
|
-
module PostInteractions
|
|
20
|
-
class Publish < Plutonium::Resource::Interaction
|
|
21
|
-
# Define the attributes this interaction accepts
|
|
22
|
-
attribute :resource, class: "Blogging::Post"
|
|
23
|
-
|
|
24
|
-
# Define how this action is presented in the UI
|
|
25
|
-
presents label: "Publish Post",
|
|
26
|
-
description: "Make this post available for the public to see."
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
# The core business logic
|
|
31
|
-
def execute
|
|
32
|
-
if resource.update(published_at: Time.current )
|
|
33
|
-
succeed(resource)
|
|
34
|
-
.with_message("Post was successfully published.")
|
|
35
|
-
.with_redirect_response(resource)
|
|
36
|
-
else
|
|
37
|
-
failed(resource.errors)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
This class defines what the action needs (`resource`, `published_at`), how it looks (`presents`), and what it does (`execute`).
|
|
46
|
-
|
|
47
|
-
## Configure the Action
|
|
48
|
-
|
|
49
|
-
Now that we've defined the logic, we need to add the action to our `Post` resource and configure its visibility. We'll do this in the post's **definition** file.
|
|
50
|
-
|
|
51
|
-
`packages/blogging/app/definitions/blogging/post_definition.rb`
|
|
52
|
-
|
|
53
|
-
Add the `action` configuration:
|
|
54
|
-
|
|
55
|
-
```ruby
|
|
56
|
-
# packages/blogging/app/definitions/blogging/post_definition.rb
|
|
57
|
-
class Blogging::PostDefinition < Blogging::ResourceDefinition
|
|
58
|
-
# ... (display helpers from the previous chapter)
|
|
59
|
-
|
|
60
|
-
action :publish,
|
|
61
|
-
interaction: "Blogging::PostInteractions::Publish"
|
|
62
|
-
end
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Control Visibility with a Policy
|
|
66
|
-
|
|
67
|
-
Plutonium actions are secure by default. If an action doesn't have a corresponding policy method allowing it, it won't be displayed.
|
|
68
|
-
|
|
69
|
-
Let's define the logic for our `publish` action. We only want to show the "Publish" button if the post hasn't been published yet.
|
|
70
|
-
|
|
71
|
-
Open the post policy file:
|
|
72
|
-
`packages/blogging/app/policies/blogging/post_policy.rb`
|
|
73
|
-
|
|
74
|
-
Add the `publish?` method:
|
|
75
|
-
|
|
76
|
-
```ruby
|
|
77
|
-
# packages/blogging/app/policies/blogging/post_policy.rb
|
|
78
|
-
class Blogging::PostPolicy < Blogging::ResourcePolicy
|
|
79
|
-
# ... (other methods from previous chapters)
|
|
80
|
-
|
|
81
|
-
def publish?
|
|
82
|
-
# Only allow publishing if the user can update the post
|
|
83
|
-
# and the post has not been published yet.
|
|
84
|
-
update? && record.published_at.nil?
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
This policy does two things:
|
|
90
|
-
1. It ensures the user has permission to `update?` the post.
|
|
91
|
-
2. It checks that `published_at` is `nil`.
|
|
92
|
-
|
|
93
|
-
Now, go to the show page for a post you created. You should see the "Publish" button. Click it, and the `published_at` field will be set. Because `record.published_at.nil?` is now false, the policy will prevent the action from being shown again. The button disappears!
|
|
94
|
-
|
|
95
|
-

|
|
96
|
-
|
|
97
|
-
## Next Steps
|
|
98
|
-
|
|
99
|
-
We've successfully added custom business logic to our application. Actions are a powerful way to build complex, maintainable features.
|
|
100
|
-
|
|
101
|
-
In the final chapter of this tutorial, we'll tighten up security by implementing proper authorization rules.
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# 7. Implementing Authorization
|
|
2
|
-
|
|
3
|
-
Our blog is now fully functional, but it's not very secure. Any user can edit or delete any other user's posts. Let's fix this by implementing proper authorization rules using Plutonium's **Policy** system.
|
|
4
|
-
|
|
5
|
-
## Understanding Policies
|
|
6
|
-
|
|
7
|
-
For every resource, there is a corresponding Policy class that controls access. The policy methods (like `update?` or `destroy?`) return `true` or `false` to grant or deny permission.
|
|
8
|
-
|
|
9
|
-
## Securing Post Actions
|
|
10
|
-
|
|
11
|
-
Let's start by restricting the `update` and `publish` actions for posts. Open the post policy file:
|
|
12
|
-
|
|
13
|
-
`packages/blogging/app/policies/blogging/post_policy.rb`
|
|
14
|
-
|
|
15
|
-
Now, let's modify the policy to check if the current user (`user`) is the owner of the post (`record`).
|
|
16
|
-
|
|
17
|
-
```ruby
|
|
18
|
-
# packages/blogging/app/policies/blogging/post_policy.rb
|
|
19
|
-
class Blogging::PostPolicy < Blogging::ResourcePolicy
|
|
20
|
-
# Users can only update their own posts
|
|
21
|
-
def update?
|
|
22
|
-
record.user_id == user.id
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Only the owner can publish their post
|
|
26
|
-
def publish?
|
|
27
|
-
update? # Inherits from update?
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
```
|
|
31
|
-
By default, `destroy?` also inherits from `update?`, so this one change secures all write operations.
|
|
32
|
-
|
|
33
|
-
## Securing Comment Actions
|
|
34
|
-
|
|
35
|
-
We should apply the same logic to comments. Open the comment policy:
|
|
36
|
-
|
|
37
|
-
`packages/blogging/app/policies/blogging/comment_policy.rb`
|
|
38
|
-
|
|
39
|
-
And add the same ownership check:
|
|
40
|
-
|
|
41
|
-
```ruby
|
|
42
|
-
# packages/blogging/app/policies/blogging/comment_policy.rb
|
|
43
|
-
class Blogging::CommentPolicy < Blogging::ResourcePolicy
|
|
44
|
-
def update?
|
|
45
|
-
record.user_id == user.id
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Scoping Visible Data
|
|
51
|
-
|
|
52
|
-
We've secured the actions, but there's one more problem: every user can see every post in the index view. We should scope the list of posts so that users only see their own.
|
|
53
|
-
|
|
54
|
-
This is done with the `relation_scope` in the post policy file. This scope is applied to all queries for the `Post` resource.
|
|
55
|
-
|
|
56
|
-
`packages/blogging/app/policies/blogging/post_policy.rb`
|
|
57
|
-
|
|
58
|
-
```ruby
|
|
59
|
-
# packages/blogging/app/policies/blogging/post_policy.rb
|
|
60
|
-
class Blogging::PostPolicy < Blogging::ResourcePolicy
|
|
61
|
-
# ... (update? and publish? methods)
|
|
62
|
-
|
|
63
|
-
# Scope all queries to only include the current user's posts
|
|
64
|
-
relation_scope do |scope|
|
|
65
|
-
scope.where(user: user)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
```
|
|
69
|
-
Now, the posts index page will only show posts created by the currently logged-in user. This is a critical feature for multi-tenant applications.
|
|
70
|
-
|
|
71
|
-
## Tutorial Complete!
|
|
72
|
-
|
|
73
|
-
Congratulations! You have built a complete, secure, and modular blog application with Plutonium.
|
|
74
|
-
|
|
75
|
-
You've learned how to:
|
|
76
|
-
- Set up a Plutonium project.
|
|
77
|
-
- Organize features into **Packages**.
|
|
78
|
-
- Define and scaffold **Resources**.
|
|
79
|
-
- Build a web interface with **Portals**.
|
|
80
|
-
- Customize the UI.
|
|
81
|
-
- Add custom business logic with **Actions**.
|
|
82
|
-
- Secure your application with **Policies**.
|
|
83
|
-
|
|
84
|
-
### Where to Go Next?
|
|
85
|
-
|
|
86
|
-
You now have a strong foundation to build your own applications. To learn more about specific topics, check out our **Deep Dive Guides**:
|
|
87
|
-
|
|
88
|
-
- **[Resources](./../deep-dive/resources.md)**: An in-depth look at fields, associations, filters, and more.
|
|
89
|
-
- **[Authorization](./../deep-dive/authorization.md)**: A detailed guide to policies, scopes, and entity-based authorization.
|
|
90
|
-
- ... more guides coming soon!
|