Class: IronAdmin::Resource

Inherits:
Object
  • Object
show all
Includes:
Concerns::Importable, Concerns::LiveUpdatable, Concerns::Nestable, Concerns::SoftDeletable
Defined in:
lib/iron_admin/resource.rb

Overview

Base class for defining admin resources in IronAdmin.

Resources are the primary way to configure how models appear in the admin panel. Each resource corresponds to a model and provides a DSL for customizing fields, filters, actions, search, and authorization.

Examples:

Basic resource definition

class UserResource < IronAdmin::Resource
  field :email, readonly: true
  field :role, type: :select, options: %w[admin user guest]
  field :password_digest, visible: false

  searchable :name, :email
  index_fields :id, :name, :email, :role, :created_at

  filter :role, type: :select, options: %w[admin user guest]

  action :suspend, icon: "pause", confirm: true do |record|
    record.update!(suspended_at: Time.current)
  end

  policy do
    allow :read
    allow :update, if: ->(user) { user.admin? }
    deny :delete
  end
end

Resource with associations

class OrderResource < IronAdmin::Resource
  belongs_to :customer
  has_many :line_items, resource: LineItemResource

  preload :customer, :line_items
end

See Also:

Class Method Summary collapse

Class Method Details

.action(name, form_fields: [], **options) {|record| ... } ⇒ void

This method returns an undefined value.

Defines a custom action for individual records.

Actions appear in the actions dropdown on show and index pages. They execute arbitrary code when triggered.

Examples:

Simple action

action :publish do |record|
  record.update!(published_at: Time.current)
end

Action with confirmation

action :suspend, icon: "pause", confirm: true do |record|
  record.update!(suspended_at: Time.current)
end

Parameters:

  • name (Symbol)

    The action name

  • options (Hash)

    Configuration options

Options Hash (**options):

  • :icon (String)

    Heroicon name (e.g., "lock-closed", "trash")

  • :confirm (Boolean)

    Whether to show a confirmation dialog

  • :confirm_message (String)

    Custom confirmation message

  • :method (Symbol)

    HTTP method (:post, :patch, :delete)

Yields:

  • (record)

    Block executed when action is triggered

Yield Parameters:

  • record (ActiveRecord::Base)

    The record to act upon



315
316
317
318
# File 'lib/iron_admin/resource.rb', line 315

def action(name, form_fields: [], **options, &block)
  coerced = coerce_action_fields(form_fields)
  self.defined_actions = defined_actions + [{ name: name, block: block, form_fields: coerced, **options }]
end

.action_allowed?(action_name) ⇒ Boolean

Checks if a CRUD action is allowed (not denied via deny_actions).

Parameters:

  • action_name (Symbol)

    The action to check

Returns:

  • (Boolean)

    True if the action is allowed



496
497
498
# File 'lib/iron_admin/resource.rb', line 496

def action_allowed?(action_name)
  denied_crud_actions.exclude?(action_name.to_sym)
end

.action_field(name) ⇒ IronAdmin::ActionField

Convenience constructor for ActionField instances.

Examples:

action :suspend,
  form_fields: [
    action_field(:reason, type: :textarea, required: true),
    action_field(:notify, type: :boolean)
  ] do |record, params|
  # ...
end

Parameters:

  • name (Symbol)

    The field name

  • opts (Hash)

    Options passed to ActionField.new

Returns:



364
365
366
# File 'lib/iron_admin/resource.rb', line 364

def action_field(name, **)
  ActionField.new(name: name, **)
end

.adapterIronAdmin::Adapters::Base

Returns the adapter instance for this resource's model.

The adapter provides a uniform interface for schema introspection, query building, and CRUD operations regardless of the underlying data source (ActiveRecord, Mongoid, HTTP, etc.).

Defensively re-resolves when the cached @adapter doesn't match the currently configured adapter_class. This catches the case where the eager Resource.inherited flow memoized an Adapters::ActiveRecord built before the subclass body's self.adapter_class = :mongoid (or :http) took effect.

Returns:



94
95
96
97
98
# File 'lib/iron_admin/resource.rb', line 94

def adapter
  wanted = resolve_adapter_class
  @adapter = nil if @adapter && !@adapter.instance_of?(wanted)
  @adapter ||= wanted.new(model)
end

.all_filtersArray<Hash>

Returns all filters (auto-inferred + manually defined).

Returns:

  • (Array<Hash>)

    All filter configurations



258
259
260
# File 'lib/iron_admin/resource.rb', line 258

def all_filters
  auto_inferred_filters + defined_filters
end

.all_scopesArray<Hash>

Returns all scopes with user-defined scopes first, soft delete scopes last.

Returns:

  • (Array<Hash>)

    Combined scope definitions



265
266
267
# File 'lib/iron_admin/resource.rb', line 265

def all_scopes
  defined_scopes + _soft_delete_scopes
end

.auto_inferred_filtersArray<Hash>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns filters automatically inferred from enum columns and database column types.

String/text columns auto-infer as :string filters. Integer/float/decimal columns auto-infer as :number filters. Enum columns auto-infer as :select filters.

Returns:

  • (Array<Hash>)

    Auto-generated filter configurations



250
251
252
253
# File 'lib/iron_admin/resource.rb', line 250

def auto_inferred_filters
  enum_filters = enum_auto_filters
  enum_filters + infer_column_filters(enum_filters)
end

.belongs_to(name, **options) ⇒ void

This method returns an undefined value.

Declares a belongs_to association for this resource.

Used to configure how the association is displayed and which resource to link to.

Examples:

belongs_to :organization
belongs_to :author, autocomplete: true

Parameters:

  • name (Symbol)

    The association name

  • options (Hash)

    Configuration options

Options Hash (**options):

  • :autocomplete (Boolean)

    Force autocomplete mode for large associations

  • :resource (Class)

    The associated resource class



547
548
549
# File 'lib/iron_admin/resource.rb', line 547

def belongs_to(name, **options)
  self.defined_associations = defined_associations.merge(name => { kind: :belongs_to, **options })
end

.bulk_action(name, form_fields: [], **options) {|records| ... } ⇒ void

This method returns an undefined value.

Defines a bulk action for multiple selected records.

Bulk actions appear in the bulk actions dropdown when records are selected on the index page. All records are processed in a single database transaction.

Examples:

Bulk deactivate

bulk_action :deactivate do |records|
  records.update_all(active: false)
end

Bulk action with rollback on failure

bulk_action :process do |records|
  return false unless ExternalService.process(records)
end

Parameters:

  • name (Symbol)

    The action name

  • options (Hash)

    Configuration options

Options Hash (**options):

  • :icon (String)

    Heroicon name

  • :confirm (Boolean)

    Whether to show a confirmation dialog

Yields:

  • (records)

    Block executed when action is triggered

Yield Parameters:

  • records (ActiveRecord::Relation)

    The selected records

Yield Returns:

  • (Boolean, nil)

    Return false to rollback the transaction



345
346
347
348
# File 'lib/iron_admin/resource.rb', line 345

def bulk_action(name, form_fields: [], **options, &block)
  coerced = coerce_action_fields(form_fields)
  self.defined_bulk_actions = defined_bulk_actions + [{ name: name, block: block, form_fields: coerced, **options }]
end

.component(name, klass) ⇒ void

This method returns an undefined value.

Registers a custom component class to override default rendering.

Examples:

Override the index table component

component :index, CustomUserIndexComponent

Parameters:

  • name (Symbol)

    The component type to override (:index, :show, :form, :row, etc.)

  • klass (Class)

    The component class to use



436
437
438
# File 'lib/iron_admin/resource.rb', line 436

def component(name, klass)
  self.component_overrides = component_overrides.merge(name => klass)
end

.deny_actions(*actions) ⇒ void

This method returns an undefined value.

Disables specific CRUD actions for this resource.

This is a simpler alternative to policies when you want to completely disable actions for all users.

Examples:

Read-only resource

deny_actions :create, :update, :delete

Parameters:

  • actions (Array<Symbol>)

    Actions to disable (:create, :update, :delete, :read)



488
489
490
# File 'lib/iron_admin/resource.rb', line 488

def deny_actions(*actions)
  self.denied_crud_actions = actions.map(&:to_sym)
end

.display_attributeSymbol

Returns the attribute used to display individual records.

Checks for common display methods (name, title, email, etc.) and falls back to :id if none found.

Returns:

  • (Symbol)

    The display attribute name



697
698
699
700
701
# File 'lib/iron_admin/resource.rb', line 697

def display_attribute
  ApplicationHelper::DISPLAY_METHODS.find do |method|
    adapter.has_column?(method)
  end || :id
end

.export_fields(*fields) ⇒ void

This method returns an undefined value.

Specifies which fields appear in exports.

By default, exports include all visible fields.

Examples:

export_fields :id, :name, :email, :created_at

Parameters:

  • fields (Array<Symbol>)

    Field names to export



528
529
530
# File 'lib/iron_admin/resource.rb', line 528

def export_fields(*fields)
  self.export_field_names = fields
end

.exports(*formats) ⇒ void

This method returns an undefined value.

Configures which export formats are available.

By default, both :csv and :json exports are enabled. Call with no arguments to disable all exports.

Examples:

Enable only CSV

exports :csv

Disable all exports

exports

Parameters:

  • formats (Array<Symbol>)

    Enabled export formats (:csv, :json)



514
515
516
# File 'lib/iron_admin/resource.rb', line 514

def exports(*formats)
  self.export_formats = formats
end

.field(name, **options) ⇒ void

This method returns an undefined value.

Configures display and behavior options for a field.

Fields are automatically inferred from the model's database schema. Use this method to override the inferred configuration.

Examples:

Basic field configuration

field :email, readonly: true
field :role, type: :select, options: %w[admin user guest]

Conditional visibility

field :salary, visible: ->(user) { user.admin? || user.hr? }

Custom formatting

field :price, format: ->(value) { number_to_currency(value) }

Parameters:

  • name (Symbol)

    The field name (must match a database column or association)

  • options (Hash)

    Configuration options for the field

Options Hash (**options):

  • :type (Symbol)

    Override the inferred field type (:string, :text, :integer, :boolean, :date, :datetime, :select, :belongs_to)

  • :visible (Boolean, Proc)

    Whether the field is visible (Proc receives the current user)

  • :readonly (Boolean, Proc)

    Whether the field is read-only (Proc receives the current user)

  • :label (String)

    Custom label for the field

  • :options (Array)

    For select fields, the available choices

  • :autocomplete (Boolean)

    For belongs_to, force autocomplete mode

  • :format (Proc)

    Custom formatting proc for display



155
156
157
# File 'lib/iron_admin/resource.rb', line 155

def field(name, **options)
  self.field_overrides = field_overrides.merge(name => options)
end

.filter(name, **options) ⇒ void

Note:

Enum columns automatically get select filters generated. Use remove_filter to remove auto-generated filters.

This method returns an undefined value.

Defines a filter for the resource index page.

Filters appear in the filter bar and allow users to narrow down the displayed records.

Examples:

Select filter

filter :status, type: :select, options: %w[pending active completed]

Date range filter

filter :created_at, type: :date_range

Parameters:

  • name (Symbol)

    The column or attribute to filter by

  • options (Hash)

    Configuration options

Options Hash (**options):

  • :type (Symbol)

    The filter type (:select, :date_range, :boolean)

  • :options (Array)

    For select filters, the available choices

  • :label (String)

    Custom label for the filter



226
227
228
# File 'lib/iron_admin/resource.rb', line 226

def filter(name, **options)
  self.defined_filters = defined_filters + [{ name: name, **options }]
end

.form_fields(*fields) ⇒ void

This method returns an undefined value.

Specifies which fields appear on create/edit forms.

By default, shows all editable fields. Use this to customize which fields appear and in what order.

Examples:

form_fields :name, :email, :role, :bio

Parameters:

  • fields (Array<Symbol>)

    Field names to display



394
395
396
# File 'lib/iron_admin/resource.rb', line 394

def form_fields(*fields)
  self.form_field_names = fields
end

.habtm_associationsArray<Hash>

Returns HABTM association configurations for multi-select on forms and badge display.

Returns:

  • (Array<Hash>)

    Association configs with :name, :reflection, :resource keys



666
667
668
669
670
671
672
673
674
675
676
677
# File 'lib/iron_admin/resource.rb', line 666

def habtm_associations
  defined_associations.filter_map do |assoc_name, config|
    next unless config[:kind] == :has_and_belongs_to_many

    reflection = adapter.association(assoc_name)
    next unless reflection

    resource = resolve_association_resource(config, reflection)

    { name: assoc_name, reflection: reflection, resource: resource, **config.except(:kind, :resource) }
  end
end

.has_and_belongs_to_many(name, **options) ⇒ void

This method returns an undefined value.

Declares a has_and_belongs_to_many association for this resource.

HABTM associations appear as multi-select checkboxes on forms and as badge lists on show pages.

Examples:

has_and_belongs_to_many :tags

Parameters:

  • name (Symbol)

    The association name

  • options (Hash)

    Configuration options

Options Hash (**options):

  • :resource (Class)

    The associated resource class



579
580
581
# File 'lib/iron_admin/resource.rb', line 579

def has_and_belongs_to_many(name, **options) # rubocop:disable Naming/PredicatePrefix
  self.defined_associations = defined_associations.merge(name => { kind: :has_and_belongs_to_many, **options })
end

.has_many_associationsArray<Hash>

Returns has_many association configurations for related lists.

Returns:

  • (Array<Hash>)

    Association configs with :name, :reflection, :resource keys



632
633
634
635
636
637
638
639
640
641
642
643
644
# File 'lib/iron_admin/resource.rb', line 632

def has_many_associations
  defined_associations.filter_map do |assoc_name, config|
    next unless config[:kind] == :has_many

    reflection = adapter.association(assoc_name)
    next unless reflection

    resource = resolve_association_resource(config, reflection)
    next unless resource

    { name: assoc_name, reflection: reflection, resource: resource, **config.except(:kind, :resource) }
  end
end

.has_one_associationsArray<Hash>

Returns has_one association configurations for display on show pages.

Returns:

  • (Array<Hash>)

    Association configs with :name, :reflection, :resource keys



649
650
651
652
653
654
655
656
657
658
659
660
661
# File 'lib/iron_admin/resource.rb', line 649

def has_one_associations # rubocop:disable Naming/PredicatePrefix
  defined_associations.filter_map do |assoc_name, config|
    next unless config[:kind] == :has_one

    reflection = adapter.association(assoc_name)
    next unless reflection

    resource = resolve_association_resource(config, reflection)
    next unless resource

    { name: assoc_name, reflection: reflection, resource: resource, **config.except(:kind, :resource) }
  end
end

.index_fields(*fields) ⇒ void

This method returns an undefined value.

Specifies which fields appear on the index (list) page.

By default, shows the first 6 fields. Use this to customize which fields appear and in what order.

Examples:

index_fields :id, :name, :email, :status, :created_at

Parameters:

  • fields (Array<Symbol>)

    Field names to display



379
380
381
# File 'lib/iron_admin/resource.rb', line 379

def index_fields(*fields)
  self.index_field_names = fields
end

.inherited(subclass) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Automatically registers subclasses with the ResourceRegistry.



71
72
73
74
75
76
77
78
79
# File 'lib/iron_admin/resource.rb', line 71

def inherited(subclass)
  super
  return if subclass.name.nil?

  begin
    IronAdmin::ResourceRegistry.register(subclass)
  rescue NameError
  end
end

.labelString

Returns the human-readable label for this resource.

Returns:

  • (String)

    Pluralized, humanized model name



687
688
689
# File 'lib/iron_admin/resource.rb', line 687

def label
  adapter.human_name.pluralize
end

This method returns an undefined value.

Configures the sidebar menu appearance for this resource.

Examples:

menu icon: "users", label: "Team Members", priority: 10, group: "People"

Parameters:

  • options (Hash)

    Menu configuration options

Options Hash (**options):

  • :icon (String)

    Heroicon name for the menu item

  • :label (String)

    Custom label (defaults to pluralized model name)

  • :priority (Integer)

    Sort order (lower = higher in menu)

  • :group (String)

    Group name for the sidebar section (sibling resources sharing the same :group are listed together; resources without a :group fall under "Resources"). :section is also accepted as an alias for :group for backward compatibility with earlier docs.



414
415
416
417
418
419
420
421
422
423
424
# File 'lib/iron_admin/resource.rb', line 414

def menu(**options)
  if options.key?(:section)
    legacy = options.delete(:section)
    # Preserve `:group` precedence based on key presence so that
    # explicitly passing `group: nil` (or `group: false`) still
    # wins over `section:`. Using `||=` would clobber falsey
    # explicit values.
    options[:group] = legacy unless options.key?(:group)
  end
  self.menu_options = options
end

.modelClass

Returns the ActiveRecord model class for this resource.

By default, infers the model from the resource class name (e.g., UserResource -> User).

Examples:

Override with model_class_override

class LegacyUserResource < IronAdmin::Resource
  self.model_class_override = OldUser
end

HTTP adapter — no Ruby model required

class ProductResource < IronAdmin::Resource
  self.adapter_class = :http
  # `model` returns `Adapters::Http::ModelProxy.new(self)` — no
  # `Product` constant required; fields come from the API
  # response on first access.
end

Returns:

  • (Class)

    The ActiveRecord model class



119
120
121
122
123
124
# File 'lib/iron_admin/resource.rb', line 119

def model
  return model_class_override if model_class_override
  return Adapters::Http::ModelProxy.new(self) if adapter_class == :http

  name.sub(/Resource\z/, "").sub(/\AIronAdmin::Resources::/, "").constantize
end

.policy { ... } ⇒ void

This method returns an undefined value.

Defines the authorization policy for this resource.

Policies control which actions users can perform. Without a policy, all actions are allowed by default.

Examples:

Basic policy

policy do
  allow :read
  allow :create, :update, if: ->(user) { user.admin? }
  deny :delete
end

Yields:

  • Block that configures the policy using the Policy DSL

See Also:



457
458
459
# File 'lib/iron_admin/resource.rb', line 457

def policy(&block)
  self._policy_block = block
end

.preload(*associations) ⇒ void

This method returns an undefined value.

Specifies which associations to preload on queries.

Use this to optimize queries by eager loading associations.

Examples:

preload :customer, :line_items, :shipping_address

Parameters:

  • associations (Array<Symbol>)

    Association names to preload



625
626
627
# File 'lib/iron_admin/resource.rb', line 625

def preload(*associations)
  @preload_associations = associations
end

.preload_associationsArray<Symbol>

Returns associations to preload for index/show queries.

By default, preloads all belongs_to associations to avoid N+1 queries. Use preload to customize.

Returns:

  • (Array<Symbol>)

    Association names to preload



611
612
613
# File 'lib/iron_admin/resource.rb', line 611

def preload_associations
  @preload_associations || infer_preload_associations
end

.remove_filter(name) ⇒ void

This method returns an undefined value.

Removes a filter (useful for removing auto-generated enum filters).

Examples:

Remove auto-generated status filter

remove_filter :status

Parameters:

  • name (Symbol)

    The filter name to remove



238
239
240
# File 'lib/iron_admin/resource.rb', line 238

def remove_filter(name)
  self.defined_filters = defined_filters.reject { |f| f[:name] == name }
end

.reset_resource_policy!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Clears the cached policy (used in testing).



472
473
474
# File 'lib/iron_admin/resource.rb', line 472

def reset_resource_policy!
  remove_instance_variable(:@resource_policy) if defined?(@resource_policy)
end

.resolved_fieldsArray<IronAdmin::Field>

Returns Field objects for all fields, with overrides applied.

Merges auto-inferred fields from the database schema with any customizations made via field and association declarations.

Returns:



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
# File 'lib/iron_admin/resource.rb', line 589

def resolved_fields
  inferred = FieldInferrer.call(adapter)

  inferred.map do |field|
    overrides = field_overrides[field.name] || {}
    assoc_overrides = defined_associations[field.name] || {}
    merged = assoc_overrides.except(:kind).merge(overrides)

    if merged.any?
      Field.new(field.name, type: field.type, **field.options, **merged)
    else
      field
    end
  end
end

.resource_nameString

Returns the URL-friendly resource name (pluralized model name).

Returns:

  • (String)

    The resource name (e.g., "users", "orders")



682
# File 'lib/iron_admin/resource.rb', line 682

delegate :resource_name, to: :adapter

.resource_policyIronAdmin::Policy?

Returns the Policy instance for this resource.

Returns:



464
465
466
467
468
# File 'lib/iron_admin/resource.rb', line 464

def resource_policy
  return @resource_policy if defined?(@resource_policy)

  @resource_policy = Policy.new(&_policy_block) if _policy_block
end

.scope(name, lambda, default: false) ⇒ void

This method returns an undefined value.

Defines a named scope for filtering records on the index page.

Scopes appear as tabs above the data table and allow quick filtering to predefined subsets of records.

Examples:

Basic scopes

scope :active, -> { where(active: true) }
scope :recent, -> { where("created_at > ?", 1.week.ago) }, default: true

Scope with complex query

scope :high_value, -> { where("total > ?", 1000).order(total: :desc) }

Parameters:

  • name (Symbol)

    The scope name (displayed as tab label)

  • lambda (Proc)

    A lambda that receives and returns an ActiveRecord::Relation

  • default (Boolean) (defaults to: false)

    Whether this scope is selected by default



286
287
288
# File 'lib/iron_admin/resource.rb', line 286

def scope(name, lambda, default: false)
  self.defined_scopes = defined_scopes + [{ name: name, scope: lambda, default: default }]
end

.searchable(*columns) ⇒ void

This method returns an undefined value.

Specifies which columns are searchable via the search box.

By default, all string and text columns are searchable (except those ending in _digest). Use this to explicitly define searchable columns.

Examples:

searchable :name, :email, :description

Parameters:

  • columns (Array<Symbol>)

    Column names to make searchable



170
171
172
# File 'lib/iron_admin/resource.rb', line 170

def searchable(*columns)
  self._searchable_columns = columns
end

.searchable_columnsArray<Symbol>

Returns the list of searchable column names.

If searchable was called, returns those columns. Otherwise, returns all string/text columns except those ending in _digest and those excluded via unsearchable.

Returns:

  • (Array<Symbol>)

    Searchable column names



196
197
198
199
200
201
202
203
# File 'lib/iron_admin/resource.rb', line 196

def searchable_columns
  return _searchable_columns if _searchable_columns

  adapter.columns.select { |c| c.type.in?(%i[string text]) }
    .map { |c| c.name.to_sym }
    .reject { |name| name.to_s.end_with?("_digest") }
    .reject { |name| _unsearchable_columns.include?(name) }
end

.unsearchable(*columns) ⇒ void

This method returns an undefined value.

Excludes columns from the default searchable set.

Use this when you want to keep most auto-inferred columns but exclude specific ones.

Examples:

unsearchable :internal_notes, :encrypted_data

Parameters:

  • columns (Array<Symbol>)

    Column names to exclude from search



185
186
187
# File 'lib/iron_admin/resource.rb', line 185

def unsearchable(*columns)
  self._unsearchable_columns = _unsearchable_columns + columns.map(&:to_sym)
end