fix categories and add topics

This commit is contained in:
Tom Preston-Werner 2008-12-24 15:24:27 -08:00
parent 6de22b318c
commit dad9a31559
6 changed files with 52 additions and 33 deletions

View File

@ -3,6 +3,7 @@
* Added --server option to start a simple WEBrick server on destination directory [github.com/johnreilly and github.com/mchung] * Added --server option to start a simple WEBrick server on destination directory [github.com/johnreilly and github.com/mchung]
* Minor Enhancements * Minor Enhancements
* Added post categories based on directories containing _posts [github.com/mreid] * Added post categories based on directories containing _posts [github.com/mreid]
* Added post topics based on directories underneath _posts
* Added new date filter that shows the full month name [github.com/mreid] * Added new date filter that shows the full month name [github.com/mreid]
* Merge Post's YAML front matter into its to_liquid payload [github.com/remi] * Merge Post's YAML front matter into its to_liquid payload [github.com/remi]
* Restrict includes to regular files underneath _includes * Restrict includes to regular files underneath _includes

View File

@ -53,12 +53,11 @@ filename is used to construct the URL in the generated site. The example post,
for instance, ends up at for instance, ends up at
<code>http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html</code>. <code>http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html</code>.
Categories for posts are derived from the directory structure the posts were Categories for posts are derived from the directory structure the posts were
found within. found within. A post that appears in the directory foo/bar/_posts is placed in
A post that appears in the directory foo/bar/_posts is placed in the categories the categories 'foo' and 'bar'. By selecting posts from particular categories
'foo' and 'bar'. in your Liquid templates, you will be able to host multiple blogs within a
By selecting posts from particular categories in your Liquid templates, you will site.
be able to host multiple blogs within a site.
Files that do not reside in directories prefixed with an underscore are Files that do not reside in directories prefixed with an underscore are
mirrored into a corresponding directory structure in the generated site. If a mirrored into a corresponding directory structure in the generated site. If a
@ -182,7 +181,7 @@ h3. Site
--lsi (latent semantic indexing) option. --lsi (latent semantic indexing) option.
site.categories.CATEGORY site.categories.CATEGORY
The list of all posts in category CATEGORY. The list of all Posts in category CATEGORY.
h3. Post h3. Post
@ -200,12 +199,21 @@ h3. Post
An identifier unique to the Post (useful in RSS feeds). An identifier unique to the Post (useful in RSS feeds).
e.g. /2008/12/14/my-post e.g. /2008/12/14/my-post
post.categories
The list of categories to which this post belongs. Categories are
derived from the directory structure above the _posts directory. For
example, a post at /work/code/_posts/2008-12-24-closures.textile
would have this field set to ['work', 'code'].
post.topics
The list of topics for this Post. Topics are derived from the directory
structure beneath the _posts directory. For example, a post at
/_posts/music/metal/2008-12-24-metalocalypse.textile would have this field
set to ['music', 'metal'].
post.content post.content
The content of the Post. The content of the Post.
post.categories
The list of categories to which this post belongs.
h2. YAML Front Matter h2. YAML Front Matter
Any files that contain a YAML front matter block will be processed by Jekyll Any files that contain a YAML front matter block will be processed by Jekyll

View File

@ -8,7 +8,7 @@ module Jekyll
attr_accessor :lsi attr_accessor :lsi
end end
MATCHER = /^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/ MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
# Post name validator. Post filenames must be like: # Post name validator. Post filenames must be like:
# 2008-11-05-my-awesome-post.textile # 2008-11-05-my-awesome-post.textile
@ -18,7 +18,7 @@ module Jekyll
name =~ MATCHER name =~ MATCHER
end end
attr_accessor :date, :slug, :ext, :categories attr_accessor :date, :slug, :ext, :categories, :topics
attr_accessor :data, :content, :output attr_accessor :data, :content, :output
# Initialize this Post instance. # Initialize this Post instance.
@ -27,15 +27,17 @@ module Jekyll
# +categories+ is an Array of Strings for the categories for this post # +categories+ is an Array of Strings for the categories for this post
# #
# Returns <Post> # Returns <Post>
def initialize(base, name) def initialize(source, dir, name)
@base = base @base = File.join(source, dir, '_posts')
@name = name @name = name
@categories = base.split('/').reject { |p| ['.', '_posts'].include? p }
self.categories = dir.split('/').reject { |x| x.empty? }
parts = name.split('/')
self.topics = parts.size > 1 ? parts[0..-2] : []
self.process(name) self.process(name)
self.read_yaml(base, name) self.read_yaml(@base, name)
#Removed to avoid munging of liquid tags, replaced in convertible.rb#48
#self.transform
end end
# Spaceship is based on Post#date # Spaceship is based on Post#date
@ -50,7 +52,7 @@ module Jekyll
# #
# Returns nothing # Returns nothing
def process(name) def process(name)
m, date, slug, ext = *name.match(MATCHER) m, cats, date, slug, ext = *name.match(MATCHER)
self.date = Time.parse(date) self.date = Time.parse(date)
self.slug = slug self.slug = slug
self.ext = ext self.ext = ext
@ -63,11 +65,11 @@ module Jekyll
# #
# Returns <String> # Returns <String>
def dir def dir
path = (@categories && !@categories.empty?) ? '/' + @categories.join('/') : ''
if permalink if permalink
permalink.to_s.split("/")[0..-2].join("/") permalink.to_s.split("/")[0..-2].join("/")
else else
"#{path}" + date.strftime("/%Y/%m/%d/") prefix = self.categories.empty? ? '' : '/' + self.categories.join('/')
prefix + date.strftime("/%Y/%m/%d/")
end end
end end
@ -156,6 +158,7 @@ module Jekyll
"url" => self.url, "url" => self.url,
"date" => self.date, "date" => self.date,
"id" => self.id, "id" => self.id,
"topics" => self.topics,
"content" => self.content }.merge(self.data) "content" => self.content }.merge(self.data)
end end
end end

View File

@ -49,14 +49,17 @@ module Jekyll
# object with each one. # object with each one.
# #
# Returns nothing # Returns nothing
def read_posts(base) def read_posts(dir)
entries = Dir.entries(base) base = File.join(self.source, dir, '_posts')
entries = []
Dir.chdir(base) { entries = Dir['**/*'] }
entries = entries.reject { |e| File.directory?(File.join(base, e)) } entries = entries.reject { |e| File.directory?(File.join(base, e)) }
# first pass processes, but does not yet render post content # first pass processes, but does not yet render post content
entries.each do |f| entries.each do |f|
if Post.valid?(f) if Post.valid?(f)
post = Post.new(base, f) post = Post.new(self.source, dir, f)
self.posts << post self.posts << post
end end
end end
@ -98,7 +101,7 @@ module Jekyll
# might not be available yet to other templates as {{ site.posts }} # might not be available yet to other templates as {{ site.posts }}
if entries.include?('_posts') if entries.include?('_posts')
entries.delete('_posts') entries.delete('_posts')
read_posts(File.join(base, '_posts')) read_posts(dir)
end end
entries.each do |f| entries.each do |f|
@ -128,9 +131,9 @@ module Jekyll
def site_payload def site_payload
# Build the category hash map of category ( names => arrays of posts ) # Build the category hash map of category ( names => arrays of posts )
# then sort each array in reverse order # then sort each array in reverse order
categories = Hash.new { |hash,key| hash[key] = Array.new } categories = Hash.new { |hash, key| hash[key] = Array.new }
self.posts.each { |p| p.categories.each { |c| categories[c] << p } } self.posts.each { |p| p.categories.each { |c| categories[c] << p } }
categories.values.map { |cats| cats.sort! { |a,b| b <=> a} } categories.values.map { |cats| cats.sort! { |a, b| b <=> a} }
{"site" => { {"site" => {
"time" => Time.now, "time" => Time.now,

View File

@ -7,6 +7,9 @@ class TestPost < Test::Unit::TestCase
def test_valid def test_valid
assert Post.valid?("2008-10-19-foo-bar.textile") assert Post.valid?("2008-10-19-foo-bar.textile")
assert Post.valid?("foo/bar/2008-10-19-foo-bar.textile")
assert !Post.valid?("lol2008-10-19-foo-bar.textile")
assert !Post.valid?("blah") assert !Post.valid?("blah")
end end
@ -21,6 +24,7 @@ class TestPost < Test::Unit::TestCase
def test_url def test_url
p = Post.allocate p = Post.allocate
p.categories = []
p.process("2008-10-19-foo-bar.textile") p.process("2008-10-19-foo-bar.textile")
assert_equal "/2008/10/19/foo-bar.html", p.url assert_equal "/2008/10/19/foo-bar.html", p.url
@ -60,7 +64,7 @@ class TestPost < Test::Unit::TestCase
end end
def test_render def test_render
p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile") p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-10-18-foo-bar.textile")
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")} layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
p.render(layouts, {"site" => {"posts" => []}}) p.render(layouts, {"site" => {"posts" => []}})
@ -70,23 +74,23 @@ class TestPost < Test::Unit::TestCase
def test_write def test_write
clear_dest clear_dest
p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile") p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-10-18-foo-bar.textile")
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")} layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
p.render(layouts, {"site" => {"posts" => []}}) p.render(layouts, {"site" => {"posts" => []}})
p.write(dest_dir) p.write(dest_dir)
end end
def test_data def test_data
p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-11-21-complex.textile") p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-11-21-complex.textile")
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")} layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
p.render(layouts, {"site" => {"posts" => []}}) p.render(layouts, {"site" => {"posts" => []}})
assert_equal "<<< <p>url: /test/source/2008/11/21/complex.html<br />\ndate: #{Time.parse("2008-11-21")}<br />\nid: /test/source/2008/11/21/complex</p> >>>", p.output assert_equal "<<< <p>url: /2008/11/21/complex.html<br />\ndate: #{Time.parse("2008-11-21")}<br />\nid: /2008/11/21/complex</p> >>>", p.output
end end
def test_include def test_include
Jekyll.source = File.join(File.dirname(__FILE__), *%w[source]) Jekyll.source = File.join(File.dirname(__FILE__), *%w[source])
p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-12-13-include.markdown") p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-12-13-include.markdown")
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")} layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
p.render(layouts, {"site" => {"posts" => []}}) p.render(layouts, {"site" => {"posts" => []}})

View File

@ -17,7 +17,7 @@ class TestSite < Test::Unit::TestCase
end end
def test_read_posts def test_read_posts
@s.read_posts(File.join(@s.source, '_posts')) @s.read_posts('')
assert_equal 4, @s.posts.size assert_equal 4, @s.posts.size
end end