Skip to content

Commit

Permalink
Added post categories based on directories containing _posts
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Reid committed Dec 16, 2008
1 parent b094b93 commit 3a8f7a8
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 22 deletions.
36 changes: 33 additions & 3 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ various data about my site. A reverse chronological list of all my blog posts
can be found in <code>site.posts</code>. Each post, in turn, contains various
fields such as <code>title</code> and <code>date</code>.

Jekyll gets the list of blog posts by parsing the files in the
"_posts":http://github.com/mojombo/tpw/tree/master/_posts directory. Each
post's filename contains the publishing date and slug (what shows up in the
Jekyll gets the list of blog posts by parsing the files in any
"_posts":http://github.com/mojombo/tpw/tree/master/_posts directory found in
subdirectories below the root.
Each post's filename contains the publishing date and slug (what shows up in the
URL) that the final HTML file should have. Open up the file corresponding to a
blog post:
"2008-11-17-blogging-like-a-hacker.textile":http://github.com/mojombo/tpw/tree/master/_posts/2008-11-17-blogging-like-a-hacker.textile.
Expand All @@ -52,6 +53,13 @@ filename is used to construct the URL in the generated site. The example post,
for instance, ends up at
<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
found within.
A post that appears in the directory foo/bar/_posts is placed in the categories
'foo' and 'bar'.
By selecting posts from particular categories in your Liquid templates, you will
be able to host multiple blogs within a site.

Files that do not reside in directories prefixed with an underscore are
mirrored into a corresponding directory structure in the generated site. If a
file does not have a YAML preface, it is not run through the Liquid
Expand Down Expand Up @@ -158,6 +166,9 @@ h3. Site
high quality but slow to compute results, run the jekyll command with the
--lsi (latent semantic indexing) option.

site.categories.CATEGORY
The list of all posts in category CATEGORY.

h3. Post

post.title
Expand All @@ -177,6 +188,9 @@ h3. Post
post.content
The content of the Post.

post.categories
The list of categories to which this post belongs.

h2. YAML Front Matter

Any files that contain a YAML front matter block will be processed by Jekyll
Expand Down Expand Up @@ -289,6 +303,22 @@ highlighting stylesheet. For an example stylesheet you can look at
are the same styles as used by GitHub and you are free to use them for your
own site.

h2. Categories

Posts are placed into categories based on the directory structure they are found
within (see above for an example). The categories can be accessed from within
a Liquid template as follows:

<pre>
{% for post in site.categories.foo %}
<li><span>{{ post.date | date_to_string }}</span> - {{ post.title }}</li>
{% endfor %}
</pre>

This would list all the posts in the category 'foo' by date and title.

The posts within each category are sorted in reverse chronological order.

h2. Contribute

If you'd like to hack on Jekyll, grab the source from GitHub. To get
Expand Down
1 change: 1 addition & 0 deletions lib/jekyll.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# core
require 'fileutils'
require 'time'
require 'yaml'

# stdlib

Expand Down
5 changes: 2 additions & 3 deletions lib/jekyll/filters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def xml_escape(input)

def number_of_words(input)
input.split.length
end
end

end
end
end
11 changes: 7 additions & 4 deletions lib/jekyll/post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Jekyll
class Post
include Comparable
include Convertible

class << self
attr_accessor :lsi
end
Expand All @@ -18,17 +18,19 @@ def self.valid?(name)
name =~ MATCHER
end

attr_accessor :date, :slug, :ext
attr_accessor :date, :slug, :ext, :categories
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
# +categories+ is an Array of Strings for the categories for this post
#
# Returns <Post>
def initialize(base, name)
@base = base
@name = name
@categories = base.split('/').reject { |p| ['.', '_posts'].include? p }

self.process(name)
self.read_yaml(base, name)
Expand Down Expand Up @@ -61,9 +63,10 @@ def process(name)
#
# Returns <String>
def dir
path = @categories ? '/' + @categories.join('/') : ''
permalink ?
permalink.to_s.split("/")[0..-2].join("/") :
date.strftime("/%Y/%m/%d/")
"#{path}" + date.strftime("/%Y/%m/%d/")
end

# The full path and filename of the post.
Expand All @@ -90,7 +93,7 @@ def url
def id
self.dir + self.slug
end

# Calculate related posts.
#
# Returns [<Post>]
Expand Down
30 changes: 21 additions & 9 deletions lib/jekyll/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ def initialize(source, dest)
# Returns nothing
def process
self.read_layouts
self.read_posts
self.write_posts
self.transform_pages
self.write_posts
end

# Read all the files in <source>/_layouts into memory for
Expand All @@ -46,12 +45,11 @@ def read_layouts
# ignore missing layout dir
end

# Read all the files in <source>/posts and create a new Post
# Read all the files in <base>/_posts and create a new Post
# object with each one.
#
# Returns nothing
def read_posts
base = File.join(self.source, "_posts")
def read_posts(base)
entries = Dir.entries(base)
entries = entries.reject { |e| File.directory?(e) }

Expand All @@ -76,18 +74,22 @@ def write_posts

# Copy all regular files from <source> to <dest>/ ignoring
# any files/directories that are hidden (start with ".") or contain
# site content (start with "_")
# site content (start with "_") unless they are "_posts" directories
# The +dir+ String is a relative path used to call this method
# recursively as it descends through directories
#
# Returns nothing
def transform_pages(dir = '')
base = File.join(self.source, dir)
entries = Dir.entries(base)
entries = entries.reject { |e| ['.', '_'].include?(e[0..0]) }
entries = entries.reject { |e|
(e != '_posts') and ['.', '_'].include?(e[0..0])
}

entries.each do |f|
if File.directory?(File.join(base, f))
if f == '_posts'
read_posts(File.join(base, f))
elsif File.directory?(File.join(base, f))
next if self.dest.sub(/\/$/, '') == File.join(base, f)
transform_pages(File.join(dir, f))
else
Expand All @@ -111,7 +113,17 @@ def transform_pages(dir = '')
#
# Returns {"site" => {"time" => <Time>, "posts" => [<Post>]}}
def site_payload
{"site" => {"time" => Time.now, "posts" => self.posts.sort.reverse}}
# Build the category hash map of category ( names => arrays of posts )
# then sort each array in reverse order
categories = Hash.new { |hash,key| hash[key] = Array.new }
self.posts.each { |p| p.categories.each { |c| categories[c] << p } }
categories.values.map { |cats| cats.sort! { |a,b| b <=> a} }

{"site" => {
"time" => Time.now,
"posts" => self.posts.sort { |a,b| b <=> a },
"categories" => categories
}}
end
end

Expand Down
2 changes: 1 addition & 1 deletion test/test_post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_data
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
p.add_layout(layouts, {"site" => {"posts" => []}})

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
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
end

def test_include
Expand Down
4 changes: 2 additions & 2 deletions test/test_site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def test_read_layouts

assert_equal ["default", "simple"].sort, @s.layouts.keys.sort
end

def test_read_posts
@s.read_posts
@s.read_posts(File.join(@s.source, '_posts'))

assert_equal 3, @s.posts.size
end
Expand Down

1 comment on commit 3a8f7a8

@grempe
Copy link

@grempe grempe commented on 3a8f7a8 Dec 22, 2008

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn’t it be better to specify the categories that a post should belong to in an array in the yaml header for the post? This would seem to be a much more flexible way to add a post to multiple categories (and easily change your mind later). The dir structure approach would seem to lead to arbitrarily deep dir structures. And would be much more labor intensive to change. Taking it to the logical extreme, if I have 100 categories, and I have a post that I want to show in all 100 cats, do I have to create a dir structure 100 levels deep? Or do I have 100 dirs and symlink the post file into each of those dirs? (Caveat : I have not tried to use this feature yet so I could be full of crap.)

Please sign in to comment.