Essence is a powerful tool for rapid database schema iteration in Rails applications. It provides a clean, YAML-based syntax with intelligent defaults and pattern matching that compiles to Atlas HCL format for seamless database migrations.
- 10x faster schema iteration - Write schemas in clean YAML instead of verbose Rails migrations or GUI tools
- Flexible data type syntax - Use familiar Rails migration types (
:string,:integer) or concise Atlas/SQL types (string(100),integer) - Smart defaults - Automatic
id,created_at,updated_atcolumns for every table - Pattern-based property inference -
league_id: ~automatically becomes a foreign key toleagues.id - Version control friendly - Text-based schemas that diff and merge cleanly
- Rails integration - Seamless workflow with rake tasks and Atlas backend
- Template generation - Quick project setup with sensible defaults
Note: This gem is not yet published to RubyGems. Install from source:
Add this line to your application's Gemfile:
gem 'essence', git: 'https://github.com/brandonzylstra/essence.git'And then execute:
$ bundle installClone the repository and build the gem:
$ git clone https://github.com/brandonzylstra/essence.git
$ cd essence
$ bundle install
$ gem build essence.gemspec
$ gem install essence-0.1.0.gem# In your Rails project
rake essence:templateThis creates db/schema.yaml with sensible defaults:
# Enhanced Essence Schema with Default Columns and Pattern Matching
schema_name: public
defaults:
"*":
columns:
id: ~
created_at: ~
updated_at: ~
column_patterns:
- "^id$": "primary_key"
- "_id$": "integer -> {table}.id on_delete=cascade not_null"
- "_at$": "datetime not_null"
- ".*": "string"
tables:
users:
columns:
email: string(255) not_null unique
name: string(100) not_null
league_id: ~
last_login_at: ~
leagues:
columns:
name: string(255) not_null unique
description: textrake essence:compileThis generates db/schema.hcl in Atlas format with foreign keys automatically resolved:
schema "public" {
comment = "Generated by Essence"
}
table "users" {
schema = schema.public
column "id" {
null = false
type = integer
auto_increment = true
}
column "email" {
null = false
type = varchar(255)
}
column "name" {
null = false
type = varchar(100)
}
column "league_id" {
null = false
type = integer
}
column "last_login_at" {
null = false
type = datetime
}
column "created_at" {
null = false
type = datetime
}
column "updated_at" {
null = false
type = datetime
}
primary_key {
columns = [column.id]
}
foreign_key "FK_USERS_LEAGUE" {
columns = [column.league_id]
ref_columns = [table.leagues.column.id]
on_delete = CASCADE
}
index "IDX_USERS_EMAIL" {
columns = [column.email]
unique = true
}
}# See what would change
rake essence:preview
# Generate Rails migration and apply
rake essence:deploy["add tournament tables"]Every table automatically gets:
idprimary key columncreated_atandupdated_attimestamps- Proper indexing and constraints
Write league_id: ~ andβbased on the pattern that name matchesβautomatically get:
- Integer column
- Foreign key to
leagues.id - Cascading delete
- Proper indexing
Choose the syntax that works best for your team:
# Atlas/SQL syntax
tables:
tournaments:
columns:
name: string(255) not_null unique
league_id: ~ # Becomes foreign key automatically
start_date: ~ # Becomes datetime not_null
# Rails syntax
users:
columns:
name: :string, limit: 255, null: false, unique: true
league_id: ~ # Pattern matching works with both syntaxes
created_at: :datetime, null: false
# Mixed syntax
posts:
columns:
title: string(255) not_null # Atlas/SQL syntax
content: :text # Rails syntax
user_id: ~ # Pattern matchingrake essence:template # Generate schema template
rake essence:convert # Convert YAML to HCL
rake essence:preview # Preview changes
rake essence:apply # Apply to database
rake essence:deploy[name] # Full workflowEssence provides comprehensive support for database schema elements, with plans to expand coverage over time.
| Status | Group | Element | Explanation |
|---|---|---|---|
| β | Core | Tables | Primary schema containers with columns, constraints, and metadata |
| β | Core | Columns | All standard data types with dual syntax support (Atlas/SQL + Rails) |
| β | Core | Data Types | Support for both :string (Rails) and string(100) (Atlas/SQL) syntax |
| β | Core | Primary Keys | Auto-incrementing ID columns with proper constraints |
| β | Core | Foreign Keys | Automatic relationship detection with cascade/set null options |
| β | Core | Indexes | Single and multi-column indexes with unique constraints |
| β³ | Core | Check Constraints | Column-level validation rules (planned for Rails 6.1+ support) |
| β³ | Advanced | Views | Read-only virtual tables (scenic gem integration) |
| β³ | Advanced | Materialized Views | Cached query results (scenic gem integration) |
| β³ | Advanced | Functions/Stored Procedures | Database-level business logic (scenic, fx gems) |
| β | Advanced | Exclusion Constraints | PostgreSQL-specific constraints |
| β | Advanced | Enums | Type-safe enumerated values (Rails native with PostgreSQL) |
| β | Advanced | Partitioned Tables | Large table performance optimization (Rails 6.1+ with PostgreSQL) |
| β | Advanced | Triggers | Event-driven database actions (hair_trigger gem integration) |
| β | Advanced | Full-text Search Indexes | Advanced search capabilities (pg_search gem) |
ActiveRecord Schema Versions:
- β 8.0 - Fully supported (current)
- π§ 7.2 - Possible support
- π§ 7.1 - Possible support
- π§ 7.0 - Possible support
Priority is given to supporting newer Rails versions as they are released (8.1, 8.2, etc.) before adding backward compatibility.
Essence automatically recognizes these column patterns (works with both Atlas/SQL and Rails syntax):
| Pattern | Example | Result |
|---|---|---|
*_id |
user_id: ~ |
integer -> users.id on_delete=cascade not_null |
*_at |
published_at: ~ |
datetime not_null |
*_on |
published_on: ~ |
date not_null |
*_date |
birth_date: ~ |
date not_null |
is_* |
is_active: ~ |
boolean default=false not_null |
has_* |
has_premium: ~ |
boolean default=false not_null |
can_* |
can_edit: ~ |
boolean default=false not_null |
*_flag |
admin_flag: ~ |
boolean default=false not_null |
*_content |
body_content: ~ |
text |
*_body |
email_body: ~ |
text |
*_text |
description_text: ~ |
text |
*_html |
content_html: ~ |
text |
*_count |
view_count: ~ |
integer default=0 not_null |
*_score |
rating_score: ~ |
decimal(8,2) |
*_amount |
price_amount: ~ |
decimal(10,2) |
*_price |
unit_price: ~ |
decimal(10,2) |
*_email |
contact_email: ~ |
string(255) |
*_url |
website_url: ~ |
string(500) |
*_code |
product_code: ~ |
string(50) |
*_slug |
permalink_slug: ~ |
string(255) unique |
*_status |
order_status: ~ |
string(50) |
*_state |
workflow_state: ~ |
string(50) |
Define your own patterns in the schema:
column_patterns:
- "_uuid$": "uuid not_null unique"
- "_json$": "json"
- "encrypted_": "text not_null"Override defaults for specific tables:
defaults:
"*":
columns:
id: ~
created_at: ~
updated_at: ~
"audit_*":
columns:
id: uuid primary_key
created_at: ~
created_by: string(255) not_nullOverride pattern matching with explicit definitions:
tables:
users:
columns:
league_id: string(50) not_null # Override foreign key pattern
created_at: timestamp not_null # Override default# 1. Set up (first time)
rake essence:init # Import existing schema
# OR
rake essence:template # Start fresh
# 2. Edit schema
vim db/schema.yaml
# 3. Iterate rapidly
rake essence:compile # YAML β HCL
rake essence:preview # See changes
rake essence:generate[name] # Create migration
rake essence:apply # Apply to DB
# 4. Deploy
rake essence:deploy[name] # All-in-one# .github/workflows/schema.yml
name: Schema Validation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Validate schema
run: rake essence:validateessence template [path] # Generate template
essence compile [yaml] [hcl] # Compile formats
essence version # Show version
essence help # Show helprake essence:template[path] # Generate schema template
rake essence:compile[yaml,hcl] # Compile YAML to HCL
rake essence:preview # Preview changes
rake essence:generate[name] # Generate Rails migration
rake essence:apply # Apply schema to database
rake essence:deploy[name] # Full workflow
rake essence:seed # Generate seed data
rake essence:init # Initialize from existing
rake essence:validate # Validate schema
rake essence:history # Show migration history
rake essence:help # Show all commandsEssence works by:
- YAML Definition - You write clean, pattern-rich schemas
- Pattern Resolution - Automatic foreign keys, constraints, indexes
- HCL Generation - Compiles to Atlas-compatible HCL format
- Atlas Integration - Uses Atlas for migration planning
- Rails Compatibility - Generates standard Rails migrations
schema.yaml β [Essence] β schema.hcl β [Atlas] β migration.rb β [Rails] β Database
schema_name: public
tables:
users: # Atlas/SQL syntax
columns:
email: string(255) not_null unique
username: string(50) not_null unique
first_name: string(100)
last_name: string(100)
is_admin: ~
last_login_at: ~
posts: # Rails syntax
columns:
title: :string, limit: 255, null: false
post_slug: ~ # Pattern matching works with both
content_html: :text
user_id: ~ # Foreign key via pattern
published_at: :datetime
is_published: :boolean, default: false
view_count: :integer, default: 0comments: columns: post_id: ~ user_id: ~ comment_body: ~ is_approved: ~
### E-commerce Schema ###
```yaml
schema_name: public
tables:
customers:
columns:
email: string(255) not_null unique
first_name: string(100) not_null
last_name: string(100) not_null
phone: string(20)
birth_date: ~
is_active: ~
products:
columns:
name: string(255) not_null
product_code: ~
description_text: ~
unit_price: ~
cost_amount: ~
stock_count: ~
is_active: ~
orders:
columns:
customer_id: ~
order_status: ~
total_amount: ~
tax_amount: ~
shipping_amount: ~
order_date: ~
shipped_at: ~
rake essence:preview # See what will change
rake essence:apply # Apply changes# Good - descriptive and clear
users:
columns:
email: string(255) not_null unique
contact_email: string(255)
# Avoid - too generic
users:
columns:
data1: string
field2: text# Good - explicit constraints
email: string(255) not_null unique
user_id: integer -> users.id on_delete=cascade not_null
# Avoid - relying only on patterns
email: ~
user_id: ~Organize your YAML file with related tables together for better readability:
# Authentication & Users
users:
# ...
user_sessions:
# ...
# Content Management
posts:
# ...
comments:
# ...# Let patterns handle repetitive definitions
published_at: ~ # Becomes: datetime not_null
is_active: ~ # Becomes: boolean default=false not_null
view_count: ~ # Becomes: integer default=0 not_null
# Override patterns when needed
special_id: bigint -> special_table.id on_delete=set_null- Fork it (https://github.com/brandonzylstra/essence/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
The gem is available as open source under the terms of the MIT License.