Back to list
radioactive-labs

plutonium-definition-actions

by radioactive-labs

Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.

52🍴 7📅 Jan 23, 2026

SKILL.md


name: plutonium-definition-actions description: Add custom actions and interactions to Plutonium resources

Definition 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

TypeShows InUse Case
resource_actionIndex pageImport, Export, Create
record_actionShow pageEdit, Delete, Archive
collection_record_actionTable rowsQuick actions per row
bulk_actionSelected recordsBulk 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

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., bulk_archive?) is checked per record - the backend fails the entire request if any selected record is not authorized
  • Records are fetched via current_authorized_scope - only records the user can access are included
  • 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:

  • current_user - The authenticated user
  • view_context - Access to helpers and view methods
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
  • plutonium-definition - Overview and structure
  • plutonium-definition-fields - Fields, inputs, displays
  • plutonium-definition-query - Search, filters, scopes
  • plutonium-interaction - Writing interaction classes
  • plutonium-policy - Controlling action access

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon