Array Resource
An Array Resource is a resource backed by in-memory data instead of a database table. Use it to let Avo display and manage structured data that doesn't come from a model. The records method can return an array of hashes, an array of Active Record objects, an ActiveRecord::Relation, or an array of StoreModel instances.
Generate one with the --array flag:
bin/rails generate avo:resource Movie --arrayThe generated class extends Avo::Resources::ArrayResource. Return the data from the records method and describe it with fields, like any other resource:
# app/avo/resources/movie.rb
class Avo::Resources::Movie < Avo::Resources::ArrayResource
def records
[
{
id: 1,
name: "The Shawshank Redemption",
release_date: "1994-09-23"
},
{
id: 2,
name: "The Godfather",
release_date: "1972-03-24",
fun_fact: "The iconic cat in the opening scene was a stray found by director Francis Ford Coppola on the studio lot."
},
{
id: 3,
name: "Pulp Fiction",
release_date: "1994-10-14"
}
]
end
def fields
main_panel do
field :id, as: :id
field :name, as: :text
field :release_date, as: :date
field :fun_fact, only_on: :index, visible: -> { resource.record.fun_fact.present? } do
record.fun_fact.truncate_words(10)
end
sidebar do
field :fun_fact do
record.fun_fact || "There is no register of a fun fact for #{record.name}"
end
end
end
end
endEach hash becomes a record, so field :name reads the name: key and computed fields can call record.fun_fact directly. If you return Active Record objects or a relation instead, Avo uses their real model class and fields behave as they do on a regular resource. Avo paginates the array for you, or lets the source do it: see Paginate at the source.
Paginate at the source
Since v4.3.6By default records returns every record and Avo cuts the page out of the array. When the source can return one page by itself, such as a cache, a search index or a store of your own, fetch only the page being shown. page and per_page hold the index's current page and page size. Return that page's records together with the total, and Avo shows them as they are:
# app/avo/resources/bookmark.rb
class Avo::Resources::Bookmark < Avo::Resources::ArrayResource
self.find_record_method = -> { BookmarkStore.find(id) }
def records
{records: BookmarkStore.page(page, per: per_page), count: BookmarkStore.count}
end
def fields
field :id, as: :id
field :title, as: :text
field :url, as: :text
end
endBookmarkStore stands in for wherever the data lives. count is the total across all pages. The pagination bar reads it, so the index shows "1-24 of 1,250" without loading the other pages.
Avo now holds one page, so it can't look a record up in it. The show page, the edit form and actions on selected rows ask for a record that may sit on another page. find_record_method tells Avo how to fetch one record by its id, and it can return a hash, like records.
page and per_page are nil outside an index, for example when Avo calls records to build the form for a new record.
INFO
Select all matching is hidden on a resource that paginates at the source: only the page shown is loaded, so there is nothing to select on the other pages.
The Array field's block can paginate the same way.
Data from an external API
Use an HTTP Resource instead. It is backed by the endpoint itself and sends the page to the API.
Create, edit and delete
Since v4.2.10Array resources are read-only by default. Set self.writable = true to show the Create, Edit and Delete controls, then tell the resource how to write. Avo fills the record from the form, but it can't save an array record by itself, so you define save_record and destroy_record:
# app/avo/resources/bookmark.rb
class Avo::Resources::Bookmark < Avo::Resources::ArrayResource
self.writable = true
def records = BookmarkStore.all
def fields
field :id, as: :id
field :title, as: :text
field :url, as: :text
end
def save_record(record)
record.id = BookmarkStore.save(id: record.id, title: record.title, url: record.url)
end
def destroy_record(record)
BookmarkStore.destroy(record.id)
end
endBookmarkStore stands in for wherever the data lives: a JSON file, a cache, or an external API. save_record handles both create and update. A new record has a nil id, so set it after saving: Avo uses it to redirect to the record's page. Both methods report success through their return value, so return something truthy, or raise to fail. The resource's policy still decides who can create, edit and delete.
INFO
Avo calls save_record and destroy_record on the resource. Since v4.2.12 On earlier versions, override save_record_action and destroy_record_action in the resource's controller instead, where the record is @record.
Render it inside another resource
The Array Resource pairs with the Array field to display array data on another resource — field :attendees, as: :array on a Course, for example. When rendered through the field, records is the last fallback in the data-fetching hierarchy; the field's block and the model's method take precedence. See the Array field documentation for the full hierarchy.
Limitations
- Sorting is not supported.
- Large datasets can be slow — the array is rebuilt on every request. If that becomes a bottleneck, cache the data inside
records, or paginate at the source.
Heavier workloads
If your data comes from an external API or the array approach starts to feel limiting, consider an HTTP Resource instead — it's backed by an endpoint and built for that kind of work.
