Entity System
Manage any ActiveRecord model through the RailsPress admin interface without writing custom controllers or views.
Quick Start
1. Include the Entity concern in your model
class Project < ApplicationRecord
include Railspress::Entity
has_rich_text :body
has_many_attached :gallery
# Declare which fields appear in the CMS
railspress_fields :title, :client, :featured
railspress_fields :description
railspress_fields :body
railspress_fields :gallery, as: :attachments
# Optional: Custom sidebar label
railspress_label "Client Projects"
validates :title, presence: true
end
2. Register the entity in an initializer
Railspress.configure do |config|
config.register_entity :project
end
That's it. Your model now has full CRUD at /railspress/admin/entities/projects.
The railspress_fields DSL
Declare which model attributes should appear in the admin forms and index table.
Basic usage (auto-detected types)
railspress_fields :title, :description, :published
Types are automatically detected from:
- ActionText associations (
has_rich_text) - ActiveStorage attachments (
has_one_attached,has_many_attached) - Database column types
Explicit type override
railspress_fields :body, as: :rich_text
railspress_fields :gallery, as: :attachments
Supported Field Types
| Type | Detection | Form Input | Index Display |
|---|---|---|---|
:string |
String columns | Text field | Truncated text |
:text |
Text columns | Textarea | Truncated text |
:rich_text |
has_rich_text |
Trix editor | Stripped/truncated |
:boolean |
Boolean columns | Checkbox | Yes/No badge |
:integer |
Integer columns | Number field | Raw value |
:decimal |
Decimal/float columns | Number field (step: any) | Raw value |
:datetime |
Datetime columns | Datetime-local picker | Formatted date |
:date |
Date columns | Date picker | Formatted date |
:attachment |
has_one_attached |
File input | Attached/None badge |
:attachments |
has_many_attached |
Multiple file input | "N images" badge |
Form layout
Fields are automatically organized into a two-column layout:
- Main column: String, text, and rich text fields
- Sidebar: Boolean, numeric, and date fields in an "Options" section
- Sidebar: Each attachment field gets its own section with preview and removal
Array Fields
Store arrays of strings in JSON columns using :list and :lines field types. These are useful for things like tech stacks, feature lists, or highlights.
Quick Start
class Project < ApplicationRecord
include Railspress::Entity
railspress_fields :title, :client
railspress_fields :tech_stack, as: :list # Comma-separated input
railspress_fields :highlights, as: :lines # Line-separated input
end
Virtual attributes are auto-generated. No extra concerns or boilerplate needed.
Migration
Use JSON or JSONB columns with array defaults:
class AddArrayFieldsToProjects < ActiveRecord::Migration[8.0]
def change
add_column :projects, :tech_stack, :jsonb, default: [], null: false
add_column :projects, :highlights, :jsonb, default: [], null: false
end
end
Two Field Types
| Type | Input Format | Best For | Deduplicates? |
|---|---|---|---|
:list |
Comma-separated | Short items (tags, tech names) | Yes |
:lines |
One per line | Sentences, paragraphs | No |
How It Works
When you declare railspress_fields :tech_stack, as: :list, RailsPress auto-generates:
# Nil guard - always returns array, never nil
def tech_stack
super || []
end
# Virtual getter: array → comma string (for form population)
def tech_stack_list
tech_stack.join(", ")
end
# Virtual setter: comma string → array (for form submission)
def tech_stack_list=(value)
self.tech_stack = value.split(",").map(&:strip).reject(&:blank?).uniq
end
For :lines fields, the separator is newline instead of comma, and duplicates are preserved.
Form Behavior
:list fields render as a single-line text input with a hint to separate items with commas.
:lines fields render as a textarea with a hint to enter one item per line.
Display Behavior
- Index view: Shows item count badge ("3 items")
- Show view
:list: Displays inline as "Ruby, Rails, PostgreSQL" - Show view
:lines: Displays as bullet list
Input Parsing
:list fields:
- Split on comma
- Strip whitespace from each item
- Remove empty items
- Deduplicate (preserves first occurrence)
project.tech_stack_list = " Ruby ,, Rails , Ruby "
project.tech_stack # => ["Ruby", "Rails"]
:lines fields:
- Split on newline (handles both
\nand\r\n) - Strip whitespace from each item
- Remove empty lines
- Preserves duplicates (order matters for content like steps)
Entity Registration
Railspress.configure do |config|
# Symbol registration (preferred)
config.register_entity :project
config.register_entity :testimonial
config.register_entity :team_member
# Custom sidebar label
config.register_entity :case_study, label: "Client Work"
end
Routes
Entity routes are automatically generated under /railspress/admin/entities/:entity_type:
| Method | Path | Action |
|---|---|---|
| GET | /entities/projects |
index |
| GET | /entities/projects/new |
new |
| POST | /entities/projects |
create |
| GET | /entities/projects/:id |
show |
| GET | /entities/projects/:id/edit |
edit |
| PATCH/PUT | /entities/projects/:id |
update |
| DELETE | /entities/projects/:id |
destroy |
Attachments
Single attachment
class Testimonial < ApplicationRecord
include Railspress::Entity
has_one_attached :avatar
railspress_fields :name, :quote
railspress_fields :avatar, as: :attachment
end
Multiple attachments
class Project < ApplicationRecord
include Railspress::Entity
has_many_attached :gallery
railspress_fields :title
railspress_fields :gallery, as: :attachments
end
Attachment fields support image preview thumbnails, individual removal checkboxes, direct upload (if configured), and adding new files while keeping existing ones.
Full Example
class TeamMember < ApplicationRecord
include Railspress::Entity
has_one_attached :headshot
has_rich_text :bio
railspress_fields :name, :role, :email
railspress_fields :bio
railspress_fields :headshot, as: :attachment
railspress_fields :featured, :display_order
railspress_label "Team"
validates :name, presence: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true
scope :featured, -> { where(featured: true) }
scope :ordered, -> { order(display_order: :asc, name: :asc) }
end
Admin available at: /railspress/admin/entities/team_members