plutonium 0.23.5 → 0.24.1

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.
@@ -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
- ## Multi-Tenancy & Entity Scoping
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
- ### Database Associations for Scoping
486
+ ### Resource Registration
482
487
  ```ruby
483
- # Direct association (auto-detected)
484
- class Post < ApplicationRecord
485
- belongs_to :organization
486
- end
487
-
488
- # Indirect association (auto-detected)
489
- class Post < ApplicationRecord
490
- belongs_to :author, class_name: 'User'
491
- has_one :organization, through: :author
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
- scope :associated_with_organization, ->(organization) do
499
- joins(post: :author).where(users: { organization_id: organization.id })
498
+ collection do
499
+ get :search # /posts/search
500
500
  end
501
501
  end
502
502
  ```
503
503
 
504
- ## Query Objects & Filtering
504
+ ### Common Routing Errors
505
505
 
506
- ### Automatic Query Handling
507
- Controllers automatically handle:
508
- - Search: `?q[search]=rails`
509
- - Filters: `?q[published]=true`
510
- - Sorting: `?q[sort_fields][]=created_at&q[sort_directions][created_at]=desc`
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
- ### Custom Query Objects
512
+ **Problem**: Routes like this create nested resource interpretation:
514
513
  ```ruby
515
- query_object = Plutonium::Resource::QueryObject.new(Post, params[:q] || {}, request.path) do |query|
516
- # Custom search
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
- ## UI Components & Theming
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 CustomComponent < Plutonium::UI::Component::Base
542
- def initialize(title:, content: nil)
543
- @title = title
544
- @content = content
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
- ### Layout Customization
530
+ 2. **Use member/collection routes**:
557
531
  ```ruby
558
- # Custom page classes in definitions
559
- class PostDefinition < Plutonium::Resource::Definition
560
- class ShowPage < Plutonium::UI::Page::Show
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
- ## Configuration
570
-
571
- ### Main Configuration
539
+ 3. **Create separate controller**:
572
540
  ```ruby
573
- # config/initializers/plutonium.rb
574
- Plutonium.configure do |config|
575
- config.load_defaults 1.0
576
-
577
- # Development settings
578
- config.development = Rails.env.development?
579
- config.cache_discovery = !Rails.env.development?
580
- config.enable_hotreload = Rails.env.development?
581
-
582
- # Assets
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.
@@ -5,14 +5,6 @@ module Plutonium
5
5
 
6
6
  included do
7
7
  defineable_prop :nested_input
8
-
9
- # def self.nested_input(name, with: nil, **)
10
- # defined_nested_inputs[name] = {}
11
- # end
12
-
13
- # def nested_input(name, with: nil, **)
14
- # instance_defined_nested_inputs[name] = {}
15
- # end
16
8
  end
17
9
  end
18
10
  end
@@ -73,7 +73,7 @@ module Plutonium
73
73
  # Returns the submitted resource parameters
74
74
  # @return [Hash] The submitted resource parameters
75
75
  def submitted_resource_params
76
- @submitted_resource_params ||= build_form(resource_class.new).extract_input(params, view_context:)[resource_param_key.to_sym]
76
+ @submitted_resource_params ||= build_form(resource_class.new).extract_input(params, view_context:)[resource_param_key.to_sym].compact
77
77
  end
78
78
 
79
79
  # Returns the resource parameters, including scoped and parent parameters
@@ -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
- f.send(:"#{tag}_tag", **tag_attributes)
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
- f.send(:"#{tag}_tag", **tag_attributes)
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)
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.23.5"
2
+ VERSION = "0.24.1"
3
3
  NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
4
  v[1] = v[1].to_i + 1
5
5
  v[2] = 0
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.23.5
4
+ version: 0.24.1
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-21 00:00:00.000000000 Z
11
+ date: 2025-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk