Claude-skill-registry plutonium-definition-actions
Add custom actions and interactions to Plutonium resources
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/definition-actions" ~/.claude/skills/majiayu000-claude-skill-registry-plutonium-definition-actions && rm -rf "$T"
skills/data/definition-actions/SKILL.mdDefinition Actions
Actions define custom operations that can be performed on resources. They can be simple (navigation) or interactive (with business logic via Interactions).
Action Types
| Type | Shows In | Use Case |
|---|---|---|
| Index page | Import, Export, Create |
| Show page | Edit, Delete, Archive |
| Table rows | Quick actions per row |
| Selected records | Bulk operations |
Simple Actions (Navigation)
Simple actions link to existing routes. The target route must already exist - these don't create new functionality, just navigation links.
class PostDefinition < ResourceDefinition # Link to external URL action :documentation, label: "Documentation", route_options: {url: "https://docs.example.com"}, icon: Phlex::TablerIcons::Book, resource_action: true # Link to custom controller action (you must add the action + route yourself) action :reports, route_options: {action: :reports}, icon: Phlex::TablerIcons::ChartBar, resource_action: true end
Important: When adding custom routes for actions, always use the
as: option to name them:
# In your portal routes or config/routes.rb resources :posts do collection do get :reports, as: :reports # Named route required! end end
This ensures
resource_url_for can generate correct URLs, especially for nested resources.
Note: For custom operations with business logic, use Interactive Actions with an Interaction class instead. That's the recommended approach for most custom actions.
Interactive Actions (with Interaction)
class PostDefinition < ResourceDefinition action :publish, interaction: PublishInteraction, icon: Phlex::TablerIcons::Send action :archive, interaction: ArchiveInteraction, color: :danger, category: :danger, position: 1000, confirmation: "Are you sure?" end
Action Options
action :name, # Display label: "Custom Label", # Button text (default: name.titleize) description: "What it does", # Tooltip/description icon: Phlex::TablerIcons::Star, # Icon component color: :danger, # :primary, :secondary, :danger # Visibility resource_action: true, # Show on index page record_action: true, # Show on show page collection_record_action: true, # Show in table rows bulk_action: true, # For selected records # Grouping category: :primary, # :primary, :secondary, :danger position: 50, # Order (lower = first) # Behavior confirmation: "Are you sure?", # Confirmation dialog turbo_frame: "_top", # Turbo frame target route_options: {action: :foo} # Route configuration
Creating an Interaction
Basic Structure
# app/interactions/resource_interaction.rb (generated during install) class ResourceInteraction < Plutonium::Resource::Interaction end # app/interactions/archive_interaction.rb class ArchiveInteraction < ResourceInteraction presents label: "Archive", icon: Phlex::TablerIcons::Archive, description: "Archive this record" attribute :resource # The record being acted on def execute resource.archived! succeed(resource).with_message("Record archived successfully.") rescue ActiveRecord::RecordInvalid => e failed(e.record.errors) rescue => error failed("Archive failed. Please try again.") end end
With Additional Inputs
# app/interactions/company/invite_user_interaction.rb class Company::InviteUserInteraction < Plutonium::Resource::Interaction presents label: "Invite User", icon: Phlex::TablerIcons::Mail attribute :resource # The company attribute :email attribute :role # Configure form inputs input :email, as: :email, hint: "User's email address" input :role, as: :select, choices: %w[admin member viewer] # Validations validates :email, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP} validates :role, presence: true, inclusion: {in: %w[admin member viewer]} def execute UserInvite.create!( company: resource, email: email, role: role, invited_by: current_user ) succeed(resource).with_message("Invitation sent to #{email}.") rescue ActiveRecord::RecordInvalid => e failed(e.record.errors) end end
Bulk Action (Multiple Records)
Bulk actions operate on multiple selected records at once. When a definition has bulk actions, the resource table automatically shows:
- Selection checkboxes in each row
- Bulk actions toolbar that appears when records are selected
# 1. Create the interaction (note: plural `resources` attribute) class BulkArchiveInteraction < Plutonium::Resource::Interaction presents label: "Archive Selected", icon: Phlex::TablerIcons::Archive attribute :resources # Array of records (note: plural) def execute count = 0 resources.each do |record| record.archived! count += 1 end succeed(resources).with_message("#{count} records archived.") rescue => error failed("Bulk archive failed: #{error.message}") end end # 2. Register the action in the definition class PostDefinition < ResourceDefinition action :bulk_archive, interaction: BulkArchiveInteraction # bulk_action: true is automatically inferred from `resources` attribute end # 3. Add policy method class PostPolicy < ResourcePolicy def bulk_archive? create? # Or whatever permission level is appropriate end end
Authorization for bulk actions:
- Policy method (e.g.,
) is checked per record - the backend fails the entire request if any selected record is not authorizedbulk_archive? - Records are fetched via
- only records the user can access are includedcurrent_authorized_scope - The UI only shows action buttons that all selected records support (intersection of allowed actions)
Resource Action (No Record)
class ImportInteraction < Plutonium::Resource::Interaction presents label: "Import CSV", icon: Phlex::TablerIcons::Upload # No :resource or :resources attribute = resource action attribute :file input :file, as: :file validates :file, presence: true def execute # Import logic... succeed(nil).with_message("Import completed.") end end
Interaction Responses
def execute # Success with message (redirects to resource automatically) succeed(resource).with_message("Done!") # Success with custom redirect (only if different from default) succeed(resource) .with_redirect_response(custom_dashboard_path) .with_message("Redirecting...") # Failure with field errors failed(resource.errors) # Failure with custom message failed("Something went wrong") # Failure with specific field failed("Invalid value", :email) end
Note: Redirect is automatic on success. Only use
with_redirect_response for a different destination.
Interaction Context
Inside an interaction:
- The authenticated usercurrent_user
- Access to helpers and view methodsview_context
def execute resource.update!( archived_by: current_user, archived_at: Time.current ) succeed(resource) end
Defining in Definition
Basic
class PostDefinition < ResourceDefinition action :publish, interaction: PublishInteraction action :archive, interaction: ArchiveInteraction end
With Overrides
class PostDefinition < ResourceDefinition action :archive, interaction: ArchiveInteraction, collection_record_action: false, # Don't show in table color: :danger, position: 1000 # Show last end
Inherited Actions
Actions defined in
ResourceDefinition (created during install) are inherited by all definitions:
# app/definitions/resource_definition.rb (created during install) class ResourceDefinition < Plutonium::Resource::Definition action :archive, interaction: ArchiveInteraction, color: :danger, position: 1000 end # All definitions inherit the archive action automatically class PostDefinition < ResourceDefinition end
Portal-Specific Actions
Override actions for a specific portal:
# packages/admin_portal/app/definitions/admin_portal/post_definition.rb class AdminPortal::PostDefinition < ::PostDefinition # Add admin-only actions action :feature, interaction: FeaturePostInteraction action :bulk_publish, interaction: BulkPublishInteraction end
Default CRUD Actions
Plutonium provides these by default:
action :new, resource_action: true, position: 10 action :show, collection_record_action: true, position: 10 action :edit, record_action: true, position: 20 action :destroy, record_action: true, position: 100, category: :danger
Authorization
Actions are authorized via policies:
# app/policies/post_policy.rb class PostPolicy < ResourcePolicy def publish? user.admin? || record.author == user end def archive? user.admin? end end
The action only appears if the policy method returns
true.
Immediate vs Form Actions
Immediate - Executes without showing a form (when interaction has no extra inputs):
class ArchiveInteraction < Plutonium::Resource::Interaction attribute :resource # Only resource, no other inputs # No input declarations def execute resource.archived! succeed(resource) end end
Form - Shows a form first (when interaction has additional inputs):
class InviteUserInteraction < Plutonium::Resource::Interaction attribute :resource attribute :email attribute :role input :email input :role, as: :select, choices: %w[admin member] # Has inputs = shows form first end
Common Patterns
Archive/Restore
class ArchiveInteraction < Plutonium::Resource::Interaction presents label: "Archive", icon: Phlex::TablerIcons::Archive attribute :resource def execute resource.archived! succeed(resource).with_message("Archived.") end end class RestoreInteraction < Plutonium::Resource::Interaction presents label: "Restore", icon: Phlex::TablerIcons::Refresh attribute :resource def execute resource.active! succeed(resource).with_message("Restored.") end end
Send Notification
class SendReminderInteraction < Plutonium::Resource::Interaction presents label: "Send Reminder", icon: Phlex::TablerIcons::Bell attribute :resource attribute :message input :message, as: :text, hint: "Custom message (optional)" def execute ReminderMailer.with(record: resource, message: message).deliver_later succeed(resource).with_message("Reminder sent.") end end
Related Skills
- Overview and structureplutonium-definition
- Fields, inputs, displaysplutonium-definition-fields
- Search, filters, scopesplutonium-definition-query
- Writing interaction classesplutonium-interaction
- Controlling action accessplutonium-policy