plutonium 0.50.0 → 0.51.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +85 -102
  3. data/.claude/skills/plutonium-app/SKILL.md +572 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +163 -300
  5. data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
  6. data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
  7. data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
  8. data/.claude/skills/plutonium-testing/SKILL.md +6 -5
  9. data/.claude/skills/plutonium-ui/SKILL.md +900 -0
  10. data/CHANGELOG.md +27 -2
  11. data/Rakefile +2 -1
  12. data/app/assets/plutonium.css +1 -11
  13. data/app/assets/plutonium.js +1009 -1214
  14. data/app/assets/plutonium.js.map +3 -3
  15. data/app/assets/plutonium.min.js +52 -51
  16. data/app/assets/plutonium.min.js.map +3 -3
  17. data/docs/.vitepress/config.ts +37 -27
  18. data/docs/getting-started/index.md +22 -29
  19. data/docs/getting-started/installation.md +37 -80
  20. data/docs/getting-started/tutorial/index.md +4 -5
  21. data/docs/guides/adding-resources.md +66 -377
  22. data/docs/guides/authentication.md +94 -463
  23. data/docs/guides/authorization.md +124 -370
  24. data/docs/guides/creating-packages.md +94 -296
  25. data/docs/guides/custom-actions.md +121 -441
  26. data/docs/guides/index.md +22 -42
  27. data/docs/guides/multi-tenancy.md +116 -187
  28. data/docs/guides/nested-resources.md +103 -431
  29. data/docs/guides/search-filtering.md +123 -240
  30. data/docs/guides/testing.md +5 -4
  31. data/docs/guides/theming.md +157 -407
  32. data/docs/guides/troubleshooting.md +5 -3
  33. data/docs/guides/user-invites.md +106 -425
  34. data/docs/guides/user-profile.md +76 -243
  35. data/docs/index.md +1 -1
  36. data/docs/reference/app/generators.md +517 -0
  37. data/docs/reference/app/index.md +158 -0
  38. data/docs/reference/app/packages.md +146 -0
  39. data/docs/reference/app/portals.md +377 -0
  40. data/docs/reference/auth/accounts.md +230 -0
  41. data/docs/reference/auth/index.md +88 -0
  42. data/docs/reference/auth/profile.md +185 -0
  43. data/docs/reference/behavior/controllers.md +395 -0
  44. data/docs/reference/behavior/index.md +22 -0
  45. data/docs/reference/behavior/interactions.md +341 -0
  46. data/docs/reference/behavior/policies.md +417 -0
  47. data/docs/reference/index.md +56 -49
  48. data/docs/reference/resource/actions.md +423 -0
  49. data/docs/reference/resource/definition.md +508 -0
  50. data/docs/reference/resource/index.md +50 -0
  51. data/docs/reference/resource/model.md +348 -0
  52. data/docs/reference/resource/query.md +305 -0
  53. data/docs/reference/tenancy/entity-scoping.md +361 -0
  54. data/docs/reference/tenancy/index.md +36 -0
  55. data/docs/reference/tenancy/invites.md +393 -0
  56. data/docs/reference/tenancy/nested-resources.md +267 -0
  57. data/docs/reference/testing/index.md +287 -0
  58. data/docs/reference/ui/assets.md +400 -0
  59. data/docs/reference/ui/components.md +165 -0
  60. data/docs/reference/ui/displays.md +104 -0
  61. data/docs/reference/ui/forms.md +284 -0
  62. data/docs/reference/ui/index.md +30 -0
  63. data/docs/reference/ui/layouts.md +106 -0
  64. data/docs/reference/ui/pages.md +189 -0
  65. data/docs/reference/ui/tables.md +117 -0
  66. data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
  67. data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
  68. data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
  69. data/gemfiles/rails_7.gemfile.lock +1 -1
  70. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  71. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  72. data/lib/generators/pu/core/update/update_generator.rb +0 -20
  73. data/lib/generators/pu/invites/install_generator.rb +1 -0
  74. data/lib/plutonium/definition/base.rb +1 -1
  75. data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
  76. data/lib/plutonium/helpers/turbo_helper.rb +11 -0
  77. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
  78. data/lib/plutonium/resource/controller.rb +1 -0
  79. data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
  80. data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
  81. data/lib/plutonium/resource/policy.rb +7 -0
  82. data/lib/plutonium/routing/mapper_extensions.rb +15 -0
  83. data/lib/plutonium/ui/component/methods.rb +4 -0
  84. data/lib/plutonium/ui/form/base.rb +6 -2
  85. data/lib/plutonium/ui/form/components/json.rb +58 -0
  86. data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
  87. data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
  88. data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
  89. data/lib/plutonium/ui/form/resource.rb +0 -4
  90. data/lib/plutonium/ui/grid/resource.rb +1 -1
  91. data/lib/plutonium/ui/layout/base.rb +1 -0
  92. data/lib/plutonium/ui/page/base.rb +0 -7
  93. data/lib/plutonium/ui/page/index.rb +4 -4
  94. data/lib/plutonium/ui/table/resource.rb +1 -1
  95. data/lib/plutonium/version.rb +1 -1
  96. data/lib/plutonium.rb +8 -0
  97. data/lib/tasks/release.rake +15 -1
  98. data/package.json +10 -10
  99. data/src/css/slim_select.css +4 -0
  100. data/src/js/controllers/slim_select_controller.js +61 -0
  101. data/src/js/turbo/turbo_actions.js +33 -0
  102. data/yarn.lock +553 -543
  103. metadata +44 -33
  104. data/.claude/skills/plutonium-assets/SKILL.md +0 -512
  105. data/.claude/skills/plutonium-controller/SKILL.md +0 -396
  106. data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
  107. data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
  108. data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
  109. data/.claude/skills/plutonium-forms/SKILL.md +0 -465
  110. data/.claude/skills/plutonium-installation/SKILL.md +0 -331
  111. data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
  112. data/.claude/skills/plutonium-invites/SKILL.md +0 -408
  113. data/.claude/skills/plutonium-model/SKILL.md +0 -440
  114. data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
  115. data/.claude/skills/plutonium-package/SKILL.md +0 -198
  116. data/.claude/skills/plutonium-policy/SKILL.md +0 -456
  117. data/.claude/skills/plutonium-portal/SKILL.md +0 -410
  118. data/.claude/skills/plutonium-views/SKILL.md +0 -651
  119. data/docs/reference/assets/index.md +0 -496
  120. data/docs/reference/controller/index.md +0 -412
  121. data/docs/reference/definition/actions.md +0 -462
  122. data/docs/reference/definition/fields.md +0 -383
  123. data/docs/reference/definition/index.md +0 -326
  124. data/docs/reference/definition/query.md +0 -351
  125. data/docs/reference/generators/index.md +0 -648
  126. data/docs/reference/interaction/index.md +0 -449
  127. data/docs/reference/model/features.md +0 -248
  128. data/docs/reference/model/index.md +0 -218
  129. data/docs/reference/policy/index.md +0 -456
  130. data/docs/reference/portal/index.md +0 -379
  131. data/docs/reference/views/forms.md +0 -411
  132. data/docs/reference/views/index.md +0 -544
@@ -1,351 +0,0 @@
1
- # Definition Query
2
-
3
- Complete reference for search, filters, scopes, and sorting.
4
-
5
- ## Overview
6
-
7
- ```ruby
8
- class PostDefinition < Plutonium::Resource::Definition
9
- # Search - global text search
10
- search do |scope, query|
11
- scope.where("title ILIKE ?", "%#{query}%")
12
- end
13
-
14
- # Filters - sidebar filter inputs
15
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
16
-
17
- # Scopes - quick filter buttons
18
- scope :published
19
- scope :draft
20
-
21
- # Default scope
22
- default_scope :published
23
-
24
- # Sorting - sortable columns
25
- sort :title
26
- sort :created_at
27
-
28
- # Default sort
29
- default_sort :created_at, :desc
30
- end
31
- ```
32
-
33
- ## Search
34
-
35
- Define global search across fields:
36
-
37
- ```ruby
38
- # Single field
39
- search do |scope, query|
40
- scope.where("title ILIKE ?", "%#{query}%")
41
- end
42
-
43
- # Multiple fields
44
- search do |scope, query|
45
- scope.where(
46
- "title ILIKE :q OR content ILIKE :q OR author_name ILIKE :q",
47
- q: "%#{query}%"
48
- )
49
- end
50
-
51
- # With associations
52
- search do |scope, query|
53
- scope.joins(:author).where(
54
- "posts.title ILIKE :q OR users.name ILIKE :q",
55
- q: "%#{query}%"
56
- ).distinct
57
- end
58
-
59
- # Split search terms
60
- search do |scope, query|
61
- terms = query.split(/\s+/)
62
- terms.reduce(scope) do |current_scope, term|
63
- current_scope.where("title ILIKE ?", "%#{term}%")
64
- end
65
- end
66
- ```
67
-
68
- ## Filters
69
-
70
- Currently Plutonium provides the **Text filter** with various predicates.
71
-
72
- ### Text Filter Predicates
73
-
74
- ```ruby
75
- # Exact match
76
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
77
-
78
- # Not equal
79
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :not_eq
80
-
81
- # Contains (LIKE %value%)
82
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
83
-
84
- # Not contains
85
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :not_contains
86
-
87
- # Starts with (LIKE value%)
88
- filter :slug, with: Plutonium::Query::Filters::Text, predicate: :starts_with
89
-
90
- # Ends with (LIKE %value)
91
- filter :email, with: Plutonium::Query::Filters::Text, predicate: :ends_with
92
-
93
- # Pattern match (* becomes %)
94
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :matches
95
-
96
- # Not matching pattern
97
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :not_matches
98
- ```
99
-
100
- ### Custom Filter with Lambda
101
-
102
- ```ruby
103
- filter :published, with: ->(scope, value) {
104
- value == "true" ? scope.where.not(published_at: nil) : scope.where(published_at: nil)
105
- }
106
- ```
107
-
108
- ### Custom Filter Class
109
-
110
- ```ruby
111
- # Define custom filter
112
- class DateRangeFilter < Plutonium::Query::Filter
113
- def apply(scope, start_date: nil, end_date: nil)
114
- scope = scope.where("#{key} >= ?", start_date.beginning_of_day) if start_date.present?
115
- scope = scope.where("#{key} <= ?", end_date.end_of_day) if end_date.present?
116
- scope
117
- end
118
-
119
- def customize_inputs
120
- input :start_date, as: :date
121
- input :end_date, as: :date
122
- end
123
- end
124
-
125
- # Use in definition
126
- filter :created_at, with: DateRangeFilter
127
- ```
128
-
129
- ## Scopes
130
-
131
- Scopes appear as quick filter buttons. They reference model scopes by name.
132
-
133
- ### Basic Usage
134
-
135
- ```ruby
136
- class PostDefinition < Plutonium::Resource::Definition
137
- scope :published # Calls Post.published
138
- scope :draft # Calls Post.draft
139
- scope :featured # Calls Post.featured
140
- end
141
- ```
142
-
143
- The model must define these scopes:
144
-
145
- ```ruby
146
- class Post < ResourceRecord
147
- scope :published, -> { where.not(published_at: nil) }
148
- scope :draft, -> { where(published_at: nil) }
149
- scope :featured, -> { where(featured: true) }
150
- end
151
- ```
152
-
153
- ### Default Scope
154
-
155
- Set a scope as the default selection:
156
-
157
- ```ruby
158
- scope :active
159
- scope :archived
160
-
161
- default_scope :active
162
- ```
163
-
164
-
165
- ### Inline Scope (Block Syntax)
166
-
167
- For scopes that don't exist on the model, use block syntax with the scope as an argument:
168
-
169
- ```ruby
170
- scope(:recent) { |scope| scope.where('created_at > ?', 1.week.ago) }
171
- scope(:this_month) { |scope| scope.where(created_at: Time.current.all_month) }
172
- ```
173
-
174
- ### With Controller Context
175
-
176
- Inline scopes have access to controller context like `current_user`:
177
-
178
- ```ruby
179
- scope(:mine) { |scope| scope.where(author: current_user) }
180
- scope(:my_team) { |scope| scope.where(team: current_user.team) }
181
- ```
182
-
183
- ## Sorting
184
-
185
- ### Basic Sorting
186
-
187
- ```ruby
188
- sort :title
189
- sort :created_at
190
- sort :view_count
191
-
192
- # Multiple at once
193
- sorts :title, :created_at, :view_count
194
- ```
195
-
196
- ### Default Sort
197
-
198
- ```ruby
199
- # Field and direction
200
- default_sort :created_at, :desc
201
- default_sort :title, :asc
202
-
203
- # Complex sorting with block
204
- default_sort { |scope| scope.order(featured: :desc, created_at: :desc) }
205
- ```
206
-
207
- **Note:** Default sort only applies when no sort params are provided. The framework default is `id DESC`.
208
-
209
- ## URL Parameters
210
-
211
- Query parameters are structured under `q`:
212
-
213
- ```
214
- /posts?q[search]=rails
215
- /posts?q[status][query]=published
216
- /posts?q[scope]=recent
217
- /posts?q[sort_fields][]=created_at&q[sort_directions][created_at]=desc
218
- ```
219
-
220
- Combined:
221
- ```
222
- /posts?q[search]=rails&q[scope]=published&q[sort_fields][]=created_at&q[sort_directions][created_at]=desc
223
- ```
224
-
225
- ## Common Patterns
226
-
227
- ### Status Filter
228
-
229
- ```ruby
230
- class PostDefinition < Plutonium::Resource::Definition
231
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
232
-
233
- scope :draft
234
- scope :published
235
- scope :archived
236
- end
237
- ```
238
-
239
- ### Date-Based Scopes
240
-
241
- Define scopes on the model:
242
-
243
- ```ruby
244
- class Post < ResourceRecord
245
- scope :today, -> { where(created_at: Time.current.all_day) }
246
- scope :this_week, -> { where(created_at: Time.current.all_week) }
247
- scope :this_month, -> { where(created_at: Time.current.all_month) }
248
- end
249
- ```
250
-
251
- Then reference them in the definition:
252
-
253
- ```ruby
254
- class PostDefinition < Plutonium::Resource::Definition
255
- scope :today
256
- scope :this_week
257
- scope :this_month
258
- end
259
- ```
260
-
261
- ### Archive State Scopes
262
-
263
- ```ruby
264
- class PostDefinition < Plutonium::Resource::Definition
265
- scope :active
266
- scope :archived
267
-
268
- # Default to showing only active
269
- default_sort { |scope| scope.active.order(created_at: :desc) }
270
- end
271
- ```
272
-
273
- ### Full-Text Search with pg_search
274
-
275
- ```ruby
276
- # Model
277
- class Post < ApplicationRecord
278
- include PgSearch::Model
279
- pg_search_scope :search_content, against: [:title, :content]
280
- end
281
-
282
- # Definition
283
- class PostDefinition < Plutonium::Resource::Definition
284
- search do |scope, query|
285
- scope.search_content(query)
286
- end
287
- end
288
- ```
289
-
290
- ### Association Filtering
291
-
292
- ```ruby
293
- class PostDefinition < Plutonium::Resource::Definition
294
- filter :author_name, with: Plutonium::Query::Filters::Text, predicate: :contains
295
-
296
- search do |scope, query|
297
- scope.joins(:author).where(
298
- "posts.title ILIKE :q OR users.name ILIKE :q",
299
- q: "%#{query}%"
300
- ).distinct
301
- end
302
- end
303
- ```
304
-
305
- ## Complete Example
306
-
307
- ```ruby
308
- class PostDefinition < Plutonium::Resource::Definition
309
- # Full-text search
310
- search do |scope, query|
311
- scope.where(
312
- "title ILIKE :q OR content ILIKE :q",
313
- q: "%#{query}%"
314
- )
315
- end
316
-
317
- # Filters
318
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
319
- filter :category, with: Plutonium::Query::Filters::Text, predicate: :eq
320
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
321
-
322
- # Quick scopes (reference model scopes)
323
- scope :published
324
- scope :draft
325
- scope :featured
326
- scope(:recent) { |scope| scope.where('created_at > ?', 1.week.ago) }
327
-
328
- # Default scope
329
- default_scope :published
330
-
331
- # Sortable columns
332
- sorts :title, :created_at, :view_count, :published_at
333
-
334
- # Default sort: newest first
335
- default_sort :created_at, :desc
336
- end
337
- ```
338
-
339
- ## Performance Tips
340
-
341
- 1. **Add indexes** for filtered/sorted columns
342
- 2. **Use `.distinct`** when joining associations in search
343
- 3. **Consider `pg_search`** for complex full-text search
344
- 4. **Limit search fields** to indexed columns
345
- 5. **Use scopes** instead of filters for common queries
346
-
347
- ## Related
348
-
349
- - [Definition Reference](./index)
350
- - [Fields Reference](./fields)
351
- - [Actions Reference](./actions)