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
@@ -1,297 +0,0 @@
1
- ---
2
- title: Query Module
3
- ---
4
-
5
- # Query Module
6
-
7
- The Query module provides a powerful and flexible system for filtering, searching, and sorting resources in Plutonium applications. It enables declarative query configuration and automatic UI generation for data exploration.
8
-
9
- ::: tip
10
- The Query module is located in `lib/plutonium/query/`. Query logic is typically defined inside a resource's Definition file.
11
- :::
12
-
13
- ## Overview
14
-
15
- - **Declarative Configuration**: Define filters, scopes, and sorters declaratively in your resource definition.
16
- - **Type-Safe Filtering**: Built-in filters for different data types (currently Text filter with various predicates).
17
- - **Search Integration**: Full-text search across multiple fields.
18
- - **Sorting Support**: Multi-field sorting with configurable directions.
19
- - **UI Integration**: Seamlessly powers search bars, filter forms, and sortable table headers.
20
-
21
- ## Defining a Query
22
-
23
- All query logic—search, filters, scopes, and sorting—is defined inside a resource definition file. This configuration is then used to build a `QueryObject` in the controller.
24
-
25
- ```ruby
26
- # app/definitions/post_definition.rb
27
- class PostDefinition < Plutonium::Resource::Definition
28
- # Define the global search logic.
29
- search do |scope, search|
30
- scope.where("title ILIKE ? OR content ILIKE ?", "%#{search}%", "%#{search}%")
31
- end
32
-
33
- # Define available filters (currently only Text filter is implemented).
34
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
35
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
36
- filter :category, with: Plutonium::Query::Filters::Text, predicate: :eq
37
-
38
- # Define named scopes that appear as buttons.
39
- scope :published
40
- scope :recent, -> { where('created_at > ?', 1.week.ago) }
41
-
42
- # Define which columns are sortable.
43
- sort :title
44
- sort :created_at
45
- sort :view_count
46
-
47
- # Define default sort (when no sort params are provided)
48
- default_sort :created_at, :desc # or default_sort { |scope| scope.order(featured: :desc, created_at: :desc) }
49
- end
50
- ```
51
-
52
- ## Built-in Filters
53
-
54
- Currently, Plutonium provides the **Text filter** with various predicates for different matching behaviors.
55
-
56
- ::: code-group
57
- ```ruby [Text Filter with Predicates]
58
- # Exact match (default)
59
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
60
-
61
- # Contains (LIKE with wildcards)
62
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
63
-
64
- # Starts with
65
- filter :slug, with: Plutonium::Query::Filters::Text, predicate: :starts_with
66
-
67
- # Ends with
68
- filter :email, with: Plutonium::Query::Filters::Text, predicate: :ends_with
69
-
70
- # Not equal
71
- filter :status, with: Plutonium::Query::Filters::Text, predicate: :not_eq
72
-
73
- # Not contains
74
- filter :content, with: Plutonium::Query::Filters::Text, predicate: :not_contains
75
-
76
- # Pattern matching with wildcards (* becomes %)
77
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :matches
78
-
79
- # Not matching pattern
80
- filter :title, with: Plutonium::Query::Filters::Text, predicate: :not_matches
81
- ```
82
- :::
83
-
84
- ### Available Text Filter Predicates
85
- - `:eq` - Equal (exact match)
86
- - `:not_eq` - Not equal
87
- - `:contains` - LIKE with wildcards on both sides
88
- - `:not_contains` - NOT LIKE with wildcards on both sides
89
- - `:starts_with` - LIKE with suffix wildcard
90
- - `:ends_with` - LIKE with prefix wildcard
91
- - `:matches` - LIKE with user-provided wildcards (* becomes %)
92
- - `:not_matches` - NOT LIKE with user-provided wildcards
93
-
94
- ## Controller Integration
95
-
96
- The `QueryObject` is automatically created in your resource controllers and applied to the base collection.
97
-
98
- ::: code-group
99
- ```ruby [Controller]
100
- class PostsController < ApplicationController
101
- include Plutonium::Resource::Controller
102
-
103
- def index
104
- # `filtered_resource_collection` applies the query object.
105
- # The results are then paginated.
106
- @pagy, @resource_records = pagy(filtered_resource_collection)
107
- end
108
-
109
- private
110
-
111
- # This helper method shows how the query is applied.
112
- def filtered_resource_collection
113
- # 1. Start with the authorized base scope (from the policy).
114
- base_query = current_authorized_scope
115
- # 2. Apply the filters, search, sort, and scope from the query object.
116
- current_query_object.apply(base_query, raw_resource_query_params)
117
- end
118
- end
119
- ```
120
- ```ruby [QueryObject]
121
- # The `current_query_object` is built automatically.
122
- # This is a simplified view of what happens behind the scenes.
123
- query_object = Plutonium::Resource::QueryObject.new(Post, params[:q] || {}) do |query|
124
- # Definitions from PostDefinition are added here.
125
- query.define_search(...)
126
- query.define_filter(:title, ...)
127
- query.define_scope(:recent, ...)
128
- query.define_sorter(:title, ...)
129
- end
130
-
131
- # The `apply` method executes the query.
132
- filtered_scope = query_object.apply(Post.all, params[:q])
133
- ```
134
- :::
135
-
136
- ## URL Parameters
137
-
138
- Query parameters are structured under the `q` key in the URL.
139
-
140
- ::: details URL Parameter Format
141
- - **Search**: `?q[search]=rails`
142
- - **Filters**: `?q[title][query]=rails&q[status][query]=published`
143
- - **Scope**: `?q[scope]=recent`
144
- - **Sorting**: `?q[sort_fields][]=created_at&q[sort_directions][created_at]=desc`
145
-
146
- A combined example:
147
- `/posts?q[search]=rails&q[title][query]=tutorial&q[scope]=recent&q[sort_fields][]=created_at&q[sort_directions][created_at]=desc`
148
- :::
149
-
150
- ## Advanced Usage
151
-
152
- ::: details Custom Filter Classes
153
- You can create custom filter classes for complex logic.
154
- ```ruby
155
- # 1. Define your custom filter class.
156
- class CustomRangeFilter < Plutonium::Query::Filter
157
- def initialize(key:, **options)
158
- super
159
- @key = key
160
- end
161
-
162
- # This method applies the filter to the scope.
163
- def apply(scope, min: nil, max: nil)
164
- scope = scope.where("#{@key} >= ?", min) if min.present?
165
- scope = scope.where("#{@key} <= ?", max) if max.present?
166
- scope
167
- end
168
-
169
- # This defines the inputs for the filter form.
170
- def customize_inputs
171
- input :min, as: :number
172
- input :max, as: :number
173
- end
174
- end
175
-
176
- # 2. Use it in your resource definition.
177
- class PostDefinition < Plutonium::Resource::Definition
178
- filter :view_count, with: CustomRangeFilter
179
- end
180
- ```
181
- :::
182
-
183
- ::: details Complex Search Examples
184
- ```ruby
185
- # Multi-field search with associations
186
- search do |scope, search|
187
- scope.joins(:author, :tags).where(
188
- "posts.title ILIKE :search OR posts.content ILIKE :search OR users.name ILIKE :search",
189
- search: "%#{search}%"
190
- ).distinct
191
- end
192
-
193
- # Search with term splitting
194
- search do |scope, search|
195
- terms = search.split(/\s+/)
196
- terms.reduce(scope) do |current_scope, term|
197
- current_scope.where(
198
- "title ILIKE ? OR content ILIKE ?",
199
- "%#{term}%", "%#{term}%"
200
- )
201
- end
202
- end
203
- ```
204
- :::
205
-
206
- ## Query Object API
207
-
208
- The QueryObject provides methods for building URLs and applying filters programmatically.
209
-
210
- ### Key Methods
211
-
212
- - `define_filter(name, body)` - Define a custom filter
213
- - `define_scope(name, body)` - Define a scope filter
214
- - `define_sorter(name, body)` - Define a custom sorter
215
- - `define_search(body)` - Define search functionality
216
- - `apply(scope, params)` - Apply filters and sorting to a scope
217
- - `build_url(**options)` - Build URLs with query parameters
218
-
219
- ### Usage Example
220
-
221
- ```ruby
222
- # Automatic usage through controllers
223
- GET /posts?q[search]=rails&q[title][query]=tutorial&q[sort_fields][]=created_at&q[sort_directions][created_at]=desc
224
-
225
- # Manual usage in controller
226
- query_object = current_query_object
227
- @resource_records = query_object.apply(current_authorized_scope, params[:q])
228
-
229
- # Building URLs for links
230
- next_page_url = query_object.build_url(search: "new search term")
231
- sorted_url = query_object.build_url(sort: :title)
232
- ```
233
-
234
- ## Advanced Query Object Examples
235
-
236
- **Custom Date Range Filter**
237
- ```ruby
238
- class DateRangeFilter < Plutonium::Query::Filter
239
- def apply(scope, start_date: nil, end_date: nil)
240
- scope = scope.where("#{key} >= ?", start_date.beginning_of_day) if start_date.present?
241
- scope = scope.where("#{key} <= ?", end_date.end_of_day) if end_date.present?
242
- scope
243
- end
244
-
245
- def customize_inputs
246
- input :start_date, as: :date
247
- input :end_date, as: :date
248
- end
249
- end
250
-
251
- # Usage in definition
252
- filter :created_at, with: DateRangeFilter
253
- ```
254
-
255
- **Conditional Scoping with User Context**
256
- ```ruby
257
- class PostDefinition < Plutonium::Resource::Definition
258
- scope :my_posts, -> { where(author: current_user) }
259
- scope :drafts, -> { where(published: false) }
260
- scope :published_last_week, -> {
261
- where(published: true, created_at: 1.week.ago..Time.current)
262
- }
263
- end
264
- ```
265
-
266
- ## Best Practices
267
-
268
- ### Filter Design
269
- - Use Text filters with appropriate predicates for most use cases
270
- - Create custom filter classes for complex multi-input filters (date ranges, number ranges)
271
- - Keep filter logic focused and single-responsibility
272
-
273
- ### Search Implementation
274
- - Include commonly searched fields in your search scope
275
- - Use ILIKE for case-insensitive matching
276
- - Consider performance impact of JOINs in search queries
277
- - Use `.distinct` when searching across associations
278
-
279
- ### Default Sorting
280
- - Define a default sort to show newest items first by default: `default_sort :id, :desc`
281
- - Use field and direction: `default_sort :created_at, :desc`
282
- - Or use a block for complex sorting: `default_sort { |scope| scope.order(featured: :desc, created_at: :desc) }`
283
- - The default sort is only applied when no sort parameters are provided by the user
284
- - Child definitions inherit the default sort from parent definitions
285
-
286
- ### URL Structure
287
- - The `q` parameter namespace keeps query params organized
288
- - All filter inputs are nested under their filter name
289
- - Sort parameters are arrays to support multi-column sorting
290
-
291
- The Query module provides a powerful foundation for building searchable, filterable interfaces while maintaining clean separation of concerns and consistent URL patterns.
292
-
293
- ## Related Modules
294
-
295
- - **[Definition](./definition.md)** - Resource definitions and DSL
296
- - **[Resource Record](./resource_record.md)** - Resource controllers and CRUD operations
297
- - **[UI](./ui.md)** - User interface components