plutonium 0.15.4 → 0.15.6

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/plutonium.css +1 -1
  3. data/app/assets/plutonium.ico +0 -0
  4. data/app/assets/plutonium.png +0 -0
  5. data/app/views/components/table_search_input/table_search_input_component.html.erb +3 -3
  6. data/app/views/resource/_resource_table.html.erb +0 -321
  7. data/docs/.vitepress/config.ts +61 -0
  8. data/docs/.vitepress/theme/custom.css +61 -0
  9. data/docs/.vitepress/theme/index.ts +4 -0
  10. data/docs/api-examples.md +49 -0
  11. data/docs/guide/getting-started/authorization.md +296 -0
  12. data/docs/guide/getting-started/core-concepts.md +432 -0
  13. data/docs/guide/getting-started/index.md +18 -0
  14. data/docs/guide/getting-started/installation.md +269 -0
  15. data/docs/guide/getting-started/resources.md +254 -0
  16. data/docs/guide/what-is-plutonium.md +211 -0
  17. data/docs/index.md +43 -0
  18. data/docs/markdown-examples.md +85 -0
  19. data/docs/public/android-chrome-192x192.png +0 -0
  20. data/docs/public/android-chrome-512x512.png +0 -0
  21. data/docs/public/apple-touch-icon.png +0 -0
  22. data/docs/public/favicon-16x16.png +0 -0
  23. data/docs/public/favicon-32x32.png +0 -0
  24. data/docs/public/favicon.ico +0 -0
  25. data/docs/public/plutonium.png +0 -0
  26. data/docs/public/site.webmanifest +1 -0
  27. data/docs/public/templates/plutonium.rb +21 -0
  28. data/lib/generators/pu/core/assets/assets_generator.rb +2 -3
  29. data/lib/generators/pu/core/assets/templates/tailwind.config.js +2 -2
  30. data/lib/generators/pu/core/install/install_generator.rb +9 -1
  31. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +0 -1
  32. data/lib/plutonium/core/controllers/authorizable.rb +1 -1
  33. data/lib/plutonium/definition/base.rb +8 -0
  34. data/lib/plutonium/definition/defineable_props.rb +1 -1
  35. data/lib/plutonium/definition/presentable.rb +71 -0
  36. data/lib/plutonium/interaction/README.md +1 -1
  37. data/lib/plutonium/interaction/base.rb +6 -6
  38. data/lib/plutonium/lib/deep_freezer.rb +31 -0
  39. data/lib/plutonium/query/adhoc_block.rb +19 -0
  40. data/lib/plutonium/query/base.rb +29 -0
  41. data/lib/plutonium/query/filter.rb +12 -0
  42. data/lib/plutonium/query/filters/text.rb +77 -0
  43. data/lib/plutonium/query/model_scope.rb +19 -0
  44. data/lib/plutonium/railtie.rb +0 -10
  45. data/lib/plutonium/resource/controller.rb +0 -3
  46. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +26 -0
  47. data/lib/plutonium/resource/controllers/crud_actions.rb +3 -6
  48. data/lib/plutonium/resource/controllers/defineable.rb +0 -2
  49. data/lib/plutonium/resource/controllers/queryable.rb +36 -20
  50. data/lib/plutonium/resource/policy.rb +5 -6
  51. data/lib/plutonium/resource/query_object.rb +61 -147
  52. data/lib/plutonium/resource/register.rb +3 -0
  53. data/lib/plutonium/{refinements/parameter_refinements.rb → support/parameters.rb} +5 -7
  54. data/lib/plutonium/ui/action_button.rb +34 -19
  55. data/lib/plutonium/ui/component/methods.rb +1 -1
  56. data/lib/plutonium/ui/display/resource.rb +19 -15
  57. data/lib/plutonium/ui/form/query.rb +171 -0
  58. data/lib/plutonium/ui/form/resource.rb +22 -17
  59. data/lib/plutonium/ui/table/components/scopes_bar.rb +1 -1
  60. data/lib/plutonium/ui/table/components/search_bar.rb +6 -139
  61. data/lib/plutonium/ui/table/resource.rb +10 -9
  62. data/lib/plutonium/version.rb +1 -1
  63. data/package-lock.json +5769 -1853
  64. data/package.json +11 -5
  65. data/src/js/core.js +0 -1
  66. data/tailwind.options.js +89 -11
  67. metadata +37 -13
  68. data/app/assets/plutonium-original.png +0 -0
  69. data/app/assets/plutonium-white.png +0 -0
  70. data/lib/plutonium/interaction/concerns/presentable.rb +0 -73
  71. data/public/plutonium-assets/fonts/bootstrap-icons.woff +0 -0
  72. data/public/plutonium-assets/fonts/bootstrap-icons.woff2 +0 -0
  73. /data/{templates → docs/public/templates}/base.rb +0 -0
@@ -0,0 +1,296 @@
1
+ # Authorization and Access Control
2
+
3
+ Plutonium provides a robust authorization system built on top of [Action Policy](https://actionpolicy.evilmartians.io/), offering fine-grained access control, resource scoping, and entity-based authorization.
4
+
5
+ ## Overview
6
+
7
+ Authorization in Plutonium operates at multiple levels:
8
+ - Resource-level policies
9
+ - Action-based permissions
10
+ - Entity-based scoping
11
+ - Attribute-level access control
12
+
13
+ ## Basic Policy Definition
14
+
15
+ Every resource in Plutonium requires a policy. Here's a basic example:
16
+
17
+ ```ruby
18
+ class BlogPolicy < ResourcePolicy
19
+ # Core CRUD Permissions
20
+
21
+ def create?
22
+ # All authenticated users can create blogs
23
+ true
24
+ end
25
+
26
+ def read?
27
+ # Allow anyone to read blogs
28
+ true
29
+ end
30
+
31
+ def update?
32
+ # Allow only the blog owner to update
33
+ owner?
34
+ end
35
+
36
+ def destroy?
37
+ # Allow only the blog owner or admins to destroy
38
+ owner? || user.admin?
39
+ end
40
+
41
+ # Attribute Control
42
+
43
+ def permitted_attributes_for_create
44
+ [:title, :content, :category]
45
+ end
46
+
47
+ def permitted_attributes_for_read
48
+ [:title, :content, :category, :created_at, :updated_at, :user_id]
49
+ end
50
+
51
+ def permitted_attributes_for_update
52
+ [:title, :content, :category]
53
+ end
54
+
55
+ # Association Access
56
+
57
+ def permitted_associations
58
+ [:comments]
59
+ end
60
+
61
+ private
62
+
63
+ def owner?
64
+ record.user_id == user.id
65
+ end
66
+ end
67
+ ```
68
+
69
+ ## Default Behaviors in Plutonium Policies
70
+
71
+ ::: info Overview
72
+ Plutonium's Policy system implements a secure-by-default pattern with clear inheritance chains for both permissions and attribute access.
73
+ :::
74
+
75
+ ### Permission Defaults
76
+
77
+ Permission methods follow two key patterns: core permissions that default to `false`, and derived permissions that inherit from core ones.
78
+
79
+ #### Core Permission Chain
80
+
81
+ ```mermaid
82
+ graph TD
83
+ subgraph Core Permissions
84
+ A[create? ❌] --> B[update? ❌]
85
+ A --> C[destroy? ❌]
86
+ D[read? ❌] --> E[index? ❌]
87
+ D --> F[show? ❌]
88
+ end
89
+ ```
90
+
91
+ ::: warning Security First
92
+ All core permissions (`create?` and `read?`) default to `false`. You must explicitly override these to grant access.
93
+ ```ruby
94
+ # Default implementations
95
+ def create?
96
+ false
97
+ end
98
+
99
+ def read?
100
+ false
101
+ end
102
+ ```
103
+ :::
104
+
105
+ #### Permission Inheritance
106
+
107
+ ::: code-group
108
+ ```ruby [Action Methods]
109
+ def update?
110
+ create? # Inherits from create?
111
+ end
112
+
113
+ def destroy?
114
+ create? # Inherits from create?
115
+ end
116
+
117
+ def index?
118
+ read? # Inherits from read?
119
+ end
120
+
121
+ def show?
122
+ read? # Inherits from read?
123
+ end
124
+ ```
125
+
126
+ ```ruby [Helper Methods]
127
+ def new?
128
+ create? # Matches create?
129
+ end
130
+
131
+ def edit?
132
+ update? # Matches update?
133
+ end
134
+
135
+ def search?
136
+ index? # Matches index?
137
+ end
138
+ ```
139
+ :::
140
+
141
+ ### Attribute Permission Defaults
142
+
143
+ Attribute permissions also follow an inheritance pattern, but with auto-detection in development:
144
+
145
+ ::: details Inheritance Chain
146
+ ```mermaid
147
+ graph TD
148
+ subgraph Core Attributes
149
+ A[permitted_attributes_for_create] --> B[permitted_attributes_for_update]
150
+ C[permitted_attributes_for_read] --> D[permitted_attributes_for_show]
151
+ C --> E[permitted_attributes_for_index]
152
+ end
153
+ ```
154
+ :::
155
+
156
+ #### Core Implementation Details
157
+
158
+ ::: code-group
159
+ ```ruby [Create Attributes]
160
+ def permitted_attributes_for_create
161
+ # Auto-detects fields but excludes system columns
162
+ autodetect_permitted_fields(:permitted_attributes_for_create) - [
163
+ resource_class.primary_key.to_sym,
164
+ :created_at,
165
+ :updated_at
166
+ ]
167
+ end
168
+ ```
169
+
170
+ ```ruby [Read Attributes]
171
+ def permitted_attributes_for_read
172
+ # Auto-detects all fields
173
+ autodetect_permitted_fields(:permitted_attributes_for_read)
174
+ end
175
+ ```
176
+
177
+ ```ruby [Update Attributes]
178
+ def permitted_attributes_for_update
179
+ # Inherits from create
180
+ permitted_attributes_for_create
181
+ end
182
+ ```
183
+ :::
184
+
185
+ ::: danger Auto-detection Warning
186
+ The default attribute detection:
187
+ - Only works in development
188
+ - Raises errors in other environments
189
+ - Shows warning messages
190
+ - Must be overridden in production
191
+ :::
192
+
193
+ ### Association Defaults
194
+
195
+ By default, no associations are permitted:
196
+
197
+ ```ruby
198
+ def permitted_associations
199
+ [] # Must be explicitly defined
200
+ end
201
+ ```
202
+
203
+ ### Context Object Defaults
204
+
205
+ Two built-in authorization contexts:
206
+
207
+ ::: code-group
208
+ ```ruby [Required Context]
209
+ # User must be present
210
+ authorize :user, allow_nil: false
211
+ ```
212
+
213
+ ```ruby [Optional Context]
214
+ # Entity scope is optional
215
+ authorize :entity_scope, allow_nil: true
216
+ ```
217
+ :::
218
+
219
+ #### Default Scoping Behavior
220
+
221
+ When an entity scope exists, records are automatically filtered:
222
+
223
+ ```ruby
224
+ relation_scope do |relation|
225
+ next relation unless entity_scope
226
+ relation.associated_with(entity_scope)
227
+ end
228
+ ```
229
+
230
+ #### Adding Custom Contexts
231
+
232
+ You can add additional authorization contexts:
233
+
234
+ ::: code-group
235
+ ```ruby [Policy]
236
+ class BlogPolicy < ResourcePolicy
237
+ authorize :ability, allow_nil: true
238
+
239
+ def promote?
240
+ user.admin? && ability&.can?(:promote, record)
241
+ end
242
+ end
243
+ ```
244
+
245
+ ```ruby [Controller]
246
+ class BlogsController < ResourceController
247
+ authorize :ability, through: :current_ability
248
+
249
+ private
250
+
251
+ def current_ability
252
+ @current_ability ||= Ability.new(current_user)
253
+ end
254
+ end
255
+ ```
256
+ :::
257
+
258
+ ## Quick Reference
259
+
260
+ | Method Type | Default | Inherits From |
261
+ |------------|---------|---------------|
262
+ | create? | false | - |
263
+ | read? | false | - |
264
+ | update? | false | create? |
265
+ | destroy? | false | create? |
266
+ | index? | false | read? |
267
+ | show? | false | read? |
268
+ | attributes_for_create | auto* | - |
269
+ | attributes_for_read | auto* | - |
270
+ | attributes_for_update | auto* | create |
271
+ | associations | [] | - |
272
+
273
+ ::: warning
274
+ \*Auto-detection only works in development
275
+ :::
276
+
277
+ ## Security Best Practices
278
+
279
+ ### 1. Never Skip Authorization
280
+
281
+ Plutonium controllers automatically verify authorization:
282
+
283
+ ```ruby
284
+ class BlogsController < ResourceController
285
+ def show
286
+ # This will raise an error if you forget to authorize
287
+ # authorize! resource_record
288
+ render :show
289
+ end
290
+ end
291
+ ```
292
+
293
+ ## Related Resources
294
+
295
+ - [Action Policy Documentation](https://actionpolicy.evilmartians.io/)
296
+ - [Rails Security Guide](https://guides.rubyonrails.org/security.html)
@@ -0,0 +1,432 @@
1
+ # Core Concepts
2
+
3
+ ::: tip What you'll learn
4
+ - Understanding Plutonium's architecture and core abstractions
5
+ - How resources and packages work together
6
+ - How to organize your application effectively
7
+ - Best practices for building maintainable applications
8
+ :::
9
+
10
+ ## Resources
11
+
12
+ Resources are the fundamental building blocks of a Plutonium application. They represent your business domain objects and their associated behavior.
13
+
14
+ ### Anatomy of a Resource
15
+
16
+ A resource consists of several interconnected components:
17
+
18
+ ```mermaid
19
+ graph TD
20
+ A[Resource] --> B[Model]
21
+ A --> C[Definition]
22
+ A --> D[Policy]
23
+ A --> G[Actions]
24
+
25
+ B --> H[Database Schema]
26
+ C --> I[Fields & UI Logic]
27
+ C --> F[QueryObject]
28
+ D --> J[Authorization]
29
+ F --> L[Querying & Filtering]
30
+ G --> M[User Operations]
31
+ ```
32
+
33
+ ::: details Complete Resource Example
34
+
35
+ ::: code-group
36
+ ```ruby [Model]
37
+ # app/models/user.rb
38
+ class User < ApplicationRecord
39
+ include Plutonium::Resource::Record
40
+
41
+ # Associations
42
+ has_many :posts
43
+ has_many :comments
44
+ belongs_to :organization
45
+
46
+ # Scopes
47
+ scope :active, -> { where(status: :active) }
48
+
49
+ # Validations
50
+ validates :name, presence: true
51
+ validates :email, presence: true, uniqueness: true
52
+ validates :role, presence: true, inclusion: {in: %w[admin user]}
53
+
54
+ def admin? = role == "admin"
55
+ end
56
+ ```
57
+
58
+ ```ruby [Definition]
59
+ # app/definitions/user_definition.rb
60
+ class UserDefinition < Plutonium::Resource::Definition
61
+ # Display configuration
62
+ field :name, as: :string
63
+ field :email, as: :email
64
+
65
+ # Search configuration
66
+ search do |scope, query|
67
+ scope.where("name LIKE :q OR email LIKE :q", q: "%#{query}%")
68
+ end
69
+
70
+ # Filters
71
+ filter :role, with: SelectFilter, choices: %w[admin user guest]
72
+ filter :status, with: SelectFilter, choices: %w[active inactive]
73
+
74
+ # Scopes
75
+ scope :active
76
+
77
+ scope :admins do
78
+ where(role: :admin)
79
+ end
80
+
81
+ # Actions
82
+ action :deactivate,
83
+ interaction: DeactivateUser,
84
+ color: :warning,
85
+ icon: Phlex::TablerIcons::UserOff
86
+
87
+ # UI Customization
88
+ show_page_title "User Details"
89
+ show_page_description "View and manage user information"
90
+ end
91
+ ```
92
+
93
+ ```ruby [Policy]
94
+ # app/policies/user_policy.rb
95
+ class UserPolicy < Plutonium::Resource::Policy
96
+ # Basic permissions
97
+ def read?
98
+ true
99
+ end
100
+
101
+ def create?
102
+ user.admin?
103
+ end
104
+
105
+ def update?
106
+ user.admin? || record.id == user.id
107
+ end
108
+
109
+ def destroy?
110
+ user.admin? && record.id != user.id
111
+ end
112
+
113
+ # Action permissions
114
+ def deactivate?
115
+ user.admin? && record.status == :active && record.id != user.id
116
+ end
117
+
118
+ # Attribute permissions
119
+ def permitted_attributes_for_read
120
+ if user.admin?
121
+ %i[name email role status created_at updated_at]
122
+ else
123
+ %i[name email status]
124
+ end
125
+ end
126
+
127
+ def permitted_attributes_for_create
128
+ if user.admin?
129
+ %i[name email role status password]
130
+ else
131
+ %i[name email password]
132
+ end
133
+ end
134
+
135
+ def permitted_attributes_for_update
136
+ if user.admin?
137
+ %i[name email role]
138
+ else
139
+ %i[name email]
140
+ end
141
+ end
142
+
143
+ # Association permissions
144
+ def permitted_associations
145
+ %i[posts comments]
146
+ end
147
+ end
148
+ ```
149
+
150
+
151
+ ```ruby [Deactivate Interaction]
152
+ # app/interactions/user_interactions/deactivate.rb
153
+ module UserInteractions
154
+ class Deactivate < Plutonium::Resource::Interaction
155
+ # Define presentable metadata
156
+ presents label: "Deactivate User",
157
+ icon: Phlex::TablerIcons::UserOff,
158
+ description: "Deactivate user account"
159
+
160
+ # Define attributes
161
+ attribute :resource, class: User
162
+ attribute :reason, :string
163
+
164
+ # Validations
165
+ validates :resource, presence: true
166
+ validates :reason, presence: true
167
+
168
+ # Business logic
169
+ def execute
170
+ resource.transaction do
171
+ resource.status = :inactive
172
+ resource.deactivated_at = Time.current
173
+ resource.deactivation_reason = reason
174
+
175
+ if resource.save
176
+ succeed(resource)
177
+ .with_message("User was successfully deactivated")
178
+ .with_redirect_response(resource)
179
+ else
180
+ failed(resource.errors)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ ```
187
+ :::
188
+
189
+ ## Packages
190
+
191
+ Packages are the way Plutonium helps you modularize your application. They're built on top of Rails Engines but provide additional structure and conventions.
192
+
193
+ There are two main types:
194
+
195
+ ### Feature Packages
196
+
197
+
198
+ Feature packages help you organize your application into logical, reusable modules.
199
+ They contain your business domain logic and resources. They're self-contained and independent.
200
+
201
+ ::: tip Key Characteristics
202
+ - Domain Models
203
+ - Business Logic
204
+ - No Web Interface
205
+ - Reusable Components
206
+ :::
207
+
208
+ ::: code-group
209
+ ```ruby [Directory Structure]
210
+ packages/
211
+ └── blogging/
212
+ ├── app/
213
+ │ ├── models/
214
+ │ │ └── blogging/
215
+ │ │ ├── post.rb
216
+ │ │ └── comment.rb
217
+ │ ├── definitions/
218
+ │ │ └── blogging/
219
+ │ │ ├── post_definition.rb
220
+ │ │ └── comment_definition.rb
221
+ │ ├── policies/
222
+ │ │ └── blogging/
223
+ │ │ ├── post_policy.rb
224
+ │ │ └── comment_policy.rb
225
+ │ └── interactions/
226
+ │ └── blogging/
227
+ │ └── post_interactions/
228
+ │ ├── publish.rb
229
+ │ └── archive.rb
230
+ ├── config/
231
+ │ └── routes.rb
232
+ └── lib/
233
+ └── engine.rb
234
+ ```
235
+
236
+ ```ruby [Engine Configuration]
237
+ # packages/blogging/lib/engine.rb
238
+ module Blogging
239
+ class Engine < ::Rails::Engine
240
+ include Plutonium::Package::Engine
241
+
242
+ # Package configuration goes here
243
+ isolate_namespace Blogging
244
+ end
245
+ end
246
+ ```
247
+ :::
248
+
249
+ ### Portal Packages
250
+
251
+ Portal packages provide web interfaces and control how users interact with features.
252
+
253
+ ::: tip Key Characteristics
254
+ - Web Interface
255
+ - Authentication
256
+ - Resource Access Control
257
+ - Feature Composition
258
+ :::
259
+
260
+ ::: code-group
261
+ ```ruby [Directory Structure]
262
+ packages/
263
+ └── admin_portal/
264
+ ├── app/
265
+ │ ├── controllers/
266
+ │ │ └── admin_portal/
267
+ │ │ ├── concerns/
268
+ │ │ │ └── controller.rb
269
+ │ │ ├── plutonium_controller.rb
270
+ │ │ └── resource_controller.rb
271
+ │ └── views/
272
+ │ └── layouts/
273
+ │ └── admin_portal.html.erb
274
+ ├── config/
275
+ │ └── routes.rb
276
+ └── lib/
277
+ └── engine.rb
278
+ ```
279
+
280
+ ```ruby [Engine Configuration]
281
+ # packages/admin_portal/lib/engine.rb
282
+ module AdminPortal
283
+ class Engine < ::Rails::Engine
284
+ include Plutonium::Portal::Engine
285
+
286
+ # Scope all resources to organization
287
+ scope_to_entity Organization, strategy: :path
288
+ end
289
+ end
290
+ ```
291
+
292
+ ```ruby [Routes Configuration]
293
+ # packages/admin_portal/config/routes.rb
294
+ AdminPortal::Engine.routes.draw do
295
+ root to: "dashboard#index"
296
+
297
+ # Register resources from feature packages
298
+ register_resource Blogging::Post
299
+ register_resource Blogging::Comment
300
+ end
301
+ ```
302
+
303
+ ```ruby [Controller Configuration]
304
+ # packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
305
+ module AdminPortal
306
+ module Concerns
307
+ class Controller < ::Rails::Engine
308
+ extend ActiveSupport::Concern
309
+ include Plutonium::Portal::Controller
310
+ # Integrate authentication
311
+ include Plutonium::Auth::Rodauth(:admin)
312
+ end
313
+ end
314
+ end
315
+ ```
316
+ :::
317
+
318
+ ## Entity Scoping
319
+
320
+ Entity scoping is a powerful feature that allows you to partition resources based on a parent entity (like Organization or Account).
321
+ It's how Plutonium achieve's multitenancy.
322
+
323
+ By properly defining associations to an entity, row-level multitenancy comes for free, out of the box.
324
+
325
+ ```ruby
326
+ # Scope definition in engine
327
+ module AdminPortal
328
+ class Engine < ::Rails::Engine
329
+ include Plutonium::Portal::Engine
330
+
331
+ # Path-based scoping (/org_123/posts)
332
+ scope_to_entity Organization, strategy: :path
333
+
334
+ # Or custom scoping
335
+ scope_to_entity Organization, strategy: :current_organization
336
+ end
337
+ end
338
+
339
+ # Model implementation
340
+ class Post < ApplicationRecord
341
+ include Plutonium::Resource::Record
342
+
343
+ # Define a direct relationship to the entity
344
+ belongs_to :user
345
+ belongs_to :organization, through: :user
346
+
347
+ # Alternatively, if there's no direct relationship
348
+ scope :associated_with_organization, ->(organization) do
349
+ # custom scoping logic goes here
350
+ joins(:user).where(users: { organization_id: organization.id })
351
+ end
352
+ end
353
+
354
+ # Controller config
355
+ class ResourceController < PlutoniumController
356
+ include Plutonium::Resource::Controller
357
+
358
+ private
359
+
360
+ def current_organization
361
+ # Get tenant from the current subdomain
362
+ @current_organization ||= Organization.where(subdomain: request.subdomain).first!
363
+ end
364
+ end
365
+ ```
366
+
367
+ ## Best Practices
368
+
369
+ ### Package Organization
370
+
371
+ ::: tip Feature Packages
372
+ 1. Keep domain logic isolated
373
+ 2. Clear boundaries between features
374
+ 3. Minimal dependencies between packages
375
+ 4. Well-defined interfaces
376
+ :::
377
+
378
+ ::: tip Portal Packages
379
+ 1. Single responsibility (admin, customer)
380
+ 2. Consistent authentication strategy
381
+ 3. Clear resource scoping rules
382
+ 4. Feature composition over duplication
383
+ :::
384
+
385
+ ### Resource Design
386
+
387
+ ::: tip Model Layer
388
+ 1. Clear validations and constraints
389
+ 2. Proper association setup
390
+ 3. Meaningful scopes
391
+ :::
392
+
393
+ ::: tip Definition Layer
394
+ 1. Appropriate field types
395
+ 2. Clear action definitions
396
+ 3. Efficient search implementation
397
+ :::
398
+
399
+ ::: tip Policy Layer
400
+ 1. Granular permissions
401
+ 2. Attribute-level access control
402
+ 3. Action-specific rules
403
+ 4. Association permissions
404
+ :::
405
+
406
+ ### Security Considerations
407
+
408
+ ::: warning Important
409
+ 1. Always implement proper policies
410
+ 2. Use entity scoping consistently
411
+ 3. Validate all inputs
412
+ 4. Control association access
413
+ 5. Audit sensitive actions
414
+ :::
415
+
416
+ ## Generator Support
417
+
418
+ Plutonium provides generators to quickly scaffold components:
419
+
420
+ ```bash
421
+ # Create a new feature package
422
+ rails generate pu:pkg:package blogging
423
+
424
+ # Create a new portal package
425
+ rails generate pu:pkg:portal admin
426
+
427
+ # Create a new resource
428
+ rails generate pu:res:scaffold post title:string content:text
429
+
430
+ # Connect a resource to a portal
431
+ rails generate pu:res:conn
432
+ ```