Merge master into jekyll-new

This commit is contained in:
Parker Moore 2013-03-17 15:45:37 +01:00
commit aa7a234c18
56 changed files with 2175 additions and 1452 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ bbin/
gh-pages/ gh-pages/
site/_site/ site/_site/
coverage coverage
.ruby-version

View File

@ -1 +0,0 @@
1.9.3-p362

View File

@ -17,6 +17,7 @@ following in mind:
* If your contribution changes any Jekyll behavior, make sure to update the * If your contribution changes any Jekyll behavior, make sure to update the
documentation. It lives in site/_posts. If the docs are missing information, documentation. It lives in site/_posts. If the docs are missing information,
please feel free to add it in. Great docs make a great project! please feel free to add it in. Great docs make a great project!
* Please follow the [Github Ruby Styleguide](https://github.com/styleguide/ruby) when modifying Ruby code.
Test Dependencies Test Dependencies
----------------- -----------------

View File

@ -1,2 +1,2 @@
source :rubygems source 'https://rubygems.org'
gemspec gemspec

View File

@ -1,7 +1,22 @@
== HEAD == HEAD
* Major Enhancements * Major Enhancements
* Refactored jekyll commands into subcommands: build, serve, and migrate. (#690) * Refactored jekyll commands into subcommands: build, serve, and migrate. (#690)
* Removed importers/migrators from main project, migrated to jekyll-import sub-gem (#793)
* Added ability to render drafts in _drafts folder via command line (#833)
* Minor Enhancements * Minor Enhancements
* Accept custom configuration file via CLI (#863)
* Load in Apache MIME Types on `jekyll serve` (#847)
* Improve debugability of error message for a malformed highlight tag (#785)
* Allow symlinked files in unsafe mode (#824)
* Add 'gist' liquid tag to core (#822)
* New format of Jekyll output (#795)
* Reinstate --limit_posts and --future switches (#788)
* Remove ambiguity from command descriptions (#815)
* Fix SafeYAML Warnings (#807)
* Relaxed Kramdown version to 0.14 (#808)
* Aliased `jekyll server` to `jekyll serve`. (#792)
* Updated gem versions for Kramdown, Rake, Shoulda, Cucumber, and RedCarpet. (#744)
* Refactored jekyll subcommands into Jekyll::Commands submodule, which now contains them (#768)
* Rescue from import errors in Wordpress.com migrator (#671) * Rescue from import errors in Wordpress.com migrator (#671)
* Massively accelerate LSI performance (#664) * Massively accelerate LSI performance (#664)
* Truncate post slugs when importing from Tumblr (#496) * Truncate post slugs when importing from Tumblr (#496)
@ -12,16 +27,26 @@
* Add source and destination directory protection (#535) * Add source and destination directory protection (#535)
* Better YAML error message (#718) * Better YAML error message (#718)
* Bug Fixes * Bug Fixes
* Patch for multibyte URI problem with jekyll serve (#723)
* Order plugin execution by priority (#864)
* Fixed Page#dir and Page#url for edge cases (#536)
* Fix broken post_url with posts with a time in their YAML Front-Matter (#831)
* Look for plugins under the source directory (#654)
* Tumblr Migrator: finds _posts dir correctly, fixes truncation of long * Tumblr Migrator: finds _posts dir correctly, fixes truncation of long
post names (#775) post names (#775)
* Force Categories to be Strings (#767) * Force Categories to be Strings (#767)
* Safe YAML plugin to prevent vulnerability (#777) * Safe YAML plugin to prevent vulnerability (#777)
* Add SVG support to Jekyll/WEBrick. (#407, #406) * Add SVG support to Jekyll/WEBrick. (#407, #406)
* Prevent custom destination from causing continuous regen (#528) * Prevent custom destination from causing continuous regen on watch (#528, #820)
* Site Enhancements * Site Enhancements
* Bring site into master branch with better preview/deploy (#709) * Bring site into master branch with better preview/deploy (#709)
* Redesigned site (#583) * Redesigned site (#583)
* Development fixes * Development fixes
* Added "features:html" rake task for debugging purposes, cleaned up
cucumber profiles (#832)
* Explicitly require HTTPS rubygems source in Gemfile (#826)
* Changed Ruby version for development to 1.9.3-p374 from p362 (#801)
* Including a link to the GitHub Ruby style guide in CONTRIBUTING.md (#806)
* Added script/bootstrap (#776) * Added script/bootstrap (#776)
* Running Simplecov under 2 conditions: ENV(COVERAGE)=true and with Ruby version * Running Simplecov under 2 conditions: ENV(COVERAGE)=true and with Ruby version
of greater than 1.9 (#771) of greater than 1.9 (#771)

View File

@ -21,6 +21,7 @@ h2. Diving In
* Put information on your site with "Template Data":http://wiki.github.com/mojombo/jekyll/template-data * Put information on your site with "Template Data":http://wiki.github.com/mojombo/jekyll/template-data
* Customize the "Permalinks":http://wiki.github.com/mojombo/jekyll/permalinks your posts are generated with * Customize the "Permalinks":http://wiki.github.com/mojombo/jekyll/permalinks your posts are generated with
* Use the built-in "Liquid Extensions":http://wiki.github.com/mojombo/jekyll/liquid-extensions to make your life easier * Use the built-in "Liquid Extensions":http://wiki.github.com/mojombo/jekyll/liquid-extensions to make your life easier
* Use custom "Plugins":http://wiki.github.com/mojombo/jekyll/Plugins to generate content specific to your site
h2. Runtime Dependencies h2. Runtime Dependencies

View File

@ -63,36 +63,13 @@ Rake::RDocTask.new do |rdoc|
rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.include('lib/**/*.rb')
end end
desc "Open an irb session preloaded with this library"
task :console do
sh "irb -rubygems -r ./lib/#{name}.rb"
end
#############################################################################
#
# Custom tasks (add your own tasks here)
#
#############################################################################
namespace :migrate do
desc "Migrate from mephisto in the current directory"
task :mephisto do
sh %q(ruby -r './lib/jekyll/migrators/mephisto' -e 'Jekyll::Mephisto.postgres(:database => "#{ENV["DB"]}")')
end
desc "Migrate from Movable Type in the current directory"
task :mt do
sh %q(ruby -r './lib/jekyll/migrators/mt' -e 'Jekyll::MT.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
end
desc "Migrate from Typo in the current directory"
task :typo do
sh %q(ruby -r './lib/jekyll/migrators/typo' -e 'Jekyll::Typo.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
end
end
begin begin
require 'cucumber/rake/task' require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:features) do |t| Cucumber::Rake::Task.new(:features) do |t|
t.cucumber_opts = "--format progress" t.profile = "travis"
end
Cucumber::Rake::Task.new(:"features:html", "Run Cucumber features and produce HTML output") do |t|
t.profile = "html_report"
end end
rescue LoadError rescue LoadError
desc 'Cucumber rake task not available' desc 'Cucumber rake task not available'
@ -101,6 +78,11 @@ rescue LoadError
end end
end end
desc "Open an irb session preloaded with this library"
task :console do
sh "irb -rubygems -r ./lib/#{name}.rb"
end
############################################################################# #############################################################################
# #
# Site tasks - http://jekyllrb.com # Site tasks - http://jekyllrb.com

View File

@ -17,6 +17,19 @@ global_option '--safe', 'Safe mode (defaults to false)'
global_option '--plugins', 'Plugins directory (defaults to ./_plugins)' global_option '--plugins', 'Plugins directory (defaults to ./_plugins)'
global_option '--layouts', 'Layouts directory (defaults to ./_layouts)' global_option '--layouts', 'Layouts directory (defaults to ./_layouts)'
# Option names don't always directly match the configuration value we'd like.
# This method will rename options to match what Jekyll configuration expects.
#
# options - The Hash of options from Commander.
#
# Returns the normalized Hash.
def normalize_options(options)
if drafts_state = options.delete(:drafts)
options[:show_drafts] = drafts_state
end
options
end
command :new do |c| command :new do |c|
c.syntax = 'jekyll new PATH' c.syntax = 'jekyll new PATH'
c.description = 'Creates a new Jekyll site scaffold in PATH' c.description = 'Creates a new Jekyll site scaffold in PATH'
@ -28,24 +41,33 @@ end
command :build do |c| command :build do |c|
c.syntax = 'jekyll build [options]' c.syntax = 'jekyll build [options]'
c.description = 'Build your site with the option of auto-renegeration' c.description = 'Build your site'
c.option '--config [CONFIG_FILE]', 'Custom configuration file'
c.option '--future', 'Publishes posts with a future date'
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 '-w', '--watch', 'Watch for changes and rebuild'
c.option '--lsi', 'Use LSI for improved related posts' c.option '--lsi', 'Use LSI for improved related posts'
c.option '--drafts', 'Render posts in the _drafts folder'
c.action do |args, options| c.action do |args, options|
options.defaults :serving => false options.defaults :serving => false
options = Jekyll.configuration(options.__hash__) options = normalize_options(options.__hash__)
options = Jekyll.configuration(options)
Jekyll::Commands::Build.process(options) Jekyll::Commands::Build.process(options)
end end
end end
command :serve do |c| command :serve do |c|
c.syntax = 'jekyll serve [options]' c.syntax = 'jekyll serve [options]'
c.description = 'Serve your site locally with the option of auto-regeneration' c.description = 'Serve your site locally'
c.option '--config [CONFIG_FILE]', 'Custom configuration file'
c.option '--future', 'Publishes posts with a future date'
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 '-w', '--watch', 'Watch for changes and rebuild'
c.option '--lsi', 'Use LSI for improved related posts' 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 '-p', '--port [PORT]', 'Port to listen on'
c.option '-h', '--host [HOST]', 'Host to bind to' c.option '-h', '--host [HOST]', 'Host to bind to'
@ -57,16 +79,19 @@ command :serve do |c|
:baseurl => '/', :baseurl => '/',
:serving => true :serving => true
options = Jekyll.configuration(options.__hash__) options = normalize_options(options.__hash__)
options = Jekyll.configuration(options)
Jekyll::Commands::Build.process(options) Jekyll::Commands::Build.process(options)
Jekyll::Commands::Serve.process(options) Jekyll::Commands::Serve.process(options)
end end
end end
alias_command :server, :serve
command :import do |c| command :import do |c|
c.syntax = 'jekyll import <platform> [options]' c.syntax = 'jekyll import <platform> [options]'
c.description = 'Import your old blog to Jekyll' c.description = 'Import your old blog to Jekyll'
c.option '--source', 'Source file or URL to migrate from'
c.option '--file', 'File to migrate from' c.option '--file', 'File to migrate from'
c.option '--dbname', 'Database name to migrate from' c.option '--dbname', 'Database name to migrate from'
c.option '--user', 'Username to use when migrating' c.option '--user', 'Username to use when migrating'
@ -74,6 +99,14 @@ command :import do |c|
c.option '--host', 'Host address to use when migrating' c.option '--host', 'Host address to use when migrating'
c.action do |args, options| c.action do |args, options|
Jekyll::Commands::Migrate.process(args.first, options) begin
require 'jekyll-import'
rescue LoadError
msg = "You must install the 'jekyll-import' gem before continuing.\n"
msg += "* Do this by running `gem install jekyll-import`.\n"
msg += "* Or if you need root privileges, run `sudo gem install jekyll-import`."
abort msg
end
Jekyll::Commands::Import.process(args.first, options)
end end
end end

View File

@ -1,2 +1,3 @@
default: --format progress default: --format pretty
travis: --format progress
html_report: --format progress --format html --out=features_report.html html_report: --format progress --format html --out=features_report.html

View File

@ -89,7 +89,7 @@ Feature: Create sites
And I have an "_includes/about.textile" file that contains "Generated by {% include jekyll.textile %}" And I have an "_includes/about.textile" file that contains "Generated by {% include jekyll.textile %}"
And I have an "_includes/jekyll.textile" file that contains "Jekyll" And I have an "_includes/jekyll.textile" file that contains "Jekyll"
And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}" And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
When I debug jekyll When I run jekyll
Then the _site directory should exist Then the _site directory should exist
And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html" And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"

25
features/drafts.feature Normal file
View File

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

View File

@ -3,7 +3,7 @@ Feature: Site configuration
I want to be able to configure jekyll I want to be able to configure jekyll
In order to make setting up a site easier In order to make setting up a site easier
Scenario: Change destination directory Scenario: Change source directory
Given I have a blank site in "_sourcedir" Given I have a blank site in "_sourcedir"
And I have an "_sourcedir/index.html" file that contains "Changing source directory" And I have an "_sourcedir/index.html" file that contains "Changing source directory"
And I have a configuration file with "source" set to "_sourcedir" And I have a configuration file with "source" set to "_sourcedir"

View File

@ -50,9 +50,8 @@ Given /^I have an? (.*) directory$/ do |dir|
FileUtils.mkdir_p(dir) FileUtils.mkdir_p(dir)
end 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| 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+/, '-') title = post['title'].downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-')
if direction && direction == "in" if direction && direction == "in"
@ -61,7 +60,14 @@ Given /^I have the following posts?(?: (.*) "(.*)")?:$/ do |direction, folder, t
after = folder || '.' after = folder || '.'
end 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 = {} matter_hash = {}
%w(title layout tag tags category categories published author).each do |key| %w(title layout tag tags category categories published author).each do |key|
@ -117,6 +123,10 @@ When /^I run jekyll$/ do
run_jekyll run_jekyll
end end
When /^I run jekyll with drafts$/ do
run_jekyll(:drafts => true)
end
When /^I debug jekyll$/ do When /^I debug jekyll$/ do
run_jekyll(:debug => true) run_jekyll(:debug => true)
end end

View File

@ -10,8 +10,9 @@ TEST_DIR = File.join('/', 'tmp', 'jekyll')
JEKYLL_PATH = File.join(ENV['PWD'], 'bin', 'jekyll') JEKYLL_PATH = File.join(ENV['PWD'], 'bin', 'jekyll')
def run_jekyll(opts = {}) def run_jekyll(opts = {})
command = JEKYLL_PATH command = JEKYLL_PATH.clone
command << " build" command << " build"
command << " --drafts" if opts[:drafts]
command << " >> /dev/null 2>&1" if opts[:debug].nil? command << " >> /dev/null 2>&1" if opts[:debug].nil?
system command system command
end end

View File

@ -4,9 +4,9 @@ Gem::Specification.new do |s|
s.rubygems_version = '1.3.5' s.rubygems_version = '1.3.5'
s.name = 'jekyll' s.name = 'jekyll'
s.version = '0.12.0' s.version = '1.0.0.beta1'
s.license = 'MIT' s.license = 'MIT'
s.date = '2012-12-22' s.date = '2013-03-14'
s.rubyforge_project = 'jekyll' s.rubyforge_project = 'jekyll'
s.summary = "A simple, blog aware, static site generator." s.summary = "A simple, blog aware, static site generator."
@ -27,31 +27,26 @@ Gem::Specification.new do |s|
s.add_runtime_dependency('classifier', "~> 1.3") s.add_runtime_dependency('classifier', "~> 1.3")
s.add_runtime_dependency('directory_watcher', "~> 1.1") s.add_runtime_dependency('directory_watcher', "~> 1.1")
s.add_runtime_dependency('maruku', "~> 0.5") s.add_runtime_dependency('maruku', "~> 0.5")
s.add_runtime_dependency('kramdown', "~> 0.13.4") s.add_runtime_dependency('kramdown', "~> 0.14")
s.add_runtime_dependency('pygments.rb', "~> 0.3.2") s.add_runtime_dependency('pygments.rb', "~> 0.3.2")
s.add_runtime_dependency('commander', "~> 4.1.3") s.add_runtime_dependency('commander', "~> 4.1.3")
s.add_runtime_dependency('safe_yaml', "~> 0.7.0") s.add_runtime_dependency('safe_yaml', "~> 0.7.0")
s.add_development_dependency('rake', "~> 0.9") s.add_development_dependency('rake', "~> 10.0.3")
s.add_development_dependency('rdoc', "~> 3.11") s.add_development_dependency('rdoc', "~> 3.11")
s.add_development_dependency('redgreen', "~> 1.2") s.add_development_dependency('redgreen', "~> 1.2")
s.add_development_dependency('shoulda', "~> 2.11") s.add_development_dependency('shoulda', "~> 3.3.2")
s.add_development_dependency('rr', "~> 1.0") s.add_development_dependency('rr', "~> 1.0")
s.add_development_dependency('cucumber', "1.1") s.add_development_dependency('cucumber', "~> 1.2.1")
s.add_development_dependency('RedCloth', "~> 4.2") s.add_development_dependency('RedCloth', "~> 4.2")
s.add_development_dependency('rdiscount', "~> 1.6") s.add_development_dependency('rdiscount', "~> 1.6")
s.add_development_dependency('redcarpet', "~> 2.1.1") s.add_development_dependency('redcarpet', "~> 2.2.2")
s.add_development_dependency('launchy', "~> 2.1.2") s.add_development_dependency('launchy', "~> 2.1.2")
s.add_development_dependency('simplecov', "~> 0.7") s.add_development_dependency('simplecov', "~> 0.7")
s.add_development_dependency('simplecov-gem-adapter', "~> 1.0.1") s.add_development_dependency('simplecov-gem-adapter', "~> 1.0.1")
# migrator dependencies:
s.add_development_dependency('sequel', "~> 3.42")
s.add_development_dependency('htmlentities', "~> 4.3")
s.add_development_dependency('hpricot', "~> 0.8")
# = MANIFEST = # = MANIFEST =
s.files = %w[ s.files = %w[
.travis.yml
CONTRIBUTING.md CONTRIBUTING.md
Gemfile Gemfile
History.txt History.txt
@ -61,6 +56,7 @@ Gem::Specification.new do |s|
bin/jekyll bin/jekyll
cucumber.yml cucumber.yml
features/create_sites.feature features/create_sites.feature
features/drafts.feature
features/embed_filters.feature features/embed_filters.feature
features/markdown.feature features/markdown.feature
features/pagination.feature features/pagination.feature
@ -74,8 +70,11 @@ Gem::Specification.new do |s|
lib/jekyll.rb lib/jekyll.rb
lib/jekyll/command.rb lib/jekyll/command.rb
lib/jekyll/commands/build.rb lib/jekyll/commands/build.rb
<<<<<<< HEAD
lib/jekyll/commands/migrate.rb lib/jekyll/commands/migrate.rb
lib/jekyll/commands/new.rb lib/jekyll/commands/new.rb
=======
>>>>>>> master
lib/jekyll/commands/serve.rb lib/jekyll/commands/serve.rb
lib/jekyll/converter.rb lib/jekyll/converter.rb
lib/jekyll/converters/identity.rb lib/jekyll/converters/identity.rb
@ -83,33 +82,23 @@ Gem::Specification.new do |s|
lib/jekyll/converters/textile.rb lib/jekyll/converters/textile.rb
lib/jekyll/convertible.rb lib/jekyll/convertible.rb
lib/jekyll/core_ext.rb lib/jekyll/core_ext.rb
lib/jekyll/draft.rb
lib/jekyll/errors.rb lib/jekyll/errors.rb
lib/jekyll/filters.rb lib/jekyll/filters.rb
lib/jekyll/generator.rb lib/jekyll/generator.rb
lib/jekyll/generators/pagination.rb lib/jekyll/generators/pagination.rb
lib/jekyll/layout.rb lib/jekyll/layout.rb
lib/jekyll/migrators/csv.rb lib/jekyll/mime.types
lib/jekyll/migrators/drupal.rb
lib/jekyll/migrators/enki.rb
lib/jekyll/migrators/joomla.rb
lib/jekyll/migrators/marley.rb
lib/jekyll/migrators/mephisto.rb
lib/jekyll/migrators/mt.rb
lib/jekyll/migrators/posterous.rb
lib/jekyll/migrators/rss.rb
lib/jekyll/migrators/textpattern.rb
lib/jekyll/migrators/tumblr.rb
lib/jekyll/migrators/typo.rb
lib/jekyll/migrators/wordpress.rb
lib/jekyll/migrators/wordpressdotcom.rb
lib/jekyll/page.rb lib/jekyll/page.rb
lib/jekyll/plugin.rb lib/jekyll/plugin.rb
lib/jekyll/post.rb lib/jekyll/post.rb
lib/jekyll/site.rb lib/jekyll/site.rb
lib/jekyll/static_file.rb lib/jekyll/static_file.rb
lib/jekyll/tags/gist.rb
lib/jekyll/tags/highlight.rb lib/jekyll/tags/highlight.rb
lib/jekyll/tags/include.rb lib/jekyll/tags/include.rb
lib/jekyll/tags/post_url.rb lib/jekyll/tags/post_url.rb
<<<<<<< HEAD
lib/site_template/_config.yml lib/site_template/_config.yml
lib/site_template/_layouts/default.html lib/site_template/_layouts/default.html
lib/site_template/_layouts/post.html lib/site_template/_layouts/post.html
@ -119,9 +108,62 @@ Gem::Specification.new do |s|
lib/site_template/images/.gitkeep lib/site_template/images/.gitkeep
lib/site_template/images/rss.png lib/site_template/images/rss.png
lib/site_template/index.html lib/site_template/index.html
=======
script/bootstrap
site/.gitignore
site/CNAME
site/README
site/_config.yml
site/_includes/analytics.html
site/_includes/docs_contents.html
site/_includes/footer.html
site/_includes/header.html
site/_includes/section_nav.html
site/_includes/top.html
site/_layouts/default.html
site/_layouts/docs.html
site/_posts/2012-07-01-configuration.md
site/_posts/2012-07-01-contributing.md
site/_posts/2012-07-01-deployment-methods.md
site/_posts/2012-07-01-extras.md
site/_posts/2012-07-01-frontmatter.md
site/_posts/2012-07-01-github-pages.md
site/_posts/2012-07-01-heroku.md
site/_posts/2012-07-01-home.md
site/_posts/2012-07-01-installation.md
site/_posts/2012-07-01-migrations.md
site/_posts/2012-07-01-pages.md
site/_posts/2012-07-01-pagination.md
site/_posts/2012-07-01-permalinks.md
site/_posts/2012-07-01-plugins.md
site/_posts/2012-07-01-posts.md
site/_posts/2012-07-01-resources.md
site/_posts/2012-07-01-sites.md
site/_posts/2012-07-01-structure.md
site/_posts/2012-07-01-templates.md
site/_posts/2012-07-01-troubleshooting.md
site/_posts/2012-07-01-usage.md
site/_posts/2012-07-01-variables.md
site/css/grid.css
site/css/normalize.css
site/css/pygments.css
site/css/style.css
site/docs/index.html
site/favicon.png
site/img/article-footer.png
site/img/footer-arrow.png
site/img/footer-logo.png
site/img/logo-2x.png
site/img/octojekyll.png
site/img/tube.png
site/img/tube1x.png
site/index.html
site/js/modernizr-2.5.3.min.js
>>>>>>> master
test/fixtures/broken_front_matter1.erb test/fixtures/broken_front_matter1.erb
test/fixtures/broken_front_matter2.erb test/fixtures/broken_front_matter2.erb
test/fixtures/broken_front_matter3.erb test/fixtures/broken_front_matter3.erb
test/fixtures/exploit_front_matter.erb
test/fixtures/front_matter.erb test/fixtures/front_matter.erb
test/helper.rb test/helper.rb
test/source/.htaccess test/source/.htaccess
@ -153,9 +195,13 @@ Gem::Specification.new do |s|
test/source/_posts/2010-01-16-override-data.textile test/source/_posts/2010-01-16-override-data.textile
test/source/_posts/2011-04-12-md-extension.md test/source/_posts/2011-04-12-md-extension.md
test/source/_posts/2011-04-12-text-extension.text test/source/_posts/2011-04-12-text-extension.text
test/source/_posts/2013-01-12-nil-layout.textile
test/source/_posts/2013-01-12-no-layout.textile
test/source/about.html test/source/about.html
test/source/category/_posts/2008-9-23-categories.textile test/source/category/_posts/2008-9-23-categories.textile
test/source/contacts.html test/source/contacts.html
test/source/contacts/bar.html
test/source/contacts/index.html
test/source/css/screen.css test/source/css/screen.css
test/source/deal.with.dots.html test/source/deal.with.dots.html
test/source/foo/_posts/bar/2008-12-12-topical-post.textile test/source/foo/_posts/bar/2008-12-12-topical-post.textile

View File

@ -33,6 +33,7 @@ require 'jekyll/convertible'
require 'jekyll/layout' require 'jekyll/layout'
require 'jekyll/page' require 'jekyll/page'
require 'jekyll/post' require 'jekyll/post'
require 'jekyll/draft'
require 'jekyll/filters' require 'jekyll/filters'
require 'jekyll/static_file' require 'jekyll/static_file'
require 'jekyll/errors' require 'jekyll/errors'
@ -48,16 +49,17 @@ require_all 'jekyll/converters'
require_all 'jekyll/generators' require_all 'jekyll/generators'
require_all 'jekyll/tags' require_all 'jekyll/tags'
SafeYAML::OPTIONS[:suppress_warnings] = true
module Jekyll module Jekyll
VERSION = '0.12.0' VERSION = '1.0.0.beta1'
# Default options. Overriden by values in _config.yml. # Default options. Overriden by values in _config.yml.
# Strings rather than symbols are used for compatability with YAML. # Strings rather than symbols are used for compatability with YAML.
DEFAULTS = { DEFAULTS = {
'source' => Dir.pwd, 'source' => Dir.pwd,
'destination' => File.join(Dir.pwd, '_site'), 'destination' => File.join(Dir.pwd, '_site'),
'plugins' => '_plugins',
'plugins' => File.join(Dir.pwd, '_plugins'),
'layouts' => '_layouts', 'layouts' => '_layouts',
'keep_files' => ['.git','.svn'], 'keep_files' => ['.git','.svn'],
@ -127,16 +129,23 @@ module Jekyll
# then, we need to know where to look for _config.yml # then, we need to know where to look for _config.yml
source = override['source'] || Jekyll::DEFAULTS['source'] source = override['source'] || Jekyll::DEFAULTS['source']
# Get configuration from <source>/_config.yml # Get configuration from <source>/_config.yml or <source>/<config_file>
config_file = File.join(source, '_config.yml') config_file = override.delete('config')
config_file = File.join(source, "_config.yml") if config_file.to_s.empty?
begin begin
config = YAML.load_file(config_file) config = YAML.safe_load_file(config_file)
raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash) raise "Configuration file: (INVALID) #{config_file}" if !config.is_a?(Hash)
$stdout.puts "Configuration from #{config_file}" $stdout.puts "Configuration file: #{config_file}"
rescue SystemCallError
# Errno:ENOENT = file not found
$stderr.puts "Configuration file: none"
config = {}
rescue => err rescue => err
$stderr.puts "WARNING: Could not read configuration. " + $stderr.puts " " +
"WARNING: Error reading configuration. " +
"Using defaults (and options)." "Using defaults (and options)."
$stderr.puts "\t" + err.to_s $stderr.puts "#{err}"
config = {} config = {}
end end

View File

@ -1,9 +1,9 @@
module Jekyll module Jekyll
class Command class Command
def self.globs(source) def self.globs(source, destination)
Dir.chdir(source) do Dir.chdir(source) do
dirs = Dir['*'].select { |x| File.directory?(x) } dirs = Dir['*'].select { |x| File.directory?(x) }
dirs -= ['_site'] dirs -= [destination]
dirs = dirs.map { |x| "#{x}/**/*" } dirs = dirs.map { |x| "#{x}/**/*" }
dirs += ['*'] dirs += ['*']
end end

View File

@ -23,7 +23,9 @@ module Jekyll
def self.build(site, options) def self.build(site, options)
source = options['source'] source = options['source']
destination = options['destination'] destination = options['destination']
puts "Building site: #{source} -> #{destination}" puts " Source: #{source}"
puts " Destination: #{destination}"
print " Generating... "
begin begin
site.process site.process
rescue Jekyll::FatalException => e rescue Jekyll::FatalException => e
@ -33,7 +35,7 @@ module Jekyll
puts e.message puts e.message
exit(1) exit(1)
end end
puts "Successfully generated site: #{source} -> #{destination}" puts "done."
end end
# Private: Watch for file changes and rebuild the site. # Private: Watch for file changes and rebuild the site.
@ -48,23 +50,26 @@ module Jekyll
source = options['source'] source = options['source']
destination = options['destination'] destination = options['destination']
puts "Auto-Regenerating enabled: #{source} -> #{destination}" puts " Source: #{source}"
puts " Destination: #{destination}"
puts " Auto-regeneration: enabled"
dw = DirectoryWatcher.new(source) dw = DirectoryWatcher.new(source)
dw.interval = 1 dw.interval = 1
dw.glob = self.globs(source) dw.glob = self.globs(source, destination)
dw.add_observer do |*args| dw.add_observer do |*args|
t = Time.now.strftime("%Y-%m-%d %H:%M:%S") t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
puts "[#{t}] regeneration: #{args.size} files changed" print " Regenerating: #{args.size} files at #{t} "
site.process site.process
puts "...done."
end end
dw.start dw.start
unless options['serving'] unless options['serving']
trap("INT") do trap("INT") do
puts "Stopping auto-regeneration..." puts " Halting auto-regeneration."
exit 0 exit 0
end end

View File

@ -1,47 +0,0 @@
module Jekyll
module Commands
class Migrate < Command
MIGRATORS = {
:csv => 'CSV',
:drupal => 'Drupal',
:enki => 'Enki',
:mephisto => 'Mephisto',
:mt => 'MT',
:posterous => 'Posterous',
:textpattern => 'TextPattern',
:tumblr => 'Tumblr',
:typo => 'Typo',
:wordpressdotcom => 'WordpressDotCom',
:wordpress => 'WordPress'
}
def self.process(migrator, options)
abort 'missing argument. Please specify a migrator' if migrator.nil?
migrator = migrator.downcase
cmd_options = []
[ :file, :dbname, :user, :pass, :host, :site ].each do |p|
cmd_options << "\"#{options[p]}\"" unless options[p].nil?
end
if MIGRATORS.keys.include?(migrator)
app_root = File.expand_path(
File.join(File.dirname(__FILE__), '..', '..', '..')
)
require "#{app_root}/lib/jekyll/migrators/#{migrator}"
if Jekyll.const_defiend?(MIGRATORS[migrator.to_sym])
puts 'Importing...'
migrator_class = Jekyll.const_get(MIGRATORS[migrator.to_sym])
migrator_class.process(*cmd_options)
exit 0
end
end
abort 'invalid migrator. Please specify a valid migrator'
end
end
end
end

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
module Jekyll module Jekyll
module Commands module Commands
class Serve < Command class Serve < Command
@ -9,9 +10,12 @@ module Jekyll
FileUtils.mkdir_p(destination) FileUtils.mkdir_p(destination)
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes mime_types_file = File.expand_path('../mime.types', File.dirname(__FILE__))
mime_types.store 'js', 'application/javascript' mime_types = WEBrick::HTTPUtils::load_mime_types(mime_types_file)
mime_types.store 'svg', 'image/svg+xml'
# recreate NondisclosureName under utf-8 circumstance
fh_option = WEBrick::Config::FileHandler
fh_option[:NondisclosureName] = ['.ht*','~*']
s = HTTPServer.new( s = HTTPServer.new(
:Port => options['port'], :Port => options['port'],
@ -19,7 +23,7 @@ module Jekyll
:MimeTypes => mime_types :MimeTypes => mime_types
) )
s.mount(options['baseurl'], HTTPServlet::FileHandler, destination) s.mount(options['baseurl'], HTTPServlet::FileHandler, destination, fh_option)
t = Thread.new { s.start } t = Thread.new { s.start }
trap("INT") { s.shutdown } trap("INT") { s.shutdown }
t.join() t.join()

View File

@ -30,7 +30,7 @@ module Jekyll
if self.content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m if self.content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = $POSTMATCH self.content = $POSTMATCH
self.data = YAML.load($1) self.data = YAML.safe_load($1)
end end
rescue => e rescue => e
puts "Error reading file #{File.join(base, name)}: #{e.message}" puts "Error reading file #{File.join(base, name)}: #{e.message}"

View File

@ -24,7 +24,7 @@ class Hash
# and then the plural key, and handling any nil entries. # and then the plural key, and handling any nil entries.
# +hash+ the hash to read from # +hash+ the hash to read from
# +singular_key+ the singular key # +singular_key+ the singular key
# +plural_key+ the singular key # +plural_key+ the plural key
# #
# Returns an array # Returns an array
def pluralized_array(singular_key, plural_key) def pluralized_array(singular_key, plural_key)

35
lib/jekyll/draft.rb Normal file
View File

@ -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)
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

View File

@ -1,26 +0,0 @@
module Jekyll
module CSV
# Reads a csv with title, permalink, body, published_at, and filter.
# It creates a post file for each row in the csv
def self.process(file = "posts.csv")
FileUtils.mkdir_p "_posts"
posts = 0
FasterCSV.foreach(file) do |row|
next if row[0] == "title"
posts += 1
name = row[3].split(" ")[0]+"-"+row[1]+(row[4] =~ /markdown/ ? ".markdown" : ".textile")
File.open("_posts/#{name}", "w") do |f|
f.puts <<-HEADER
---
layout: post
title: #{row[0]}
---
HEADER
f.puts row[2]
end
end
"Created #{posts} posts!"
end
end
end

View File

@ -1,103 +0,0 @@
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'safe_yaml'
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
# installed, running the following commands should work:
# $ sudo gem install sequel
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
module Jekyll
module Drupal
# Reads a MySQL database via Sequel and creates a post file for each post
# in wp_posts that has post_status = 'publish'. This restriction is made
# because 'draft' posts are not guaranteed to have valid dates.
QUERY = "SELECT n.nid, \
n.title, \
nr.body, \
n.created, \
n.status \
FROM node AS n, \
node_revisions AS nr \
WHERE (n.type = 'blog' OR n.type = 'story') \
AND n.vid = nr.vid"
def self.process(dbname, user, pass, host = 'localhost', prefix = '')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
if prefix != ''
QUERY[" node "] = " " + prefix + "node "
QUERY[" node_revisions "] = " " + prefix + "node_revisions "
end
FileUtils.mkdir_p "_posts"
FileUtils.mkdir_p "_drafts"
# Create the refresh layout
# Change the refresh url if you customized your permalink config
File.open("_layouts/refresh.html", "w") do |f|
f.puts <<EOF
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="0;url={{ page.refresh_to_post_id }}.html" />
</head>
</html>
EOF
end
db[QUERY].each do |post|
# Get required fields and construct Jekyll compatible name
node_id = post[:nid]
title = post[:title]
content = post[:body]
created = post[:created]
time = Time.at(created)
is_published = post[:status] == 1
dir = is_published ? "_posts" : "_drafts"
slug = title.strip.downcase.gsub(/(&|&amp;)/, ' and ').gsub(/[\s\.\/\\]/, '-').gsub(/[^\w-]/, '').gsub(/[-_]{2,}/, '-').gsub(/^[-_]/, '').gsub(/[-_]$/, '')
name = time.strftime("%Y-%m-%d-") + slug + '.md'
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header
data = {
'layout' => 'post',
'title' => title.to_s,
'created' => created,
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
# Write out the data and content to file
File.open("#{dir}/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
# Make a file to redirect from the old Drupal URL
if is_published
aliases = db["SELECT dst FROM #{prefix}url_alias WHERE src = ?", "node/#{node_id}"].all
aliases.push(:dst => "node/#{node_id}")
aliases.each do |url_alias|
FileUtils.mkdir_p url_alias[:dst]
File.open("#{url_alias[:dst]}/index.md", "w") do |f|
f.puts "---"
f.puts "layout: refresh"
f.puts "refresh_to_post_id: /#{time.strftime("%Y/%m/%d/") + slug}"
f.puts "---"
end
end
end
end
# TODO: Make dirs & files for nodes of type 'page'
# Make refresh pages for these as well
# TODO: Make refresh dirs & files according to entries in url_alias table
end
end
end

View File

@ -1,49 +0,0 @@
# Adapted by Rodrigo Pinto <rodrigopqn@gmail.com>
# Based on typo.rb by Toby DiPasquale
require 'fileutils'
require 'rubygems'
require 'sequel'
module Jekyll
module Enki
SQL = <<-EOS
SELECT p.id,
p.title,
p.slug,
p.body,
p.published_at as date,
p.cached_tag_list as tags
FROM posts p
EOS
# Just working with postgres, but can be easily adapted
# to work with both mysql and postgres.
def self.process(dbname, user, pass, host = 'localhost')
FileUtils.mkdir_p('_posts')
db = Sequel.postgres(:database => dbname,
:user => user,
:password => pass,
:host => host,
:encoding => 'utf8')
db[SQL].each do |post|
name = [ sprintf("%.04d", post[:date].year),
sprintf("%.02d", post[:date].month),
sprintf("%.02d", post[:date].day),
post[:slug].strip ].join('-')
name += '.textile'
File.open("_posts/#{name}", 'w') do |f|
f.puts({ 'layout' => 'post',
'title' => post[:title].to_s,
'enki_id' => post[:id],
'categories' => post[:tags]
}.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
f.puts '---'
f.puts post[:body].delete("\r")
end
end
end
end
end

View File

@ -1,53 +0,0 @@
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'safe_yaml'
# NOTE: This migrator is made for Joomla 1.5 databases.
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
# installed, running the following commands should work:
# $ sudo gem install sequel
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
module Jekyll
module Joomla
def self.process(dbname, user, pass, host = 'localhost', table_prefix = 'jos_', section = '1')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
FileUtils.mkdir_p("_posts")
# Reads a MySQL database via Sequel and creates a post file for each
# post in wp_posts that has post_status = 'publish'. This restriction is
# made because 'draft' posts are not guaranteed to have valid dates.
query = "SELECT `title`, `alias`, CONCAT(`introtext`,`fulltext`) as content, `created`, `id` FROM #{table_prefix}content WHERE state = '0' OR state = '1' AND sectionid = '#{section}'"
db[query].each do |post|
# Get required fields and construct Jekyll compatible name.
title = post[:title]
slug = post[:alias]
date = post[:created]
content = post[:content]
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
slug]
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header.
data = {
'layout' => 'post',
'title' => title.to_s,
'joomla_id' => post[:id],
'joomla_url' => post[:alias],
'date' => date
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
# Write out the data and content to file
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
end
end
end

View File

@ -1,52 +0,0 @@
require 'safe_yaml'
require 'fileutils'
module Jekyll
module Marley
def self.regexp
{ :id => /^\d{0,4}-{0,1}(.*)$/,
:title => /^#\s*(.*)\s+$/,
:title_with_date => /^#\s*(.*)\s+\(([0-9\/]+)\)$/,
:published_on => /.*\s+\(([0-9\/]+)\)$/,
:perex => /^([^\#\n]+\n)$/,
:meta => /^\{\{\n(.*)\}\}\n$/mi # Multiline Regexp
}
end
def self.process(marley_data_dir)
raise ArgumentError, "marley dir #{marley_data_dir} not found" unless File.directory?(marley_data_dir)
FileUtils.mkdir_p "_posts"
posts = 0
Dir["#{marley_data_dir}/**/*.txt"].each do |f|
next unless File.exists?(f)
#copied over from marley's app/lib/post.rb
file_content = File.read(f)
meta_content = file_content.slice!( self.regexp[:meta] )
body = file_content.sub( self.regexp[:title], '').sub( self.regexp[:perex], '').strip
title = file_content.scan( self.regexp[:title] ).first.to_s.strip
prerex = file_content.scan( self.regexp[:perex] ).first.to_s.strip
published_on = DateTime.parse( post[:published_on] ) rescue File.mtime( File.dirname(f) )
meta = ( meta_content ) ? YAML::load( meta_content.scan( self.regexp[:meta]).to_s ) : {}
meta['title'] = title
meta['layout'] = 'post'
formatted_date = published_on.strftime('%Y-%m-%d')
post_name = File.dirname(f).split(%r{/}).last.gsub(/\A\d+-/, '')
name = "#{formatted_date}-#{post_name}"
File.open("_posts/#{name}.markdown", "w") do |f|
f.puts meta.to_yaml
f.puts "---\n"
f.puts "\n#{prerex}\n\n" if prerex
f.puts body
end
posts += 1
end
"Created #{posts} posts!"
end
end
end

View File

@ -1,84 +0,0 @@
# Quickly hacked together my Michael Ivey
# Based on mt.rb by Nick Gerakines, open source and publically
# available under the MIT license. Use this module at your own risk.
require 'rubygems'
require 'sequel'
require 'fastercsv'
require 'fileutils'
require File.join(File.dirname(__FILE__),"csv.rb")
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
# installed, running the following commands should work:
# $ sudo gem install sequel
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
module Jekyll
module Mephisto
#Accepts a hash with database config variables, exports mephisto posts into a csv
#export PGPASSWORD if you must
def self.postgres(c)
sql = <<-SQL
BEGIN;
CREATE TEMP TABLE jekyll AS
SELECT title, permalink, body, published_at, filter FROM contents
WHERE user_id = 1 AND type = 'Article' ORDER BY published_at;
COPY jekyll TO STDOUT WITH CSV HEADER;
ROLLBACK;
SQL
command = %Q(psql -h #{c[:host] || "localhost"} -c "#{sql.strip}" #{c[:database]} #{c[:username]} -o #{c[:filename] || "posts.csv"})
puts command
`#{command}`
CSV.process
end
# This query will pull blog posts from all entries across all blogs. If
# you've got unpublished, deleted or otherwise hidden posts please sift
# through the created posts to make sure nothing is accidently published.
QUERY = "SELECT id, \
permalink, \
body, \
published_at, \
title \
FROM contents \
WHERE user_id = 1 AND \
type = 'Article' AND \
published_at IS NOT NULL \
ORDER BY published_at"
def self.process(dbname, user, pass, host = 'localhost')
db = Sequel.mysql(dbname, :user => user,
:password => pass,
:host => host,
:encoding => 'utf8')
FileUtils.mkdir_p "_posts"
db[QUERY].each do |post|
title = post[:title]
slug = post[:permalink]
date = post[:published_at]
content = post[:body]
# Ideally, this script would determine the post format (markdown,
# html, etc) and create files with proper extensions. At this point
# it just assumes that markdown will be acceptable.
name = [date.year, date.month, date.day, slug].join('-') + ".markdown"
data = {
'layout' => 'post',
'title' => title.to_s,
'mt_id' => post[:entry_id],
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
end
end
end

View File

@ -1,86 +0,0 @@
# Created by Nick Gerakines, open source and publically available under the
# MIT license. Use this module at your own risk.
# I'm an Erlang/Perl/C++ guy so please forgive my dirty ruby.
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'safe_yaml'
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
# installed, running the following commands should work:
# $ sudo gem install sequel
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
module Jekyll
module MT
# This query will pull blog posts from all entries across all blogs. If
# you've got unpublished, deleted or otherwise hidden posts please sift
# through the created posts to make sure nothing is accidently published.
QUERY = "SELECT entry_id, \
entry_basename, \
entry_text, \
entry_text_more, \
entry_authored_on, \
entry_title, \
entry_convert_breaks \
FROM mt_entry"
def self.process(dbname, user, pass, host = 'localhost')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
FileUtils.mkdir_p "_posts"
db[QUERY].each do |post|
title = post[:entry_title]
slug = post[:entry_basename].gsub(/_/, '-')
date = post[:entry_authored_on]
content = post[:entry_text]
more_content = post[:entry_text_more]
entry_convert_breaks = post[:entry_convert_breaks]
# Be sure to include the body and extended body.
if more_content != nil
content = content + " \n" + more_content
end
# Ideally, this script would determine the post format (markdown,
# html, etc) and create files with proper extensions. At this point
# it just assumes that markdown will be acceptable.
name = [date.year, date.month, date.day, slug].join('-') + '.' +
self.suffix(entry_convert_breaks)
data = {
'layout' => 'post',
'title' => title.to_s,
'mt_id' => post[:entry_id],
'date' => date
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
end
def self.suffix(entry_type)
if entry_type.nil? || entry_type.include?("markdown")
# The markdown plugin I have saves this as
# "markdown_with_smarty_pants", so I just look for "markdown".
"markdown"
elsif entry_type.include?("textile")
# This is saved as "textile_2" on my installation of MT 5.1.
"textile"
elsif entry_type == "0" || entry_type.include?("richtext")
# Richtext looks to me like it's saved as HTML, so I include it here.
"html"
else
# Other values might need custom work.
entry_type
end
end
end
end

View File

@ -1,67 +0,0 @@
require 'rubygems'
require 'jekyll'
require 'fileutils'
require 'net/http'
require 'uri'
require "json"
# ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, api_key, blog)'
module Jekyll
module Posterous
def self.fetch(uri_str, limit = 10)
# You should choose better exception.
raise ArgumentError, 'Stuck in a redirect loop. Please double check your email and password' if limit == 0
response = nil
Net::HTTP.start('posterous.com') do |http|
req = Net::HTTP::Get.new(uri_str)
req.basic_auth @email, @pass
response = http.request(req)
end
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
else response.error!
end
end
def self.process(email, pass, api_token, blog = 'primary')
@email, @pass, @api_token = email, pass, api_token
FileUtils.mkdir_p "_posts"
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}").body)
page = 1
while posts.any?
posts.each do |post|
title = post["title"]
slug = title.gsub(/[^[:alnum:]]+/, '-').downcase
date = Date.parse(post["display_date"])
content = post["body_html"]
published = !post["is_private"]
name = "%02d-%02d-%02d-%s.html" % [date.year, date.month, date.day, slug]
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header
data = {
'layout' => 'post',
'title' => title.to_s,
'published' => published
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
# Write out the data and content to file
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
page += 1
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}&page=#{page}").body)
end
end
end
end

View File

@ -1,47 +0,0 @@
# Created by Kendall Buchanan (https://github.com/kendagriff) on 2011-12-22.
# Use at your own risk. The end.
#
# Usage:
# (URL)
# ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('http://yourdomain.com/your-favorite-feed.xml')"
#
# (Local file)
# ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('./somefile/on/your/computer.xml')"
require 'rubygems'
require 'rss/1.0'
require 'rss/2.0'
require 'open-uri'
require 'fileutils'
require 'safe_yaml'
module Jekyll
module MigrateRSS
# The `source` argument may be a URL or a local file.
def self.process(source)
content = ""
open(source) { |s| content = s.read }
rss = RSS::Parser.parse(content, false)
raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless rss
rss.items.each do |item|
formatted_date = item.date.strftime('%Y-%m-%d')
post_name = item.title.split(%r{ |!|/|:|&|-|$|,}).map { |i| i.downcase if i != '' }.compact.join('-')
name = "#{formatted_date}-#{post_name}"
header = {
'layout' => 'post',
'title' => item.title
}
File.open("_posts/#{name}.html", "w") do |f|
f.puts header.to_yaml
f.puts "---\n"
f.puts item.description
end
end
end
end
end

View File

@ -1,58 +0,0 @@
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'safe_yaml'
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
# installed, running the following commands should work:
# $ sudo gem install sequel
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
module Jekyll
module TextPattern
# Reads a MySQL database via Sequel and creates a post file for each post.
# The only posts selected are those with a status of 4 or 5, which means
# "live" and "sticky" respectively.
# Other statuses are 1 => draft, 2 => hidden and 3 => pending.
QUERY = "SELECT Title, \
url_title, \
Posted, \
Body, \
Keywords \
FROM textpattern \
WHERE Status = '4' OR \
Status = '5'"
def self.process(dbname, user, pass, host = 'localhost')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
FileUtils.mkdir_p "_posts"
db[QUERY].each do |post|
# Get required fields and construct Jekyll compatible name.
title = post[:Title]
slug = post[:url_title]
date = post[:Posted]
content = post[:Body]
name = [date.strftime("%Y-%m-%d"), slug].join('-') + ".textile"
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header.
data = {
'layout' => 'post',
'title' => title.to_s,
'tags' => post[:Keywords].split(',')
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
# Write out the data and content to file.
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
end
end
end

View File

@ -1,195 +0,0 @@
require 'rubygems'
require 'open-uri'
require 'fileutils'
require 'nokogiri'
require 'date'
require 'json'
require 'uri'
require 'jekyll'
module Jekyll
module Tumblr
def self.process(url, format = "html", grab_images = false,
add_highlights = false, rewrite_urls = true)
@grab_images = grab_images
FileUtils.mkdir_p "_posts/tumblr"
url += "/api/read/json/"
per_page = 50
posts = []
# Two passes are required so that we can rewrite URLs.
# First pass builds up an array of each post as a hash.
begin
current_page = (current_page || -1) + 1
feed = open(url + "?num=#{per_page}&start=#{current_page * per_page}")
json = feed.readlines.join("\n")[21...-2] # Strip Tumblr's JSONP chars.
blog = JSON.parse(json)
puts "Page: #{current_page + 1} - Posts: #{blog["posts"].size}"
posts += blog["posts"].map { |post| post_to_hash(post, format) }
end until blog["posts"].size < per_page
# Rewrite URLs and create redirects.
posts = rewrite_urls_and_redirects posts if rewrite_urls
# Second pass for writing post files.
posts.each do |post|
if format == "md"
post[:content] = html_to_markdown post[:content]
post[:content] = add_syntax_highlights post[:content] if add_highlights
end
File.open("_posts/tumblr/#{post[:name]}", "w") do |f|
f.puts post[:header].to_yaml + "---\n" + post[:content]
end
end
end
private
# Converts each type of Tumblr post to a hash with all required
# data for Jekyll.
def self.post_to_hash(post, format)
case post['type']
when "regular"
title = post["regular-title"]
content = post["regular-body"]
when "link"
title = post["link-text"] || post["link-url"]
content = "<a href=\"#{post["link-url"]}\">#{title}</a>"
unless post["link-description"].nil?
content << "<br/>" + post["link-description"]
end
when "photo"
title = post["photo-caption"]
max_size = post.keys.map{ |k| k.gsub("photo-url-", "").to_i }.max
url = post["photo-url"] || post["photo-url-#{max_size}"]
ext = "." + post[post.keys.select { |k|
k =~ /^photo-url-/ && post[k].split("/").last =~ /\./
}.first].split(".").last
content = "<img src=\"#{save_file(url, ext)}\"/>"
unless post["photo-link-url"].nil?
content = "<a href=\"#{post["photo-link-url"]}\">#{content}</a>"
end
when "audio"
if !post["id3-title"].nil?
title = post["id3-title"]
content = post.at["audio-player"] + "<br/>" + post["audio-caption"]
else
title = post["audio-caption"]
content = post.at["audio-player"]
end
when "quote"
title = post["quote-text"]
content = "<blockquote>#{post["quote-text"]}</blockquote>"
unless post["quote-source"].nil?
content << "&#8212;" + post["quote-source"]
end
when "conversation"
title = post["conversation-title"]
content = "<section><dialog>"
post["conversation"]["line"].each do |line|
content << "<dt>#{line['label']}</dt><dd>#{line}</dd>"
end
content << "</section></dialog>"
when "video"
title = post["video-title"]
content = post["video-player"]
unless post["video-caption"].nil?
content << "<br/>" + post["video-caption"]
end
end
date = Date.parse(post['date']).to_s
title = Nokogiri::HTML(title).text
slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
slug = slug.slice(0..200) if slug.length > 200
{
:name => "#{date}-#{slug}.#{format}",
:header => {
"layout" => "post",
"title" => title,
"tags" => post["tags"],
},
:content => content,
:url => post["url"],
:slug => post["url-with-slug"],
}
end
# Create a Hash of old urls => new urls, for rewriting and
# redirects, and replace urls in each post. Instantiate Jekyll
# site/posts to get the correct permalink format.
def self.rewrite_urls_and_redirects(posts)
site = Jekyll::Site.new(Jekyll.configuration({}))
urls = Hash[posts.map { |post|
# Create an initial empty file for the post so that
# we can instantiate a post object.
File.open("_posts/tumblr/#{post[:name]}", "w")
tumblr_url = URI.parse(post[:slug]).path
jekyll_url = Jekyll::Post.new(site, Dir.pwd, "", "tumblr/" + post[:name]).url
redirect_dir = tumblr_url.sub(/\//, "") + "/"
FileUtils.mkdir_p redirect_dir
File.open(redirect_dir + "index.html", "w") do |f|
f.puts "<html><head><meta http-equiv='Refresh' content='0; " +
"url=#{jekyll_url}'></head><body></body></html>"
end
[tumblr_url, jekyll_url]
}]
posts.map { |post|
urls.each do |tumblr_url, jekyll_url|
post[:content].gsub!(/#{tumblr_url}/i, jekyll_url)
end
post
}
end
# Uses Python's html2text to convert a post's content to
# markdown. Preserve HTML tables as per the markdown docs.
def self.html_to_markdown(content)
preserve = ["table", "tr", "th", "td"]
preserve.each do |tag|
content.gsub!(/<#{tag}/i, "$$" + tag)
content.gsub!(/<\/#{tag}/i, "||" + tag)
end
content = %x[echo '#{content.gsub("'", "''")}' | html2text]
preserve.each do |tag|
content.gsub!("$$" + tag, "<" + tag)
content.gsub!("||" + tag, "</" + tag)
end
content
end
# Adds pygments highlight tags to code blocks in posts that use
# markdown format. This doesn't guess the language of the code
# block, so you should modify this to suit your own content.
# For example, my code block only contain Python and JavaScript,
# so I can assume the block is JavaScript if it contains a
# semi-colon.
def self.add_syntax_highlights(content)
lines = content.split("\n")
block, indent, lang, start = false, /^ /, nil, nil
lines.each_with_index do |line, i|
if !block && line =~ indent
block = true
lang = "python"
start = i
elsif block
lang = "javascript" if line =~ /;$/
block = line =~ indent && i < lines.size - 1 # Also handle EOF
if !block
lines[start] = "{% highlight #{lang} %}"
lines[i - 1] = "{% endhighlight %}"
end
lines[i] = lines[i].sub(indent, "")
end
end
lines.join("\n")
end
def self.save_file(url, ext)
if @grab_images
path = "tumblr_files/#{url.split('/').last}"
path += ext unless path =~ /#{ext}$/
FileUtils.mkdir_p "tumblr_files"
File.open(path, "w") { |f| f.write(open(url).read) }
url = "/" + path
end
url
end
end
end

View File

@ -1,51 +0,0 @@
# Author: Toby DiPasquale <toby@cbcg.net>
require 'fileutils'
require 'rubygems'
require 'sequel'
require 'safe_yaml'
module Jekyll
module Typo
# This SQL *should* work for both MySQL and PostgreSQL, but I haven't
# tested PostgreSQL yet (as of 2008-12-16).
SQL = <<-EOS
SELECT c.id id,
c.title title,
c.permalink slug,
c.body body,
c.published_at date,
c.state state,
COALESCE(tf.name, 'html') filter
FROM contents c
LEFT OUTER JOIN text_filters tf
ON c.text_filter_id = tf.id
EOS
def self.process dbname, user, pass, host='localhost'
FileUtils.mkdir_p '_posts'
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
db[SQL].each do |post|
next unless post[:state] =~ /published/
name = [ sprintf("%.04d", post[:date].year),
sprintf("%.02d", post[:date].month),
sprintf("%.02d", post[:date].day),
post[:slug].strip ].join('-')
# Can have more than one text filter in this field, but we just want
# the first one for this.
name += '.' + post[:filter].split(' ')[0]
File.open("_posts/#{name}", 'w') do |f|
f.puts({ 'layout' => 'post',
'title' => post[:title].to_s,
'typo_id' => post[:id]
}.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
f.puts '---'
f.puts post[:body].delete("\r")
end
end
end
end
end

View File

@ -1,295 +0,0 @@
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'safe_yaml'
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
# installed, running the following commands should work:
# $ sudo gem install sequel
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
module Jekyll
module WordPress
# Main migrator function. Call this to perform the migration.
#
# dbname:: The name of the database
# user:: The database user name
# pass:: The database user's password
# host:: The address of the MySQL database host. Default: 'localhost'
# options:: A hash table of configuration options.
#
# Supported options are:
#
# :table_prefix:: Prefix of database tables used by WordPress.
# Default: 'wp_'
# :clean_entities:: If true, convert non-ASCII characters to HTML
# entities in the posts, comments, titles, and
# names. Requires the 'htmlentities' gem to
# work. Default: true.
# :comments:: If true, migrate post comments too. Comments
# are saved in the post's YAML front matter.
# Default: true.
# :categories:: If true, save the post's categories in its
# YAML front matter.
# :tags:: If true, save the post's tags in its
# YAML front matter.
# :more_excerpt:: If true, when a post has no excerpt but
# does have a <!-- more --> tag, use the
# preceding post content as the excerpt.
# Default: true.
# :more_anchor:: If true, convert a <!-- more --> tag into
# two HTML anchors with ids "more" and
# "more-NNN" (where NNN is the post number).
# Default: true.
# :status:: Array of allowed post statuses. Only
# posts with matching status will be migrated.
# Known statuses are :publish, :draft, :private,
# and :revision. If this is nil or an empty
# array, all posts are migrated regardless of
# status. Default: [:publish].
#
def self.process(dbname, user, pass, host='localhost', options={})
options = {
:table_prefix => 'wp_',
:clean_entities => true,
:comments => true,
:categories => true,
:tags => true,
:more_excerpt => true,
:more_anchor => true,
:status => [:publish] # :draft, :private, :revision
}.merge(options)
if options[:clean_entities]
begin
require 'htmlentities'
rescue LoadError
STDERR.puts "Could not require 'htmlentities', so the " +
":clean_entities option is now disabled."
options[:clean_entities] = false
end
end
FileUtils.mkdir_p("_posts")
db = Sequel.mysql(dbname, :user => user, :password => pass,
:host => host, :encoding => 'utf8')
px = options[:table_prefix]
posts_query = "
SELECT
posts.ID AS `id`,
posts.guid AS `guid`,
posts.post_type AS `type`,
posts.post_status AS `status`,
posts.post_title AS `title`,
posts.post_name AS `slug`,
posts.post_date AS `date`,
posts.post_content AS `content`,
posts.post_excerpt AS `excerpt`,
posts.comment_count AS `comment_count`,
users.display_name AS `author`,
users.user_login AS `author_login`,
users.user_email AS `author_email`,
users.user_url AS `author_url`
FROM #{px}posts AS `posts`
LEFT JOIN #{px}users AS `users`
ON posts.post_author = users.ID"
if options[:status] and not options[:status].empty?
status = options[:status][0]
posts_query << "
WHERE posts.post_status = '#{status.to_s}'"
options[:status][1..-1].each do |status|
posts_query << " OR
posts.post_status = '#{status.to_s}'"
end
end
db[posts_query].each do |post|
process_post(post, db, options)
end
end
def self.process_post(post, db, options)
px = options[:table_prefix]
title = post[:title]
if options[:clean_entities]
title = clean_entities(title)
end
slug = post[:slug]
if !slug or slug.empty?
slug = sluggify(title)
end
date = post[:date] || Time.now
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month,
date.day, slug]
content = post[:content].to_s
if options[:clean_entities]
content = clean_entities(content)
end
excerpt = post[:excerpt].to_s
more_index = content.index(/<!-- *more *-->/)
more_anchor = nil
if more_index
if options[:more_excerpt] and
(post[:excerpt].nil? or post[:excerpt].empty?)
excerpt = content[0...more_index]
end
if options[:more_anchor]
more_link = "more"
content.sub!(/<!-- *more *-->/,
"<a id=\"more\"></a>" +
"<a id=\"more-#{post[:id]}\"></a>")
end
end
categories = []
tags = []
if options[:categories] or options[:tags]
cquery =
"SELECT
terms.name AS `name`,
ttax.taxonomy AS `type`
FROM
#{px}terms AS `terms`,
#{px}term_relationships AS `trels`,
#{px}term_taxonomy AS `ttax`
WHERE
trels.object_id = '#{post[:id]}' AND
trels.term_taxonomy_id = ttax.term_taxonomy_id AND
terms.term_id = ttax.term_id"
db[cquery].each do |term|
if options[:categories] and term[:type] == "category"
if options[:clean_entities]
categories << clean_entities(term[:name])
else
categories << term[:name]
end
elsif options[:tags] and term[:type] == "post_tag"
if options[:clean_entities]
tags << clean_entities(term[:name])
else
tags << term[:name]
end
end
end
end
comments = []
if options[:comments] and post[:comment_count].to_i > 0
cquery =
"SELECT
comment_ID AS `id`,
comment_author AS `author`,
comment_author_email AS `author_email`,
comment_author_url AS `author_url`,
comment_date AS `date`,
comment_date_gmt AS `date_gmt`,
comment_content AS `content`
FROM #{px}comments
WHERE
comment_post_ID = '#{post[:id]}' AND
comment_approved != 'spam'"
db[cquery].each do |comment|
comcontent = comment[:content].to_s
if comcontent.respond_to?(:force_encoding)
comcontent.force_encoding("UTF-8")
end
if options[:clean_entities]
comcontent = clean_entities(comcontent)
end
comauthor = comment[:author].to_s
if options[:clean_entities]
comauthor = clean_entities(comauthor)
end
comments << {
'id' => comment[:id].to_i,
'author' => comauthor,
'author_email' => comment[:author_email].to_s,
'author_url' => comment[:author_url].to_s,
'date' => comment[:date].to_s,
'date_gmt' => comment[:date_gmt].to_s,
'content' => comcontent,
}
end
comments.sort!{ |a,b| a['id'] <=> b['id'] }
end
# Get the relevant fields as a hash, delete empty fields and
# convert to YAML for the header.
data = {
'layout' => post[:type].to_s,
'status' => post[:status].to_s,
'published' => (post[:status].to_s == "publish"),
'title' => title.to_s,
'author' => post[:author].to_s,
'author_login' => post[:author_login].to_s,
'author_email' => post[:author_email].to_s,
'author_url' => post[:author_url].to_s,
'excerpt' => excerpt,
'more_anchor' => more_anchor,
'wordpress_id' => post[:id],
'wordpress_url' => post[:guid].to_s,
'date' => date,
'categories' => options[:categories] ? categories : nil,
'tags' => options[:tags] ? tags : nil,
'comments' => options[:comments] ? comments : nil,
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
# Write out the data and content to file
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
def self.clean_entities( text )
if text.respond_to?(:force_encoding)
text.force_encoding("UTF-8")
end
text = HTMLEntities.new.encode(text, :named)
# We don't want to convert these, it would break all
# HTML tags in the post and comments.
text.gsub!("&amp;", "&")
text.gsub!("&lt;", "<")
text.gsub!("&gt;", ">")
text.gsub!("&quot;", '"')
text.gsub!("&apos;", "'")
text.gsub!("/", "&#47;")
text
end
def self.sluggify( title )
begin
require 'unidecode'
title = title.to_ascii
rescue LoadError
STDERR.puts "Could not require 'unidecode'. If your post titles have non-ASCII characters, you could get nicer permalinks by installing unidecode."
end
title.downcase.gsub(/[^0-9A-Za-z]+/, " ").strip.gsub(" ", "-")
end
end
end

View File

@ -1,82 +0,0 @@
# coding: utf-8
require 'rubygems'
require 'hpricot'
require 'fileutils'
require 'safe_yaml'
require 'time'
module Jekyll
# This importer takes a wordpress.xml file, which can be exported from your
# wordpress.com blog (/wp-admin/export.php).
module WordpressDotCom
def self.process(filename = "wordpress.xml")
import_count = Hash.new(0)
doc = Hpricot::XML(File.read(filename))
(doc/:channel/:item).each do |item|
title = item.at(:title).inner_text.strip
permalink_title = item.at('wp:post_name').inner_text
# Fallback to "prettified" title if post_name is empty (can happen)
if permalink_title == ""
permalink_title = sluggify(title)
end
date = Time.parse(item.at('wp:post_date').inner_text)
status = item.at('wp:status').inner_text
if status == "publish"
published = true
else
published = false
end
type = item.at('wp:post_type').inner_text
tags = (item/:category).map{|c| c.inner_text}.reject{|c| c == 'Uncategorized'}.uniq
metas = Hash.new
item.search("wp:postmeta").each do |meta|
key = meta.at('wp:meta_key').inner_text
value = meta.at('wp:meta_value').inner_text
metas[key] = value;
end
name = "#{date.strftime('%Y-%m-%d')}-#{permalink_title}.html"
header = {
'layout' => type,
'title' => title,
'tags' => tags,
'status' => status,
'type' => type,
'published' => published,
'meta' => metas
}
begin
FileUtils.mkdir_p "_#{type}s"
File.open("_#{type}s/#{name}", "w") do |f|
f.puts header.to_yaml
f.puts '---'
f.puts item.at('content:encoded').inner_text
end
rescue => e
puts "Couldn't import post!"
puts "Title: #{title}"
puts "Name/Slug: #{name}\n"
puts "Error: #{e.message}"
next
end
import_count[type] += 1
end
import_count.each do |key, value|
puts "Imported #{value} #{key}s"
end
end
def self.sluggify(title)
title.downcase.split.join('-').gsub('/','-')
end
end
end

1588
lib/jekyll/mime.types Normal file

File diff suppressed because it is too large Load Diff

View File

@ -56,10 +56,16 @@ module Jekyll
# #
# Returns the template String. # Returns the template String.
def template def template
if self.site.permalink_style == :pretty && !index? && html? if self.site.permalink_style == :pretty
"/:basename/" if index? && html?
"/:path/"
elsif html?
"/:path/:basename/"
else else
"/:basename:output_ext" "/:path/:basename:output_ext"
end
else
"/:path/:basename:output_ext"
end end
end end
@ -73,6 +79,7 @@ module Jekyll
permalink permalink
else else
{ {
"path" => @dir,
"basename" => self.basename, "basename" => self.basename,
"output_ext" => self.output_ext, "output_ext" => self.output_ext,
}.inject(template) { |result, token| }.inject(template) { |result, token|
@ -116,7 +123,7 @@ module Jekyll
# Returns the Hash representation of this Page. # Returns the Hash representation of this Page.
def to_liquid def to_liquid
self.data.deep_merge({ self.data.deep_merge({
"url" => File.join(@dir, self.url), "url" => self.url,
"content" => self.content }) "content" => self.content })
end end
@ -128,7 +135,7 @@ module Jekyll
def destination(dest) def destination(dest)
# The url needs to be unescaped in order to preserve the correct # The url needs to be unescaped in order to preserve the correct
# filename. # filename.
path = File.join(dest, @dir, CGI.unescape(self.url)) path = File.join(dest, CGI.unescape(self.url))
path = File.join(path, "index.html") if self.url =~ /\/$/ path = File.join(path, "index.html") if self.url =~ /\/$/
path path
end end

View File

@ -29,12 +29,11 @@ module Jekyll
# site - The Site. # site - The Site.
# base - The String path to the dir containing the post file. # base - The String path to the dir containing the post file.
# name - The String filename of 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. # Returns the new Post.
def initialize(site, source, dir, name) def initialize(site, source, dir, name)
@site = site @site = site
@base = File.join(source, dir, '_posts') @base = self.containing_dir(source, dir)
@name = name @name = name
self.categories = dir.split('/').reject { |x| x.empty? } self.categories = dir.split('/').reject { |x| x.empty? }
@ -65,6 +64,11 @@ module Jekyll
end end
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. # Read the YAML frontmatter.
# #
# base - The String path to the dir containing the file. # base - The String path to the dir containing the file.

View File

@ -5,7 +5,7 @@ module Jekyll
attr_accessor :config, :layouts, :posts, :pages, :static_files, attr_accessor :config, :layouts, :posts, :pages, :static_files,
:categories, :exclude, :include, :source, :dest, :lsi, :pygments, :categories, :exclude, :include, :source, :dest, :lsi, :pygments,
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts, :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts,
:keep_files :show_drafts, :keep_files
attr_accessor :converters, :generators attr_accessor :converters, :generators
@ -18,13 +18,14 @@ module Jekyll
self.safe = config['safe'] self.safe = config['safe']
self.source = File.expand_path(config['source']) self.source = File.expand_path(config['source'])
self.dest = File.expand_path(config['destination']) self.dest = File.expand_path(config['destination'])
self.plugins = Array(config['plugins']).map { |d| File.expand_path(d) } self.plugins = plugins_path
self.lsi = config['lsi'] self.lsi = config['lsi']
self.pygments = config['pygments'] self.pygments = config['pygments']
self.permalink_style = config['permalink'].to_sym self.permalink_style = config['permalink'].to_sym
self.exclude = config['exclude'] || [] self.exclude = config['exclude'] || []
self.include = config['include'] || [] self.include = config['include'] || []
self.future = config['future'] self.future = config['future']
self.show_drafts = config['show_drafts'] || nil
self.limit_posts = config['limit_posts'] || nil self.limit_posts = config['limit_posts'] || nil
self.keep_files = config['keep_files'] || [] self.keep_files = config['keep_files'] || []
@ -87,16 +88,18 @@ module Jekyll
end end
end end
self.converters = Jekyll::Converter.subclasses.select do |c| self.converters = instantiate_subclasses(Jekyll::Converter)
!self.safe || c.safe self.generators = instantiate_subclasses(Jekyll::Generator)
end.map do |c|
c.new(self.config)
end end
self.generators = Jekyll::Generator.subclasses.select do |c| # Internal: Setup the plugin search path
!self.safe || c.safe #
end.map do |c| # Returns an Array of plugin search paths
c.new(self.config) def plugins_path
if (config['plugins'] == Jekyll::DEFAULTS['plugins'])
[File.join(self.source, config['plugins'])]
else
Array(config['plugins']).map { |d| File.expand_path(d) }
end end
end end
@ -137,6 +140,18 @@ module Jekyll
self.read_posts(dir) 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| entries.each do |f|
f_abs = File.join(base, f) f_abs = File.join(base, f)
f_rel = File.join(dir, f) f_rel = File.join(dir, f)
@ -163,9 +178,7 @@ module Jekyll
# #
# Returns nothing. # Returns nothing.
def read_posts(dir) def read_posts(dir)
base = File.join(self.source, dir, '_posts') entries = get_entries(dir, '_posts')
return unless File.exists?(base)
entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
# 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|
@ -173,19 +186,28 @@ module Jekyll
post = Post.new(self, self.source, dir, f) post = Post.new(self, self.source, dir, f)
if post.published && (self.future || post.date <= self.time) if post.published && (self.future || post.date <= self.time)
self.posts << post aggregate_post_info(post)
post.categories.each { |c| self.categories[c] << post } end
post.tags.each { |c| self.tags[c] << post }
end end
end end
end end
self.posts.sort! # Read all the files in <source>/<dir>/_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)
entries = get_entries(dir, '_drafts')
# limit the posts if :limit_posts option is set # first pass processes, but does not yet render draft content
if limit_posts entries.each do |f|
limit = self.posts.length < limit_posts ? self.posts.length : limit_posts if Draft.valid?(f)
self.posts = self.posts[-limit, limit] draft = Draft.new(self, self.source, dir, f)
aggregate_post_info(draft)
end
end end
end end
@ -338,7 +360,7 @@ module Jekyll
['.', '_', '#'].include?(e[0..0]) || ['.', '_', '#'].include?(e[0..0]) ||
e[-1..-1] == '~' || e[-1..-1] == '~' ||
self.exclude.glob_include?(e) || self.exclude.glob_include?(e) ||
File.symlink?(e) (File.symlink?(e) && self.safe)
end end
end end
end end
@ -356,5 +378,43 @@ module Jekyll
raise "Converter implementation not found for #{klass}" raise "Converter implementation not found for #{klass}"
end end
end end
# Create array of instances of the subclasses of the class or module
# passed in as argument.
#
# klass - class or module containing the subclasses which should be
# instantiated
#
# Returns array of instances of subclasses of parameter
def instantiate_subclasses(klass)
klass.subclasses.select do |c|
!self.safe || c.safe
end.sort.map do |c|
c.new(self.config)
end
end
# Read the entries from a particular directory for processing
#
# dir - The String relative path of the directory to read
# subfolder - The String directory to read
#
# Returns the list of entries to process
def get_entries(dir, subfolder)
base = File.join(self.source, dir, subfolder)
return [] unless File.exists?(base)
entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
end
# Aggregate post information
#
# post - The Post object to aggregate information for
#
# Returns nothing
def aggregate_post_info(post)
self.posts << post
post.categories.each { |c| self.categories[c] << post }
post.tags.each { |c| self.tags[c] << post }
end
end end
end end

19
lib/jekyll/tags/gist.rb Normal file
View File

@ -0,0 +1,19 @@
# Gist Liquid Tag
#
# Example:
# {% gist 1234567 %}
module Jekyll
class GistTag < Liquid::Tag
def initialize(tag_name, gist, tokens)
super
@gist = gist.strip
end
def render(context)
"<script src=\"https://gist.github.com/#{@gist}.js\"> </script>"
end
end
end
Liquid::Template.register_tag('gist', Jekyll::GistTag)

View File

@ -30,7 +30,13 @@ module Jekyll
end end
end end
else else
raise SyntaxError.new("Syntax Error in 'highlight' - Valid syntax: highlight <lang> [linenos]") raise SyntaxError.new <<-eos
Syntax Error in tag 'highlight' while parsing the following markup:
#{markup}
Valid syntax: highlight <lang> [linenos]
eos
end end
end end

View File

@ -23,7 +23,11 @@ module Jekyll
site = context.registers[:site] site = context.registers[:site]
site.posts.each do |p| site.posts.each do |p|
if p == @post if p.slug == @post.slug \
and p.date.year == @post.date.year \
and p.date.month == @post.date.month \
and p.date.day == @post.date.day
return p.url return p.url
end end
end end

View File

@ -5,6 +5,7 @@ if RUBY_VERSION > '1.9' && ENV["COVERAGE"] == "true"
end end
require 'rubygems' require 'rubygems'
require 'test/unit'
gem 'RedCloth', '>= 4.2.1' gem 'RedCloth', '>= 4.2.1'
require 'jekyll' require 'jekyll'

View File

@ -0,0 +1,8 @@
module Jekyll
class Dummy < Generator
priority :high
def generate(site)
end
end
end

View File

@ -0,0 +1,5 @@
---
title: Contact Information
---
Contact Information

View File

@ -0,0 +1,5 @@
---
title: Contact Information
---
Contact Information

View File

@ -7,23 +7,49 @@ class TestConfiguration < Test::Unit::TestCase
end end
should "fire warning with no _config.yml" do should "fire warning with no _config.yml" do
mock(YAML).load_file(@path) { raise "No such file or directory - #{@path}" } mock(YAML).safe_load_file(@path) { raise SystemCallError, "No such file or directory - #{@path}" }
mock($stderr).puts("WARNING: Could not read configuration. Using defaults (and options).") mock($stderr).puts("Configuration file: none")
mock($stderr).puts("\tNo such file or directory - #{@path}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({}) assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end end
should "load configuration as hash" do should "load configuration as hash" do
mock(YAML).load_file(@path) { Hash.new } mock(YAML).safe_load_file(@path) { Hash.new }
mock($stdout).puts("Configuration from #{@path}") mock($stdout).puts("Configuration file: #{@path}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({}) assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end end
should "fire warning with bad config" do should "fire warning with bad config" do
mock(YAML).load_file(@path) { Array.new } mock(YAML).safe_load_file(@path) { Array.new }
mock($stderr).puts("WARNING: Could not read configuration. Using defaults (and options).") mock($stderr).puts(" WARNING: Error reading configuration. Using defaults (and options).")
mock($stderr).puts("\tInvalid configuration - #{@path}") mock($stderr).puts("Configuration file: (INVALID) #{@path}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({}) assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end end
end end
context "loading config from external file" do
setup do
@paths = {
:default => File.join(Dir.pwd, '_config.yml'),
:other => File.join(Dir.pwd, '_config.live.yml'),
:empty => ""
}
end
should "load default config if no config_file is set" do
mock(YAML).safe_load_file(@paths[:default]) { Hash.new }
mock($stdout).puts("Configuration file: #{@paths[:default]}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end
should "load different config if specified" do
mock(YAML).safe_load_file(@paths[:other]) { {"baseurl" => "http://wahoo.dev"} }
mock($stdout).puts("Configuration file: #{@paths[:other]}")
assert_equal Jekyll::DEFAULTS.deep_merge({ "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => @paths[:other] })
end
should "load default config if path passed is empty" do
mock(YAML).safe_load_file(@paths[:default]) { Hash.new }
mock($stdout).puts("Configuration file: #{@paths[:default]}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({ "config" => @paths[:empty] })
end
end
end end

View File

@ -23,11 +23,11 @@ class TestKramdown < Test::Unit::TestCase
should "convert quotes to smart quotes" do should "convert quotes to smart quotes" do
markdown = Converters::Markdown.new(@config) markdown = Converters::Markdown.new(@config)
assert_equal "<p>&ldquo;Pit&rsquo;hy&rdquo;</p>", markdown.convert(%{"Pit'hy"}).strip assert_equal "<p>&#8220;Pit&#8217;hy&#8221;</p>", markdown.convert(%{"Pit'hy"}).strip
override = { 'kramdown' => { 'smart_quotes' => 'lsaquo,rsaquo,laquo,raquo' } } override = { 'kramdown' => { 'smart_quotes' => 'lsaquo,rsaquo,laquo,raquo' } }
markdown = Converters::Markdown.new(@config.deep_merge(override)) markdown = Converters::Markdown.new(@config.deep_merge(override))
assert_equal "<p>&laquo;Pit&rsaquo;hy&raquo;</p>", markdown.convert(%{"Pit'hy"}).strip assert_equal "<p>&#171;Pit&#8250;hy&#187;</p>", markdown.convert(%{"Pit'hy"}).strip
end end
end end
end end

View File

@ -1,8 +1,10 @@
require 'helper' require 'helper'
class TestPage < Test::Unit::TestCase class TestPage < Test::Unit::TestCase
def setup_page(file) def setup_page(*args)
@page = Page.new(@site, source_dir, '', file) dir, file = args
dir, file = ['', dir] if file.nil?
@page = Page.new(@site, source_dir, dir, file)
end end
def do_render(page) def do_render(page)
@ -23,6 +25,18 @@ class TestPage < Test::Unit::TestCase
assert_equal "/contacts.html", @page.url assert_equal "/contacts.html", @page.url
end end
context "in a directory hierarchy" do
should "create url based on filename" do
@page = setup_page('/contacts', 'bar.html')
assert_equal "/contacts/bar.html", @page.url
end
should "create index url based on filename" do
@page = setup_page('/contacts', 'index.html')
assert_equal "/contacts/index.html", @page.url
end
end
should "deal properly with extensions" do should "deal properly with extensions" do
@page = setup_page('deal.with.dots.html') @page = setup_page('deal.with.dots.html')
assert_equal ".html", @page.ext assert_equal ".html", @page.ext
@ -47,6 +61,28 @@ class TestPage < Test::Unit::TestCase
@page = setup_page('index.html') @page = setup_page('index.html')
assert_equal '/', @page.dir assert_equal '/', @page.dir
end end
context "in a directory hierarchy" do
should "create url based on filename" do
@page = setup_page('/contacts', 'bar.html')
assert_equal "/contacts/bar/", @page.url
end
should "create index url based on filename" do
@page = setup_page('/contacts', 'index.html')
assert_equal "/contacts/", @page.url
end
should "return dir correctly" do
@page = setup_page('/contacts', 'bar.html')
assert_equal '/contacts/bar/', @page.dir
end
should "return dir correctly for index page" do
@page = setup_page('/contacts', 'index.html')
assert_equal '/contacts/', @page.dir
end
end
end end
context "with any other url style" do context "with any other url style" do
@ -131,6 +167,36 @@ class TestPage < Test::Unit::TestCase
assert File.directory?(dest_dir) assert File.directory?(dest_dir)
assert File.exists?(File.join(dest_dir, '.htaccess')) assert File.exists?(File.join(dest_dir, '.htaccess'))
end end
context "in a directory hierarchy" do
should "write properly the index" do
page = setup_page('/contacts', 'index.html')
do_render(page)
page.write(dest_dir)
assert File.directory?(dest_dir)
assert File.exists?(File.join(dest_dir, 'contacts', 'index.html'))
end
should "write properly" do
page = setup_page('/contacts', 'bar.html')
do_render(page)
page.write(dest_dir)
assert File.directory?(dest_dir)
assert File.exists?(File.join(dest_dir, 'contacts', 'bar.html'))
end
should "write properly without html extension" do
page = setup_page('/contacts', 'bar.html')
page.site.permalink_style = :pretty
do_render(page)
page.write(dest_dir)
assert File.directory?(dest_dir)
assert File.exists?(File.join(dest_dir, 'contacts', 'bar', 'index.html'))
end
end
end end
end end

View File

@ -7,6 +7,11 @@ class TestSite < Test::Unit::TestCase
assert_equal [File.join(Dir.pwd, '_plugins')], site.plugins assert_equal [File.join(Dir.pwd, '_plugins')], site.plugins
end end
should "look for plugins under the site directory by default" do
site = Site.new(Jekyll::DEFAULTS.merge({'source' => File.expand_path(source_dir)}))
assert_equal [File.join(source_dir, '_plugins')], site.plugins
end
should "have an array for plugins if passed as a string" do should "have an array for plugins if passed as a string" do
site = Site.new(Jekyll::DEFAULTS.merge({'plugins' => '/tmp/plugins'})) site = Site.new(Jekyll::DEFAULTS.merge({'plugins' => '/tmp/plugins'}))
assert_equal ['/tmp/plugins'], site.plugins assert_equal ['/tmp/plugins'], site.plugins
@ -26,6 +31,7 @@ class TestSite < Test::Unit::TestCase
site = Site.new(Jekyll::DEFAULTS.merge({'plugins' => nil})) site = Site.new(Jekyll::DEFAULTS.merge({'plugins' => nil}))
assert_equal [], site.plugins assert_equal [], site.plugins
end end
end end
context "creating sites" do context "creating sites" do
setup do setup do
@ -39,6 +45,21 @@ class TestSite < Test::Unit::TestCase
assert_equal Hash.new, @site.tags assert_equal Hash.new, @site.tags
end end
should "give site with parsed pages and posts to generators" do
@site.reset
@site.read
class MyGenerator < Generator
def generate(site)
site.pages.dup.each do |page|
raise "#{page} isn't a page" unless page.is_a?(Page)
raise "#{page} doesn't respond to :name" unless page.respond_to?(:name)
end
end
end
@site.generate
assert_not_equal 0, @site.pages.size
end
should "reset data before processing" do should "reset data before processing" do
clear_dest clear_dest
@site.process @site.process
@ -118,6 +139,11 @@ class TestSite < Test::Unit::TestCase
assert_equal mtime3, mtime4 # no modifications, so must be the same assert_equal mtime3, mtime4 # no modifications, so must be the same
end end
should "setup plugins in priority order" do
assert_equal @site.converters.sort_by(&:class).map{|c|c.class.priority}, @site.converters.map{|c|c.class.priority}
assert_equal @site.generators.sort_by(&:class).map{|g|g.class.priority}, @site.generators.map{|g|g.class.priority}
end
should "read layouts" do should "read layouts" do
@site.read_layouts @site.read_layouts
assert_equal ["default", "simple"].sort, @site.layouts.keys.sort assert_equal ["default", "simple"].sort, @site.layouts.keys.sort
@ -166,6 +192,22 @@ class TestSite < Test::Unit::TestCase
assert_equal files, @site.filter_entries(files) assert_equal files, @site.filter_entries(files)
end end
should "filter symlink entries when safe mode enabled" do
stub(Jekyll).configuration do
Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'safe' => true})
end
site = Site.new(Jekyll.configuration)
stub(File).symlink?('symlink.js') {true}
files = %w[symlink.js]
assert_equal [], site.filter_entries(files)
end
should "not filter symlink entries when safe mode disabled" do
stub(File).symlink?('symlink.js') {true}
files = %w[symlink.js]
assert_equal files, @site.filter_entries(files)
end
context 'error handling' do context 'error handling' do
should "raise if destination is included in source" do should "raise if destination is included in source" do
stub(Jekyll).configuration do stub(Jekyll).configuration do

View File

@ -203,4 +203,22 @@ CONTENT
assert_match %r{/2008/11/21/complex/}, @result assert_match %r{/2008/11/21/complex/}, @result
end end
end end
context "simple gist inclusion" do
setup do
@gist = 358471
content = <<CONTENT
---
title: My Cool Gist
---
{% gist #{@gist} %}
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
should "write script tag" do
assert_match %r{<script src='https://gist.github.com/#{@gist}.js'>\s</script>}, @result
end
end
end end

View File

@ -1,10 +0,0 @@
require 'helper'
require 'jekyll/migrators/wordpress'
require 'htmlentities'
class TestWordpressMigrator < Test::Unit::TestCase
should "clean slashes from slugs" do
test_title = "blogs part 1/2"
assert_equal("blogs-part-1-2", Jekyll::WordPress.sluggify(test_title))
end
end

View File

@ -1,9 +0,0 @@
require 'helper'
require 'jekyll/migrators/wordpressdotcom'
class TestWordpressDotComMigrator < Test::Unit::TestCase
should "clean slashes from slugs" do
test_title = "blogs part 1/2"
assert_equal("blogs-part-1-2", Jekyll::WordpressDotCom.sluggify(test_title))
end
end