add comments and refactor out common page/post methods into Convertible module

This commit is contained in:
Tom Preston-Werner 2008-11-05 17:49:22 -08:00
parent d8bdb6d366
commit e4d07dc547
6 changed files with 135 additions and 35 deletions

View File

@ -15,8 +15,9 @@ require 'redcloth'
# internal requires # internal requires
require 'autoblog/site' require 'autoblog/site'
require 'autoblog/post' require 'autoblog/convertible'
require 'autoblog/page' require 'autoblog/page'
require 'autoblog/post'
require 'autoblog/filters' require 'autoblog/filters'
module AutoBlog module AutoBlog

View File

@ -0,0 +1,28 @@
module AutoBlog
module Convertible
# Read the YAML frontmatter
# +base+ is the String path to the dir containing the file
# +name+ is the String filename of the file
#
# Returns nothing
def read_yaml(base, name)
self.content = File.read(File.join(base, name))
if self.content =~ /^(---\n.*?)\n---\n/
self.content = self.content[($1.size + 5)..-1]
self.data = YAML.load($1)
end
end
# Transform the contents based on the file extension.
#
# Returns nothing
def transform
if self.ext == ".textile"
self.ext = ".html"
self.content = RedCloth.new(self.content).to_html
end
end
end
end

View File

@ -1,9 +1,17 @@
module AutoBlog module AutoBlog
class Page class Page
include Convertible
attr_accessor :ext attr_accessor :ext
attr_accessor :data, :content attr_accessor :data, :content
# Initialize a new Page.
# +base+ is the String path to the <source>
# +dir+ is the String path between <source> and the file
# +name+ is the String filename of the post file
#
# Returns <Page>
def initialize(base, dir, name) def initialize(base, dir, name)
@base = base @base = base
@dir = dir @dir = dir
@ -12,36 +20,31 @@ module AutoBlog
self.data = {} self.data = {}
self.process(name) self.process(name)
self.read_yaml(base, dir, name) self.read_yaml(File.join(base, dir), name)
self.set_defaults self.set_defaults
self.transform self.transform
end end
# Extract information from the post filename
# +name+ is the String filename of the post file
#
# Returns nothing
def process(name) def process(name)
self.ext = File.extname(name) self.ext = File.extname(name)
end end
def read_yaml(base, dir, name) # Set the data defaults.
self.content = File.read(File.join(base, dir, name)) #
# Returns nothing
if self.content =~ /^(---\n.*?)\n---\n/
self.content = self.content[($1.size + 5)..-1]
self.data = YAML.load($1)
end
end
def set_defaults def set_defaults
self.data["layout"] ||= "default" self.data["layout"] ||= "default"
end end
def transform # Add any necessary layouts to this post
if self.ext == ".textile" # +layouts+ is a Hash of {"name" => "layout"}
self.ext = ".html" # +site_payload+ is the site payload hash
self.content = RedCloth.new(self.content).to_html #
end # Returns nothing
end
def add_layout(layouts, site_payload) def add_layout(layouts, site_payload)
payload = {"page" => self.data}.merge(site_payload) payload = {"page" => self.data}.merge(site_payload)
self.content = Liquid::Template.parse(self.content).render(payload, [AutoBlog::Filters]) self.content = Liquid::Template.parse(self.content).render(payload, [AutoBlog::Filters])
@ -52,6 +55,10 @@ module AutoBlog
self.content = Liquid::Template.parse(layout).render(payload, [AutoBlog::Filters]) self.content = Liquid::Template.parse(layout).render(payload, [AutoBlog::Filters])
end end
# Write the generated page file to the destination directory.
# +dest+ is the String path to the destination dir
#
# Returns nothing
def write(dest) def write(dest)
FileUtils.mkdir_p(File.join(dest, @dir)) FileUtils.mkdir_p(File.join(dest, @dir))

View File

@ -2,9 +2,14 @@ module AutoBlog
class Post class Post
include Comparable include Comparable
include Convertible
MATCHER = /^(\d+-\d+-\d+)-(.*)\.([^.]+)$/ MATCHER = /^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
# Post name validator. Post filenames must be like:
# 2008-11-05-my-awesome-post.textile
#
# Returns <Bool>
def self.valid?(name) def self.valid?(name)
name =~ MATCHER name =~ MATCHER
end end
@ -12,6 +17,11 @@ module AutoBlog
attr_accessor :date, :slug, :ext attr_accessor :date, :slug, :ext
attr_accessor :data, :content, :output attr_accessor :data, :content, :output
# Initialize this Post instance.
# +base+ is the String path to the dir containing the post file
# +name+ is the String filename of the post file
#
# Returns <Post>
def initialize(base, name) def initialize(base, name)
@base = base @base = base
@name = name @name = name
@ -22,10 +32,17 @@ module AutoBlog
self.transform self.transform
end end
# Spaceship is based on Post#date
#
# Returns -1, 0, 1
def <=>(other) def <=>(other)
self.date <=> other.date self.date <=> other.date
end end
# Extract information from the post filename
# +name+ is the String filename of the post file
#
# Returns nothing
def process(name) def process(name)
m, date, slug, ext = *name.match(MATCHER) m, date, slug, ext = *name.match(MATCHER)
self.date = Time.parse(date) self.date = Time.parse(date)
@ -33,40 +50,49 @@ module AutoBlog
self.ext = ext self.ext = ext
end end
# The generated directory into which the post will be placed
# upon generation. e.g. "/2008/11/05/"
#
# Returns <String>
def dir def dir
self.date.strftime("/%Y/%m/%d/") self.date.strftime("/%Y/%m/%d/")
end end
# The generated relative url of this post
# e.g. /2008/11/05/my-awesome-post.html
#
# Returns <String>
def url def url
self.dir + self.slug + ".html" self.dir + self.slug + ".html"
end end
# The UID for this post (useful in feeds)
# e.g. /2008/11/05/my-awesome-post
#
# Returns <String>
def id def id
self.dir + self.slug self.dir + self.slug
end end
def read_yaml(base, name) # Set the data defaults.
self.content = File.read(File.join(base, name)) #
# Returns nothing
if self.content =~ /^(---\n.*?)\n---\n/
self.content = self.content[($1.size + 5)..-1]
self.data = YAML.load($1)
end
end
def set_defaults def set_defaults
self.data["layout"] ||= "default" self.data["layout"] ||= "default"
end end
def transform # Calculate related posts.
self.content = RedCloth.new(self.content).to_html #
end # Returns [<Post>]
def related_posts(posts) def related_posts(posts)
related = posts - [self] related = posts - [self]
end end
# Add any necessary layouts to this post
# +layouts+ is a Hash of {"name" => "layout"}
# +site_payload+ is the site payload hash
#
# Returns nothing
def add_layout(layouts, site_payload) def add_layout(layouts, site_payload)
related = related_posts(site_payload["site"]["posts"]) related = related_posts(site_payload["site"]["posts"])
@ -79,6 +105,10 @@ module AutoBlog
self.output = Liquid::Template.parse(layout).render(payload, [AutoBlog::Filters]) self.output = Liquid::Template.parse(layout).render(payload, [AutoBlog::Filters])
end end
# Write the generated post file to the destination directory.
# +dest+ is the String path to the destination dir
#
# Returns nothing
def write(dest) def write(dest)
FileUtils.mkdir_p(File.join(dest, self.dir)) FileUtils.mkdir_p(File.join(dest, self.dir))
@ -88,6 +118,9 @@ module AutoBlog
end end
end end
# Convert this post into a Hash for use in Liquid templates.
#
# Returns <Hash>
def to_liquid def to_liquid
{ "title" => self.data["title"] || "", { "title" => self.data["title"] || "",
"url" => self.url, "url" => self.url,

View File

@ -4,6 +4,13 @@ module AutoBlog
attr_accessor :source, :dest attr_accessor :source, :dest
attr_accessor :layouts, :posts attr_accessor :layouts, :posts
# Initialize the site
# +source+ is String path to the source directory containing
# the proto-site
# +dest+ is the String path to the directory where the generated
# site should be written
#
# Returns <Site>
def initialize(source, dest) def initialize(source, dest)
self.source = source self.source = source
self.dest = dest self.dest = dest
@ -11,6 +18,10 @@ module AutoBlog
self.posts = [] self.posts = []
end end
# Do the actual work of processing the site and generating the
# real deal.
#
# Returns nothing
def process def process
self.read_layouts self.read_layouts
self.read_posts self.read_posts
@ -18,6 +29,10 @@ module AutoBlog
self.transform_pages self.transform_pages
end end
# Read all the files in <source>/_layouts into memory for
# later use.
#
# Returns nothing
def read_layouts def read_layouts
base = File.join(self.source, "_layouts") base = File.join(self.source, "_layouts")
entries = Dir.entries(base) entries = Dir.entries(base)
@ -31,6 +46,10 @@ module AutoBlog
# ignore missing layout dir # ignore missing layout dir
end end
# Read all the files in <source>/posts and create a new Post
# object with each one.
#
# Returns nothing
def read_posts def read_posts
base = File.join(self.source, "posts") base = File.join(self.source, "posts")
entries = Dir.entries(base) entries = Dir.entries(base)
@ -45,6 +64,9 @@ module AutoBlog
# ignore missing layout dir # ignore missing layout dir
end end
# Write each post to <dest>/<year>/<month>/<day>/<slug>
#
# Returns nothing
def write_posts def write_posts
self.posts.each do |post| self.posts.each do |post|
post.add_layout(self.layouts, site_payload) post.add_layout(self.layouts, site_payload)
@ -52,6 +74,11 @@ module AutoBlog
end end
end end
# Recursively transform and write all non-post pages to <dest>/
# +dir+ is the String path part representing the path from
# <source> to the currently processing dir (default '')
#
# Returns nothing
def transform_pages(dir = '') def transform_pages(dir = '')
base = File.join(self.source, dir) base = File.join(self.source, dir)
entries = Dir.entries(base) entries = Dir.entries(base)
@ -74,6 +101,9 @@ module AutoBlog
end end
end end
# The Hash payload containing site-wide data
#
# Returns {"site" => {"time" => <Time>, "posts" => [<Post>]}}
def site_payload def site_payload
{"site" => {"time" => Time.now, "posts" => self.posts.sort.reverse}} {"site" => {"time" => Time.now, "posts" => self.posts.sort.reverse}}
end end

View File

@ -16,7 +16,7 @@ class TestPost < Test::Unit::TestCase
assert_equal Time.parse("2008-10-19"), p.date assert_equal Time.parse("2008-10-19"), p.date
assert_equal "foo-bar", p.slug assert_equal "foo-bar", p.slug
assert_equal "textile", p.ext assert_equal ".textile", p.ext
end end
def test_url def test_url
@ -36,6 +36,7 @@ class TestPost < Test::Unit::TestCase
def test_transform def test_transform
p = Post.allocate p = Post.allocate
p.process("2008-10-18-foo-bar.textile")
p.read_yaml(File.join(File.dirname(__FILE__), *%w[source posts]), "2008-10-18-foo-bar.textile") p.read_yaml(File.join(File.dirname(__FILE__), *%w[source posts]), "2008-10-18-foo-bar.textile")
p.transform p.transform