A lightweight Rails Engine that enables any ActiveRecord model to use predefined templates for content management. Built with a simple one-to-many relationship, allowing each model instance to have one template while templates can be reused across multiple instances.
- 🎯 Simple 1:N Relationship - Each record has one template, templates can be reused
- 📝 Multiple Content Formats - Support for HTML, Markdown, and plain text
- 🏷️ Flexible Categories - Pre-defined categories (feature_request, bug_report, etc.) or custom
- 🔗 Direct Foreign Key - No join table needed, cleaner database schema
- 🚀 Easy Integration - Simple concern-based inclusion
Add this line to your application's Gemfile:
gem "rails_templatable"And then execute:
$ bundle install
$ bin/rails railties:install:migrations FROM=rails_templatable
$ bin/rails db:migrateFor each model that will use templates, add a foreign key:
add_reference :posts, :template,
foreign_key: { to_table: :rails_templatable_templates },
index: falseclass Post < ApplicationRecord
include RailsTemplatable::HasTemplate
end
class WorkLog < ApplicationRecord
include RailsTemplatable::HasTemplate
end# Feature request template
feature_template = RailsTemplatable::Template.create!(
category: "feature_request",
content: "# Feature Request\n\n## Description\n\n## Acceptance Criteria",
content_format: :markdown
)
# Bug report template
bug_template = RailsTemplatable::Template.create!(
category: "bug_report",
content: "## Bug Description\n\n## Steps to Reproduce\n\n## Expected Behavior",
content_format: :markdown
)post = Post.create!(
title: "Add user authentication",
content: "Implement OAuth2 login",
template: feature_template
)
# View template
post.template.category # => "feature_request"
post.template.content # => "# Feature Request..."Common template categories included:
| Category | Description |
|---|---|
feature_request |
Feature requests |
bug_report |
Bug reports |
tech_improvement |
Technical improvements |
meeting_note |
Meeting notes |
api_design |
API designs |
You can create any custom category as needed.
One-to-Many (1:N)
- One Post/WorkLog instance → One Template
- One Template → Many instances
Example:
Post 1 → Template A (feature_request)
Post 2 → Template B (bug_report)
Post 3 → Template A (feature_request) # Reusable
Three content formats are supported:
html(0) - HTML contentmarkdown(1) - Markdown contenttxt(2) - Plain text content (default)
RailsTemplatable::Template.create!(
category: "notification",
content: "Simple text notification",
content_format: :txt
)# Get a record's template
post.template
# Query by category
Post.joins(:template).where(rails_templatable_templates: { category: 'feature_request' })
# Get all records using a specific template
Post.where(template: feature_template)
# Query by content format
Post.joins(:template).where(rails_templatable_templates: { content_format: 1 })| Column | Type | Description |
|---|---|---|
category |
string | Template category (user-defined) |
content |
text | Template content |
content_format |
integer | Content format (0: html, 1: markdown, 2: txt) |
created_at |
datetime | Creation timestamp |
updated_at |
datetime | Update timestamp |
Add template_id foreign key:
| Column | Type | Description |
|---|---|---|
template_id |
integer | Foreign key to rails_templatable_templates |
# Assigning a new template replaces the old one
post.update(template: bug_template)
post.template.category # => "bug_report"post.update(template: nil)
post.template # => nilpost = Post.create(title: "New post")
post.update(template: feature_template)class AddTemplateToPosts < ActiveRecord::Migration[6.0]
def change
add_reference :posts, :template,
foreign_key: { to_table: :rails_templatable_templates },
index: false
end
endcd test/dummy
ruby test_templatable.rb- Direct Foreign Key - No join table needed
- Simple & Clean - One record = One template
- Flexible - Templates can be changed anytime
- Efficient Queries - Direct JOIN without intermediate table
If you need "one record has multiple templates", consider using a tag system or polymorphic many-to-many associations instead.
We use category instead of type to avoid conflicts with Rails' Single Table Inheritance (STI) feature, which reserves the type column for storing class names.
- Template variable interpolation
- Template versioning
- Template inheritance
- Template preview functionality
- I18n multi-language support
Contributions are welcome! Please feel free to submit a Pull Request.
The gem is available as open source under the terms of the MIT License.
Built with inspiration from rails_badgeable.