Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added post categories based on directories containing _posts

  • Loading branch information...
commit 3a8f7a8e3a20778573e782514e535e9a2ddcad49 1 parent b094b93
Mark Reid authored
View
36 README.textile
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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
View
1  lib/jekyll.rb
@@ -6,6 +6,7 @@
# core
require 'fileutils'
require 'time'
+require 'yaml'
# stdlib
View
5 lib/jekyll/filters.rb
@@ -15,7 +15,6 @@ def xml_escape(input)
def number_of_words(input)
input.split.length
- end
- end
-
+ end
+ end
end
View
11 lib/jekyll/post.rb
@@ -3,7 +3,7 @@ module Jekyll
class Post
include Comparable
include Convertible
-
+
class << self
attr_accessor :lsi
end
@@ -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)
@@ -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.
@@ -90,7 +93,7 @@ def url
def id
self.dir + self.slug
end
-
+
# Calculate related posts.
#
# Returns [<Post>]
View
30 lib/jekyll/site.rb
@@ -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
@@ -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) }
@@ -76,7 +74,7 @@ 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
#
@@ -84,10 +82,14 @@ def write_posts
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
@@ -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
View
2  test/test_post.rb
@@ -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
View
4 test/test_site.rb
@@ -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

1 comment on commit 3a8f7a8

@grempe

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.
Something went wrong with that request. Please try again.