plutonium 0.23.4 → 0.23.5

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/plutonium.css +2 -2
  3. data/config/initializers/sqlite_json_alias.rb +1 -1
  4. data/docs/.vitepress/config.ts +60 -19
  5. data/docs/guide/cursor-rules.md +75 -0
  6. data/docs/guide/deep-dive/authorization.md +189 -0
  7. data/docs/guide/{getting-started → deep-dive}/resources.md +137 -0
  8. data/docs/guide/getting-started/{installation.md → 01-installation.md} +0 -105
  9. data/docs/guide/index.md +28 -0
  10. data/docs/guide/introduction/02-core-concepts.md +440 -0
  11. data/docs/guide/tutorial/01-project-setup.md +75 -0
  12. data/docs/guide/tutorial/02-creating-a-feature-package.md +45 -0
  13. data/docs/guide/tutorial/03-defining-resources.md +90 -0
  14. data/docs/guide/tutorial/04-creating-a-portal.md +101 -0
  15. data/docs/guide/tutorial/05-customizing-the-ui.md +128 -0
  16. data/docs/guide/tutorial/06-adding-custom-actions.md +101 -0
  17. data/docs/guide/tutorial/07-implementing-authorization.md +90 -0
  18. data/docs/index.md +24 -31
  19. data/docs/modules/action.md +190 -0
  20. data/docs/modules/authentication.md +236 -0
  21. data/docs/modules/configuration.md +599 -0
  22. data/docs/modules/controller.md +398 -0
  23. data/docs/modules/core.md +316 -0
  24. data/docs/modules/definition.md +876 -0
  25. data/docs/modules/display.md +759 -0
  26. data/docs/modules/form.md +605 -0
  27. data/docs/modules/generator.md +288 -0
  28. data/docs/modules/index.md +167 -0
  29. data/docs/modules/interaction.md +470 -0
  30. data/docs/modules/package.md +151 -0
  31. data/docs/modules/policy.md +176 -0
  32. data/docs/modules/portal.md +710 -0
  33. data/docs/modules/query.md +287 -0
  34. data/docs/modules/resource_record.md +618 -0
  35. data/docs/modules/routing.md +641 -0
  36. data/docs/modules/table.md +293 -0
  37. data/docs/modules/ui.md +631 -0
  38. data/docs/public/plutonium.mdc +667 -0
  39. data/lib/generators/pu/core/assets/assets_generator.rb +0 -5
  40. data/lib/plutonium/ui/display/resource.rb +7 -2
  41. data/lib/plutonium/ui/table/resource.rb +8 -3
  42. data/lib/plutonium/version.rb +1 -1
  43. metadata +36 -9
  44. data/docs/guide/getting-started/authorization.md +0 -296
  45. data/docs/guide/getting-started/core-concepts.md +0 -432
  46. data/docs/guide/getting-started/index.md +0 -21
  47. data/docs/guide/tutorial.md +0 -401
  48. /data/docs/guide/{what-is-plutonium.md → introduction/01-what-is-plutonium.md} +0 -0
@@ -0,0 +1,440 @@
1
+ # Core Concepts
2
+
3
+ ::: tip What you'll learn
4
+ - Plutonium's architecture and how its core components work together
5
+ - The role of Resources as your application's building blocks
6
+ - How Packages help organize and modularize your code
7
+ - Entity Scoping for building multi-tenant applications
8
+ - Best practices for maintainable, scalable applications
9
+ :::
10
+
11
+ ## Understanding Resources
12
+
13
+ Think of Resources as the DNA of your Plutonium application. Every meaningful piece of data or functionality in your app—whether it's a User, Product, or Blog Post—is represented as a Resource. But unlike traditional Rails models, Plutonium Resources are rich, self-contained units that know how to display themselves, authorize access, and handle user interactions.
14
+
15
+ ### The Anatomy of a Resource
16
+
17
+ Each Resource is composed of four key components that work together seamlessly:
18
+
19
+ ```mermaid
20
+ graph TD
21
+ A[Resource] --> B[Model]
22
+ A --> C[Definition]
23
+ A --> D[Policy]
24
+ A --> G[Actions]
25
+
26
+ B --> H[Database Schema]
27
+ C --> I[Fields & UI Logic]
28
+ C --> F[QueryObject]
29
+ D --> J[Authorization]
30
+ F --> L[Querying & Filtering]
31
+ G --> M[User Operations]
32
+ ```
33
+
34
+ - **Model**: Your familiar Active Record model with validations, associations, and business logic
35
+ - **Definition**: Describes how the Resource appears and behaves in the UI
36
+ - **Policy**: Controls who can access what and when
37
+ - **Actions**: Custom operations users can perform on the Resource
38
+
39
+ ::: details Complete Resource Example
40
+
41
+ Let's see how all these pieces fit together in a real User resource:
42
+
43
+ ::: code-group
44
+ ```ruby [Model]
45
+ # app/models/user.rb
46
+ class User < ApplicationRecord
47
+ include Plutonium::Resource::Record
48
+
49
+ # Standard Rails associations
50
+ has_many :posts
51
+ has_many :comments
52
+ belongs_to :organization
53
+
54
+ # Helpful scopes for filtering
55
+ scope :active, -> { where(status: :active) }
56
+
57
+ # Standard validations
58
+ validates :name, presence: true
59
+ validates :email, presence: true, uniqueness: true
60
+ validates :role, presence: true, inclusion: {in: %w[admin user]}
61
+
62
+ def admin? = role == "admin"
63
+ end
64
+ ```
65
+
66
+ ```ruby [Definition]
67
+ # app/definitions/user_definition.rb
68
+ class UserDefinition < Plutonium::Resource::Definition
69
+ # How fields appear in forms and tables
70
+ field :name, as: :string
71
+ field :email, as: :email
72
+
73
+ # Enable search functionality
74
+ search do |scope, query|
75
+ scope.where("name LIKE :q OR email LIKE :q", q: "%#{query}%")
76
+ end
77
+
78
+ # Add filters for easy data browsing
79
+ filter :role, with: SelectFilter, choices: %w[admin user guest]
80
+ filter :status, with: SelectFilter, choices: %w[active inactive]
81
+
82
+ # Expose useful scopes
83
+ scope :active
84
+
85
+ scope :admins do
86
+ where(role: :admin)
87
+ end
88
+
89
+ # Custom actions users can perform
90
+ action :deactivate,
91
+ interaction: DeactivateUser,
92
+ color: :warning,
93
+ icon: Phlex::TablerIcons::UserOff
94
+
95
+ # Customize the detail page
96
+ show_page_title "User Details"
97
+ show_page_description "View and manage user information"
98
+ end
99
+ ```
100
+
101
+ ```ruby [Policy]
102
+ # app/policies/user_policy.rb
103
+ class UserPolicy < Plutonium::Resource::Policy
104
+ # Basic CRUD permissions
105
+ def read?
106
+ true # Everyone can view users
107
+ end
108
+
109
+ def create?
110
+ user.admin? # Only admins can create users
111
+ end
112
+
113
+ def update?
114
+ user.admin? || record.id == user.id # Admins or self
115
+ end
116
+
117
+ def destroy?
118
+ user.admin? && record.id != user.id # Admins, but not themselves
119
+ end
120
+
121
+ # Custom action permissions
122
+ def deactivate?
123
+ user.admin? && record.status == :active && record.id != user.id
124
+ end
125
+
126
+ # Control which fields are visible/editable
127
+ def permitted_attributes_for_read
128
+ if user.admin?
129
+ %i[name email role status created_at updated_at]
130
+ else
131
+ %i[name email status]
132
+ end
133
+ end
134
+
135
+ def permitted_attributes_for_create
136
+ if user.admin?
137
+ %i[name email role status password]
138
+ else
139
+ %i[name email password]
140
+ end
141
+ end
142
+
143
+ def permitted_attributes_for_update
144
+ if user.admin?
145
+ %i[name email role]
146
+ else
147
+ %i[name email]
148
+ end
149
+ end
150
+
151
+ # Control access to related resources
152
+ def permitted_associations
153
+ %i[posts comments]
154
+ end
155
+ end
156
+ ```
157
+
158
+ ```ruby [Deactivate Interaction]
159
+ # app/interactions/user_interactions/deactivate.rb
160
+ module UserInteractions
161
+ class Deactivate < Plutonium::Resource::Interaction
162
+ # How this action appears in the UI
163
+ presents label: "Deactivate User",
164
+ icon: Phlex::TablerIcons::UserOff,
165
+ description: "Deactivate user account"
166
+
167
+ # What data this action needs
168
+ attribute :resource, class: User
169
+ attribute :reason, :string
170
+
171
+ # Validation rules
172
+ validates :resource, presence: true
173
+ validates :reason, presence: true
174
+
175
+ # The actual business logic
176
+ def execute
177
+ resource.transaction do
178
+ resource.status = :inactive
179
+ resource.deactivated_at = Time.current
180
+ resource.deactivation_reason = reason
181
+
182
+ if resource.save
183
+ succeed(resource)
184
+ .with_message("User was successfully deactivated")
185
+ .with_redirect_response(resource)
186
+ else
187
+ failed(resource.errors)
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ ```
194
+ :::
195
+
196
+ ## Organizing with Packages
197
+
198
+ As your application grows, you need a way to organize your code that doesn't turn into a tangled mess. That's where Packages come in. Think of them as specialized containers that help you group related functionality and keep your application modular.
199
+
200
+ Plutonium uses two types of packages, each with a specific purpose:
201
+
202
+ ### Feature Packages: Your Business Logic Containers
203
+
204
+ Feature packages are where your core business logic lives. They're self-contained modules that focus on a specific domain area of your application—like blogging, e-commerce, or user management.
205
+
206
+ ::: tip What Makes a Good Feature Package
207
+ - **Domain-focused**: Centers around a specific business area
208
+ - **Self-contained**: Has everything it needs to function
209
+ - **No web interface**: Pure business logic, no controllers or views
210
+ - **Reusable**: Can be used across different parts of your app
211
+ :::
212
+
213
+ ::: code-group
214
+ ```ruby [Directory Structure]
215
+ packages/
216
+ └── blogging/
217
+ ├── app/
218
+ │ ├── models/
219
+ │ │ └── blogging/
220
+ │ │ ├── post.rb
221
+ │ │ └── comment.rb
222
+ │ ├── definitions/
223
+ │ │ └── blogging/
224
+ │ │ ├── post_definition.rb
225
+ │ │ └── comment_definition.rb
226
+ │ ├── policies/
227
+ │ │ └── blogging/
228
+ │ │ ├── post_policy.rb
229
+ │ │ └── comment_policy.rb
230
+ │ └── interactions/
231
+ │ └── blogging/
232
+ │ └── post_interactions/
233
+ │ ├── publish.rb
234
+ │ └── archive.rb
235
+ ├── config/
236
+ │ └── routes.rb
237
+ └── lib/
238
+ └── engine.rb
239
+ ```
240
+
241
+ ```ruby [Engine Configuration]
242
+ # packages/blogging/lib/engine.rb
243
+ module Blogging
244
+ class Engine < ::Rails::Engine
245
+ include Plutonium::Package::Engine
246
+ end
247
+ end
248
+ ```
249
+ :::
250
+
251
+ ### Portal Packages: Your User Interface Gateways
252
+
253
+ Portal packages are the web-facing part of your application. They take the business logic from Feature packages and present it to users through a web interface. Each portal typically serves a specific audience—like administrators, customers, or partners.
254
+
255
+ ::: tip What Makes a Good Portal Package
256
+ - **User-focused**: Designed for a specific type of user
257
+ - **Interface-heavy**: Controllers, views, and authentication
258
+ - **Feature composition**: Combines multiple feature packages
259
+ - **Access control**: Manages who can see and do what
260
+ :::
261
+
262
+ ::: code-group
263
+ ```ruby [Directory Structure]
264
+ packages/
265
+ └── admin_portal/
266
+ ├── app/
267
+ │ ├── controllers/
268
+ │ │ └── admin_portal/
269
+ │ │ ├── concerns/
270
+ │ │ │ └── controller.rb
271
+ │ │ ├── plutonium_controller.rb
272
+ │ │ └── resource_controller.rb
273
+ │ └── views/
274
+ │ └── layouts/
275
+ │ └── admin_portal.html.erb
276
+ ├── config/
277
+ │ └── routes.rb
278
+ └── lib/
279
+ └── engine.rb
280
+ ```
281
+
282
+ ```ruby [Engine Configuration]
283
+ # packages/admin_portal/lib/engine.rb
284
+ module AdminPortal
285
+ class Engine < ::Rails::Engine
286
+ include Plutonium::Portal::Engine
287
+
288
+ # All resources in this portal are scoped to an organization
289
+ scope_to_entity Organization, strategy: :path
290
+ end
291
+ end
292
+ ```
293
+
294
+ ```ruby [Routes Configuration]
295
+ # packages/admin_portal/config/routes.rb
296
+ AdminPortal::Engine.routes.draw do
297
+ root to: "dashboard#index"
298
+
299
+ # Make resources from feature packages available in this portal
300
+ register_resource Blogging::Post
301
+ register_resource Blogging::Comment
302
+ end
303
+ ```
304
+
305
+ ```ruby [Controller Configuration]
306
+ # packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
307
+ module AdminPortal
308
+ module Concerns
309
+ module Controller
310
+ extend ActiveSupport::Concern
311
+ include Plutonium::Portal::Controller
312
+
313
+ # Add authentication to this portal
314
+ include Plutonium::Auth::Rodauth(:admin)
315
+ end
316
+ end
317
+ end
318
+ ```
319
+ :::
320
+
321
+ ## Entity Scoping: Building Multi-Tenant Applications
322
+
323
+ One of Plutonium's most powerful features is Entity Scoping—a clean way to build multi-tenant applications where data is automatically partitioned based on a parent entity like Organization, Account, or Team.
324
+
325
+ Here's the magic: when you properly set up entity scoping, Plutonium automatically ensures users only see and can access data that belongs to their organization. No manual filtering required!
326
+
327
+ ### How It Works
328
+
329
+ ```ruby
330
+ # 1. Configure scoping in your portal engine
331
+ module AdminPortal
332
+ class Engine < ::Rails::Engine
333
+ include Plutonium::Portal::Engine
334
+
335
+ # Path-based scoping creates URLs like /org_123/posts
336
+ scope_to_entity Organization, strategy: :path
337
+
338
+ # Or use a custom strategy for more control
339
+ scope_to_entity Organization, strategy: :current_organization
340
+ end
341
+ end
342
+
343
+ # 2. Set up your models with proper associations
344
+ class Post < ApplicationRecord
345
+ include Plutonium::Resource::Record
346
+
347
+ # Direct relationship to the scoping entity
348
+ belongs_to :user
349
+ belongs_to :organization, through: :user
350
+
351
+ # Alternative: custom scoping for complex relationships
352
+ scope :associated_with_organization, ->(organization) do
353
+ joins(:user).where(users: { organization_id: organization.id })
354
+ end
355
+ end
356
+
357
+ # 3. Implement the scoping strategy in your controller
358
+ module AdminPortal::Concerns::Controller
359
+ private
360
+
361
+ # This method name must match your strategy name
362
+ def current_organization
363
+ # Extract tenant from subdomain, path, or any other source
364
+ @current_organization ||= Organization.find_by!(subdomain: request.subdomain)
365
+ end
366
+ end
367
+ ```
368
+
369
+ ### The Result
370
+
371
+ With this setup, when a user visits `/posts`, they automatically see only posts from their organization. When they create a new post, it's automatically associated with their organization. All of this happens transparently—no extra code needed in your controllers or views.
372
+
373
+ ## Best Practices for Success
374
+
375
+ ### Organizing Your Packages
376
+
377
+ ::: tip Feature Packages
378
+ - **Keep domains separate**: Each package should focus on one business area
379
+ - **Minimize dependencies**: Packages should be as independent as possible
380
+ - **Clear interfaces**: Use well-defined APIs for package interactions
381
+ - **Single responsibility**: One clear purpose per package
382
+ :::
383
+
384
+ ::: tip Portal Packages
385
+ - **User-centric design**: Each portal serves a specific user type
386
+ - **Consistent authentication**: Use the same auth strategy throughout a portal
387
+ - **Clear scoping rules**: Be explicit about data access boundaries
388
+ - **Compose, don't duplicate**: Reuse feature packages rather than rebuilding
389
+ :::
390
+
391
+ ### Designing Robust Resources
392
+
393
+ ::: tip Model Layer
394
+ - **Clear validations**: Make invalid states impossible
395
+ - **Meaningful associations**: Reflect real business relationships
396
+ - **Useful scopes**: Provide common query patterns
397
+ - **Business methods**: Encode domain logic in the model
398
+ :::
399
+
400
+ ::: tip Definition Layer
401
+ - **Appropriate field types**: Match the UI to the data
402
+ - **Efficient search**: Index searchable fields properly
403
+ - **Logical grouping**: Organize fields and actions intuitively
404
+ - **Clear labeling**: Use human-friendly names and descriptions
405
+ :::
406
+
407
+ ::: tip Policy Layer
408
+ - **Granular permissions**: Control access at the field level when needed
409
+ - **Explicit rules**: Make authorization logic clear and testable
410
+ - **Action-specific controls**: Different actions may need different rules
411
+ - **Association security**: Control access to related data
412
+ :::
413
+
414
+ ### Security First
415
+
416
+ ::: warning Critical Security Practices
417
+ - **Always implement policies**: Never rely on "security through obscurity"
418
+ - **Use entity scoping consistently**: Don't mix scoped and unscoped access
419
+ - **Validate all inputs**: Trust nothing from the user
420
+ - **Control associations**: Prevent unauthorized data access through relationships
421
+ - **Audit sensitive actions**: Log important operations for compliance
422
+ :::
423
+
424
+ ## Generator Support
425
+
426
+ Plutonium provides generators to quickly scaffold components:
427
+
428
+ ```bash
429
+ # Create a new feature package
430
+ rails generate pu:pkg:package blogging
431
+
432
+ # Create a new portal package
433
+ rails generate pu:pkg:portal admin
434
+
435
+ # Create a new resource
436
+ rails generate pu:res:scaffold post title:string content:text
437
+
438
+ # Connect a resource to a portal
439
+ rails generate pu:res:conn
440
+ ```
@@ -0,0 +1,75 @@
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
+ rails server
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.
@@ -0,0 +1,45 @@
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.
@@ -0,0 +1,90 @@
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.