Building advanced content management systems with Ruby on Rails requires careful planning and implementation of several key functionalities. I’ll share my experience developing these systems and provide practical techniques that have proven effective.
Content versioning forms the foundation of any robust CMS. I implement this using a Version model that tracks changes to content over time. Here’s how I structure it:
class Content < ApplicationRecord
has_many :versions
belongs_to :current_version, class_name: 'Version'
def create_version
versions.create!(
content: self.raw_content,
metadata: self.metadata,
version_number: next_version_number
)
end
def revert_to(version)
self.raw_content = version.content
self.metadata = version.metadata
self.current_version = version
save!
end
end
Publishing workflows ensure content goes through proper review and approval processes. I implement this using state machines:
class Content < ApplicationRecord
include AASM
aasm do
state :draft, initial: true
state :review, :approved, :published
event :submit do
transitions from: :draft, to: :review
after do
NotificationService.notify_reviewers(self)
end
end
event :approve do
transitions from: :review, to: :approved
after do
schedule_publication if scheduled_at
end
end
end
end
Dynamic template handling allows content administrators to create and modify layouts without developer intervention. I implement this using liquid templates:
class TemplateRenderer
def render(template, context)
Liquid::Template.parse(template).render(
context.to_liquid,
filters: [CustomFilters],
registers: { user: current_user }
)
end
end
class Content < ApplicationRecord
def to_liquid
{
'title' => title,
'body' => processed_body,
'author' => author.name,
'categories' => categories.map(&:name)
}
end
end
Content categorization helps organize and filter content effectively. I use a flexible taxonomy system:
class Category < ApplicationRecord
has_ancestry
has_many :categorizations
has_many :contents, through: :categorizations
def self.tree_for_select
arrangement = arrange(order: :name)
build_select_options(arrangement, 0)
end
private
def self.build_select_options(nodes, depth)
nodes.map do |node, children|
prefix = "—" * depth
["#{prefix} #{node.name}", node.id] +
build_select_options(children, depth + 1)
end.flatten(1)
end
end
Media management requires handling various file types, processing them efficiently, and serving them optimally. I use Active Storage with background processing:
class MediaProcessor
include Sidekiq::Worker
def perform(attachment_id)
attachment = MediaAttachment.find(attachment_id)
attachment.file.open do |file|
processor = processor_for(file.content_type)
processed = processor.process(file)
attachment.update!(
processed_file: processed,
status: :ready
)
end
end
private
def processor_for(content_type)
case content_type
when /^image/
ImageProcessor.new
when /^video/
VideoProcessor.new
else
DefaultProcessor.new
end
end
end
SEO optimization is crucial for content visibility. I implement this through metadata management and URL optimization:
class Content < ApplicationRecord
before_save :generate_slug
def meta_tags
{
title: seo_title.presence || title,
description: meta_description,
keywords: meta_keywords,
canonical: canonical_url,
og: open_graph_data
}
end
private
def generate_slug
self.slug = title.parameterize
end
def open_graph_data
{
title: og_title.presence || title,
description: og_description.presence || meta_description,
image: featured_image&.url
}
end
end
Content scheduling allows for automated publishing at specific times. I implement this using background jobs:
class PublishScheduler
include Sidekiq::Worker
def perform(content_id)
content = Content.find(content_id)
return if content.published?
Content.transaction do
content.publish!
notify_subscribers(content)
update_sitemap
end
end
private
def notify_subscribers(content)
content.subscribers.each do |subscriber|
SubscriberMailer.new_content(subscriber, content).deliver_later
end
end
def update_sitemap
SitemapGenerator.refresh
end
end
To ensure content security and access control, I implement a comprehensive authorization system:
class ContentPolicy
def initialize(user, content)
@user = user
@content = content
end
def show?
return true if content.published?
return false unless user
user.can_access?(content)
end
def edit?
return false unless user
user.can_edit?(content)
end
def publish?
return false unless user
user.has_role?(:editor) || user.has_role?(:admin)
end
private
attr_reader :user, :content
end
Caching is essential for performance in content-heavy applications:
class ContentCacheManager
def cache_content(content)
Rails.cache.write(
cache_key(content),
rendered_content(content),
expires_in: 12.hours
)
end
def invalidate_content(content)
Rails.cache.delete_matched("content/#{content.id}/*")
end
private
def cache_key(content)
"content/#{content.id}/#{content.updated_at.to_i}"
end
def rendered_content(content)
ContentRenderer.new(content).render
end
end
Testing these components thoroughly ensures reliability:
RSpec.describe ContentManager do
describe '#create_content' do
let(:params) { valid_content_params }
it 'creates content with initial version' do
content = described_class.new.create_content(params)
expect(content).to be_persisted
expect(content.versions.count).to eq(1)
expect(content.current_version).to eq(content.versions.first)
end
context 'with scheduled publication' do
let(:params) { valid_content_params.merge(scheduled_at: 1.day.from_now) }
it 'schedules publication job' do
expect {
described_class.new.create_content(params)
}.to change(PublishWorker.jobs, :size).by(1)
end
end
end
end
These techniques provide a solid foundation for building advanced content management systems in Ruby on Rails. The key is to maintain clean, modular code while providing powerful features that content administrators need.
Remember to regularly update dependencies, monitor performance metrics, and gather user feedback to continuously improve the system. Regular security audits and performance optimization should be part of the maintenance routine.