plutonium 0.24.0 → 0.24.2
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.
- checksums.yaml +4 -4
- data/app/views/plutonium/_resource_header.html.erb +1 -1
- data/docs/modules/definition.md +44 -14
- data/docs/modules/form.md +30 -0
- data/docs/public/plutonium.mdc +57 -172
- data/lib/plutonium/ui/display/resource.rb +5 -1
- data/lib/plutonium/ui/form/resource.rb +5 -1
- data/lib/plutonium/ui/layout/rodauth_layout.rb +3 -3
- data/lib/plutonium/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03aa87f0e65ffb98dfce96f4c6009672a7ceff510a565eba0cbfc226d7a94c50
|
4
|
+
data.tar.gz: af20fee6adbece0a3232f033589a6c58f230772d09e7c7b2ddb0c0415c8324e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 986901850a137be1347b026e759c1607fdaf62bcdae83d46273f1a16ddcaafd1635f2d85c27110e1ff700705d6686b869572822ab12abcd4c7d41b4d84faaa9d
|
7
|
+
data.tar.gz: 0c3e4057ab75582c34f1ce06f849bfb78d63177816252b71042025b0bd6569efab9853001f991d2a437fe1c14d18759448aa511afee2093b022fc7af2117b902
|
data/docs/modules/definition.md
CHANGED
@@ -214,17 +214,26 @@ To create forms that dynamically show/hide inputs based on other form values, pa
|
|
214
214
|
|
215
215
|
Plutonium offers three main approaches for rendering fields in a definition. Choose the one that best fits your needs for clarity, flexibility, and control.
|
216
216
|
|
217
|
+
**Quick Reference:**
|
218
|
+
- **`as: :symbol`** - Use built-in components for input and display (e.g., `:rich_text`, `:date`, `:markdown`)
|
219
|
+
- **`as: ComponentClass`** - Use custom component classes for input and display (new feature)
|
220
|
+
- **Block syntax** - Use for conditional logic and custom builder method calls
|
221
|
+
- **`as: :phlexi_tag`** - Advanced inline rendering with maximum flexibility (display only)
|
222
|
+
|
217
223
|
#### 1. The `as:` Option (Recommended)
|
218
224
|
|
219
|
-
The `as:` option is the simplest and most common way to specify a rendering component for
|
225
|
+
The `as:` option is the simplest and most common way to specify a rendering component for both `input` and `display` declarations. It's ideal for using standard built-in components or overriding auto-detected types.
|
226
|
+
|
227
|
+
**New Feature**: You can now pass a component class directly to the `as:` option for custom rendering in both input and display contexts.
|
220
228
|
|
221
229
|
**Use When:**
|
222
|
-
- Using standard or enhanced built-in components.
|
230
|
+
- Using standard or enhanced built-in components for input or display.
|
223
231
|
- You want clean, readable code with minimal boilerplate.
|
224
|
-
- Overriding an auto-detected type (e.g., `text` to `rich_text`).
|
232
|
+
- Overriding an auto-detected type (e.g., `text` to `rich_text` for input, or `text` to `markdown` for display).
|
233
|
+
- Using custom component classes for rendering.
|
225
234
|
|
226
235
|
::: code-group
|
227
|
-
```ruby [Input Fields]
|
236
|
+
```ruby [Standard Input Fields]
|
228
237
|
# Simple and concise overrides
|
229
238
|
class PostDefinition < Plutonium::Resource::Definition
|
230
239
|
input :content, as: :rich_text
|
@@ -235,7 +244,19 @@ class PostDefinition < Plutonium::Resource::Definition
|
|
235
244
|
input :email, as: :email, placeholder: "Enter email"
|
236
245
|
end
|
237
246
|
```
|
238
|
-
```ruby [
|
247
|
+
```ruby [Custom Component Classes]
|
248
|
+
# Using custom component classes
|
249
|
+
class PostDefinition < Plutonium::Resource::Definition
|
250
|
+
# Pass component class to input
|
251
|
+
input :color_picker, as: ColorPickerComponent
|
252
|
+
input :custom_widget, as: MyCustomInputComponent
|
253
|
+
|
254
|
+
# Pass component class to display
|
255
|
+
display :status_badge, as: StatusBadgeComponent
|
256
|
+
display :chart, as: ChartComponent
|
257
|
+
end
|
258
|
+
```
|
259
|
+
```ruby [Standard Display Fields]
|
239
260
|
# Simple and concise overrides
|
240
261
|
class PostDefinition < Plutonium::Resource::Definition
|
241
262
|
display :content, as: :markdown
|
@@ -252,25 +273,34 @@ end
|
|
252
273
|
|
253
274
|
The block syntax offers more control over rendering, allowing for custom components, complex layouts, and conditional logic. The block receives a `field` object that you can use to render custom output.
|
254
275
|
|
276
|
+
**Important for Input Blocks**: When using blocks with `input` declarations, you can only use existing form builder methods (like `f.date_tag`, `f.text_tag`, etc.). You cannot return arbitrary components because the form system requires inputs to be registered internally.
|
277
|
+
|
255
278
|
**Use When:**
|
256
|
-
- Integrating custom-built Phlex or ViewComponent components.
|
257
|
-
- Building complex layouts with multiple components or custom HTML.
|
279
|
+
- Integrating custom-built Phlex or ViewComponent components (for `display` only).
|
280
|
+
- Building complex layouts with multiple components or custom HTML (for `display` only).
|
258
281
|
- You need conditional logic to determine which component to render.
|
282
|
+
- You need to call specific form builder methods with custom logic (for `input`).
|
259
283
|
|
260
284
|
::: code-group
|
261
285
|
```ruby [Custom Display Components]
|
262
|
-
# Custom display component
|
286
|
+
# Custom display component - can return any component
|
263
287
|
display :chart_data do |field|
|
264
288
|
ChartComponent.new(data: field.value, type: :bar)
|
265
289
|
end
|
266
290
|
```
|
267
|
-
```ruby [Custom Input
|
268
|
-
# Custom input
|
269
|
-
input :
|
270
|
-
|
291
|
+
```ruby [Custom Input with Builder Methods]
|
292
|
+
# Custom input - can only use form builder methods
|
293
|
+
input :birth_date do |f|
|
294
|
+
# Can use builder methods with custom logic
|
295
|
+
case object.age_category
|
296
|
+
when 'adult'
|
297
|
+
f.date_tag(min: 18.years.ago.to_date)
|
298
|
+
when 'minor'
|
299
|
+
f.date_tag(max: 18.years.ago.to_date)
|
300
|
+
else
|
301
|
+
f.date_tag
|
302
|
+
end
|
271
303
|
end
|
272
|
-
```
|
273
|
-
|
274
304
|
```
|
275
305
|
```ruby [Conditional Rendering]
|
276
306
|
# Conditional display based on value
|
data/docs/modules/form.md
CHANGED
@@ -124,6 +124,36 @@ end
|
|
124
124
|
|
125
125
|
Plutonium replaces standard form inputs with enhanced versions that provide a modern user experience.
|
126
126
|
|
127
|
+
### Input Block Syntax
|
128
|
+
|
129
|
+
Input blocks provide additional flexibility for custom logic while ensuring proper form integration. **Important**: Input blocks can only use existing form builder methods (like `date_tag`, `text_tag`, etc.) because the form system requires inputs to be registered internally.
|
130
|
+
|
131
|
+
::: code-group
|
132
|
+
```ruby [Input Block Example]
|
133
|
+
# Valid: Using form builder methods with custom logic
|
134
|
+
input :birth_date do |f|
|
135
|
+
case object.age_category
|
136
|
+
when 'adult'
|
137
|
+
f.date_tag(min: 18.years.ago.to_date)
|
138
|
+
when 'minor'
|
139
|
+
f.date_tag(max: 18.years.ago.to_date)
|
140
|
+
else
|
141
|
+
f.date_tag
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
```ruby [Custom Component Classes]
|
147
|
+
# New: Pass component classes directly to as: option
|
148
|
+
# Works for both input and display
|
149
|
+
input :color_picker, as: ColorPickerComponent
|
150
|
+
input :custom_slider, as: RangeSliderComponent
|
151
|
+
|
152
|
+
display :status_badge, as: StatusBadgeComponent
|
153
|
+
display :progress_chart, as: ProgressChartComponent
|
154
|
+
```
|
155
|
+
:::
|
156
|
+
|
127
157
|
### Markdown Editor (Easymde)
|
128
158
|
|
129
159
|
For rich text content, Plutonium integrates a client-side markdown editor based on [EasyMDE](https://github.com/Ionaru/easy-markdown-editor). It's automatically used for `rich_text` fields (like ActionText) and provides a live preview.
|
data/docs/public/plutonium.mdc
CHANGED
@@ -121,6 +121,20 @@ class PostDefinition < Plutonium::Resource::Definition
|
|
121
121
|
input :content, as: :rich_text
|
122
122
|
input :category, as: :select, collection: %w[Tech Business Lifestyle]
|
123
123
|
|
124
|
+
# New: Custom component classes
|
125
|
+
input :color_picker, as: ColorPickerComponent
|
126
|
+
display :status_badge, as: StatusBadgeComponent
|
127
|
+
|
128
|
+
# Input blocks for custom logic (must use form builder methods)
|
129
|
+
input :birth_date do |f|
|
130
|
+
case object.age_category
|
131
|
+
when 'adult'
|
132
|
+
f.date_tag(min: 18.years.ago.to_date)
|
133
|
+
else
|
134
|
+
f.date_tag
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
124
138
|
# Conditional inputs (for forms only)
|
125
139
|
# Use for COSMETIC/STATE-BASED logic only, NOT authorization (use policies for that)
|
126
140
|
input :debug_info, condition: -> { Rails.env.development? }
|
@@ -467,201 +481,72 @@ include Plutonium::Auth::Rodauth(:admin) # For admin authentication
|
|
467
481
|
include Plutonium::Auth::Public # For public access
|
468
482
|
```
|
469
483
|
|
470
|
-
##
|
471
|
-
|
472
|
-
### Path-Based Scoping
|
473
|
-
```ruby
|
474
|
-
# Portal engine
|
475
|
-
scope_to_entity Organization, strategy: :path
|
476
|
-
|
477
|
-
# Generates routes like: /organizations/:organization_id/posts
|
478
|
-
# Automatically scopes all queries to the organization
|
479
|
-
```
|
484
|
+
## Routing & Custom Routes
|
480
485
|
|
481
|
-
###
|
486
|
+
### Resource Registration
|
482
487
|
```ruby
|
483
|
-
#
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
end
|
493
|
-
|
494
|
-
# Custom scope (manual)
|
495
|
-
class Comment < ApplicationRecord
|
496
|
-
belongs_to :post
|
488
|
+
# Basic resource registration
|
489
|
+
register_resource Post
|
490
|
+
|
491
|
+
# Custom routes with member/collection actions
|
492
|
+
register_resource Post do
|
493
|
+
member do
|
494
|
+
get :publish # /posts/1/publish
|
495
|
+
post :archive # /posts/1/archive
|
496
|
+
end
|
497
497
|
|
498
|
-
|
499
|
-
|
498
|
+
collection do
|
499
|
+
get :search # /posts/search
|
500
500
|
end
|
501
501
|
end
|
502
502
|
```
|
503
503
|
|
504
|
-
|
504
|
+
### Common Routing Errors
|
505
505
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
- Scopes: `?q[scope]=published`
|
506
|
+
#### "Unresolvable Association" Error
|
507
|
+
When adding custom routes inside `register_resource` blocks, Plutonium may interpret them as nested resources and fail with:
|
508
|
+
```
|
509
|
+
RuntimeError (Could not resolve the association between 'Model' and 'Model')
|
510
|
+
```
|
512
511
|
|
513
|
-
|
512
|
+
**Problem**: Routes like this create nested resource interpretation:
|
514
513
|
```ruby
|
515
|
-
|
516
|
-
|
517
|
-
query.define_search proc { |scope, search:|
|
518
|
-
scope.joins(:author, :tags)
|
519
|
-
.where("posts.title ILIKE :search OR users.name ILIKE :search", search: "%#{search}%")
|
520
|
-
.distinct
|
521
|
-
}
|
522
|
-
|
523
|
-
# Custom filter
|
524
|
-
query.define_filter :date_range, proc { |scope, start_date:, end_date:|
|
525
|
-
scope.where(created_at: start_date.beginning_of_day..end_date.end_of_day)
|
526
|
-
}
|
527
|
-
|
528
|
-
# Custom sorter
|
529
|
-
query.define_sorter :author_name, proc { |scope, direction:|
|
530
|
-
scope.joins(:author).order("users.name #{direction}")
|
531
|
-
}
|
514
|
+
register_resource ::UniversalFlow::Definition do
|
515
|
+
get "editor", to: "universal_flow/definitions#editor"
|
532
516
|
end
|
533
517
|
```
|
534
518
|
|
535
|
-
|
536
|
-
|
537
|
-
### Component Architecture
|
538
|
-
Built on Phlex, components inherit from `Plutonium::UI::Component::Base`:
|
519
|
+
**Solutions**:
|
539
520
|
|
521
|
+
1. **Add custom scope to model (Recommended)**:
|
540
522
|
```ruby
|
541
|
-
class
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
end
|
546
|
-
|
547
|
-
def view_template
|
548
|
-
div(class: "custom-component") do
|
549
|
-
h2(class: "text-xl font-bold") { @title }
|
550
|
-
div(class: "content") { @content } if @content
|
551
|
-
end
|
552
|
-
end
|
523
|
+
class UniversalFlow::Definition < ApplicationRecord
|
524
|
+
scope :associated_with_universal_flow_definition, ->(universal_flow_definition) {
|
525
|
+
all # or specific logic for your use case
|
526
|
+
}
|
553
527
|
end
|
554
528
|
```
|
555
529
|
|
556
|
-
|
530
|
+
2. **Use member/collection routes**:
|
557
531
|
```ruby
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
def render_content
|
562
|
-
# Custom show page rendering
|
563
|
-
super
|
564
|
-
end
|
532
|
+
register_resource ::UniversalFlow::Definition do
|
533
|
+
member do
|
534
|
+
get :editor
|
565
535
|
end
|
566
536
|
end
|
567
537
|
```
|
568
538
|
|
569
|
-
|
570
|
-
|
571
|
-
### Main Configuration
|
539
|
+
3. **Create separate controller**:
|
572
540
|
```ruby
|
573
|
-
#
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
config.assets.logo = "custom_logo.png"
|
584
|
-
config.assets.stylesheet = "custom_theme.css"
|
585
|
-
config.assets.script = "custom_plutonium.js"
|
586
|
-
config.assets.favicon = "custom_favicon.ico"
|
541
|
+
# In routes file
|
542
|
+
get 'universal_flow/editor/:id', to: 'universal_flow/editor#show'
|
543
|
+
|
544
|
+
# Create dedicated controller
|
545
|
+
class UniversalFlow::EditorController < CustomerPortal::ResourceController
|
546
|
+
def show
|
547
|
+
@definition = UniversalFlow::Definition.find(params[:id])
|
548
|
+
authorize_current! @definition
|
549
|
+
# ... custom logic
|
550
|
+
end
|
587
551
|
end
|
588
552
|
```
|
589
|
-
|
590
|
-
## Generator Commands
|
591
|
-
|
592
|
-
### Core Setup
|
593
|
-
```bash
|
594
|
-
# Install Plutonium in existing app
|
595
|
-
bin/rails app:template LOCATION=https://radioactive-labs.github.io/plutonium-core/templates/base.rb
|
596
|
-
rails generate pu:core:install
|
597
|
-
|
598
|
-
# Install authentication
|
599
|
-
rails generate pu:rodauth:install
|
600
|
-
rails generate pu:rodauth:account user
|
601
|
-
```
|
602
|
-
|
603
|
-
### Package Creation
|
604
|
-
```bash
|
605
|
-
# Create feature package
|
606
|
-
rails generate pu:pkg:package blogging
|
607
|
-
|
608
|
-
# Create portal package
|
609
|
-
rails generate pu:pkg:portal admin
|
610
|
-
```
|
611
|
-
|
612
|
-
### Resource Management
|
613
|
-
```bash
|
614
|
-
# Full resource scaffold
|
615
|
-
rails generate pu:res:scaffold Post user:belongs_to title:string content:text
|
616
|
-
|
617
|
-
# Individual resource components
|
618
|
-
rails generate pu:res:model Post title:string content:text
|
619
|
-
rails generate pu:res:definition Post
|
620
|
-
rails generate pu:res:policy Post
|
621
|
-
```
|
622
|
-
|
623
|
-
## Best Practices
|
624
|
-
|
625
|
-
### Resource Design
|
626
|
-
1. Keep models focused on data and basic validations
|
627
|
-
2. Use definitions for UI configuration, not business logic
|
628
|
-
3. Implement complex business logic in interactions
|
629
|
-
4. Use policies for all authorization logic
|
630
|
-
5. Leverage scopes for common queries
|
631
|
-
|
632
|
-
### Package Organization
|
633
|
-
1. Create feature packages for domain logic
|
634
|
-
2. Use portal packages for different user interfaces
|
635
|
-
3. Keep packages focused and cohesive
|
636
|
-
4. Namespace everything properly to avoid conflicts
|
637
|
-
|
638
|
-
### Security
|
639
|
-
1. Always define explicit permissions in policies
|
640
|
-
2. Use secure defaults (deny by default)
|
641
|
-
3. Implement proper entity scoping for multi-tenancy
|
642
|
-
4. Validate all user inputs in interactions
|
643
|
-
5. Use CSRF protection and implement rate limiting
|
644
|
-
|
645
|
-
### Performance
|
646
|
-
1. Use `includes` and `joins` in relation scopes
|
647
|
-
2. Add database indexes for filtered and sorted fields
|
648
|
-
3. Implement caching where appropriate
|
649
|
-
4. Use background jobs for heavy operations
|
650
|
-
5. Monitor N+1 queries with tools like Prosopite
|
651
|
-
|
652
|
-
### Code Organization
|
653
|
-
1. Follow Rails naming conventions
|
654
|
-
2. Keep controllers thin - use interactions for business logic
|
655
|
-
3. Use consistent patterns across resources
|
656
|
-
4. Write comprehensive tests for policies and interactions
|
657
|
-
5. Document complex business logic
|
658
|
-
|
659
|
-
### Development Workflow
|
660
|
-
1. Start with resource scaffolding for rapid prototyping
|
661
|
-
2. Customize definitions for UI requirements
|
662
|
-
3. Implement business logic through interactions
|
663
|
-
4. Add proper authorization with policies
|
664
|
-
5. Create appropriate packages for organization
|
665
|
-
6. Set up portals for different user types
|
666
|
-
|
667
|
-
This comprehensive guide should enable you to build robust, maintainable Plutonium applications following the framework's conventions and best practices.
|
@@ -92,7 +92,11 @@ module Plutonium
|
|
92
92
|
tag_attributes = display_options.except(:wrapper, :as, :condition)
|
93
93
|
tag_block = display_definition[:block] || ->(f) {
|
94
94
|
tag ||= f.inferred_field_component
|
95
|
-
|
95
|
+
if tag.is_a?(Class)
|
96
|
+
f.send :create_component, tag, tag.name.demodulize.underscore.sub(/component$/, "").to_sym
|
97
|
+
else
|
98
|
+
f.send(:"#{tag}_tag", **tag_attributes)
|
99
|
+
end
|
96
100
|
}
|
97
101
|
|
98
102
|
wrapper_options = display_options[:wrapper] || {}
|
@@ -68,7 +68,11 @@ module Plutonium
|
|
68
68
|
end
|
69
69
|
tag_block = input_definition[:block] || ->(f) do
|
70
70
|
tag ||= f.inferred_field_component
|
71
|
-
|
71
|
+
if tag.is_a?(Class)
|
72
|
+
f.send :create_component, tag, tag.name.demodulize.underscore.sub(/component$/, "").to_sym
|
73
|
+
else
|
74
|
+
f.send(:"#{tag}_tag", **tag_attributes)
|
75
|
+
end
|
72
76
|
end
|
73
77
|
|
74
78
|
field_options = field_options.except(:as, :condition)
|
@@ -11,7 +11,7 @@ module Plutonium
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def main_attributes = mix(super, {
|
14
|
-
class: "flex flex-col items-center justify-center px-6 py-8 mx-auto lg:py-0"
|
14
|
+
class: "flex flex-col items-center justify-center gap-2 px-6 py-8 mx-auto lg:py-0"
|
15
15
|
})
|
16
16
|
|
17
17
|
def render_content(&)
|
@@ -25,8 +25,8 @@ module Plutonium
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def render_logo
|
28
|
-
link_to root_path, class: "flex items-center text-2xl font-semibold text-gray-900 dark:text-white" do
|
29
|
-
helpers.resource_logo_tag classname: "w-24 h-24 mr-2"
|
28
|
+
link_to root_path, class: "flex items-center text-2xl font-semibold text-gray-900 dark:text-white mb-2" do
|
29
|
+
helpers.resource_logo_tag classname: "w-24 h-24 mr-2 rounded-md"
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
data/lib/plutonium/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plutonium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.24.
|
4
|
+
version: 0.24.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|