Skip to Content
Course content

161: Building a Blog Engine with Rails

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When people hear the term "Blog Engine," they often imagine they need to build some complex, proprietary system for delivering content—almost like they're writing a mini-WordPress from scratch. You might feel like you need a specialized "engine" class or a complicated routing layer to handle the way posts are fetched and displayed. I'll let you in on a secret: in Rails, the "engine" isn't a specific piece of code you write; it's the synergy between your database schema and RESTful routing.

The "Engine" isn't a separate system; it's just CRUD with a strategy

If you're thinking you need a massive service object to "run" the blog, you're overcomplicating it. Most of what makes a blog function is simply the ability to Create, Read, Update, and Delete posts, combined with a few smart filters. Look at this common mistake: trying to create a BlogManager class to handle everything.

# DON'T DO THIS
class BlogManager
  def self.get_latest_posts
    Post.where(published: true).order(created_at: :desc)
  end
  # ... more methods that just wrap ActiveRecord
end

This is just an unnecessary wrapper. Rails is designed so that your PostsController and your Post model handle this directly. The "engine" is actually just the Rails MVC pattern applied to a specific set of data. Instead of a manager class, you use scopes. It's cleaner, faster, and is the "Rails way."

Turning basic records into a content system with Slugs

A real blog doesn't use IDs in the URL—/posts/12 is ugly and terrible for SEO. You want /posts/building-a-rails-blog. To do this, we introduce a "slug." A slug is a URL-friendly version of the title.

I usually handle this by adding a slug column to the posts table. But don't just let the user type it in; automate it in the model using a callback. Here is how I'd set that up:

class Post < ApplicationRecord
  before_validation :generate_slug, on: :create

  scope :published, -> { where.not(published_at: nil).where('published_at <= ?', Time.current) }

  private

  def generate_slug
    self.slug = title.parameterize if title.present?
  end
end

Notice the published scope. This is where the "engine" logic actually lives. By using published_at (a datetime) instead of a simple boolean published (true/false), you gain the ability to schedule posts for the future. If the published_at date is tomorrow, the scope simply won't return it today. It's a small change that adds a huge amount of professional functionality.

Wiring the associations for a complete ecosystem

A blog isn't just a list of posts; it's a conversation. You need authors and comments. The magic here happens in the associations. You don't want to manually query for comments every time; you want them tied to the post's lifecycle.

I recommend a structure where a User has many Posts, and a Post has many Comments. To keep the "engine" efficient, use dependent: :destroy. If you delete a post, you don't want "orphan" comments floating around your database like ghosts.

class Post < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy
  
  validates :title, presence: true
  validates :body, length: { minimum: 10 }
end

class Comment < ApplicationRecord
  belongs_to :post
  belongs_to :user
  
  validates :content, presence: true
end

When you put this all together—the slug for the URL, the timestamp for scheduling, and the associations for the community—you haven't built a complex "engine" in the traditional sense. You've simply configured Rails to behave like one. The power isn't in writing more code; it's in using the framework's conventions to handle the heavy lifting.




📋 Practical Task

Implementing a Slug-based URL System for Blog Posts

Up until now, your blog has likely been accessing posts via their ID (e.g., /posts/1). Your task is to transition the application to use slugs for a more professional URL structure.

  • Step 1: Generate a migration to add a slug column (string) to the posts table. Make sure to add an index to the slug column, as you'll be querying it frequently.
  • Step 2: In the Post model, implement a before_validation callback that takes the title and uses the .parameterize method to populate the slug field before the record is saved.
  • Step 3: Update the PostsController#show action. Instead of finding the post by params[:id], update it to find the post by params[:id] (which will now contain the slug string).
  • Step 4: Update your posts_path links in the view to ensure they are passing the post.slug instead of the post.id.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.