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.

Revisions

  1. metaskills revised this gist May 9, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ def directory
    private

    def all_slugs
    @all_slugs ||= Dir.glob("#{directory}/*.md").map { |f| File.basename(f).sub(/\.md$/,'') }
    @all_slugs ||= Dir.glob("#{directory}/*.md").map { |f| File.basename(f,'.md') }
    end

    end
  2. metaskills revised this gist May 9, 2014. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions site.html.haml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    # app/views/layouts/site.html.haml

    .container
    = render partial: 'blog/header' if blog_post?
    ~ yield :layout
    = render partial: 'blog/call_to_action' if blog_post?
    = render partial: 'blog/comments' if blog_post? && Rails.env.production?
  3. metaskills revised this gist May 9, 2014. 1 changed file with 9 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions blog_helper.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    # app/helpers/blog_helper.rb

    module BlogHelper

    def blog_post?
    controller_name == 'blog' && action_name == 'show'
    end

    end
  4. metaskills revised this gist Apr 21, 2014. 1 changed file with 61 additions and 0 deletions.
    61 changes: 61 additions & 0 deletions highlight.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    .highlight .hll { background-color: #ffffcc }
    .highlight .c { color: #999988; font-style: italic } /* Comment */
    .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
    .highlight .k { color: #000000; font-weight: bold } /* Keyword */
    .highlight .o { color: #000000; font-weight: bold } /* Operator */
    .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
    .highlight .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */
    .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
    .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
    .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
    .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
    .highlight .gr { color: #aa0000 } /* Generic.Error */
    .highlight .gh { color: #999999 } /* Generic.Heading */
    .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
    .highlight .go { color: #888888 } /* Generic.Output */
    .highlight .gp { color: #555555 } /* Generic.Prompt */
    .highlight .gs { font-weight: bold } /* Generic.Strong */
    .highlight .gu { color: #aaaaaa } /* Generic.Subheading */
    .highlight .gt { color: #aa0000 } /* Generic.Traceback */
    .highlight .kc { color: #000000; font-weight: bold } /* Keyword.Constant */
    .highlight .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
    .highlight .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */
    .highlight .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
    .highlight .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
    .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
    .highlight .m { color: #009999 } /* Literal.Number */
    .highlight .s { color: #d01040 } /* Literal.String */
    .highlight .na { color: #008080 } /* Name.Attribute */
    .highlight .nb { color: #0086B3 } /* Name.Builtin */
    .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
    .highlight .no { color: #008080 } /* Name.Constant */
    .highlight .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */
    .highlight .ni { color: #800080 } /* Name.Entity */
    .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
    .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
    .highlight .nl { color: #990000; font-weight: bold } /* Name.Label */
    .highlight .nn { color: #555555 } /* Name.Namespace */
    .highlight .nt { color: #000080 } /* Name.Tag */
    .highlight .nv { color: #008080 } /* Name.Variable */
    .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */
    .highlight .w { color: #bbbbbb } /* Text.Whitespace */
    .highlight .mf { color: #009999 } /* Literal.Number.Float */
    .highlight .mh { color: #009999 } /* Literal.Number.Hex */
    .highlight .mi { color: #009999 } /* Literal.Number.Integer */
    .highlight .mo { color: #009999 } /* Literal.Number.Oct */
    .highlight .sb { color: #d01040 } /* Literal.String.Backtick */
    .highlight .sc { color: #d01040 } /* Literal.String.Char */
    .highlight .sd { color: #d01040 } /* Literal.String.Doc */
    .highlight .s2 { color: #d01040 } /* Literal.String.Double */
    .highlight .se { color: #d01040 } /* Literal.String.Escape */
    .highlight .sh { color: #d01040 } /* Literal.String.Heredoc */
    .highlight .si { color: #d01040 } /* Literal.String.Interpol */
    .highlight .sx { color: #d01040 } /* Literal.String.Other */
    .highlight .sr { color: #009926 } /* Literal.String.Regex */
    .highlight .s1 { color: #d01040 } /* Literal.String.Single */
    .highlight .ss { color: #990073 } /* Literal.String.Symbol */
    .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
    .highlight .vc { color: #008080 } /* Name.Variable.Class */
    .highlight .vg { color: #008080 } /* Name.Variable.Global */
    .highlight .vi { color: #008080 } /* Name.Variable.Instance */
    .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
  5. metaskills revised this gist Apr 19, 2014. 1 changed file with 0 additions and 5 deletions.
    5 changes: 0 additions & 5 deletions blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -64,11 +64,6 @@ 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
  6. metaskills revised this gist Apr 19, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Gemfile
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    gem 'jekyll', '~> 2.0.0.alpha', github: 'jekyll/jekyll'
    gem 'jekyll', '~> 2.0.0.alpha'
    gem 'nokogiri'
    gem 'redcarpet'
    gem 'rouge'
  7. metaskills revised this gist Apr 19, 2014. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -35,10 +35,7 @@ def initialize(slug)
    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
    @data['title'] || slug.sub(/\d{4}-\d{2}-\d{2}-/, '').titleize
    end

    def date
  8. metaskills revised this gist Apr 19, 2014. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -63,10 +63,6 @@ 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
  9. metaskills revised this gist Apr 19, 2014. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -16,10 +16,6 @@ def find(slug)
    all.detect { |post| post.slug == slug }
    end

    def first
    all.first
    end

    def directory
    Rails.root.join 'app', 'views', 'blog'
    end
  10. metaskills revised this gist Apr 19, 2014. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -20,10 +20,6 @@ def first
    all.first
    end

    def exists?(slug)
    all_slugs.include?(slug)
    end

    def directory
    Rails.root.join 'app', 'views', 'blog'
    end
  11. metaskills revised this gist Apr 19, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion blog_controller.rb
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,8 @@
    # In app/controllers/blog_controller.rb

    class BlogController < ApplicationController


    layout 'site'
    before_filter :find_post, only: [:show]

    def index
  12. metaskills revised this gist Apr 19, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion blog_controller.rb
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@ def show

    def find_post
    @post = BlogPost.find params[:id]
    redirect_to root_path unless @post
    redirect_to blog_index_path unless @post
    end

    end
  13. metaskills created this gist Apr 19, 2014.
    4 changes: 4 additions & 0 deletions Gemfile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,4 @@
    gem 'jekyll', '~> 2.0.0.alpha', github: 'jekyll/jekyll'
    gem 'nokogiri'
    gem 'redcarpet'
    gem 'rouge'
    18 changes: 18 additions & 0 deletions blog.rake
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    # 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
    25 changes: 25 additions & 0 deletions blog_controller.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    # In app/controllers/blog_controller.rb

    class BlogController < ApplicationController

    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 root_path unless @post
    end

    end
    141 changes: 141 additions & 0 deletions blog_post.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,141 @@
    # 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
    3 changes: 3 additions & 0 deletions routes.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    # config/routes.rb

    resources :blog, only: [:index, :show]