ElaineCrud
A Rails engine for rapidly generating CRUD interfaces for ActiveRecord models with minimal configuration.
Features
- Zero Configuration: Works out of the box with any ActiveRecord model
- Rails Conventions: Follows standard Rails patterns
- Minimal Code: Just specify model and permitted params
- Search & Filter: Built-in search across text fields
- Sortable Columns: Click column headers to sort
- Pagination: Automatic pagination with configurable page size
- Export: Download data as CSV, Excel, or JSON
- Extensible: Override any view or behavior in your host app
- Modern UI: Clean, responsive interface with TailwindCSS
- Inline Editing: Edit records in place with Turbo Frames
Installation
Add to your Gemfile:
gem 'elaine_crud'Then run:
bundle installQuick Start
1. Ensure you have ActiveRecord model representing your data.
bin/rails generate model Task title:string description:text priority:integer completed:boolean due_date:date
bin/rails db:migrate2. Create a Controller for your ActiveRecord Model
Specify which layout ElaineCrud should use with the layout directive:
class TaskController < ElaineCrud::BaseController
layout 'application' # Use your app's layout (wraps ElaineCrud's content)
model Task
permit_params :title, :description, :priority, :completed, :due_date
endImportant: The layout 'application' line tells ElaineCrud to render its CRUD views inside your application's layout. Without this, you'll see unstyled content with no HTML structure.
3. Add Routes
# config/routes.rb
Rails.application.routes.draw do
resources :tasks
root "tasks#index"
end4. Ensure Your Application Has a Layout
ElaineCrud is a content-only engine - it provides CRUD views but relies on your application to provide the HTML structure (layout, navigation, styling).
Your Rails app should have a layout file (typically app/views/layouts/application.html.erb) that includes:
- Basic HTML structure (
<html>,<head>,<body>) - TailwindCSS stylesheets
- JavaScript imports (including Turbo)
- Navigation/header/footer (optional, your choice)
Here's an example layout file, which contains the critical stylesheet_link_tag for elaine_crud:
<!DOCTYPE html>
<html>
<head>
<title><%= content_for(:title) || "Taskmanager" %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "elaine_crud", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body class="bg-gray-100">
<main class="w-full px-4 py-6">
<%= yield %>
</main>
</body>
</html>5. Start Your Server
bin/devUsage
Basic Controller
class TasksController < ElaineCrud::BaseController
layout 'application'
model Task
permit_params :title, :description, :priority, :completed, :due_date
endThat's it - 6 lines of code for a full CRUD interface with search, sorting, pagination, and export.
DSL Reference
-
model(ModelClass)- Specify the ActiveRecord model to manage -
permit_params(*attrs)- Define permitted attributes for strong parameters -
field(field_name, **options)- Configure individual field display and behavior -
default_sort(column:, direction:)- Set default sort column and direction (:asc or :desc) -
disable_turbo- Disable Turbo Frames (use full-page navigation instead of inline editing) -
show_view_button(enabled)- Show/hide the View button in actions column -
max_export(limit)- Set maximum records for export (default: 10,000)
Requirements
- Rails 7.0+
- TailwindCSS (included via precompiled CSS)
Examples
More detailed example with customisations
A more comprehensive example showing custom field formatting, sorting, and relationships:
# app/controllers/products_controller.rb
class ProductsController < ElaineCrud::BaseController
layout 'application'
model Product
permit_params :name, :description, :price, :stock_quantity, :category_id, :active
# Sort by name alphabetically by default
default_sort column: :name, direction: :asc
# Show the View button in actions column
show_view_button
# Format price as currency
field :price do |f|
f.title "Price"
f.display_as { |value, record| number_to_currency(value) if value.present? }
end
# Custom display for stock status
field :stock_quantity do |f|
f.title "Stock"
f.display_as { |value, record|
if value.to_i > 10
content_tag(:span, "#{value} in stock", class: "text-green-600")
elsif value.to_i > 0
content_tag(:span, "Low: #{value}", class: "text-yellow-600 font-semibold")
else
content_tag(:span, "Out of stock", class: "text-red-600 font-semibold")
end
}
end
# Boolean with badge display
field :active do |f|
f.title "Status"
f.display_as { |value, record|
if value
content_tag(:span, "Active", class: "px-2 py-1 text-xs rounded bg-green-100 text-green-800")
else
content_tag(:span, "Inactive", class: "px-2 py-1 text-xs rounded bg-gray-100 text-gray-600")
end
}
end
# Foreign key - automatically renders as dropdown in forms
field :category_id do |f|
f.foreign_key model: Category, display: :name
end
endExample Output
The generated interface includes:
- Index Page: Responsive grid-based table with all records
- Inline Editing: Click Edit to modify records in place (Turbo Frames)
- Sortable Columns: Click headers to sort ascending/descending
- Search: Filter records by text across searchable columns
- Pagination: Navigate through large datasets with configurable page size
- Export: Download as CSV, Excel (.xlsx), or JSON
- Smart Formatting: Dates, booleans, and nil values formatted nicely
- Action Buttons: Edit and Delete functionality
- Empty States: Helpful messages when no records exist
- Visual Feedback: Row highlights after saving changes
Example Controllers Reference
The test/dummy_app contains example controllers demonstrating various features:
| Controller | Features Demonstrated |
|---|---|
| LibrariesController |
has_many relationships (auto-detected), email as mailto link, date formatting |
| AuthorsController | Boolean field with badge display, has_many relationship display, show_view_button
|
| MembersController | Enum-style field with colored badges, email links, date formatting |
| LibrariansController | Role badges, salary as currency, email links |
| LoansController | Multiple foreign_key fields, date sorting, status display |
| TagsController | Color preview display, simple CRUD |
| BookCopiesController | Minimal controller (just model and permit_params) |
| ProfilesController | Minimal controller example |
| BooksController |
field with display_as, foreign_key, nested_create, currency formatting, custom calculate_layout for multi-row display, URL links |
Architecture
ElaineCrud follows a separation of concerns approach:
- Engine provides: CRUD logic, data formatting, content templates
- Host app provides: Layout, styling, HTML structure, navigation
Layout Control
The gem doesn't impose any layout - your app controls the HTML structure:
class UsersController < ElaineCrud::BaseController
layout 'admin' # Use admin layout
...
endThis means:
- Your app controls: Headers, footers, navigation, CSS frameworks
- Engine provides: Table content, buttons, data formatting
- Zero view files needed: No templates to create in your app
See ARCHITECTURE.md for detailed technical documentation.
Contributing
- Open a new issue and suggest a feature
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
License
MIT License. See LICENSE for details.
