diff --git a/bin/jekyll b/bin/jekyll index 37fdd312..0855ff03 100755 --- a/bin/jekyll +++ b/bin/jekyll @@ -25,6 +25,7 @@ command :build do |c| c.option '--limit_posts MAX_POSTS', 'Limits the number of posts to parse and publish' c.option '-w', '--watch', 'Watch for changes and rebuild' c.option '--lsi', 'Use LSI for improved related posts' + c.option '--drafts', 'Render posts in the _drafts folder' c.action do |args, options| options.defaults :serving => false @@ -41,6 +42,7 @@ command :serve do |c| c.option '--limit_posts MAX_POSTS', 'Limits the number of posts to parse and publish' c.option '-w', '--watch', 'Watch for changes and rebuild' c.option '--lsi', 'Use LSI for improved related posts' + c.option '--drafts', 'Render posts in the _drafts folder' c.option '-p', '--port [PORT]', 'Port to listen on' c.option '-h', '--host [HOST]', 'Host to bind to' diff --git a/features/drafts.feature b/features/drafts.feature new file mode 100644 index 00000000..27832fc4 --- /dev/null +++ b/features/drafts.feature @@ -0,0 +1,25 @@ +Feature: Draft Posts + As a hacker who likes to blog + I want to be able to preview drafts locally + In order to see if they look alright before publishing + + Scenario: Preview a draft + Given I have a configuration file with "permalink" set to "none" + And I have a _drafts directory + And I have the following draft: + | title | date | layout | content | + | Recipe | 3/27/2009 | default | Not baked yet. | + When I run jekyll with drafts + Then the _site directory should exist + And I should see "Not baked yet." in "_site/recipe.html" + + Scenario: Don't preview a draft + Given I have a configuration file with "permalink" set to "none" + And I have an "index.html" page that contains "Totally index" + And I have a _drafts directory + And I have the following draft: + | title | date | layout | content | + | Recipe | 3/27/2009 | default | Not baked yet. | + When I run jekyll + Then the _site directory should exist + And the "_site/recipe.html" file should not exist diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb index 20128964..1b0eb519 100644 --- a/features/step_definitions/jekyll_steps.rb +++ b/features/step_definitions/jekyll_steps.rb @@ -50,9 +50,8 @@ Given /^I have an? (.*) directory$/ do |dir| FileUtils.mkdir_p(dir) end -Given /^I have the following posts?(?: (.*) "(.*)")?:$/ do |direction, folder, table| +Given /^I have the following (draft|post)s?(?: (.*) "(.*)")?:$/ do |status, direction, folder, table| table.hashes.each do |post| - date = Date.strptime(post['date'], '%m/%d/%Y').strftime('%Y-%m-%d') title = post['title'].downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-') if direction && direction == "in" @@ -61,7 +60,14 @@ Given /^I have the following posts?(?: (.*) "(.*)")?:$/ do |direction, folder, t after = folder || '.' end - path = File.join(before || '.', '_posts', after || '.', "#{date}-#{title}.#{post['type'] || 'textile'}") + ext = post['type'] || 'textile' + + if "draft" == status + path = File.join(before || '.', '_drafts', after || '.', "#{title}.#{ext}") + else + date = Date.strptime(post['date'], '%m/%d/%Y').strftime('%Y-%m-%d') + path = File.join(before || '.', '_posts', after || '.', "#{date}-#{title}.#{ext}") + end matter_hash = {} %w(title layout tag tags category categories published author).each do |key| @@ -117,6 +123,10 @@ When /^I run jekyll$/ do run_jekyll end +When /^I run jekyll with drafts$/ do + run_jekyll(:drafts => true) +end + When /^I debug jekyll$/ do run_jekyll(:debug => true) end diff --git a/features/support/env.rb b/features/support/env.rb index 1ed330a1..7e550c6c 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -10,8 +10,9 @@ TEST_DIR = File.join('/', 'tmp', 'jekyll') JEKYLL_PATH = File.join(ENV['PWD'], 'bin', 'jekyll') def run_jekyll(opts = {}) - command = JEKYLL_PATH + command = JEKYLL_PATH.clone command << " build" + command << " --drafts" if opts[:drafts] command << " >> /dev/null 2>&1" if opts[:debug].nil? system command end diff --git a/lib/jekyll.rb b/lib/jekyll.rb index ca729c87..51011764 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -33,6 +33,7 @@ require 'jekyll/convertible' require 'jekyll/layout' require 'jekyll/page' require 'jekyll/post' +require 'jekyll/draft' require 'jekyll/filters' require 'jekyll/static_file' require 'jekyll/errors' diff --git a/lib/jekyll/draft.rb b/lib/jekyll/draft.rb new file mode 100644 index 00000000..32457fac --- /dev/null +++ b/lib/jekyll/draft.rb @@ -0,0 +1,35 @@ +module Jekyll + + class Draft < Post + + # Valid post name regex (no date) + MATCHER = /^(.*)(\.[^.]+)$/ + + # Draft name validator. Draft filenames must be like: + # my-awesome-post.textile + # + # Returns true if valid, false if not. + def self.valid?(name) + name =~ MATCHER + end + + # Get the full path to the directory containing the draft files + def containing_dir(source, dir) + return File.join(source, dir, '_drafts') + end + + # Extract information from the post filename. + # + # name - The String filename of the post file. + # + # Returns nothing. + def process(name) + m, slug, ext = *name.match(MATCHER) + self.date = File.mtime(File.join(@base, name)) + self.slug = slug + self.ext = ext + end + + end + +end diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb index b8d0ad55..d40993de 100644 --- a/lib/jekyll/post.rb +++ b/lib/jekyll/post.rb @@ -29,12 +29,11 @@ module Jekyll # site - The Site. # base - The String path to the dir containing the post file. # name - The String filename of the post file. - # categories - An Array of Strings for the categories for this post. # # Returns the new Post. def initialize(site, source, dir, name) @site = site - @base = File.join(source, dir, '_posts') + @base = self.containing_dir(source, dir) @name = name self.categories = dir.split('/').reject { |x| x.empty? } @@ -65,6 +64,11 @@ module Jekyll end end + # Get the full path to the directory containing the post files + def containing_dir(source, dir) + return File.join(source, dir, '_posts') + end + # Read the YAML frontmatter. # # base - The String path to the dir containing the file. diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index f0ef9e68..ad85d42f 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -5,7 +5,7 @@ module Jekyll attr_accessor :config, :layouts, :posts, :pages, :static_files, :categories, :exclude, :include, :source, :dest, :lsi, :pygments, :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts, - :keep_files + :show_drafts, :keep_files attr_accessor :converters, :generators @@ -25,6 +25,7 @@ module Jekyll self.exclude = config['exclude'] || [] self.include = config['include'] || [] self.future = config['future'] + self.show_drafts = config['drafts'] || nil self.limit_posts = config['limit_posts'] || nil self.keep_files = config['keep_files'] || [] @@ -148,6 +149,18 @@ module Jekyll self.read_posts(dir) + if self.show_drafts + self.read_drafts(dir) + end + + self.posts.sort! + + # limit the posts if :limit_posts option is set + if limit_posts + limit = self.posts.length < limit_posts ? self.posts.length : limit_posts + self.posts = self.posts[-limit, limit] + end + entries.each do |f| f_abs = File.join(base, f) f_rel = File.join(dir, f) @@ -190,13 +203,28 @@ module Jekyll end end end + end - self.posts.sort! + # Read all the files in //_drafts and create a new Post + # object with each one. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_drafts(dir) + base = File.join(self.source, dir, '_drafts') + return unless File.exists?(base) + entries = Dir.chdir(base) { filter_entries(Dir['**/*']) } - # limit the posts if :limit_posts option is set - if limit_posts - limit = self.posts.length < limit_posts ? self.posts.length : limit_posts - self.posts = self.posts[-limit, limit] + # first pass processes, but does not yet render draft content + entries.each do |f| + if Draft.valid?(f) + draft = Draft.new(self, self.source, dir, f) + + self.posts << draft + draft.categories.each { |c| self.categories[c] << draft } + draft.tags.each { |c| self.tags[c] << draft } + end end end