plutonium 0.33.1 → 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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/# Plutonium: The pre-alpha demo.md +4 -2
  3. data/.claude/skills/assets/SKILL.md +416 -0
  4. data/.claude/skills/connect-resource/SKILL.md +112 -0
  5. data/.claude/skills/controller/SKILL.md +302 -0
  6. data/.claude/skills/create-resource/SKILL.md +240 -0
  7. data/.claude/skills/definition/SKILL.md +218 -0
  8. data/.claude/skills/definition-actions/SKILL.md +386 -0
  9. data/.claude/skills/definition-fields/SKILL.md +474 -0
  10. data/.claude/skills/definition-query/SKILL.md +334 -0
  11. data/.claude/skills/forms/SKILL.md +439 -0
  12. data/.claude/skills/installation/SKILL.md +300 -0
  13. data/.claude/skills/interaction/SKILL.md +382 -0
  14. data/.claude/skills/model/SKILL.md +267 -0
  15. data/.claude/skills/model-features/SKILL.md +286 -0
  16. data/.claude/skills/nested-resources/SKILL.md +274 -0
  17. data/.claude/skills/package/SKILL.md +191 -0
  18. data/.claude/skills/policy/SKILL.md +352 -0
  19. data/.claude/skills/portal/SKILL.md +400 -0
  20. data/.claude/skills/resource/SKILL.md +281 -0
  21. data/.claude/skills/rodauth/SKILL.md +452 -0
  22. data/.claude/skills/views/SKILL.md +563 -0
  23. data/Appraisals +46 -4
  24. data/CHANGELOG.md +32 -1
  25. data/app/assets/plutonium.css +2 -2
  26. data/config/brakeman.ignore +239 -0
  27. data/config/initializers/action_policy.rb +1 -1
  28. data/docs/.vitepress/config.ts +132 -47
  29. data/docs/concepts/architecture.md +226 -0
  30. data/docs/concepts/auto-detection.md +254 -0
  31. data/docs/concepts/index.md +61 -0
  32. data/docs/concepts/packages-portals.md +304 -0
  33. data/docs/concepts/resources.md +224 -0
  34. data/docs/cookbook/blog.md +412 -0
  35. data/docs/cookbook/index.md +289 -0
  36. data/docs/cookbook/saas.md +481 -0
  37. data/docs/getting-started/index.md +56 -0
  38. data/docs/getting-started/installation.md +146 -0
  39. data/docs/getting-started/tutorial/01-setup.md +118 -0
  40. data/docs/getting-started/tutorial/02-first-resource.md +180 -0
  41. data/docs/getting-started/tutorial/03-authentication.md +246 -0
  42. data/docs/getting-started/tutorial/04-authorization.md +170 -0
  43. data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
  44. data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
  45. data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
  46. data/docs/getting-started/tutorial/index.md +64 -0
  47. data/docs/guides/adding-resources.md +420 -0
  48. data/docs/guides/authentication.md +551 -0
  49. data/docs/guides/authorization.md +468 -0
  50. data/docs/guides/creating-packages.md +380 -0
  51. data/docs/guides/custom-actions.md +523 -0
  52. data/docs/guides/index.md +45 -0
  53. data/docs/guides/multi-tenancy.md +302 -0
  54. data/docs/guides/nested-resources.md +411 -0
  55. data/docs/guides/search-filtering.md +266 -0
  56. data/docs/guides/theming.md +321 -0
  57. data/docs/index.md +67 -26
  58. data/docs/public/CLAUDE.md +64 -21
  59. data/docs/reference/assets/index.md +496 -0
  60. data/docs/reference/controller/index.md +363 -0
  61. data/docs/reference/definition/actions.md +400 -0
  62. data/docs/reference/definition/fields.md +350 -0
  63. data/docs/reference/definition/index.md +252 -0
  64. data/docs/reference/definition/query.md +342 -0
  65. data/docs/reference/generators/index.md +469 -0
  66. data/docs/reference/index.md +49 -0
  67. data/docs/reference/interaction/index.md +445 -0
  68. data/docs/reference/model/features.md +248 -0
  69. data/docs/reference/model/index.md +219 -0
  70. data/docs/reference/policy/index.md +385 -0
  71. data/docs/reference/portal/index.md +382 -0
  72. data/docs/reference/views/forms.md +396 -0
  73. data/docs/reference/views/index.md +479 -0
  74. data/gemfiles/rails_7.gemfile +9 -2
  75. data/gemfiles/rails_7.gemfile.lock +146 -111
  76. data/gemfiles/rails_8.0.gemfile +20 -0
  77. data/gemfiles/rails_8.0.gemfile.lock +417 -0
  78. data/gemfiles/rails_8.1.gemfile +20 -0
  79. data/gemfiles/rails_8.1.gemfile.lock +419 -0
  80. data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
  81. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
  82. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
  83. data/lib/generators/pu/pkg/portal/USAGE +65 -0
  84. data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
  85. data/lib/generators/pu/res/conn/USAGE +71 -0
  86. data/lib/generators/pu/res/model/USAGE +106 -110
  87. data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
  88. data/lib/generators/pu/res/scaffold/USAGE +85 -0
  89. data/lib/generators/pu/rodauth/install_generator.rb +2 -6
  90. data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
  91. data/lib/generators/pu/skills/sync/USAGE +14 -0
  92. data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
  93. data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
  94. data/lib/plutonium/core/controller.rb +2 -2
  95. data/lib/plutonium/interaction/base.rb +1 -0
  96. data/lib/plutonium/package/engine.rb +2 -2
  97. data/lib/plutonium/query/adhoc_block.rb +6 -2
  98. data/lib/plutonium/query/model_scope.rb +1 -1
  99. data/lib/plutonium/railtie.rb +4 -0
  100. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
  101. data/lib/plutonium/resource/query_object.rb +38 -8
  102. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  103. data/lib/plutonium/version.rb +1 -1
  104. data/lib/tasks/release.rake +19 -4
  105. data/package.json +1 -1
  106. metadata +76 -39
  107. data/brakeman.ignore +0 -28
  108. data/docs/api-examples.md +0 -49
  109. data/docs/guide/claude-code-guide.md +0 -74
  110. data/docs/guide/deep-dive/authorization.md +0 -189
  111. data/docs/guide/deep-dive/multitenancy.md +0 -256
  112. data/docs/guide/deep-dive/resources.md +0 -390
  113. data/docs/guide/getting-started/01-installation.md +0 -165
  114. data/docs/guide/index.md +0 -28
  115. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  116. data/docs/guide/introduction/02-core-concepts.md +0 -440
  117. data/docs/guide/tutorial/01-project-setup.md +0 -75
  118. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  119. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  120. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  121. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  122. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  123. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  124. data/docs/markdown-examples.md +0 -85
  125. data/docs/modules/action.md +0 -244
  126. data/docs/modules/authentication.md +0 -236
  127. data/docs/modules/configuration.md +0 -599
  128. data/docs/modules/controller.md +0 -443
  129. data/docs/modules/core.md +0 -316
  130. data/docs/modules/definition.md +0 -1308
  131. data/docs/modules/display.md +0 -759
  132. data/docs/modules/form.md +0 -495
  133. data/docs/modules/generator.md +0 -400
  134. data/docs/modules/index.md +0 -167
  135. data/docs/modules/interaction.md +0 -642
  136. data/docs/modules/package.md +0 -151
  137. data/docs/modules/policy.md +0 -176
  138. data/docs/modules/portal.md +0 -710
  139. data/docs/modules/query.md +0 -297
  140. data/docs/modules/resource_record.md +0 -618
  141. data/docs/modules/routing.md +0 -690
  142. data/docs/modules/table.md +0 -301
  143. data/docs/modules/ui.md +0 -631
@@ -0,0 +1,481 @@
1
+ # Recipe: SaaS Application
2
+
3
+ Build a multi-tenant SaaS application with organizations, team management, and role-based access.
4
+
5
+ ## Overview
6
+
7
+ This recipe covers:
8
+ - Multi-tenant data isolation
9
+ - Organization and team management
10
+ - Role-based permissions
11
+ - Invitation system
12
+ - Subscription tiers
13
+
14
+ ## Architecture
15
+
16
+ ```
17
+ packages/
18
+ ├── core/ # Shared: users, organizations
19
+ ├── projects/ # Feature: project management
20
+ ├── billing/ # Feature: subscriptions
21
+ ├── app_portal/ # Main application interface
22
+ └── admin_portal/ # Super admin interface
23
+ ```
24
+
25
+ ## Core Models
26
+
27
+ ### Organization
28
+
29
+ ```ruby
30
+ module Core
31
+ class Organization < Core::ResourceRecord
32
+ has_many :memberships, dependent: :destroy
33
+ has_many :users, through: :memberships
34
+ has_many :projects, class_name: 'Projects::Project'
35
+
36
+ validates :name, presence: true
37
+ validates :slug, presence: true, uniqueness: true
38
+
39
+ before_validation :generate_slug, on: :create
40
+
41
+ def owner
42
+ memberships.find_by(role: 'owner')&.user
43
+ end
44
+
45
+ private
46
+
47
+ def generate_slug
48
+ self.slug ||= name&.parameterize
49
+ end
50
+ end
51
+ end
52
+ ```
53
+
54
+ ### Membership
55
+
56
+ ```ruby
57
+ module Core
58
+ class Membership < Core::ResourceRecord
59
+ belongs_to :organization
60
+ belongs_to :user
61
+
62
+ ROLES = %w[owner admin member viewer].freeze
63
+
64
+ validates :role, presence: true, inclusion: { in: ROLES }
65
+ validates :user_id, uniqueness: { scope: :organization_id }
66
+
67
+ scope :admins, -> { where(role: %w[owner admin]) }
68
+
69
+ def admin?
70
+ role.in?(%w[owner admin])
71
+ end
72
+
73
+ def owner?
74
+ role == 'owner'
75
+ end
76
+ end
77
+ end
78
+ ```
79
+
80
+ ### User Extensions
81
+
82
+ ```ruby
83
+ class User < ApplicationRecord
84
+ has_many :memberships, class_name: 'Core::Membership'
85
+ has_many :organizations, through: :memberships
86
+
87
+ def role_in(organization)
88
+ memberships.find_by(organization: organization)&.role
89
+ end
90
+
91
+ def admin_of?(organization)
92
+ memberships.admins.exists?(organization: organization)
93
+ end
94
+
95
+ def member_of?(organization)
96
+ memberships.exists?(organization: organization)
97
+ end
98
+ end
99
+ ```
100
+
101
+ ## Multi-Tenant Setup
102
+
103
+ ### Project Model
104
+
105
+ ```ruby
106
+ module Projects
107
+ class Project < Core::ResourceRecord
108
+ belongs_to :organization, class_name: 'Core::Organization'
109
+ belongs_to :creator, class_name: 'User'
110
+ has_many :tasks, dependent: :destroy
111
+
112
+ validates :name, presence: true
113
+ end
114
+ end
115
+ ```
116
+
117
+ ### Portal Engine
118
+
119
+ ```ruby
120
+ module AppPortal
121
+ class Engine < Rails::Engine
122
+ include Plutonium::Portal::Engine
123
+
124
+ config.after_initialize do
125
+ # Scope all data to current organization
126
+ scope_to_entity Core::Organization
127
+ end
128
+ end
129
+ end
130
+ ```
131
+
132
+ ### Portal Authentication
133
+
134
+ ```ruby
135
+ # packages/app_portal/app/controllers/app_portal/concerns/controller.rb
136
+ module AppPortal
137
+ module Concerns
138
+ module Controller
139
+ extend ActiveSupport::Concern
140
+ include Plutonium::Portal::Controller
141
+ include Plutonium::Auth::Rodauth(:user)
142
+ end
143
+ end
144
+ end
145
+ ```
146
+
147
+ ### Organization Context
148
+
149
+ ```ruby
150
+ module AppPortal
151
+ class ResourceController < Plutonium::Portal::ResourceController
152
+ before_action :set_current_organization
153
+
154
+ private
155
+
156
+ def set_current_organization
157
+ @current_organization = current_user.organizations.find_by!(
158
+ slug: params[:org_slug]
159
+ )
160
+ rescue ActiveRecord::RecordNotFound
161
+ redirect_to select_organization_path
162
+ end
163
+
164
+ def current_organization
165
+ @current_organization
166
+ end
167
+ helper_method :current_organization
168
+
169
+ def current_membership
170
+ @current_membership ||= current_user.memberships.find_by(
171
+ organization: current_organization
172
+ )
173
+ end
174
+ helper_method :current_membership
175
+ end
176
+ end
177
+ ```
178
+
179
+ ## Role-Based Policies
180
+
181
+ ### Project Policy
182
+
183
+ ```ruby
184
+ module Projects
185
+ class ProjectPolicy < Plutonium::Resource::Policy
186
+ def read?
187
+ member?
188
+ end
189
+
190
+ def create?
191
+ admin?
192
+ end
193
+
194
+ def update?
195
+ admin? || creator?
196
+ end
197
+
198
+ def destroy?
199
+ owner?
200
+ end
201
+
202
+ def permitted_attributes_for_create
203
+ [:name, :description]
204
+ end
205
+
206
+ def permitted_attributes_for_update
207
+ if admin?
208
+ [:name, :description, :status, :archived]
209
+ else
210
+ [:description]
211
+ end
212
+ end
213
+
214
+ # Entity scope handles organization filtering automatically
215
+ # Add role-based filtering here
216
+ def relation_scope(relation)
217
+ if viewer?
218
+ relation.where(archived: false)
219
+ else
220
+ relation
221
+ end
222
+ end
223
+
224
+ private
225
+
226
+ def membership
227
+ @membership ||= context[:membership] ||
228
+ user.memberships.find_by(organization: record.organization)
229
+ end
230
+
231
+ def member?
232
+ membership.present?
233
+ end
234
+
235
+ def viewer?
236
+ membership&.role == 'viewer'
237
+ end
238
+
239
+ def admin?
240
+ membership&.admin?
241
+ end
242
+
243
+ def owner?
244
+ membership&.owner?
245
+ end
246
+
247
+ def creator?
248
+ record.creator_id == user.id
249
+ end
250
+ end
251
+ end
252
+ ```
253
+
254
+ ## Invitation System
255
+
256
+ ### Invitation Model
257
+
258
+ ```ruby
259
+ module Core
260
+ class Invitation < Core::ResourceRecord
261
+ belongs_to :organization
262
+ belongs_to :inviter, class_name: 'User'
263
+ belongs_to :user, optional: true # Set when accepted
264
+
265
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
266
+ validates :role, presence: true, inclusion: { in: Membership::ROLES - ['owner'] }
267
+ validates :token, presence: true, uniqueness: true
268
+
269
+ before_validation :generate_token, on: :create
270
+
271
+ scope :pending, -> { where(accepted_at: nil, expired_at: nil) }
272
+
273
+ def pending?
274
+ accepted_at.nil? && !expired?
275
+ end
276
+
277
+ def expired?
278
+ expired_at.present? || created_at < 7.days.ago
279
+ end
280
+
281
+ def accept!(user)
282
+ transaction do
283
+ update!(accepted_at: Time.current, user: user)
284
+ organization.memberships.create!(user: user, role: role)
285
+ end
286
+ end
287
+
288
+ private
289
+
290
+ def generate_token
291
+ self.token ||= SecureRandom.urlsafe_base64(32)
292
+ end
293
+ end
294
+ end
295
+ ```
296
+
297
+ ### Invite User Interaction
298
+
299
+ ```ruby
300
+ module Core
301
+ class InviteUser < Plutonium::Interaction::Base
302
+ presents model_class: Organization
303
+ presents label: "Invite Team Member"
304
+
305
+ attribute :email, :string
306
+ attribute :role, :string
307
+
308
+ validates :email, presence: true
309
+ validates :role, presence: true, inclusion: { in: Membership::ROLES - ['owner'] }
310
+ validate :not_already_member
311
+
312
+ def execute
313
+ invitation = resource.invitations.create!(
314
+ email: email,
315
+ role: role,
316
+ inviter: context[:user]
317
+ )
318
+
319
+ InvitationMailer.invite(invitation).deliver_later
320
+
321
+ succeed(resource)
322
+ .with_message("Invitation sent to #{email}")
323
+ end
324
+
325
+ private
326
+
327
+ def not_already_member
328
+ if resource.users.exists?(email: email)
329
+ errors.add(:email, "is already a member")
330
+ end
331
+ end
332
+ end
333
+ end
334
+ ```
335
+
336
+ ## Organization Switcher
337
+
338
+ ### Routes
339
+
340
+ ```ruby
341
+ # config/routes.rb
342
+ Rails.application.routes.draw do
343
+ get '/org/select', to: 'organizations#select', as: :select_organization
344
+ post '/org/switch/:id', to: 'organizations#switch', as: :switch_organization
345
+
346
+ scope '/:org_slug' do
347
+ mount AppPortal::Engine, at: '/app'
348
+ end
349
+ end
350
+ ```
351
+
352
+ ### Controller
353
+
354
+ ```ruby
355
+ class OrganizationsController < ApplicationController
356
+ before_action :authenticate_user!
357
+
358
+ def select
359
+ @organizations = current_user.organizations
360
+ end
361
+
362
+ def switch
363
+ organization = current_user.organizations.find(params[:id])
364
+ session[:current_organization_id] = organization.id
365
+ redirect_to app_root_path(org_slug: organization.slug)
366
+ end
367
+ end
368
+ ```
369
+
370
+ ## Definitions
371
+
372
+ ### Organization Definition
373
+
374
+ ```ruby
375
+ module Core
376
+ class OrganizationDefinition < Plutonium::Resource::Definition
377
+ field :name
378
+ field :slug, readonly: true
379
+ field :logo, as: :file, accept: "image/*"
380
+
381
+ column :name, sortable: true
382
+ column :members_count do |org|
383
+ org.memberships.count
384
+ end
385
+ column :created_at
386
+
387
+ association :memberships, fields: [:user, :role, :created_at]
388
+
389
+ action :invite, interaction: InviteUser
390
+ end
391
+ end
392
+ ```
393
+
394
+ ### Membership Definition
395
+
396
+ ```ruby
397
+ module Core
398
+ class MembershipDefinition < Plutonium::Resource::Definition
399
+ field :user
400
+ field :role, as: :select, collection: Membership::ROLES
401
+
402
+ column :user
403
+ column :role
404
+ column :created_at
405
+
406
+ action :change_role, interaction: ChangeMemberRole
407
+ action :remove, interaction: RemoveMember, color: :danger
408
+ end
409
+ end
410
+ ```
411
+
412
+ ## Subscription Integration
413
+
414
+ ### Plan Model
415
+
416
+ ```ruby
417
+ module Billing
418
+ class Plan < Core::ResourceRecord
419
+ has_many :subscriptions
420
+
421
+ validates :name, presence: true
422
+ validates :price_cents, presence: true
423
+ validates :max_projects, presence: true
424
+ validates :max_members, presence: true
425
+
426
+ has_cents :price
427
+ end
428
+ end
429
+ ```
430
+
431
+ ### Subscription Checks
432
+
433
+ ```ruby
434
+ module Core
435
+ class Organization < Core::ResourceRecord
436
+ has_one :subscription, class_name: 'Billing::Subscription'
437
+
438
+ def can_add_project?
439
+ projects.count < subscription.plan.max_projects
440
+ end
441
+
442
+ def can_add_member?
443
+ memberships.count < subscription.plan.max_members
444
+ end
445
+ end
446
+ end
447
+
448
+ # In policy
449
+ def create?
450
+ admin? && record.organization.can_add_project?
451
+ end
452
+ ```
453
+
454
+ ## Usage
455
+
456
+ ```bash
457
+ # Create packages
458
+ rails generate pu:pkg:package core
459
+ rails generate pu:pkg:package projects
460
+ rails generate pu:pkg:package billing
461
+ rails generate pu:pkg:portal app
462
+
463
+ # Generate models
464
+ rails generate pu:res:scaffold Organization name:string slug:string --package core
465
+ rails generate pu:res:scaffold Membership role:string organization:belongs_to user:belongs_to --package core
466
+ rails generate pu:res:scaffold Project name:string organization:belongs_to --package projects
467
+
468
+ # Connect to portal
469
+ rails generate pu:res:conn Project --package projects --portal app
470
+
471
+ rails db:migrate
472
+ ```
473
+
474
+ ## Security Checklist
475
+
476
+ - [ ] All models scoped to organization
477
+ - [ ] Policies check membership
478
+ - [ ] Invitations expire
479
+ - [ ] Role changes audited
480
+ - [ ] Owner cannot be removed
481
+ - [ ] Data isolated in queries
@@ -0,0 +1,56 @@
1
+ # Getting Started
2
+
3
+ Welcome to Plutonium! This guide will help you get up and running quickly.
4
+
5
+ ## What You'll Learn
6
+
7
+ - How to install Plutonium in a new or existing Rails application
8
+ - The basic concepts behind Plutonium's architecture
9
+ - How to create your first resource and connect it to a portal
10
+
11
+ ## Prerequisites
12
+
13
+ Before you begin, make sure you have:
14
+
15
+ - **Ruby 3.2+** installed
16
+ - **Rails 7.1+** (Rails 8 recommended)
17
+ - **Node.js 18+** (for asset compilation)
18
+ - Basic familiarity with Ruby on Rails
19
+
20
+ ## Choose Your Path
21
+
22
+ ### New Application
23
+
24
+ If you're starting fresh, use our application template:
25
+
26
+ ```bash
27
+ rails new myapp -m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
28
+ ```
29
+
30
+ This creates a fully configured Plutonium application with authentication ready to go.
31
+
32
+ [Continue to Installation →](./installation)
33
+
34
+ ### Existing Application
35
+
36
+ Adding Plutonium to an existing Rails app requires a few more steps but is fully supported.
37
+
38
+ [Continue to Installation →](./installation#existing-application)
39
+
40
+ ### Tutorial
41
+
42
+ Want to learn by building? Follow our step-by-step tutorial to create a complete blog application.
43
+
44
+ [Start the Tutorial →](./tutorial/)
45
+
46
+ ## Next Steps
47
+
48
+ After installation, you'll typically:
49
+
50
+ 1. **Create a Feature Package** - Organize your business logic
51
+ 2. **Generate Resources** - Create your models and scaffolds
52
+ 3. **Create a Portal** - Set up the web interface
53
+ 4. **Connect Resources** - Make resources accessible through the portal
54
+ 5. **Customize** - Override defaults as needed
55
+
56
+ Each of these steps is covered in detail in the [Tutorial](./tutorial/).
@@ -0,0 +1,146 @@
1
+ # Installation
2
+
3
+ This guide covers installing Plutonium in both new and existing Rails applications.
4
+
5
+ ## New Application
6
+
7
+ The fastest way to get started is with our application template:
8
+
9
+ ```bash
10
+ rails new myapp -m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
11
+ ```
12
+
13
+ This template:
14
+ - Adds the Plutonium gem
15
+ - Configures TailwindCSS 4 with Plutonium's theme
16
+ - Sets up Rodauth for authentication
17
+ - Creates initial migrations
18
+ - Configures the asset pipeline
19
+
20
+ After the template completes:
21
+
22
+ ```bash
23
+ cd myapp
24
+ rails db:migrate
25
+ bin/dev
26
+ ```
27
+
28
+ Visit `http://localhost:3000` to see your new application.
29
+
30
+ ## Existing Application
31
+
32
+ ### Step 1: Add the Gem
33
+
34
+ Add Plutonium to your Gemfile:
35
+
36
+ ```ruby
37
+ gem "plutonium"
38
+ ```
39
+
40
+ Then install:
41
+
42
+ ```bash
43
+ bundle install
44
+ ```
45
+
46
+ ### Step 2: Run the Installer
47
+
48
+ ```bash
49
+ rails generate pu:core:install
50
+ ```
51
+
52
+ This generator:
53
+ - Creates the Plutonium initializer
54
+ - Adds required configurations
55
+ - Sets up the asset pipeline integration
56
+
57
+ ### Step 3: Install Rodauth (Optional)
58
+
59
+ If you want Plutonium's built-in authentication:
60
+
61
+ ```bash
62
+ rails generate pu:rodauth:install
63
+ ```
64
+
65
+ This creates:
66
+ - Rodauth configuration files
67
+ - Account model and migrations
68
+ - Email templates for authentication flows
69
+
70
+ ### Step 4: Run Migrations
71
+
72
+ ```bash
73
+ rails db:migrate
74
+ ```
75
+
76
+ ### Step 5: Configure Assets
77
+
78
+ Run the assets generator to set up TailwindCSS and Plutonium styles:
79
+
80
+ ```bash
81
+ rails generate pu:core:assets
82
+ ```
83
+
84
+ This configures PostCSS, TailwindCSS, and imports Plutonium's styles into your application.
85
+
86
+ ## Verifying Installation
87
+
88
+ After installation, verify everything is working:
89
+
90
+ ```bash
91
+ rails runner "puts Plutonium::VERSION"
92
+ ```
93
+
94
+ You should see the installed version number.
95
+
96
+ ## Configuration
97
+
98
+ Plutonium is configured in `config/initializers/plutonium.rb`:
99
+
100
+ ```ruby
101
+ Plutonium.configure do |config|
102
+ # Load default settings for version 1.0
103
+ config.load_defaults 1.0
104
+
105
+ # Development mode (auto-detected from PLUTONIUM_DEV env var)
106
+ # config.development = true
107
+
108
+ # Cache discovery (defaults to true in production, false in development)
109
+ # config.cache_discovery = false
110
+
111
+ # Hot reloading (defaults to true in development)
112
+ # config.enable_hotreload = true
113
+
114
+ # Asset configuration
115
+ # config.assets.logo = "custom_logo.png"
116
+ # config.assets.favicon = "custom_favicon.ico"
117
+ # config.assets.stylesheet = "plutonium.css"
118
+ # config.assets.script = "plutonium.min.js"
119
+ end
120
+ ```
121
+
122
+ ## Development Setup
123
+
124
+ For the best development experience:
125
+
126
+ ### 1. Use bin/dev
127
+
128
+ Plutonium includes a Procfile for `foreman`:
129
+
130
+ ```bash
131
+ bin/dev
132
+ ```
133
+
134
+ This starts Rails and the CSS watcher together.
135
+
136
+ ### 2. Enable Reloading
137
+
138
+ In development, Plutonium automatically reloads definitions and policies when files change. This is controlled by `config.enable_hotreload` (enabled by default in development).
139
+
140
+ ## Next Steps
141
+
142
+ Now that Plutonium is installed:
143
+
144
+ - [Create your first Feature Package](/guides/creating-packages)
145
+ - [Generate a Resource](/guides/adding-resources)
146
+ - [Follow the Tutorial](/getting-started/tutorial/)