plutonium 0.15.4 → 0.15.6

Sign up to get free protection for your applications and to get access to all the features.
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
+ ```