Skip to content

Instantly share code, notes, and snippets.

@metaskills
Last active October 18, 2025 19:40
Show Gist options
  • Select an option

  • Save metaskills/11071934 to your computer and use it in GitHub Desktop.

Select an option

Save metaskills/11071934 to your computer and use it in GitHub Desktop.
# In lib/tasks/blog.rake
namespace :blog do
desc "Generate a new blog post with a title. Ex: rake blog:post['My New Post']"
task :post, [:title] => [:environment] do |t, args|
date = Time.now.strftime("%G-%m-%d")
title = args[:title].to_s.strip
if title.blank?
fail 'Please supply a title.'
else
slug = title.split(' ').map{ |s| s.downcase }.join('-')
file = Rails.root.join 'app', 'views', 'blog', "#{date}-#{slug}.md"
File.open(file,'w') { |f| f.write("---\ntitle: #{title}\n---\n\n\n") }
end
end
end
# In app/controllers/blog_controller.rb
class BlogController < ApplicationController
layout 'site'
before_filter :find_post, only: [:show]
def index
@posts = BlogPost.all
end
def show
if stale?(@post, public: true)
render text: @post.html, layout: 'site'
end
end
private
def find_post
@post = BlogPost.find params[:id]
redirect_to blog_index_path unless @post
end
end
# In app/models/blog_post.rb
class BlogPost
include ActionView::Helpers::TextHelper
attr_reader :slug
class << self
def all
all_slugs.map{ |slug| new(slug) }.sort
end
def find(slug)
all.detect { |post| post.slug == slug }
end
def first
all.first
end
def exists?(slug)
all_slugs.include?(slug)
end
def directory
Rails.root.join 'app', 'views', 'blog'
end
private
def all_slugs
@all_slugs ||= Dir.glob("#{directory}/*.md").map { |f| File.basename(f).sub(/\.md$/,'') }
end
end
def initialize(slug)
@slug = slug
@data = {}
parse_file
end
def title
@data['title'] || slug.sub(/\d{4}-\d{2}-\d{2}-/, '').tap do |t|
t.replace t.titleize
t.sub! /Homemarks/, 'HomeMarks'
end
end
def date
Date.parse(slug)
end
def date_formatted
day_format = ActiveSupport::Inflector.ordinalize(date.day)
date.strftime "%B #{day_format}, %G"
end
def html
Rails.cache.fetch("#{cache_key}/html") { to_html }
end
def excerpt
first_ptag = Nokogiri::HTML(html).css('p:first').text.squish
truncate strip_tags(first_ptag), length: 200, separator: ' '
end
def path
"/blog/#{slug}"
end
def permalink
"/blog/#{slug}"
end
def inspect
"<#{self.class.name} date: #{date.iso8601}, title: #{title.inspect}, slug: #{slug.inspect}>"
end
def == other
self.inspect == other.inspect
end
alias :eql? :==
def <=> other
other.date <=> date
end
def updated_at
File.mtime(file_path)
end
def cache_key
ActiveSupport::Cache.expand_cache_key ['blog', slug, updated_at.to_i]
end
private
def parse_file
@markdown = Tilt::ErubisTemplate.new do
fdata = file_data
if fdata =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
@data = SafeYAML.load($1)
$POSTMATCH
else
fdata
end
end.render(scope, post: self)
end
def file_path
self.class.directory.join "#{slug}.md"
end
def file_data
Rails.cache.fetch("#{cache_key}/markdown") { File.read(file_path) }
end
def markdown
@markdown
end
def to_html
Jekyll::Converters::Markdown::RedcarpetParser.new({
'highlighter' => 'rouge',
'redcarpet' => {
'extensions' => ["no_intra_emphasis", "fenced_code_blocks", "autolink", "strikethrough", "lax_spacing", "superscript", "with_toc_data"]
}
}).convert(markdown)
end
def scope
ApplicationController.helpers.clone.tap do |h|
h.singleton_class.send :include, Rails.application.routes.url_helpers
end
end
end
gem 'jekyll', '~> 2.0.0.alpha', github: 'jekyll/jekyll'
gem 'nokogiri'
gem 'redcarpet'
gem 'rouge'
# config/routes.rb
resources :blog, only: [:index, :show]
@felixbuenemann
Copy link

File.basename has a second param to strip the file ending, no need for sub.

@jnillo
Copy link

jnillo commented Apr 24, 2014

In blog_post model:

attr_reader :slug, :data

@acandael
Copy link

I'm getting an error when I go to http://localhost:3000/blog

Missing template blog/index, application/index with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in: * "/Users/acandael/Sites/mysite/app/views"

in my views/blog folder I created an .md file called '2014-04-24-my-first-post.md'

@acandael
Copy link

what does the directive layout 'site' in blog_controller.rb mean?

@acandael
Copy link

ok, when I go to http://localhost:3000/blog/2014-04-24.md I got the blogpost rendering. The only issue I have now, is that no css is rendered. When I look in the page source, I see that the page source is missing the header section.

@metaskills
Copy link
Author

Added examples of a BlogHelper and a site layout to show how I got headers, comments, etc in too.

@metaskills
Copy link
Author

what does the directive layout 'site' in blog_controller.rb mean?

@acandael That is just my example layout. Your's may be "application".

@metaskills
Copy link
Author

@equivalentideas
Copy link

Thanks for this @metaskills . Could you list a few of the other dependencies in the gemfile, such as Rails and Ruby versions?

@michaelkeenan
Copy link

Beware that this gist uses %G for the year when formatting dates, but %G is a weird thing that displays the week year, not the current year, so it'll give the wrong result for a few days each year (as described here). (What's the week year? It's the year that most of the current week is in. If it's Monday on December 31, you're in a a week that is mostly in the next year, so it'll return the next year, not the current year.) It's a strange, hard-to-detect bug. The solution is to use %Y rather than %G.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment