Andromeda

Content and schemas

View as Markdown

Frontmatter, attribute types, validation and queries.

Every collection declares the frontmatter it expects. Files that do not match fail the build with the file name and the problem, instead of surfacing as nil in a view.

Declaring attributes

module Content
  class Post < Andromeda::Entry
    collection :posts, base: "app/content/posts", pattern: "**/*.{md,mdx}"

    attribute :title, :string, required: true
    attribute :pub_date, :date, required: true
    attribute :draft, :boolean, default: false
    attribute :tags, :array, of: :string, default: []
    attribute :hero_image, :image
    attribute :author, :reference, collection: :authors

    scope :published, -> { where(draft: false) }

    def reading_time = (body.length / 400.0).ceil
  end
end
TypeAcceptsBecomes
:string :integer :float :booleanthe matching YAML typeitself
:date :datetime2026-09-01, 'Jul 08 2022', ISO stringsDate / Time
:arraya list, with of: for the element typeArray
:enumone of values:the value
:hasha mappingHash
:imagea path relative to the entryan image, published through the asset pipeline
:referencean id in another collectionthe target entry, resolved lazily

Options: required:, default: (a value or a callable), of:, values:, collection:.

Frontmatter

YAML (---) and TOML (+++) are both supported. YAML is read with 1.2 semantics, matching Astro: yes and no stay strings, while true and false are booleans. Dates and timestamps become Date and Time.

Keys are snake_case. Content copied from an Astro project usually has camelCase keys; bin/rails andromeda:fix rewrites them, and bin/rails andromeda:check reports them.

Querying

Content::Post.all
Content::Post.published.order(pub_date: :desc).limit(10)
Content::Post.where(draft: false, lang: "en")          # every key must match
Content::Post.where { |post| post.tags.include?("rails") }
Content::Post.find("hello-world")   # raises Andromeda::EntryNotFound

where with a hash compares values for equality, so use the block form to look inside an array. order is stable: entries that tie keep the order they were loaded in, as they do in Astro.

Entries expose id, data, body, file_path, digest, html and headings, plus a reader per declared attribute (post.title).

Entry ids

Ids follow Astro’s glob() loader rules, so URLs from an Astro project keep working:

FileId
hello-world.mdhello-world
Guides/Getting Started.mdguides/getting-started
posts/hello/index.mdxposts/hello
a slug: in the frontmatterthat value, verbatim

Last updated September 24, 2026