diff --git a/.gitignore b/.gitignore
index aa6c14f8..baf9f1df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,5 +12,8 @@ gh-pages/
site/_site/
coverage
.ruby-version
+.ruby-gemset
.sass-cache
-tmp/stackprof-*
+tmp/*
+.jekyll-metadata
+/vendor
diff --git a/.travis.yml b/.travis.yml
index 90e08adf..d7960ce7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,13 +2,13 @@ language: ruby
cache: bundler
sudo: false
rvm:
+- 2.2
- 2.1
- 2.0
-- 1.9.3
env:
matrix:
- - TEST_SUITE=test
- - TEST_SUITE=cucumber
+ - TEST_SUITE=test
+ - TEST_SUITE=cucumber
before_script: bundle update
script: script/cibuild
notifications:
@@ -16,9 +16,9 @@ notifications:
on_success: change
on_failure: change
channels:
- - irc.freenode.org#jekyll
+ - irc.freenode.org#jekyll
template:
- - "%{repository}#%{build_number} (%{branch}) %{message} %{build_url}"
+ - "%{repository}#%{build_number} (%{branch}) %{message} %{build_url}"
email:
on_success: never
on_failure: never
diff --git a/CONTRIBUTING.markdown b/CONTRIBUTING.markdown
index 94533415..2145db65 100644
--- a/CONTRIBUTING.markdown
+++ b/CONTRIBUTING.markdown
@@ -4,6 +4,7 @@ Contribute
So you've got an awesome idea to throw into Jekyll. Great! Please keep the
following in mind:
+* **Use https://talk.jekyllrb.com for non-technical or indirect Jekyll questions that are not bugs.**
* **Contributions will not be accepted without tests or necessary documentation updates.**
* If you're creating a small fix or patch to an existing feature, just a simple
test will do. Please stay in the confines of the current test suite and use
@@ -67,7 +68,7 @@ You can find the documentation for jekyllrb.com in the
[site](https://github.com/jekyll/jekyll/tree/master/site) directory of
Jekyll's repo on GitHub.com.
-All documentation pull requests should be directed at `master`. Pull
+All documentation pull requests should be directed at `master`. Pull
requests directed at another branch will not be accepted.
The [Jekyll wiki](https://github.com/jekyll/jekyll/wiki) on GitHub
diff --git a/Gemfile b/Gemfile
index 25b40ed2..4299c72e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,40 @@
source 'https://rubygems.org'
gemspec
+gem 'pygments.rb', '~> 0.6.0'
+gem 'redcarpet', '~> 3.1'
+gem 'toml', '~> 0.1.0'
+gem 'jekyll-paginate', '~> 1.0'
+gem 'jekyll-gist', '~> 1.0'
+gem 'jekyll-coffeescript', '~> 1.0'
+gem 'classifier-reborn', '~> 2.0'
+
+gem 'rake', '~> 10.1'
+gem 'rdoc', '~> 3.11'
+gem 'redgreen', '~> 1.2'
+gem 'shoulda', '~> 3.5'
+gem 'cucumber', '1.3.18'
+gem 'maruku', '~> 0.7.0'
+gem 'rdiscount', '~> 2.0'
+gem 'launchy', '~> 2.3'
+gem 'simplecov', '~> 0.9'
+gem 'mime-types', '~> 1.5'
+gem 'activesupport', '~> 3.2.13'
+gem 'jekyll_test_plugin'
+gem 'jekyll_test_plugin_malicious'
+gem 'liquid-c', '~> 3.0'
+gem 'minitest'
+gem 'minitest-reporters'
+gem 'minitest-profile'
+gem 'test-unit' if RUBY_PLATFORM =~ /cygwin/ || RUBY_VERSION.start_with?("2.2")
+gem 'rspec-mocks'
+
if ENV['BENCHMARK']
gem 'rbtrace'
gem 'stackprof'
+ gem 'benchmark-ips'
+end
+
+if ENV['PROOF']
+ gem 'html-proofer', '~> 2.0'
end
diff --git a/History.markdown b/History.markdown
index 225e6000..7d69a616 100644
--- a/History.markdown
+++ b/History.markdown
@@ -2,25 +2,172 @@
### Major Enhancements
+ * Incremental regeneration (#3116)
+ * Drop support for Ruby 1.9.3. (#3235)
+ * Upgrade to Liquid 3.0.0 (#3002)
+ * Support Ruby v2.2 (#3234)
+ * Support RDiscount 2 (#2767)
+ * Remove most runtime deps (#3323)
+ * Move to Rouge as default highlighter (#3323)
+ * Mimic GitHub Pages `.html` extension stripping behavior in WEBrick (#3452)
+ * Always include file extension on output files (#3490)
+ * Improved permalinks for pages and collections (#3538)
+
### Minor Enhancements
+ * Sort static files just once, and call `site_payload` once for all collections (#3204)
+ * Separate `jekyll docs` and optimize external gem handling (#3241)
+ * Improve `Site#getConverterImpl` and call it `Site#find_converter_instance` (#3240)
+ * Use relative path for `path` Liquid variable in Documents for consistency (#2908)
+ * Generalize `Utils#slugify` for any scripts (#3047)
+ * Added basic microdata to post template in site template (#3189)
+ * Store log messages in an array of messages. (#3244)
+ * Allow collection documents to override `output` property in front matter (#3172)
+ * Keep file modification times between builds for static files (#3220)
+ * Only downcase mixed-case categories for the URL (#2571)
+ * Added per post `excerpt_separator` functionality (#3274)
+ * Allow collections YAML to end with three dots (#3134)
+ * Add mode parameter to `slugify` Liquid filter (#2918)
+ * Perf: `Markdown#matches` should avoid regexp (#3321)
+ * Perf: Use frozen regular expressions for `Utils#slugify` (#3321)
+ * Split off Textile support into jekyll-textile-converter (#3319)
+ * Improve the navigation menu alignment in the site template on small
+ screens (#3331)
+ * Show the regeneration time after the initial generation (#3378)
+ * Site template: Switch default font to Helvetica Neue (#3376)
+ * Make the `include` tag a teensy bit faster. (#3391)
+ * Add `pkill -f jekyll` to ways to kill. (#3397)
+ * Site template: collapsed, variable-driven font declaration (#3360)
+ * Site template: Don't always show the scrollbar in code blocks (#3419)
+ * Site template: Remove undefined `text` class from `p` element (#3440)
+ * Site template: Optimize text rendering for legibility (#3382)
+ * Add `draft?` method to identify if Post is a Draft & expose to Liquid (#3456)
+ * Write regeneration metadata even on full rebuild (#3464)
+ * Perf: Use `String#end_with?("/")` instead of regexp when checking paths (#3516)
+ * Docs: document 'ordinal' built-in permalink style (#3532)
+ * Upgrade liquid-c to 3.x (#3531)
+ * Use consistent syntax for deprecation warning (#3535)
+ * Added build --destination and --source flags (#3418)
+ * Site template: remove unused `page.meta` attribute (#3537)
+ * Improve the error message when sorting null objects (#3520)
+
+### Bug Fixes
+
+ * Make permalink parsing consistent with pages (#3014)
+ * `time()`pre-filter method should accept a `Date` object (#3299)
+ * Remove unneeded end tag for `link` in site template (#3236)
+ * Kramdown: Use `enable_coderay` key instead of `use_coderay` (#3237)
+ * Unescape `Document` output path (#2924)
+ * Fix nav items alignment when on multiple rows (#3264)
+ * Highlight: Only Strip Newlines/Carriage Returns, not Spaces (#3278)
+ * Find variables in front matter defaults by searching with relative file path. (#2774)
+ * Allow variables (e.g `:categories`) in YAML front matter permalinks (#3320)
+ * Handle nil URL placeholders in permalinks (#3325)
+ * Template: Fix nav items alignment when in "burger" mode (#3329)
+ * Template: Remove `!important` from nav SCSS introduced in #3329 (#3375)
+ * The `:title` URL placeholder for collections should be the filename slug. (#3383)
+ * Trim the generate time diff to just 3 places past the decimal place (#3415)
+ * The highlight tag should only clip the newlines before and after the *entire* block, not in between (#3401)
+ * highlight: fix problem with linenos and rouge. (#3436)
+ * `Site#read_data_file`: read CSV's with proper file encoding (#3455)
+ * Ignore `.jekyll-metadata` in site template (#3496)
+ * Template: Point documentation link to the documentation pages (#3502)
+ * Removed the trailing slash from the example `/blog` baseurl comment (#3485)
+
+### Development Fixes
+
+ * Improve the grammar in the documentation (#3233)
+ * Update the LICENSE text to match the MIT license exactly (#3253)
+ * Update rake task `site:publish` to fix minor bugs. (#3254)
+ * Switch to shields.io for the README badges. (#3255)
+ * Use `FileList` instead of `Dir.glob` in `site:publish` rake task (#3261)
+ * Fix test script to be platform-independent (#3279)
+ * Instead of symlinking `/tmp`, create and symlink a local `tmp` in the tests (#3258)
+ * Fix some spacing (#3312)
+ * Fix comment typo in `lib/jekyll/frontmatter_defaults.rb` (#3322)
+ * Move all `regenerate?` checking to `Regenerator` (#3326)
+ * Factor out a `read_data_file` call to keep things clean (#3380)
+ * Proof the site with CircleCI. (#3427)
+ * Update LICENSE to 2015. (#3477)
+ * Upgrade tests to use Minitest (#3492)
+ * Remove trailing whitespace (#3497)
+ * Use `fixture_site` for Document tests (#3511)
+ * Remove adapters deprecation warning (#3529)
+ * Minor fixes to `url.rb` to follow GitHub style guide (#3544)
+ * Minor changes to resolve deprecation warnings (#3547)
+ * Convert remaining textile test documents to markdown (#3528)
+ * Migrate the tests to use rspec-mocks (#3552)
+
+### Site Enhancements
+
+ * Add blog post announcing Jekyll Help (#3523)
+ * Add Jekyll Talk to Help page on site (#3518)
+ * Fixing the default host on docs (#3229)
+ * Add `jekyll-thumbnail-filter` to list of third-party plugins (#2790)
+ * Add link to 'Adding Ajax pagination to Jekyll' to Resources page (#3186)
+ * Add a Resources link to tutorial on building dynamic navbars (#3185)
+ * Semantic structure improvements to the post and page layouts (#3251)
+ * Add new AsciiDoc plugin to list of third-party plugins. (#3277)
+ * Specify that all transformable collection documents must contain YAML front matter (#3271)
+ * Assorted accessibility fixes (#3256)
+ * Update configuration docs to mention `keep_files` for `destination` (#3288, #3296)
+ * Break when we successfully generate nav link to save CPU cycles. (#3291)
+ * Update usage docs to mention `keep_files` and a warning about `destination` cleaning (#3295)
+ * Add logic to automatically generate the `next_section` and `prev_section` navigation items (#3292)
+ * Some small fixes for the Plugins TOC. (#3306)
+ * Added versioning comment to configuration file (#3314)
+ * Add `jekyll-minifier` to list of third-party plugins (#3333)
+ * Add blog post about the Jekyll meet-up (#3332)
+ * Use `highlight` Liquid tag instead of the four-space tabs for code (#3336)
+ * 3.0.0.beta1 release post (#3346)
+ * Add `twa` to the list of third-party plugins (#3384)
+ * Remove extra spaces (#3388)
+ * Fix small grammar errors on a couple pages (#3396)
+ * Fix typo on Templates docs page (#3420)
+ * s/three/four for plugin type list (#3424)
+ * Release jekyllrb.com as a locally-compiled site. (#3426)
+ * Add a jekyllrb.com/help page which elucidates places from which to get help (#3428)
+ * Remove extraneous dash on Plugins doc page which caused a formatting error (#3431)
+ * Fix broken link to Jordan Thornquest's website. (#3438)
+ * Change the link to an extension (#3457)
+ * Fix Twitter link on the help page (#3466)
+ * Fix wording in code snippet highlighting section (#3475)
+ * Add a `/` to `paginate_path` in the Pagination documentation (#3479)
+ * Add a link on all the docs pages to "Improve this page". (#3510)
+ * Add jekyll-auto-image generator to the list of third-party plugins (#3489)
+ * Replace link to the proposed `picture` element spec (#3530)
+ * Add frontmatter date formatting information (#3469)
+ * Improve consistency and clarity of plugins options note (#3546)
+ * Add permalink warning to pagination docs (#3551)
+ * Fix grammar in Collections docs API stability warning (#3560)
+
+## 2.5.3 / 2014-12-22
+
### Bug Fixes
* When checking a Markdown extname, include position of the `.` (#3147)
* Fix `jsonify` Liquid filter handling of boolean values (#3154)
* Add comma to value of `viewport` meta tag (#3170)
* Set the link type for the RSS feed to `application/rss+xml` (#3176)
+ * Refactor `#as_liquid` (#3158)
### Development Fixes
+ * Exclude built-in bundles from being added to coverage report (#3180)
+
### Site Enhancements
+ * Add `@alfredxing` to the `@jekyll/core` team. :tada: (#3218)
* Document the `-q` option for the `build` and `serve` commands (#3149)
* Fix some minor typos/flow fixes in documentation website content (#3165)
* Add `keep_files` to configuration documentation (#3162)
* Repeat warning about cleaning of the `destination` directory (#3161)
* Add jekyll-500px-embed to list of third-party plugins (#3163)
* Simplified platform detection in Gemfile example for Windows (#3177)
+ * Add the `jekyll-jalali` plugin added to the list of third-party plugins. (#3198)
+ * Add Table of Contents to Troubleshooting page (#3196)
+ * Add `inline_highlight` plugin to list of third-party plugins (#3212)
+ * Add `jekyll-mermaid` plugin to list of third-party plugins (#3222)
## 2.5.2 / 2014-11-17
@@ -477,7 +624,7 @@
* Add `Jekyll::LiquidExtensions` with `.lookup_variable` method for easy
looking up of variable values in a Liquid context. (#2253)
* Remove literal lang name from class (#2292)
- * Return `utf-8` encoding in header for webrick error page response (#2289)
+ * Return `utf-8` encoding in header for webrick error page response (#2289)
* Make template site easier to customize (#2268)
* Add two-digit year to permalink template option (#2301)
* Add `site.documents` to Liquid payload (list of all docs) (#2295)
diff --git a/LICENSE b/LICENSE
index 0e5f8440..527166b6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,9 +1,9 @@
-(The MIT License)
+The MIT License (MIT)
-Copyright (c) 2008-2014 Tom Preston-Werner
+Copyright (c) 2008-2015 Tom Preston-Werner
Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the 'Software'), to deal
+of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
@@ -12,7 +12,7 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
diff --git a/README.markdown b/README.markdown
index b68166cb..95c49bd7 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,9 +1,9 @@
# [Jekyll](http://jekyllrb.com/)
-[](https://rubygems.org/gems/jekyll)
-[](https://travis-ci.org/jekyll/jekyll)
-[](https://codeclimate.com/github/jekyll/jekyll)
-[](https://gemnasium.com/jekyll/jekyll)
+[](https://rubygems.org/gems/jekyll)
+[](https://travis-ci.org/jekyll/jekyll)
+[](https://codeclimate.com/github/jekyll/jekyll)
+[](https://gemnasium.com/jekyll/jekyll)
[](https://hakiri.io/github/jekyll/jekyll/master)
By Tom Preston-Werner, Nick Quaranto, Parker Moore, and many [awesome contributors](https://github.com/jekyll/jekyll/graphs/contributors)!
diff --git a/Rakefile b/Rakefile
index 991f63e0..98610a3c 100644
--- a/Rakefile
+++ b/Rakefile
@@ -26,7 +26,7 @@ def gemspec_file
end
def gem_file
- "#{name}-#{version}.gem"
+ "#{name}-#{Gem::Version.new(version).to_s}.gem"
end
def normalize_bullets(markdown)
@@ -89,6 +89,7 @@ end
multitask :default => [:test, :features]
+task :spec => :test
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
@@ -179,33 +180,44 @@ namespace :site do
# Ensure the gh-pages dir exists so we can generate into it.
puts "Checking for gh-pages dir..."
unless File.exist?("./gh-pages")
- puts "No gh-pages directory found. Run the following commands first:"
- puts " `git clone git@github.com:jekyll/jekyll gh-pages"
- puts " `cd gh-pages"
- puts " `git checkout gh-pages`"
- exit(1)
+ puts "Creating gh-pages dir..."
+ sh "git clone git@github.com:jekyll/jekyll gh-pages"
end
- # Ensure gh-pages branch is up to date.
+ # Ensure latest gh-pages branch history.
Dir.chdir('gh-pages') do
+ sh "git checkout gh-pages"
sh "git pull origin gh-pages"
end
- # Copy to gh-pages dir.
- puts "Copying site to gh-pages branch..."
- Dir.glob("site/*") do |path|
- next if path.include? "_site"
- sh "cp -R #{path} gh-pages/"
+ # Proceed to purge all files in case we removed a file in this release.
+ puts "Cleaning gh-pages directory..."
+ purge_exclude = %w[
+ gh-pages/.
+ gh-pages/..
+ gh-pages/.git
+ gh-pages/.gitignore
+ ]
+ FileList["gh-pages/{*,.*}"].exclude(*purge_exclude).each do |path|
+ sh "rm -rf #{path}"
end
- # Change any configuration settings for production.
- config = YAML.load_file("gh-pages/_config.yml")
- config.merge!({'sass' => {'style' => 'compressed'}})
- File.write('gh-pages/_config.yml', YAML.dump(config))
+ # Copy site to gh-pages dir.
+ puts "Building site into gh-pages branch..."
+ ENV['JEKYLL_ENV'] = 'production'
+ require "jekyll"
+ Jekyll::Commands::Build.process({
+ "source" => File.expand_path("site"),
+ "destination" => File.expand_path("gh-pages"),
+ "sass" => { "style" => "compressed" },
+ "full_rebuild" => true
+ })
+
+ File.open('gh-pages/.nojekyll', 'wb') { |f| f.puts(":dog: food.") }
# Commit and push.
puts "Committing and pushing to GitHub Pages..."
- sha = `git log`.match(/[a-z0-9]{40}/)[0]
+ sha = `git rev-parse HEAD`.strip
Dir.chdir('gh-pages') do
sh "git add ."
sh "git commit --allow-empty -m 'Updating to #{sha}.'"
@@ -221,8 +233,7 @@ namespace :site do
front_matter = {
"layout" => "docs",
"title" => "History",
- "permalink" => "/docs/history/",
- "prev_section" => "contributing"
+ "permalink" => "/docs/history/"
}
Dir.chdir('site/_docs/') do
File.open("history.md", "w") do |file|
@@ -237,7 +248,7 @@ namespace :site do
desc "Write the site latest_version.txt file"
task :version_file do
- File.open('site/latest_version.txt', 'wb') { |f| f.write(version) }
+ File.open('site/latest_version.txt', 'wb') { |f| f.puts(version) } unless version =~ /(beta|rc|alpha)/i
end
namespace :releases do
diff --git a/benchmark/end-with-vs-regexp b/benchmark/end-with-vs-regexp
new file mode 100644
index 00000000..cb849f42
--- /dev/null
+++ b/benchmark/end-with-vs-regexp
@@ -0,0 +1,13 @@
+require 'benchmark/ips'
+
+Benchmark.ips do |x|
+ path_without_ending_slash = '/some/very/very/long/path/to/a/file/i/like'
+ x.report('no slash regexp') { path_without_ending_slash =~ /\/$/ }
+ x.report('no slash end_with?') { path_without_ending_slash.end_with?("/") }
+end
+
+Benchmark.ips do |x|
+ path_with_ending_slash = '/some/very/very/long/path/to/a/file/i/like/'
+ x.report('slash regexp') { path_with_ending_slash =~ /\/$/ }
+ x.report('slash end_with?') { path_with_ending_slash.end_with?("/") }
+end
diff --git a/benchmark/flat-map b/benchmark/flat-map
index b10c5c9a..547bcee1 100644
--- a/benchmark/flat-map
+++ b/benchmark/flat-map
@@ -14,4 +14,3 @@ Benchmark.ips do |x|
x.report('.map.flatten with no nested arrays') { enum.map { |i| do_thing(i) }.flatten(1) }
x.report('.flat_map with no nested arrays') { enum.flat_map { |i| do_thing(i) } }
end
-
diff --git a/benchmark/string-replacement b/benchmark/string-replacement
index 13715376..36de613c 100644
--- a/benchmark/string-replacement
+++ b/benchmark/string-replacement
@@ -8,6 +8,6 @@ Benchmark.ips do |x|
x.report('#tr') { str.tr('some', 'a') }
x.report('#gsub') { str.gsub('some', 'a') }
x.report('#gsub!') { str.gsub!('some', 'a') }
- x.report('#sub') { str.sub('some', 'a') }
- x.report('#sub!') { str.sub!('some', 'a') }
+ x.report('#sub') { str.sub('some', 'a') }
+ x.report('#sub!') { str.sub!('some', 'a') }
end
diff --git a/bin/jekyll b/bin/jekyll
index 060c9125..6c968cec 100755
--- a/bin/jekyll
+++ b/bin/jekyll
@@ -6,12 +6,9 @@ $:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
require 'jekyll'
require 'mercenary'
-%w[jekyll-import].each do |blessed_gem|
- begin
- require blessed_gem
- rescue LoadError
- end
-end
+Jekyll::External.require_if_present(
+ Jekyll::External.blessed_gems
+)
Jekyll::PluginManager.require_from_bundler
@@ -32,6 +29,7 @@ Mercenary.program(:jekyll) do |p|
p.action do |args, options|
if args.empty?
+ Jekyll.logger.error "A subcommand is required."
puts p
else
unless p.has_command?(args.first)
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 00000000..02259027
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,14 @@
+machine:
+ timezone: UTC
+ ruby:
+ version: 2.1.5
+ environment:
+ PROOF: true
+test:
+ override:
+ - script/proof -f
+general:
+ branches:
+ ignore:
+ - gh-pages # no proof script here
+ - master # don't need to duplicate work
diff --git a/features/collections.feature b/features/collections.feature
index 4f69e8b9..91a271fc 100644
--- a/features/collections.feature
+++ b/features/collections.feature
@@ -9,7 +9,7 @@ Feature: Collections
And I have a configuration file with "collections" set to "['methods']"
When I run jekyll build
Then the _site directory should exist
- And I should see "Collections:
Use Jekyll.configuration to build a full configuration for use w/Jekyll.
\n\n
Whatever: foo.bar
\n
Jekyll.sanitized_path is used to make sure your path is in your source.
\n
Run your generators! default
\n
Page without title.
\n
Run your generators! default
" in "_site/index.html"
+ And I should see "Collections:
Use Jekyll.configuration to build a full configuration for use w/Jekyll.
\n\n
Whatever: foo.bar
\n
Signs are nice
\n
Jekyll.sanitized_path is used to make sure your path is in your source.
\n
Run your generators! default
\n
Page without title.
\n
Run your generators! default
" in "_site/index.html"
And the "_site/methods/configuration.html" file should not exist
Scenario: Rendered collection
@@ -70,7 +70,7 @@ Feature: Collections
"""
When I run jekyll build
Then the _site directory should exist
- And I should see "Collections: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
+ And I should see "Collections: _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
Scenario: Collections specified as an hash
Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}"
@@ -82,7 +82,7 @@ Feature: Collections
"""
When I run jekyll build
Then the _site directory should exist
- And I should see "Collections: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
+ And I should see "Collections: _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
Scenario: All the documents
Given I have an "index.html" page that contains "All documents: {% for doc in site.documents %}{{ doc.relative_path }} {% endfor %}"
@@ -94,7 +94,7 @@ Feature: Collections
"""
When I run jekyll build
Then the _site directory should exist
- And I should see "All documents: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
+ And I should see "All documents: _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
Scenario: Documents have an output attribute, which is the converted HTML
Given I have an "index.html" page that contains "First document's output: {{ site.documents.first.output }}"
@@ -118,7 +118,7 @@ Feature: Collections
"""
When I run jekyll build
Then the _site directory should exist
- And I should see "Item count: 1" in "_site/index.html"
+ And I should see "Item count: 2" in "_site/index.html"
Scenario: Sort by title
Given I have an "index.html" page that contains "{% assign items = site.methods | sort: 'title' %}1. of {{ items.size }}: {{ items.first.output }}"
@@ -130,7 +130,7 @@ Feature: Collections
"""
When I run jekyll build
Then the _site directory should exist
- And I should see "1. of 5:
Page without title.
" in "_site/index.html"
+ And I should see "1. of 7:
Page without title.
" in "_site/index.html"
Scenario: Sort by relative_path
Given I have an "index.html" page that contains "Collections: {% assign methods = site.methods | sort: 'relative_path' %}{% for method in methods %}{{ method.title }}, {% endfor %}"
@@ -142,4 +142,4 @@ Feature: Collections
"""
When I run jekyll build
Then the _site directory should exist
- And I should see "Collections: Jekyll.configuration, Jekyll.sanitized_path, Site#generate, , Site#generate," in "_site/index.html"
+ And I should see "Collections: Jekyll.configuration, Jekyll.escape, Jekyll.sanitized_path, Site#generate, , Site#generate," in "_site/index.html"
diff --git a/features/drafts.feature b/features/drafts.feature
index c55edbb9..5d7982ec 100644
--- a/features/drafts.feature
+++ b/features/drafts.feature
@@ -43,4 +43,4 @@ Feature: Draft Posts
| Recipe | 2009-03-27 | simple | Post path: {{ page.path }} |
When I run jekyll build --drafts
Then the _site directory should exist
- And I should see "Post path: _drafts/recipe.textile" in "_site/recipe.html"
+ And I should see "Post path: _drafts/recipe.markdown" in "_site/recipe.html"
diff --git a/features/embed_filters.feature b/features/embed_filters.feature
index 889a1fc7..d6f99fc7 100644
--- a/features/embed_filters.feature
+++ b/features/embed_filters.feature
@@ -20,8 +20,6 @@ Feature: Embed filters
And I have the following post:
| title | date | layout | content |
| Star & Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
-
-
And I have a default layout that contains "{{ page.title | xml_escape }}"
When I run jekyll build
Then the _site directory should exist
@@ -33,7 +31,7 @@ Feature: Embed filters
And I have the following post:
| title | date | layout | content |
| Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
- And I have a default layout that contains "{{ content | xml_escape }}"
+ And I have a default layout that contains "{{ content | number_of_words }}"
When I run jekyll build
Then the _site directory should exist
And I should see "7" in "_site/2009/03/27/star-wars.html"
@@ -49,13 +47,13 @@ Feature: Embed filters
Then the _site directory should exist
And I should see "scifi, movies, and force" in "_site/2009/03/27/star-wars.html"
- Scenario: Textilize a given string
+ Scenario: Markdownify a given string
Given I have a _posts directory
And I have a _layouts directory
And I have the following post:
| title | date | layout | content |
| Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
- And I have a default layout that contains "By {{ '_Obi-wan_' | textilize }}"
+ And I have a default layout that contains "By {{ '_Obi-wan_' | markdownify }}"
When I run jekyll build
Then the _site directory should exist
And I should see "By
Obi-wan
" in "_site/2009/03/27/star-wars.html"
diff --git a/features/frontmatter_defaults.feature b/features/frontmatter_defaults.feature
index 7ac2d0a8..a9a64ae2 100644
--- a/features/frontmatter_defaults.feature
+++ b/features/frontmatter_defaults.feature
@@ -25,7 +25,7 @@ Feature: frontmatter defaults
And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {custom: "some special data", author: "Ben"}}]"
When I run jekyll build
Then the _site directory should exist
- And I should see "
some special data
Ben
" in "_site/2013/09/11/default-data.html"
+ And I should see "
some special data
\n
Ben
" in "_site/2013/09/11/default-data.html"
And I should see "just some special data by Ben" in "_site/index.html"
Scenario: Override frontmatter defaults by path
@@ -54,6 +54,28 @@ Feature: frontmatter defaults
And I should see "root: Overview for the webpage" in "_site/index.html"
And I should see "subfolder: Overview for the special section" in "_site/special/index.html"
+ Scenario: Use frontmatter variables by relative path
+ Given I have a _layouts directory
+ And I have a main layout that contains "main: {{ content }}"
+
+ And I have a _posts directory
+ And I have the following post:
+ | title | date | content |
+ | about | 2013-10-14 | content of site/2013/10/14/about.html |
+ And I have a special/_posts directory
+ And I have the following post in "special":
+ | title | date | path | content |
+ | about1 | 2013-10-14 | local | content of site/special/2013/10/14/about1.html |
+ | about2 | 2013-10-14 | local | content of site/special/2013/10/14/about2.html |
+
+ And I have a configuration file with "defaults" set to "[{scope: {path: "special"}, values: {layout: "main"}}, {scope: {path: "special/_posts"}, values: {layout: "main"}}, {scope: {path: "_posts"}, values: {layout: "main"}}]"
+
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "main:
content of site/2013/10/14/about.html
" in "_site/2013/10/14/about.html"
+ And I should see "main:
content of site/special/2013/10/14/about1.html
" in "_site/special/2013/10/14/about1.html"
+ And I should see "main:
content of site/special/2013/10/14/about2.html
" in "_site/special/2013/10/14/about2.html"
+
Scenario: Override frontmatter defaults by type
Given I have a _posts directory
And I have the following post:
@@ -78,10 +100,19 @@ Feature: frontmatter defaults
And I should see "nothing" in "_site/override.html"
But the "_site/perma.html" file should not exist
+ Scenario: Define permalink default for posts
+ Given I have a _posts directory
+ And I have the following post:
+ | title | date | category | content |
+ | testpost | 2013-10-14 | blog | blabla |
+ And I have a configuration file with "defaults" set to "[{scope: {path: "", type: "posts"}, values: {permalink: "/:categories/:title/"}}]"
+ When I run jekyll build
+ Then I should see "blabla" in "_site/blog/testpost/index.html"
+
Scenario: Use frontmatter defaults in collections
Given I have a _slides directory
And I have a "index.html" file that contains "nothing"
- And I have a "_slides/slide1.html" file with content:
+ And I have a "_slides/slide1.html" file with content:
"""
---
---
@@ -107,7 +138,7 @@ Feature: frontmatter defaults
Scenario: Override frontmatter defaults inside a collection
Given I have a _slides directory
And I have a "index.html" file that contains "nothing"
- And I have a "_slides/slide2.html" file with content:
+ And I have a "_slides/slide2.html" file with content:
"""
---
myval: Override
diff --git a/features/include_tag.feature b/features/include_tag.feature
index 22a83480..c7e03ded 100644
--- a/features/include_tag.feature
+++ b/features/include_tag.feature
@@ -9,15 +9,15 @@ Feature: Include tags
And I have an "_includes/params.html" file that contains "Parameters:
{% for param in include %}
{{param[0]}} = {{param[1]}}
{% endfor %}
"
And I have an "_includes/ignore.html" file that contains ""
And I have a _posts directory
- And I have the following post:
- | title | date | layout | content |
- | Include Files | 2013-03-21 | default | {% include header.html param="myparam" %} |
- | Ignore params if unused | 2013-03-21 | default | {% include ignore.html date="today" %} |
- | List multiple parameters | 2013-03-21 | default | {% include params.html date="today" start="tomorrow" %} |
- | Dont keep parameters | 2013-03-21 | default | {% include ignore.html param="test" %}\n{% include header.html %} |
- | Allow params with spaces and quotes | 2013-04-07 | default | {% include params.html cool="param with spaces" super="\"quoted\"" single='has "quotes"' escaped='\'single\' quotes' %} |
- | Parameter syntax | 2013-04-12 | default | {% include params.html param1_or_2="value" %} |
- | Pass a variable | 2013-06-22 | default | {% assign var = 'some text' %}{% include params.html local=var layout=page.layout %} |
+ And I have the following posts:
+ | title | date | type | content |
+ | Include Files | 2013-03-21 | html | {% include header.html param="myparam" %} |
+ | Ignore params if unused | 2013-03-21 | html | {% include ignore.html date="today" %} |
+ | List multiple parameters | 2013-03-21 | html | {% include params.html date="today" start="tomorrow" %} |
+ | Dont keep parameters | 2013-03-21 | html | {% include ignore.html param="test" %}\n{% include header.html %} |
+ | Allow params with spaces and quotes | 2013-04-07 | html | {% include params.html cool="param with spaces" super="\"quoted\"" single='has "quotes"' escaped='\'single\' quotes' %} |
+ | Parameter syntax | 2013-04-12 | html | {% include params.html param1_or_2="value" %} |
+ | Pass a variable | 2013-06-22 | html | {% assign var = 'some text' %}{% include params.html local=var title=page.title %} |
When I run jekyll build
Then the _site directory should exist
And I should see "My awesome blog header: myparam" in "_site/2013/03/21/include-files.html"
@@ -27,12 +27,12 @@ Feature: Include tags
And I should not see "My awesome blog header: myparam" in "_site/2013/03/21/dont-keep-parameters.html"
But I should see "My awesome blog header: " in "_site/2013/03/21/dont-keep-parameters.html"
And I should see "
cool = param with spaces
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
- And I should see "
super = “quoted”
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
- And I should see "
single = has “quotes”
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
- And I should see "
escaped = ‘single’ quotes
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
super = \"quoted\"
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
single = has \"quotes\"
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
escaped = 'single' quotes
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
And I should see "
param1_or_2 = value
" in "_site/2013/04/12/parameter-syntax.html"
And I should see "
local = some text
" in "_site/2013/06/22/pass-a-variable.html"
- And I should see "
layout = default
" in "_site/2013/06/22/pass-a-variable.html"
+ And I should see "
title = Pass a variable
" in "_site/2013/06/22/pass-a-variable.html"
Scenario: Include a file from a variable
Given I have an _includes directory
diff --git a/features/incremental_rebuild.feature b/features/incremental_rebuild.feature
new file mode 100644
index 00000000..08739d3e
--- /dev/null
+++ b/features/incremental_rebuild.feature
@@ -0,0 +1,60 @@
+Feature: Incremental rebuild
+ As an impatient hacker who likes to blog
+ I want to be able to make a static site
+ Without waiting too long for it to build
+
+ Scenario: Produce correct output site
+ Given I have a _layouts directory
+ And I have a _posts directory
+ And I have the following posts:
+ | title | date | layout | content |
+ | Wargames | 2009-03-27 | default | The only winning move is not to play. |
+ And I have a default layout that contains "Post Layout: {{ content }}"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Post Layout:
The only winning move is not to play.
" in "_site/2009/03/27/wargames.html"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Post Layout:
The only winning move is not to play.
" in "_site/2009/03/27/wargames.html"
+
+ Scenario: Generate a metadata file
+ Given I have an "index.html" file that contains "Basic Site"
+ When I run jekyll build
+ Then the ".jekyll-metadata" file should exist
+
+ Scenario: Rebuild when content is changed
+ Given I have an "index.html" file that contains "Basic Site"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Basic Site" in "_site/index.html"
+ When I wait 1 second
+ Then I have an "index.html" file that contains "Bacon Site"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Bacon Site" in "_site/index.html"
+
+ Scenario: Rebuild when layout is changed
+ Given I have a _layouts directory
+ And I have an "index.html" page with layout "default" that contains "Basic Site with Layout"
+ And I have a default layout that contains "Page Layout: {{ content }}"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Page Layout: Basic Site with Layout" in "_site/index.html"
+ When I wait 1 second
+ Then I have a default layout that contains "Page Layout Changed: {{ content }}"
+ When I run jekyll build --full-rebuild
+ Then the _site directory should exist
+ And I should see "Page Layout Changed: Basic Site with Layout" in "_site/index.html"
+
+ Scenario: Rebuild when an include is changed
+ Given I have a _includes directory
+ And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
+ And I have an "_includes/about.textile" file that contains "Generated by Jekyll"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"
+ When I wait 1 second
+ Then I have an "_includes/about.textile" file that contains "Regenerated by Jekyll"
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Basic Site with include tag: Regenerated by Jekyll" in "_site/index.html"
diff --git a/features/markdown.feature b/features/markdown.feature
index 659eed0f..2342951a 100644
--- a/features/markdown.feature
+++ b/features/markdown.feature
@@ -17,7 +17,10 @@ Feature: Markdown
And I should see "
My Title
" in "_site/index.html"
Scenario: Markdown in pagination on index
- Given I have a configuration file with "paginate" set to "5"
+ Given I have a configuration file with:
+ | key | value |
+ | paginate | 5 |
+ | gems | [jekyll-paginate] |
And I have an "index.html" page that contains "Index - {% for post in paginator.posts %} {{ post.content }} {% endfor %}"
And I have a _posts directory
And I have the following post:
@@ -47,7 +50,7 @@ Feature: Markdown
And I should see "My awesome code" in "_site/index.html"
And I should see "
My awesome code
" in "_site/index.html"
- Scenario: Maruku fenced codeblocks
+ Scenario: Maruku fenced codeblocks with syntax highlighting
Given I have a configuration file with "markdown" set to "maruku"
And I have an "index.markdown" file with content:
"""
diff --git a/features/pagination.feature b/features/pagination.feature
index 21b96b08..b8b89ed0 100644
--- a/features/pagination.feature
+++ b/features/pagination.feature
@@ -4,7 +4,10 @@ Feature: Site pagination
I want divide the posts in several pages
Scenario Outline: Paginate with N posts per page
- Given I have a configuration file with "paginate" set to ""
+ Given I have a configuration file with:
+ | key | value |
+ | paginate | |
+ | gems | [jekyll-paginate] |
And I have a _layouts directory
And I have an "index.html" page that contains "{{ paginator.posts.size }}"
And I have a _posts directory
@@ -32,6 +35,7 @@ Feature: Site pagination
| paginate | 1 |
| paginate_path | /blog/page-:num |
| permalink | /blog/:year/:month/:day/:title |
+ | gems | [jekyll-paginate] |
And I have a blog directory
And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}"
And I have a _posts directory
@@ -59,6 +63,7 @@ Feature: Site pagination
| paginate | 1 |
| paginate_path | /blog/page/:num |
| permalink | /blog/:year/:month/:day/:title |
+ | gems | [jekyll-paginate] |
And I have a blog directory
And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}"
And I have an "index.html" page that contains "Don't pick me!"
diff --git a/features/permalinks.feature b/features/permalinks.feature
index 67dfbb47..cb90d1f7 100644
--- a/features/permalinks.feature
+++ b/features/permalinks.feature
@@ -39,7 +39,7 @@ Feature: Fancy permalinks
And I have the following post:
| title | category | date | content |
| Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. |
- And I have a configuration file with "permalink" set to "/blog/:year/:month/:day/:title"
+ And I have a configuration file with "permalink" set to "/blog/:year/:month/:day/:title/"
When I run jekyll build
Then the _site directory should exist
And I should see "Totally custom." in "_site/blog/2009/03/27/custom-permalink-schema/index.html"
@@ -68,7 +68,7 @@ Feature: Fancy permalinks
Given I have a _posts directory
And I have the following post:
| title | date | permalink | content |
- | Some post | 2013-04-14 | /custom/posts/1 | bla bla |
+ | Some post | 2013-04-14 | /custom/posts/1/ | bla bla |
When I run jekyll build
Then the _site directory should exist
And the _site/custom/posts/1 directory should exist
@@ -83,13 +83,3 @@ Feature: Fancy permalinks
Then the _site directory should exist
And the _site/custom/posts directory should exist
And I should see "bla bla" in "_site/custom/posts/some.html"
-
- Scenario: Use per-post ending in .htm
- Given I have a _posts directory
- And I have the following post:
- | title | date | permalink | content |
- | Some post | 2013-04-14 | /custom/posts/some.htm | bla bla |
- When I run jekyll build
- Then the _site directory should exist
- And the _site/custom/posts directory should exist
- And I should see "bla bla" in "_site/custom/posts/some.htm"
diff --git a/features/post_data.feature b/features/post_data.feature
index 0ecaeef6..736fc936 100644
--- a/features/post_data.feature
+++ b/features/post_data.feature
@@ -141,7 +141,7 @@ Feature: Post data
And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}"
When I run jekyll build
Then the _site directory should exist
- And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
+ And I should see "Post categories: scifi and Movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
Scenario: Use post.categories variable when category is in YAML
Given I have a _posts directory
@@ -163,7 +163,7 @@ Feature: Post data
And I have a simple layout that contains "Post category: {{ page.categories }}"
When I run jekyll build
Then the _site directory should exist
- And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html"
+ And I should see "Post category: Movies" in "_site/movies/2009/03/27/star-wars.html"
Scenario: Use post.categories variable when categories are in YAML
Given I have a _posts directory
@@ -197,8 +197,8 @@ Feature: Post data
And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}"
When I run jekyll build
Then the _site directory should exist
- And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
- And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2013/03/17/star-trek.html"
+ And I should see "Post categories: scifi and Movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
+ And I should see "Post categories: SciFi and movies" in "_site/scifi/movies/2013/03/17/star-trek.html"
Scenario Outline: Use page.path variable
Given I have a /_posts directory
diff --git a/features/post_excerpts.feature b/features/post_excerpts.feature
index 4ad6d128..e9d048bd 100644
--- a/features/post_excerpts.feature
+++ b/features/post_excerpts.feature
@@ -46,5 +46,5 @@ Feature: Post excerpts
And the _site/2007/12 directory should exist
And the _site/2007/12/31 directory should exist
And the "_site/2007/12/31/entry1.html" file should exist
- And I should see exactly "
content for entry1.
" in "_site/index.html"
- And I should see exactly "
content for entry1.
" in "_site/2007/12/31/entry1.html"
+ And I should see "
content for entry1.
" in "_site/index.html"
+ And I should see "
content for entry1.
\n\n" in "_site/2007/12/31/entry1.html"
diff --git a/features/rendering.feature b/features/rendering.feature
index e0b9d8f0..55a9d3d2 100644
--- a/features/rendering.feature
+++ b/features/rendering.feature
@@ -15,6 +15,7 @@ Feature: Rendering
Scenario: Don't place asset files in layout
Given I have an "index.scss" page with layout "simple" that contains ".foo-bar { color:black; }"
And I have an "index.coffee" page with layout "simple" that contains "whatever()"
+ And I have a configuration file with "gems" set to "[jekyll-coffeescript]"
And I have a simple layout that contains "{{ content }}Ahoy, indeed!"
When I run jekyll build
Then the _site directory should exist
@@ -28,8 +29,15 @@ Feature: Rendering
Then the _site directory should exist
And I should see ".foo-bar {\n color: red; }" in "_site/index.css"
- Scenario: Render liquid in CoffeeScript
+ Scenario: Not render liquid in CoffeeScript without explicitly including jekyll-coffeescript
Given I have an "index.coffee" page with animal "cicada" that contains "hey='for {{page.animal}}'"
When I run jekyll build
Then the _site directory should exist
+ And the "_site/index.js" file should not exist
+
+ Scenario: Render liquid in CoffeeScript with jekyll-coffeescript enabled
+ Given I have an "index.coffee" page with animal "cicada" that contains "hey='for {{page.animal}}'"
+ And I have a configuration file with "gems" set to "[jekyll-coffeescript]"
+ When I run jekyll build
+ Then the _site directory should exist
And I should see "hey = 'for cicada';" in "_site/index.js"
diff --git a/features/site_configuration.feature b/features/site_configuration.feature
index 1d067f0c..d3dadd2a 100644
--- a/features/site_configuration.feature
+++ b/features/site_configuration.feature
@@ -170,8 +170,8 @@ Feature: Site configuration
When I run jekyll build
Then the _site directory should exist
And I should see "Page Layout: 2" in "_site/index.html"
- And I should see "Post Layout:
content for entry1.
built at 2013-04-09T23:22:00-04:00" in "_site/2013/04/09/entry1.html"
- And I should see "Post Layout:
content for entry2.
built at 2013-04-10T03:14:00-04:00" in "_site/2013/04/10/entry2.html"
+ And I should see "Post Layout:
content for entry1.
\n built at 2013-04-09T23:22:00-04:00" in "_site/2013/04/09/entry1.html"
+ And I should see "Post Layout:
content for entry2.
\n built at 2013-04-10T03:14:00-04:00" in "_site/2013/04/10/entry2.html"
Scenario: Generate proper dates with explicitly set timezone (different than posts' time)
Given I have a _layouts directory
@@ -180,19 +180,19 @@ Feature: Site configuration
And I have an "index.html" page with layout "page" that contains "site index page"
And I have a configuration file with:
| key | value |
- | timezone | Australia/Melbourne |
+ | timezone | Pacific/Honolulu |
And I have a _posts directory
And I have the following posts:
| title | date | layout | content |
- | entry1 | 2013-04-09 23:22 -0400 | post | content for entry1. |
- | entry2 | 2013-04-10 03:14 -0400 | post | content for entry2. |
+ | entry1 | 2013-04-09 23:22 +0400 | post | content for entry1. |
+ | entry2 | 2013-04-10 03:14 +0400 | post | content for entry2. |
When I run jekyll build
Then the _site directory should exist
And I should see "Page Layout: 2" in "_site/index.html"
- And the "_site/2013/04/10/entry1.html" file should exist
- And the "_site/2013/04/10/entry2.html" file should exist
- And I should see escaped "Post Layout:
content for entry1.
built at 2013-04-10T13:22:00+10:00" in "_site/2013/04/10/entry1.html"
- And I should see escaped "Post Layout:
content for entry2.
built at 2013-04-10T17:14:00+10:00" in "_site/2013/04/10/entry2.html"
+ And the "_site/2013/04/09/entry1.html" file should exist
+ And the "_site/2013/04/09/entry2.html" file should exist
+ And I should see "Post Layout:
content for entry1.
\n built at 2013-04-09T09:22:00-10:00" in "_site/2013/04/09/entry1.html"
+ And I should see "Post Layout:
content for entry2.
\n built at 2013-04-09T13:14:00-10:00" in "_site/2013/04/09/entry2.html"
Scenario: Limit the number of posts generated by most recent date
Given I have a _posts directory
diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb
index b75379f5..5e0509e1 100644
--- a/features/step_definitions/jekyll_steps.rb
+++ b/features/step_definitions/jekyll_steps.rb
@@ -8,7 +8,7 @@ def file_content_from_hash(input_hash)
input_hash['content']
end
- <= 0") if s.respond_to? :required_rubygems_version=
+ s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
s.rubygems_version = '2.2.2'
- s.required_ruby_version = '>= 1.9.3'
+ s.required_ruby_version = '>= 2.0.0'
- s.name = 'jekyll'
- s.version = Jekyll::VERSION
- s.license = 'MIT'
+ s.name = 'jekyll'
+ s.version = Jekyll::VERSION
+ s.license = 'MIT'
- s.summary = "A simple, blog aware, static site generator."
- s.description = "Jekyll is a simple, blog aware, static site generator."
+ s.summary = 'A simple, blog aware, static site generator.'
+ s.description = 'Jekyll is a simple, blog aware, static site generator.'
- s.authors = ["Tom Preston-Werner"]
- s.email = 'tom@mojombo.com'
- s.homepage = 'https://github.com/jekyll/jekyll'
+ s.authors = ['Tom Preston-Werner']
+ s.email = 'tom@mojombo.com'
+ s.homepage = 'https://github.com/jekyll/jekyll'
all_files = `git ls-files -z`.split("\x0")
s.files = all_files.grep(%r{^(bin|lib)/})
s.executables = all_files.grep(%r{^bin/}) { |f| File.basename(f) }
- s.require_paths = ["lib"]
+ s.require_paths = ['lib']
- s.rdoc_options = ["--charset=UTF-8"]
+ s.rdoc_options = ['--charset=UTF-8']
s.extra_rdoc_files = %w[README.markdown LICENSE]
- s.add_runtime_dependency('liquid', "~> 2.6.1")
- s.add_runtime_dependency('kramdown', "~> 1.3")
- s.add_runtime_dependency('mercenary', "~> 0.3.3")
- s.add_runtime_dependency('safe_yaml', "~> 1.0")
- s.add_runtime_dependency('colorator', "~> 0.1")
-
- # Before 3.0 drops, phase the following gems out as dev dependencies
- # and gracefully handle their absence.
- s.add_runtime_dependency('pygments.rb', "~> 0.6.0")
- s.add_runtime_dependency('redcarpet', "~> 3.1")
- s.add_runtime_dependency('toml', '~> 0.1.0')
- s.add_runtime_dependency('jekyll-paginate', '~> 1.0')
- s.add_runtime_dependency('jekyll-gist', '~> 1.0')
- s.add_runtime_dependency('jekyll-coffeescript', '~> 1.0')
+ s.add_runtime_dependency('liquid', '~> 3.0')
+ s.add_runtime_dependency('kramdown', '~> 1.3')
+ s.add_runtime_dependency('mercenary', '~> 0.3.3')
+ s.add_runtime_dependency('safe_yaml', '~> 1.0')
+ s.add_runtime_dependency('colorator', '~> 0.1')
+ s.add_runtime_dependency('rouge', '~> 1.7')
s.add_runtime_dependency('jekyll-sass-converter', '~> 1.0')
s.add_runtime_dependency('jekyll-watch', '~> 1.1')
- s.add_runtime_dependency('classifier-reborn', "~> 2.0")
-
- s.add_development_dependency('rake', "~> 10.1")
- s.add_development_dependency('rdoc', "~> 3.11")
- s.add_development_dependency('redgreen', "~> 1.2")
- s.add_development_dependency('shoulda', "~> 3.5")
- s.add_development_dependency('rr', "~> 1.1")
- s.add_development_dependency('cucumber', "1.3.11")
- s.add_development_dependency('RedCloth', "~> 4.2")
- s.add_development_dependency('maruku', "~> 0.7.0")
- s.add_development_dependency('rdiscount', "~> 1.6")
- s.add_development_dependency('launchy', "~> 2.3")
- s.add_development_dependency('simplecov', "~> 0.9")
- s.add_development_dependency('simplecov-gem-adapter', "~> 1.0.1")
- s.add_development_dependency('mime-types', "~> 1.5")
- s.add_development_dependency('activesupport', '~> 3.2.13')
- s.add_development_dependency('jekyll_test_plugin')
- s.add_development_dependency('jekyll_test_plugin_malicious')
- s.add_development_dependency('rouge', '~> 1.7')
- s.add_development_dependency('minitest') if RUBY_PLATFORM =~ /cygwin/
- s.add_development_dependency('test-unit') if RUBY_PLATFORM =~ /cygwin/
end
diff --git a/lib/jekyll.rb b/lib/jekyll.rb
index 03724100..02aa40b1 100644
--- a/lib/jekyll.rb
+++ b/lib/jekyll.rb
@@ -21,6 +21,7 @@ require 'time'
require 'English'
require 'pathname'
require 'logger'
+require 'set'
# 3rd party
require 'safe_yaml/load'
@@ -29,6 +30,7 @@ require 'kramdown'
require 'colorator'
SafeYAML::OPTIONS[:suppress_warnings] = true
+Liquid::Template.error_mode = :strict
module Jekyll
@@ -43,6 +45,7 @@ module Jekyll
autoload :EntryFilter, 'jekyll/entry_filter'
autoload :Errors, 'jekyll/errors'
autoload :Excerpt, 'jekyll/excerpt'
+ autoload :External, 'jekyll/external'
autoload :Filters, 'jekyll/filters'
autoload :FrontmatterDefaults, 'jekyll/frontmatter_defaults'
autoload :Layout, 'jekyll/layout'
@@ -52,6 +55,7 @@ module Jekyll
autoload :PluginManager, 'jekyll/plugin_manager'
autoload :Post, 'jekyll/post'
autoload :Publisher, 'jekyll/publisher'
+ autoload :Regenerator, 'jekyll/regenerator'
autoload :RelatedPosts, 'jekyll/related_posts'
autoload :Renderer, 'jekyll/renderer'
autoload :Site, 'jekyll/site'
@@ -153,6 +157,9 @@ module Jekyll
end
end
+ # Conditional optimizations
+ Jekyll::External.require_if_present('liquid-c')
+
end
end
@@ -162,11 +169,4 @@ require_all 'jekyll/converters/markdown'
require_all 'jekyll/generators'
require_all 'jekyll/tags'
-# Eventually remove these for 3.0 as non-core
-Jekyll::Deprecator.gracefully_require(%w[
- toml
- jekyll-paginate
- jekyll-gist
- jekyll-coffeescript
- jekyll-sass-converter
-])
+require 'jekyll-sass-converter'
diff --git a/lib/jekyll/cleaner.rb b/lib/jekyll/cleaner.rb
index 6dd59ea0..a8b269f4 100644
--- a/lib/jekyll/cleaner.rb
+++ b/lib/jekyll/cleaner.rb
@@ -1,95 +1,101 @@
require 'set'
module Jekyll
- class Site
- # Handles the cleanup of a site's destination before it is built.
- class Cleaner
- attr_reader :site
+ # Handles the cleanup of a site's destination before it is built.
+ class Cleaner
+ attr_reader :site
- def initialize(site)
- @site = site
+ def initialize(site)
+ @site = site
+ end
+
+ # Cleans up the site's destination directory
+ def cleanup!
+ FileUtils.rm_rf(obsolete_files)
+ FileUtils.rm_rf(metadata_file) if @site.full_rebuild?
+ end
+
+ private
+
+ # Private: The list of files and directories to be deleted during cleanup process
+ #
+ # Returns an Array of the file and directory paths
+ def obsolete_files
+ (existing_files - new_files - new_dirs + replaced_files).to_a
+ end
+
+ # Private: The metadata file storing dependency tree and build history
+ #
+ # Returns an Array with the metdata file as the only item
+ def metadata_file
+ [site.regenerator.metadata_file]
+ end
+
+ # Private: The list of existing files, apart from those included in keep_files and hidden files.
+ #
+ # Returns a Set with the file paths
+ def existing_files
+ files = Set.new
+ Dir.glob(site.in_dest_dir("**", "*"), File::FNM_DOTMATCH) do |file|
+ files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex || keep_dirs.include?(file)
end
+ files
+ end
- # Cleans up the site's destination directory
- def cleanup!
- FileUtils.rm_rf(obsolete_files)
+ # Private: The list of files to be created when site is built.
+ #
+ # Returns a Set with the file paths
+ def new_files
+ files = Set.new
+ site.each_site_file { |item| files << item.destination(site.dest) }
+ files
+ end
+
+ # Private: The list of directories to be created when site is built.
+ # These are the parent directories of the files in #new_files.
+ #
+ # Returns a Set with the directory paths
+ def new_dirs
+ new_files.map { |file| parent_dirs(file) }.flatten.to_set
+ end
+
+ # Private: The list of parent directories of a given file
+ #
+ # Returns an Array with the directory paths
+ def parent_dirs(file)
+ parent_dir = File.dirname(file)
+ if parent_dir == site.dest
+ []
+ else
+ [parent_dir] + parent_dirs(parent_dir)
end
+ end
- private
+ # Private: The list of existing files that will be replaced by a directory during build
+ #
+ # Returns a Set with the file paths
+ def replaced_files
+ new_dirs.select { |dir| File.file?(dir) }.to_set
+ end
- # Private: The list of files and directories to be deleted during cleanup process
- #
- # Returns an Array of the file and directory paths
- def obsolete_files
- (existing_files - new_files - new_dirs + replaced_files).to_a
- end
+ # Private: The list of directories that need to be kept because they are parent directories
+ # of files specified in keep_files
+ #
+ # Returns a Set with the directory paths
+ def keep_dirs
+ site.keep_files.map { |file| parent_dirs(site.in_dest_dir(file)) }.flatten.to_set
+ end
- # Private: The list of existing files, apart from those included in keep_files and hidden files.
- #
- # Returns a Set with the file paths
- def existing_files
- files = Set.new
- Dir.glob(site.in_dest_dir("**", "*"), File::FNM_DOTMATCH) do |file|
- files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex || keep_dirs.include?(file)
- end
- files
- end
-
- # Private: The list of files to be created when site is built.
- #
- # Returns a Set with the file paths
- def new_files
- files = Set.new
- site.each_site_file { |item| files << item.destination(site.dest) }
- files
- end
-
- # Private: The list of directories to be created when site is built.
- # These are the parent directories of the files in #new_files.
- #
- # Returns a Set with the directory paths
- def new_dirs
- new_files.map { |file| parent_dirs(file) }.flatten.to_set
- end
-
- # Private: The list of parent directories of a given file
- #
- # Returns an Array with the directory paths
- def parent_dirs(file)
- parent_dir = File.dirname(file)
- if parent_dir == site.dest
- []
- else
- [parent_dir] + parent_dirs(parent_dir)
- end
- end
-
- # Private: The list of existing files that will be replaced by a directory during build
- #
- # Returns a Set with the file paths
- def replaced_files
- new_dirs.select { |dir| File.file?(dir) }.to_set
- end
-
- # Private: The list of directories that need to be kept because they are parent directories
- # of files specified in keep_files
- #
- # Returns a Set with the directory paths
- def keep_dirs
- site.keep_files.map { |file| parent_dirs(site.in_dest_dir(file)) }.flatten.to_set
- end
-
- # Private: Creates a regular expression from the config's keep_files array
- #
- # Examples
- # ['.git','.svn'] creates the following regex: /\/(\.git|\/.svn)/
- #
- # Returns the regular expression
- def keep_file_regex
- or_list = site.keep_files.join("|")
- pattern = "\/(#{or_list.gsub(".", "\.")})"
- Regexp.new pattern
- end
+ # Private: Creates a regular expression from the config's keep_files array
+ #
+ # Examples
+ # ['.git','.svn'] creates the following regex: /\/(\.git|\/.svn)/
+ #
+ # Returns the regular expression
+ def keep_file_regex
+ or_list = site.keep_files.join("|")
+ pattern = "\/(#{or_list.gsub(".", "\.")})"
+ Regexp.new pattern
end
end
end
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 73e480dd..e8740cf6 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -40,7 +40,7 @@ module Jekyll
if Utils.has_yaml_header? full_path
doc = Jekyll::Document.new(full_path, { site: site, collection: self })
doc.read
- docs << doc
+ docs << doc if site.publisher.publish?(doc)
else
relative_dir = Jekyll.sanitized_path(relative_directory, File.dirname(file_path)).chomp("/.")
files << StaticFile.new(site, site.source, relative_dir, File.basename(full_path), self)
@@ -170,7 +170,9 @@ module Jekyll
#
# Returns the URL template to render collection's documents at.
def url_template
- metadata.fetch('permalink', "/:collection/:path:output_ext")
+ metadata.fetch('permalink') do
+ Utils.add_permalink_suffix("/:collection/:path", site.permalink_style)
+ end
end
# Extract options for this collection from the site configuration.
@@ -183,6 +185,5 @@ module Jekyll
{}
end
end
-
end
end
diff --git a/lib/jekyll/command.rb b/lib/jekyll/command.rb
index a1cc8c01..a72640fb 100644
--- a/lib/jekyll/command.rb
+++ b/lib/jekyll/command.rb
@@ -49,6 +49,8 @@ module Jekyll
# Returns nothing
def add_build_options(c)
c.option 'config', '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
+ c.option 'destination', '-d', '--destination DESTINATION', 'The current folder will be generated into DESTINATION'
+ c.option 'source', '-s', '--source SOURCE', 'Custom source directory'
c.option 'future', '--future', 'Publishes posts with a future date'
c.option 'limit_posts', '--limit_posts MAX_POSTS', Integer, 'Limits the number of posts to parse and publish'
c.option 'watch', '-w', '--[no-]watch', 'Watch for changes and rebuild'
@@ -58,6 +60,7 @@ module Jekyll
c.option 'unpublished', '--unpublished', 'Render posts that were marked as unpublished'
c.option 'quiet', '-q', '--quiet', 'Silence output.'
c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
+ c.option 'full_rebuild', '-f', '--full-rebuild', 'Disable incremental rebuild.'
end
end
diff --git a/lib/jekyll/commands/build.rb b/lib/jekyll/commands/build.rb
index de234328..cc1e121f 100644
--- a/lib/jekyll/commands/build.rb
+++ b/lib/jekyll/commands/build.rb
@@ -48,13 +48,16 @@ module Jekyll
#
# Returns nothing.
def build(site, options)
+ t = Time.now
source = options['source']
destination = options['destination']
+ full_build = options['full_rebuild']
Jekyll.logger.info "Source:", source
Jekyll.logger.info "Destination:", destination
+ Jekyll.logger.info "Incremental build:", (full_build ? "disabled" : "enabled")
Jekyll.logger.info "Generating..."
process_site(site)
- Jekyll.logger.info "", "done."
+ Jekyll.logger.info "", "done in #{(Time.now - t).round(3)} seconds."
end
# Private: Watch for file changes and rebuild the site.
@@ -64,7 +67,7 @@ module Jekyll
#
# Returns nothing.
def watch(site, options)
- Deprecator.gracefully_require 'jekyll-watch'
+ External.require_with_graceful_fail 'jekyll-watch'
Jekyll::Watcher.watch(options)
end
diff --git a/lib/jekyll/commands/clean.rb b/lib/jekyll/commands/clean.rb
new file mode 100644
index 00000000..7d787d30
--- /dev/null
+++ b/lib/jekyll/commands/clean.rb
@@ -0,0 +1,42 @@
+module Jekyll
+ module Commands
+ class Clean < Command
+ class << self
+
+ def init_with_program(prog)
+ prog.command(:clean) do |c|
+ c.syntax 'clean [subcommand]'
+ c.description 'Clean the site (removes site output and metadata file) without building.'
+
+ c.action do |args, _|
+ Jekyll::Commands::Clean.process({})
+ end
+ end
+ end
+
+ def process(options)
+ options = configuration_from_options(options)
+ destination = options['destination']
+ metadata_file = File.join(options['source'], '.jekyll-metadata')
+
+ if File.directory? destination
+ Jekyll.logger.info "Cleaning #{destination}..."
+ FileUtils.rm_rf(destination)
+ Jekyll.logger.info "", "done."
+ else
+ Jekyll.logger.info "Nothing to do for #{destination}."
+ end
+
+ if File.file? metadata_file
+ Jekyll.logger.info "Removing #{metadata_file}..."
+ FileUtils.rm_rf(metadata_file)
+ Jekyll.logger.info "", "done."
+ else
+ Jekyll.logger.info "Nothing to do for #{metadata_file}."
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/jekyll/commands/docs.rb b/lib/jekyll/commands/docs.rb
deleted file mode 100644
index 71c10160..00000000
--- a/lib/jekyll/commands/docs.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module Jekyll
- module Commands
- class Docs < Command
-
- class << self
-
- def init_with_program(prog)
- prog.command(:docs) do |c|
- c.syntax 'docs'
- c.description "Launch local server with docs for Jekyll v#{Jekyll::VERSION}"
-
- c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
- c.option 'host', '-H', '--host [HOST]', 'Host to bind to'
-
- c.action do |args, options|
- options.merge!({
- 'source' => File.expand_path("../../../site", File.dirname(__FILE__)),
- 'destination' => File.expand_path("../../../site/_site", File.dirname(__FILE__))
- })
- Jekyll::Commands::Build.process(options)
- Jekyll::Commands::Serve.process(options)
- end
- end
- end
-
- end
-
- end
- end
-end
diff --git a/lib/jekyll/commands/doctor.rb b/lib/jekyll/commands/doctor.rb
index 0fab8b3a..e0ae36a1 100644
--- a/lib/jekyll/commands/doctor.rb
+++ b/lib/jekyll/commands/doctor.rb
@@ -39,7 +39,7 @@ module Jekyll
contains_deprecated_pages = false
site.pages.each do |page|
if page.uses_relative_permalinks
- Jekyll.logger.warn "Deprecation:", "'#{page.path}' uses relative" +
+ Jekyll::Deprecator.deprecation_message "'#{page.path}' uses relative" +
" permalinks which will be deprecated in" +
" Jekyll v2.0.0 and beyond."
contains_deprecated_pages = true
diff --git a/lib/jekyll/commands/new.rb b/lib/jekyll/commands/new.rb
index 724a1d8d..433d33b7 100644
--- a/lib/jekyll/commands/new.rb
+++ b/lib/jekyll/commands/new.rb
@@ -3,7 +3,7 @@ require 'erb'
module Jekyll
module Commands
class New < Command
- class << self
+ class << self
def init_with_program(prog)
prog.command(:new) do |c|
c.syntax 'new PATH'
@@ -11,7 +11,7 @@ module Jekyll
c.option 'force', '--force', 'Force creation even if PATH already exists'
c.option 'blank', '--blank', 'Creates scaffolding but with empty files'
-
+
c.action do |args, options|
Jekyll::Commands::New.process(args, options)
end
@@ -76,7 +76,7 @@ module Jekyll
def scaffold_path
"_posts/0000-00-00-welcome-to-jekyll.markdown.erb"
end
- end
+ end
end
end
end
diff --git a/lib/jekyll/commands/serve.rb b/lib/jekyll/commands/serve.rb
index 2909f323..111d6709 100644
--- a/lib/jekyll/commands/serve.rb
+++ b/lib/jekyll/commands/serve.rb
@@ -40,7 +40,7 @@ module Jekyll
s.mount(
options['baseurl'],
- WEBrick::HTTPServlet::FileHandler,
+ custom_file_handler,
destination,
file_handler_options
)
@@ -50,7 +50,7 @@ module Jekyll
if options['detach'] # detach the server
pid = Process.fork { s.start }
Process.detach(pid)
- Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `kill -9 #{pid}' to stop the server."
+ Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
else # create a new server thread, then join it with current terminal
t = Thread.new { s.start }
trap("INT") { s.shutdown }
@@ -99,6 +99,21 @@ module Jekyll
opts
end
+ # Custom WEBrick FileHandler servlet for serving "/file.html" at "/file"
+ # when no exact match is found. This mirrors the behavior of GitHub
+ # Pages and many static web server configs.
+ def custom_file_handler
+ Class.new WEBrick::HTTPServlet::FileHandler do
+ def search_file(req, res, basename)
+ if file = super
+ file
+ else
+ super(req, res, "#{basename}.html")
+ end
+ end
+ end
+ end
+
def start_callback(detached)
unless detached
Proc.new { Jekyll.logger.info "Server running...", "press ctrl-c to stop." }
diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb
index 7f1148a6..ca5d4839 100644
--- a/lib/jekyll/configuration.rb
+++ b/lib/jekyll/configuration.rb
@@ -21,7 +21,7 @@ module Jekyll
'keep_files' => ['.git','.svn'],
'encoding' => 'utf-8',
'markdown_ext' => 'markdown,mkdown,mkdn,mkd,md',
- 'textile_ext' => 'textile',
+ 'full_rebuild' => false,
# Filtering Content
'show_drafts' => nil,
@@ -35,7 +35,7 @@ module Jekyll
# Conversion
'markdown' => 'kramdown',
- 'highlighter' => 'pygments',
+ 'highlighter' => 'rouge',
'lsi' => false,
'excerpt_separator' => "\n\n",
@@ -74,12 +74,12 @@ module Jekyll
},
'kramdown' => {
- 'auto_ids' => true,
- 'footnote_nr' => 1,
- 'entity_output' => 'as_char',
- 'toc_levels' => '1..6',
- 'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
- 'use_coderay' => false,
+ 'auto_ids' => true,
+ 'footnote_nr' => 1,
+ 'entity_output' => 'as_char',
+ 'toc_levels' => '1..6',
+ 'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
+ 'enable_coderay' => false,
'coderay' => {
'coderay_wrap' => 'div',
@@ -89,10 +89,6 @@ module Jekyll
'coderay_bold_every' => 10,
'coderay_css' => 'style'
}
- },
-
- 'redcloth' => {
- 'hard_breaks' => true
}
}
@@ -119,6 +115,7 @@ module Jekyll
def safe_load_file(filename)
case File.extname(filename)
when /\.toml/i
+ Jekyll::External.require_with_graceful_fail('toml') unless defined?(TOML)
TOML.load_file(filename)
when /\.ya?ml/i
SafeYAML.load_file(filename)
@@ -140,7 +137,7 @@ module Jekyll
config_files = override.delete('config')
if config_files.to_s.empty?
default = %w[yml yaml].find(Proc.new { 'yml' }) do |ext|
- File.exists? Jekyll.sanitized_path(source(override), "_config.#{ext}")
+ File.exist?(Jekyll.sanitized_path(source(override), "_config.#{ext}"))
end
config_files = Jekyll.sanitized_path(source(override), "_config.#{default}")
@default_config_file = true
@@ -209,7 +206,7 @@ module Jekyll
config = clone
# Provide backwards-compatibility
if config.key?('auto') || config.key?('watch')
- Jekyll.logger.warn "Deprecation:", "Auto-regeneration can no longer" +
+ Jekyll::Deprecator.deprecation_message "Auto-regeneration can no longer" +
" be set from your configuration file(s). Use the"+
" --[no-]watch/-w command-line option instead."
config.delete('auto')
@@ -217,14 +214,14 @@ module Jekyll
end
if config.key? 'server'
- Jekyll.logger.warn "Deprecation:", "The 'server' configuration option" +
+ Jekyll::Deprecator.deprecation_message "The 'server' configuration option" +
" is no longer accepted. Use the 'jekyll serve'" +
" subcommand to serve your site with WEBrick."
config.delete('server')
end
if config.key? 'server_port'
- Jekyll.logger.warn "Deprecation:", "The 'server_port' configuration option" +
+ Jekyll::Deprecator.deprecation_message "The 'server_port' configuration option" +
" has been renamed to 'port'. Please update your config" +
" file accordingly."
# copy but don't overwrite:
@@ -233,7 +230,7 @@ module Jekyll
end
if config.key? 'pygments'
- Jekyll.logger.warn "Deprecation:", "The 'pygments' configuration option" +
+ Jekyll::Deprecator.deprecation_message "The 'pygments' configuration option" +
" has been renamed to 'highlighter'. Please update your" +
" config file accordingly. The allowed values are 'rouge', " +
"'pygments' or null."
@@ -244,7 +241,7 @@ module Jekyll
%w[include exclude].each do |option|
if config.fetch(option, []).is_a?(String)
- Jekyll.logger.warn "Deprecation:", "The '#{option}' configuration option" +
+ Jekyll::Deprecator.deprecation_message "The '#{option}' configuration option" +
" must now be specified as an array, but you specified" +
" a string. For now, we've treated the string you provided" +
" as a list of comma-separated values."
@@ -253,11 +250,23 @@ module Jekyll
config[option].map!(&:to_s)
end
+ if (config['kramdown'] || {}).key?('use_coderay')
+ Jekyll::Deprecator.deprecation_message "Please change 'use_coderay'" +
+ " to 'enable_coderay' in your configuration file."
+ config['kramdown']['use_coderay'] = config['kramdown'].delete('enable_coderay')
+ end
+
if config.fetch('markdown', 'kramdown').to_s.downcase.eql?("maruku")
Jekyll::Deprecator.deprecation_message "You're using the 'maruku' " +
"Markdown processor. Maruku support has been deprecated and will " +
"be removed in 3.0.0. We recommend you switch to Kramdown."
end
+
+ if config.key?('paginate') && config['paginate'] && !(config['gems'] || []).include?('jekyll-paginate')
+ Jekyll::Deprecator.deprecation_message "You appear to have pagination " +
+ "turned on, but you haven't included the `jekyll-paginate` gem. " +
+ "Ensure you have `gems: [jekyll-paginate]` in your configuration file."
+ end
config
end
diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb
index f0d1afb0..39389874 100644
--- a/lib/jekyll/converters/markdown.rb
+++ b/lib/jekyll/converters/markdown.rb
@@ -46,15 +46,12 @@ module Jekyll
].map(&:to_sym)
end
- def extname_matches_regexp
- @extname_matches_regexp ||= Regexp.new(
- '^\.(' + @config['markdown_ext'].gsub(',','|') +')$',
- Regexp::IGNORECASE
- )
+ def extname_list
+ @extname_list ||= @config['markdown_ext'].split(',').map { |e| ".#{e.downcase}" }
end
def matches(ext)
- ext =~ extname_matches_regexp
+ extname_list.include? ext.downcase
end
def output_ext(ext)
diff --git a/lib/jekyll/converters/markdown/kramdown_parser.rb b/lib/jekyll/converters/markdown/kramdown_parser.rb
index 097c9212..a9dbec96 100644
--- a/lib/jekyll/converters/markdown/kramdown_parser.rb
+++ b/lib/jekyll/converters/markdown/kramdown_parser.rb
@@ -13,14 +13,14 @@ module Jekyll
def convert(content)
# Check for use of coderay
- if @config['kramdown']['use_coderay']
+ if @config['kramdown']['enable_coderay']
%w[wrap line_numbers line_numbers_start tab_width bold_every css default_lang].each do |opt|
key = "coderay_#{opt}"
@config['kramdown'][key] = @config['kramdown']['coderay'][key] unless @config['kramdown'].key?(key)
end
end
- Kramdown::Document.new(content, Utils.symbolize_hash_keys(@config["kramdown"])).to_html
+ Kramdown::Document.new(content, Utils.symbolize_hash_keys(@config['kramdown'])).to_html
end
end
diff --git a/lib/jekyll/converters/markdown/rdiscount_parser.rb b/lib/jekyll/converters/markdown/rdiscount_parser.rb
index 0c8634e4..fb5172e7 100644
--- a/lib/jekyll/converters/markdown/rdiscount_parser.rb
+++ b/lib/jekyll/converters/markdown/rdiscount_parser.rb
@@ -3,7 +3,7 @@ module Jekyll
class Markdown
class RDiscountParser
def initialize(config)
- Jekyll::Deprecator.gracefully_require "rdiscount"
+ Jekyll::External.require_with_graceful_fail "rdiscount"
@config = config
@rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym }
end
diff --git a/lib/jekyll/converters/markdown/redcarpet_parser.rb b/lib/jekyll/converters/markdown/redcarpet_parser.rb
index 468069e9..3be60739 100644
--- a/lib/jekyll/converters/markdown/redcarpet_parser.rb
+++ b/lib/jekyll/converters/markdown/redcarpet_parser.rb
@@ -14,7 +14,7 @@ module Jekyll
module WithPygments
include CommonMethods
def block_code(code, lang)
- Jekyll::Deprecator.gracefully_require("pygments")
+ Jekyll::External.require_with_graceful_fail("pygments")
lang = lang && lang.split.first || "text"
add_code_tags(
Pygments.highlight(code, :lexer => lang, :options => { :encoding => 'utf-8' }),
@@ -55,7 +55,7 @@ module Jekyll
def initialize(config)
- Deprecator.gracefully_require("redcarpet")
+ External.require_with_graceful_fail("redcarpet")
@config = config
@redcarpet_extensions = {}
@config['redcarpet']['extensions'].each { |e| @redcarpet_extensions[e.to_sym] = true }
@@ -71,7 +71,7 @@ module Jekyll
end
when "rouge"
Class.new(Redcarpet::Render::HTML) do
- Jekyll::Deprecator.gracefully_require(%w[
+ Jekyll::External.require_with_graceful_fail(%w[
rouge
rouge/plugins/redcarpet
])
diff --git a/lib/jekyll/converters/textile.rb b/lib/jekyll/converters/textile.rb
deleted file mode 100644
index c8137a3a..00000000
--- a/lib/jekyll/converters/textile.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-module Jekyll
- module Converters
- class Textile < Converter
- safe true
-
- highlighter_prefix ''
- highlighter_suffix ''
-
- def setup
- return if @setup
- require 'redcloth'
- @setup = true
- rescue LoadError
- STDERR.puts 'You are missing a library required for Textile. Please run:'
- STDERR.puts ' $ [sudo] gem install RedCloth'
- raise Errors::FatalException.new("Missing dependency: RedCloth")
- end
-
- def extname_matches_regexp
- @extname_matches_regexp ||= Regexp.new(
- '(' + @config['textile_ext'].gsub(',','|') +')$',
- Regexp::IGNORECASE
- )
- end
-
- def matches(ext)
- ext =~ extname_matches_regexp
- end
-
- def output_ext(ext)
- ".html"
- end
-
- def convert(content)
- setup
-
- # Shortcut if config doesn't contain RedCloth section
- return RedCloth.new(content).to_html if @config['redcloth'].nil?
-
- # List of attributes defined on RedCloth
- # (from https://github.com/jgarber/redcloth/blob/master/lib/redcloth/textile_doc.rb)
- attrs = ['filter_classes', 'filter_html', 'filter_ids', 'filter_styles',
- 'hard_breaks', 'lite_mode', 'no_span_caps', 'sanitize_html']
-
- r = RedCloth.new(content)
-
- # Set attributes in r if they are NOT nil in the config
- attrs.each do |attr|
- r.instance_variable_set("@#{attr}".to_sym, @config['redcloth'][attr]) unless @config['redcloth'][attr].nil?
- end
-
- r.to_html
- end
- end
- end
-end
diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb
index 4b21a66b..d587f8f3 100644
--- a/lib/jekyll/convertible.rb
+++ b/lib/jekyll/convertible.rb
@@ -207,6 +207,12 @@ module Jekyll
info,
File.join(site.config['layouts'], layout.name))
+ # Add layout to dependency tree
+ site.regenerator.add_dependency(
+ site.in_source_dir(path),
+ site.in_source_dir(layout.path)
+ )
+
if layout = layouts[layout.data["layout"]]
if used.include?(layout)
layout = nil # avoid recursive chain
diff --git a/lib/jekyll/deprecator.rb b/lib/jekyll/deprecator.rb
index 8e4daf9c..dfd3183c 100644
--- a/lib/jekyll/deprecator.rb
+++ b/lib/jekyll/deprecator.rb
@@ -21,7 +21,7 @@ module Jekyll
def no_subcommand(args)
if args.size > 0 && args.first =~ /^--/ && !%w[--help --version].include?(args.first)
deprecation_message "Jekyll now uses subcommands instead of just \
- switches. Run `jekyll --help' to find out more."
+ switches. Run `jekyll --help` to find out more."
end
end
@@ -40,22 +40,5 @@ module Jekyll
Jekyll.logger.warn "Defaults:", "Please update your front-matter defaults to use 'type: #{current}'."
end
- def gracefully_require(gem_name)
- Array(gem_name).each do |name|
- begin
- require name
- rescue LoadError => e
- Jekyll.logger.error "Dependency Error:", <<-MSG
- Yikes! It looks like you don't have #{name} or one of its dependencies installed.
- In order to use Jekyll as currently configured, you'll need to install this gem.
-
- The full error message from Ruby is: '#{e.message}'
-
- If you run into trouble, you can find helpful resources at http://jekyllrb.com/help/!
-MSG
- raise Errors::MissingDependencyException.new(name)
- end
- end
- end
end
end
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index 003c04eb..cd407dfa 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -4,9 +4,11 @@ module Jekyll
class Document
include Comparable
- attr_reader :path, :site, :extname
+ attr_reader :path, :site, :extname, :output_ext
attr_accessor :content, :collection, :output
+ YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
+
# Create a new Document.
#
# site - the Jekyll::Site instance to which this Document belongs
@@ -17,6 +19,7 @@ module Jekyll
@site = relations[:site]
@path = path
@extname = File.extname(path)
+ @output_ext = Jekyll::Renderer.new(site, self).output_ext
@collection = relations[:collection]
@has_yaml_header = nil
end
@@ -128,9 +131,9 @@ module Jekyll
{
collection: collection.label,
path: cleaned_relative_path,
- output_ext: Jekyll::Renderer.new(site, self).output_ext,
+ output_ext: output_ext,
name: Utils.slugify(basename_without_ext),
- title: Utils.slugify(data['title']) || Utils.slugify(basename_without_ext)
+ title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext)
}
end
@@ -160,8 +163,9 @@ module Jekyll
# Returns the full path to the output file of this document.
def destination(base_directory)
dest = site.in_dest_dir(base_directory)
- path = site.in_dest_dir(dest, url)
- path = File.join(path, "index.html") if url =~ /\/$/
+ path = site.in_dest_dir(dest, URL.unescape_path(url))
+ path = File.join(path, "index.html") if url.end_with?("/")
+ path << output_ext unless path.end_with?(output_ext)
path
end
@@ -210,7 +214,7 @@ module Jekyll
@data = defaults
end
@content = File.read(path, merged_file_read_opts(opts))
- if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
+ if content =~ YAML_FRONT_MATTER_REGEXP
@content = $POSTMATCH
data_file = SafeYAML.load($1)
unless data_file.nil?
@@ -233,8 +237,8 @@ module Jekyll
Utils.deep_merge_hashes data, {
"output" => output,
"content" => content,
- "path" => path,
"relative_path" => relative_path,
+ "path" => relative_path,
"url" => url,
"collection" => collection.label
}
diff --git a/lib/jekyll/excerpt.rb b/lib/jekyll/excerpt.rb
index 41d4976c..347be217 100644
--- a/lib/jekyll/excerpt.rb
+++ b/lib/jekyll/excerpt.rb
@@ -105,8 +105,7 @@ module Jekyll
#
# Returns excerpt String
def extract_excerpt(post_content)
- separator = site.config['excerpt_separator']
- head, _, tail = post_content.to_s.partition(separator)
+ head, _, tail = post_content.to_s.partition(post.excerpt_separator)
"" << head << "\n\n" << tail.scan(/^\[[^\]]+\]:.+$/).join("\n")
end
diff --git a/lib/jekyll/external.rb b/lib/jekyll/external.rb
new file mode 100644
index 00000000..e41bce27
--- /dev/null
+++ b/lib/jekyll/external.rb
@@ -0,0 +1,59 @@
+module Jekyll
+ module External
+ class << self
+
+ #
+ # Gems that, if installed, should be loaded.
+ # Usually contain subcommands.
+ #
+ def blessed_gems
+ %w{
+ jekyll-docs
+ jekyll-import
+ }
+ end
+
+ #
+ # Require a gem or file if it's present, otherwise silently fail.
+ #
+ # names - a string gem name or array of gem names
+ #
+ def require_if_present(names)
+ Array(names).each do |name|
+ begin
+ require name
+ rescue LoadError
+ Jekyll.logger.debug "Couldn't load #{name}. Skipping."
+ false
+ end
+ end
+ end
+
+ #
+ # Require a gem or gems. If it's not present, show a very nice error
+ # message that explains everything and is much more helpful than the
+ # normal LoadError.
+ #
+ # names - a string gem name or array of gem names
+ #
+ def require_with_graceful_fail(names)
+ Array(names).each do |name|
+ begin
+ require name
+ rescue LoadError => e
+ Jekyll.logger.error "Dependency Error:", <<-MSG
+Yikes! It looks like you don't have #{name} or one of its dependencies installed.
+In order to use Jekyll as currently configured, you'll need to install this gem.
+
+The full error message from Ruby is: '#{e.message}'
+
+If you run into trouble, you can find helpful resources at http://jekyllrb.com/help/!
+ MSG
+ raise Jekyll::Errors::MissingDependencyException.new(name)
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb
index 2e4a1174..12d4eb5a 100644
--- a/lib/jekyll/filters.rb
+++ b/lib/jekyll/filters.rb
@@ -1,19 +1,9 @@
require 'uri'
require 'json'
+require 'date'
module Jekyll
module Filters
- # Convert a Textile string into HTML output.
- #
- # input - The Textile String to convert.
- #
- # Returns the HTML formatted String.
- def textilize(input)
- site = @context.registers[:site]
- converter = site.getConverterImpl(Jekyll::Converters::Textile)
- converter.convert(input)
- end
-
# Convert a Markdown string into HTML output.
#
# input - The Markdown String to convert.
@@ -21,7 +11,7 @@ module Jekyll
# Returns the HTML formatted String.
def markdownify(input)
site = @context.registers[:site]
- converter = site.getConverterImpl(Jekyll::Converters::Markdown)
+ converter = site.find_converter_instance(Jekyll::Converters::Markdown)
converter.convert(input)
end
@@ -32,7 +22,7 @@ module Jekyll
# Returns the CSS formatted String.
def sassify(input)
site = @context.registers[:site]
- converter = site.getConverterImpl(Jekyll::Converters::Sass)
+ converter = site.find_converter_instance(Jekyll::Converters::Sass)
converter.convert(input)
end
@@ -43,19 +33,19 @@ module Jekyll
# Returns the CSS formatted String.
def scssify(input)
site = @context.registers[:site]
- converter = site.getConverterImpl(Jekyll::Converters::Scss)
+ converter = site.find_converter_instance(Jekyll::Converters::Scss)
converter.convert(input)
end
# Slugify a filename or title.
#
# input - The filename or title to slugify.
+ # mode - how string is slugified
#
- # Returns the given filename or title as a lowercase String, with every
- # sequence of spaces and non-alphanumeric characters replaced with a
- # hyphen.
- def slugify(input)
- Utils.slugify(input)
+ # Returns the given filename or title as a lowercase URL String.
+ # See Utils.slugify for more detail.
+ def slugify(input, mode=nil)
+ Utils.slugify(input, mode)
end
# Format a date in short format e.g. "27 Jan 2011".
@@ -232,6 +222,9 @@ module Jekyll
#
# Returns the filtered array of objects
def sort(input, property = nil, nils = "first")
+ if input.nil?
+ raise ArgumentError.new("Cannot sort a null object.")
+ end
if property.nil?
input.sort
else
@@ -302,6 +295,8 @@ module Jekyll
case input
when Time
input
+ when Date
+ input.to_time
when String
Time.parse(input) rescue Time.at(input.to_i)
when Numeric
@@ -309,7 +304,7 @@ module Jekyll
else
Jekyll.logger.error "Invalid Date:", "'#{input}' is not a valid datetime."
exit(1)
- end
+ end.localtime
end
def groupable?(element)
@@ -328,14 +323,23 @@ module Jekyll
def as_liquid(item)
case item
- when String, Numeric, true, false, nil
- item.to_liquid
when Hash
- Hash[item.map { |k, v| [as_liquid(k), as_liquid(v)] }]
+ pairs = item.map { |k, v| as_liquid([k, v]) }
+ Hash[pairs]
when Array
item.map{ |i| as_liquid(i) }
else
- item.respond_to?(:to_liquid) ? as_liquid(item.to_liquid) : item
+ if item.respond_to?(:to_liquid)
+ liquidated = item.to_liquid
+ # prevent infinite recursion for simple types (which return `self`)
+ if liquidated == item
+ item
+ else
+ as_liquid(liquidated)
+ end
+ else
+ item
+ end
end
end
end
diff --git a/lib/jekyll/frontmatter_defaults.rb b/lib/jekyll/frontmatter_defaults.rb
index 8355fa95..11eebf3f 100644
--- a/lib/jekyll/frontmatter_defaults.rb
+++ b/lib/jekyll/frontmatter_defaults.rb
@@ -168,7 +168,7 @@ module Jekyll
end.compact
end
- # Sanitizes the given path by removing a leading and addding a trailing slash
+ # Sanitizes the given path by removing a leading and adding a trailing slash
def sanitize_path(path)
if path.nil? || path.empty?
""
diff --git a/lib/jekyll/layout.rb b/lib/jekyll/layout.rb
index 4dde59b6..c29f353f 100644
--- a/lib/jekyll/layout.rb
+++ b/lib/jekyll/layout.rb
@@ -8,6 +8,9 @@ module Jekyll
# Gets the name of this layout.
attr_reader :name
+ # Gets the path to this layout.
+ attr_reader :path
+
# Gets/Sets the extension of this layout.
attr_accessor :ext
@@ -26,6 +29,7 @@ module Jekyll
@site = site
@base = base
@name = name
+ @path = site.in_source_dir(base, name)
self.data = {}
diff --git a/lib/jekyll/log_adapter.rb b/lib/jekyll/log_adapter.rb
index 63e23fcd..46e0fe1f 100644
--- a/lib/jekyll/log_adapter.rb
+++ b/lib/jekyll/log_adapter.rb
@@ -1,6 +1,6 @@
module Jekyll
class LogAdapter
- attr_reader :writer
+ attr_reader :writer, :messages
LOG_LEVELS = {
:debug => ::Logger::DEBUG,
@@ -16,6 +16,7 @@ module Jekyll
#
# Returns nothing
def initialize(writer, level = :info)
+ @messages = []
@writer = writer
self.log_level = level
end
@@ -87,7 +88,9 @@ module Jekyll
#
# Returns the formatted message
def message(topic, message)
- formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ')
+ msg = formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ')
+ messages << msg
+ msg
end
# Internal: Format the topic
diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb
index 98f730bf..a4ddb698 100644
--- a/lib/jekyll/page.rb
+++ b/lib/jekyll/page.rb
@@ -63,16 +63,12 @@ module Jekyll
#
# Returns the template String.
def template
- if site.permalink_style == :pretty
- if index? && html?
- "/:path/"
- elsif html?
- "/:path/:basename/"
- else
- "/:path/:basename:output_ext"
- end
- else
+ if !html?
"/:path/:basename:output_ext"
+ elsif index?
+ "/:path/"
+ else
+ Utils.add_permalink_suffix("/:path/:basename", site.permalink_style)
end
end
@@ -141,7 +137,8 @@ module Jekyll
# Returns the destination file path String.
def destination(dest)
path = site.in_dest_dir(dest, URL.unescape_path(url))
- path = File.join(path, "index.html") if url =~ /\/$/
+ path = File.join(path, "index.html") if url.end_with?("/")
+ path << output_ext unless path.end_with?(output_ext)
path
end
diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb
index c377e376..780f40f7 100644
--- a/lib/jekyll/post.rb
+++ b/lib/jekyll/post.rb
@@ -23,6 +23,8 @@ module Jekyll
ATTRIBUTES_FOR_LIQUID = EXCERPT_ATTRIBUTES_FOR_LIQUID + %w[
content
excerpt
+ excerpt_separator
+ draft?
]
# Post name validator. Post filenames must be like:
@@ -52,12 +54,12 @@ module Jekyll
@base = containing_dir(dir)
@name = name
- self.categories = dir.downcase.split('/').reject { |x| x.empty? }
+ self.categories = dir.split('/').reject { |x| x.empty? }
process(name)
read_yaml(@base, name)
data.default_proc = proc do |hash, key|
- site.frontmatter_defaults.find(File.join(dir, name), type, key)
+ site.frontmatter_defaults.find(relative_path, type, key)
end
if data.key?('date')
@@ -80,7 +82,7 @@ module Jekyll
categories_from_data = Utils.pluralized_array_from_hash(data, 'category', 'categories')
self.categories = (
Array(categories) + categories_from_data
- ).map {|c| c.to_s.downcase}.flatten.uniq
+ ).map { |c| c.to_s }.flatten.uniq
end
def populate_tags
@@ -118,6 +120,14 @@ module Jekyll
data.fetch('title') { titleized_slug }
end
+ # Public: the Post excerpt_separator, from the YAML Front-Matter or site default
+ # excerpt_separator value
+ #
+ # Returns the post excerpt_separator
+ def excerpt_separator
+ (data['excerpt_separator'] || site.config['excerpt_separator']).to_s
+ end
+
# Turns the post slug into a suitable title
def titleized_slug
slug.split('-').select {|w| w.capitalize! || w }.join(' ')
@@ -218,7 +228,7 @@ module Jekyll
:title => slug,
:i_day => date.strftime("%-d"),
:i_month => date.strftime("%-m"),
- :categories => (categories || []).map { |c| c.to_s }.join('/'),
+ :categories => (categories || []).map { |c| c.to_s.downcase }.uniq.join('/'),
:short_month => date.strftime("%b"),
:short_year => date.strftime("%y"),
:y_day => date.strftime("%j"),
@@ -269,7 +279,8 @@ module Jekyll
def destination(dest)
# The url needs to be unescaped in order to preserve the correct filename
path = site.in_dest_dir(dest, URL.unescape_path(url))
- path = File.join(path, "index.html") if path[/\.html?$/].nil?
+ path = File.join(path, "index.html") if self.url.end_with?("/")
+ path << output_ext unless path.end_with?(output_ext)
path
end
@@ -296,6 +307,11 @@ module Jekyll
end
end
+ # Returns if this Post is a Draft
+ def draft?
+ is_a?(Jekyll::Draft)
+ end
+
protected
def extract_excerpt
@@ -307,7 +323,7 @@ module Jekyll
end
def generate_excerpt?
- !(site.config['excerpt_separator'].to_s.empty?)
+ !excerpt_separator.empty?
end
end
end
diff --git a/lib/jekyll/regenerator.rb b/lib/jekyll/regenerator.rb
new file mode 100644
index 00000000..0616fdd9
--- /dev/null
+++ b/lib/jekyll/regenerator.rb
@@ -0,0 +1,141 @@
+module Jekyll
+ class Regenerator
+ attr_reader :site, :metadata, :cache
+
+ def initialize(site)
+ @site = site
+
+ # Read metadata from file
+ read_metadata
+
+ # Initialize cache to an empty hash
+ @cache = {}
+ end
+
+ # Checks if a renderable object needs to be regenerated
+ #
+ # Returns a boolean.
+ def regenerate?(document)
+ case document
+ when Post, Page
+ document.asset_file? || document.data['regenerate'] ||
+ modified?(site.in_source_dir(document.relative_path))
+ when Document
+ !document.write? || document.data['regenerate'] || modified?(document.path)
+ else
+ if document.respond_to?(:path)
+ modified?(document.path)
+ else
+ true
+ end
+ end
+ end
+
+ # Add a path to the metadata
+ #
+ # Returns true, also on failure.
+ def add(path)
+ return true unless File.exist?(path)
+
+ metadata[path] = {
+ "mtime" => File.mtime(path),
+ "deps" => []
+ }
+ cache[path] = true
+ end
+
+ # Force a path to regenerate
+ #
+ # Returns true.
+ def force(path)
+ cache[path] = true
+ end
+
+ # Clear the metadata and cache
+ #
+ # Returns nothing
+ def clear
+ @metadata = {}
+ @cache = {}
+ end
+
+ # Checks if a path's (or one of its dependencies)
+ # mtime has changed
+ #
+ # Returns a boolean.
+ def modified?(path)
+ return true if disabled?
+
+ # Check for path in cache
+ if cache.has_key? path
+ return cache[path]
+ end
+
+ # Check path that exists in metadata
+ data = metadata[path]
+ if data
+ data["deps"].each do |dependency|
+ if modified?(dependency)
+ return cache[dependency] = cache[path] = true
+ end
+ end
+ if data["mtime"].eql? File.mtime(path)
+ return cache[path] = false
+ else
+ return add(path)
+ end
+ end
+
+ # Path does not exist in metadata, add it
+ return add(path)
+ end
+
+ # Add a dependency of a path
+ #
+ # Returns nothing.
+ def add_dependency(path, dependency)
+ return if (metadata[path].nil? || @disabled)
+
+ metadata[path]["deps"] << dependency unless metadata[path]["deps"].include? dependency
+ regenerate? dependency
+ end
+
+ # Write the metadata to disk
+ #
+ # Returns nothing.
+ def write_metadata
+ File.open(metadata_file, 'w') do |f|
+ f.write(metadata.to_yaml)
+ end
+ end
+
+ # Produce the absolute path of the metadata file
+ #
+ # Returns the String path of the file.
+ def metadata_file
+ site.in_source_dir('.jekyll-metadata')
+ end
+
+ # Check if metadata has been disabled
+ #
+ # Returns a Boolean (true for disabled, false for enabled).
+ def disabled?
+ @disabled = site.full_rebuild? if @disabled.nil?
+ @disabled
+ end
+
+ private
+
+ # Read metadata from the metadata file, if no file is found,
+ # initialize with an empty hash
+ #
+ # Returns the read metadata.
+ def read_metadata
+ @metadata = if !disabled? && File.file?(metadata_file)
+ SafeYAML.load(File.read(metadata_file))
+ else
+ {}
+ end
+ end
+ end
+end
diff --git a/lib/jekyll/related_posts.rb b/lib/jekyll/related_posts.rb
index 041ee551..b6899ec3 100644
--- a/lib/jekyll/related_posts.rb
+++ b/lib/jekyll/related_posts.rb
@@ -10,7 +10,7 @@ module Jekyll
def initialize(post)
@post = post
@site = post.site
- require 'classifier-reborn' if site.lsi
+ Jekyll::External.require_with_graceful_fail('classifier-reborn') if site.lsi
end
def build
diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb
index f88a4187..f25ca343 100644
--- a/lib/jekyll/renderer.rb
+++ b/lib/jekyll/renderer.rb
@@ -3,11 +3,12 @@
module Jekyll
class Renderer
- attr_reader :document, :site
+ attr_reader :document, :site, :site_payload
- def initialize(site, document)
- @site = site
- @document = document
+ def initialize(site, document, site_payload = nil)
+ @site = site
+ @document = document
+ @site_payload = site_payload
end
# Determine which converters to use based on this document's
@@ -32,7 +33,7 @@ module Jekyll
def run
payload = Utils.deep_merge_hashes({
"page" => document.to_liquid
- }, site.site_payload)
+ }, site_payload || site.site_payload)
info = {
filters: [Jekyll::Filters],
@@ -138,6 +139,12 @@ module Jekyll
File.join(site.config['layouts'], layout.name)
)
+ # Add layout to dependency tree
+ site.regenerator.add_dependency(
+ site.in_source_dir(document.path),
+ site.in_source_dir(layout.path)
+ ) if document.write?
+
if layout = site.layouts[layout.data["layout"]]
if used.include?(layout)
layout = nil # avoid recursive chain
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 115b4133..96910255 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -11,6 +11,7 @@ module Jekyll
:gems, :plugin_manager
attr_accessor :converters, :generators
+ attr_reader :regenerator
# Public: Initialize a new Site.
#
@@ -27,6 +28,9 @@ module Jekyll
@source = File.expand_path(config['source']).freeze
@dest = File.expand_path(config['destination']).freeze
+ # Initialize incremental regenerator
+ @regenerator = Regenerator.new(self)
+
self.plugin_manager = Jekyll::PluginManager.new(self)
self.plugins = plugin_manager.plugins_path
@@ -183,6 +187,7 @@ module Jekyll
end
pages.sort_by!(&:name)
+ static_files.sort_by!(&:relative_path)
end
# Read all the files in //_posts and create a new Post
@@ -253,16 +258,26 @@ module Jekyll
if File.directory?(path)
read_data_to(path, data[key] = {})
else
- case File.extname(path).downcase
- when '.csv'
- data[key] = CSV.read(path, :headers => true).map(&:to_hash)
- else
- data[key] = SafeYAML.load_file(path)
- end
+ data[key] = read_data_file(path)
end
end
end
+ # Determines how to read a data file.
+ #
+ # Returns the contents of the data file.
+ def read_data_file(path)
+ case File.extname(path).downcase
+ when '.csv'
+ CSV.read(path, {
+ :headers => true,
+ :encoding => config['encoding']
+ }).map(&:to_hash)
+ else
+ SafeYAML.load_file(path)
+ end
+ end
+
# Read in all collections specified in the configuration
#
# Returns nothing.
@@ -287,17 +302,22 @@ module Jekyll
def render
relative_permalinks_deprecation_method
+ payload = site_payload
collections.each do |label, collection|
collection.docs.each do |document|
- document.output = Jekyll::Renderer.new(self, document).run
+ if regenerator.regenerate?(document)
+ document.output = Jekyll::Renderer.new(self, document, payload).run
+ end
end
end
payload = site_payload
[posts, pages].flatten.each do |page_or_post|
- page_or_post.render(layouts, payload)
+ if regenerator.regenerate?(page_or_post)
+ page_or_post.render(layouts, payload)
+ end
end
- rescue Errno::ENOENT => e
+ rescue Errno::ENOENT
# ignore missing layout dir
end
@@ -312,7 +332,10 @@ module Jekyll
#
# Returns nothing.
def write
- each_site_file { |item| item.write(dest) }
+ each_site_file { |item|
+ item.write(dest) if regenerator.regenerate?(item)
+ }
+ regenerator.write_metadata
end
# Construct a Hash of Posts indexed by the specified Post attribute.
@@ -377,7 +400,7 @@ module Jekyll
"time" => time,
"posts" => posts.sort { |a, b| b <=> a },
"pages" => pages,
- "static_files" => static_files.sort { |a, b| a.relative_path <=> b.relative_path },
+ "static_files" => static_files,
"html_pages" => pages.select { |page| page.html? || page.url.end_with?("/") },
"categories" => post_attr_hash('categories'),
"tags" => post_attr_hash('tags'),
@@ -405,13 +428,8 @@ module Jekyll
# klass - The Class of the Converter to fetch.
#
# Returns the Converter instance implementing the given Converter.
- def getConverterImpl(klass)
- matches = converters.select { |c| c.class == klass }
- if impl = matches.first
- impl
- else
- raise "Converter implementation not found for #{klass}"
- end
+ def find_converter_instance(klass)
+ converters.find { |c| c.class == klass } || proc { raise "No converter for #{klass}" }.call
end
# Create array of instances of the subclasses of the class or module
@@ -453,7 +471,7 @@ module Jekyll
def relative_permalinks_deprecation_method
if config['relative_permalinks'] && has_relative_page?
- Jekyll.logger.warn "Deprecation:", "Starting in 2.0, permalinks for pages" +
+ Jekyll::Deprecator.deprecation_message "Since v2.0, permalinks for pages" +
" in subfolders must be relative to the" +
" site source directory, not the parent" +
" directory. Check http://jekyllrb.com/docs/upgrading/"+
@@ -483,6 +501,17 @@ module Jekyll
@frontmatter_defaults ||= FrontmatterDefaults.new(self)
end
+ # Whether to perform a full rebuild without incremental regeneration
+ #
+ # Returns a Boolean: true for a full rebuild, false for normal build
+ def full_rebuild?(override = {})
+ override['full_rebuild'] || config['full_rebuild']
+ end
+
+ def publisher
+ @publisher ||= Publisher.new(self)
+ end
+
private
def has_relative_page?
@@ -499,13 +528,9 @@ module Jekyll
end
def sanitize_filename(name)
- name.gsub!(/[^\w\s_-]+/, '')
+ name.gsub!(/[^\w\s-]+/, '')
name.gsub!(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
name.gsub(/\s+/, '_')
end
-
- def publisher
- @publisher ||= Publisher.new(self)
- end
end
end
diff --git a/lib/jekyll/static_file.rb b/lib/jekyll/static_file.rb
index eae85b54..b83f4fae 100644
--- a/lib/jekyll/static_file.rb
+++ b/lib/jekyll/static_file.rb
@@ -3,6 +3,8 @@ module Jekyll
# The cache of last modification times [path] -> mtime.
@@mtimes = Hash.new
+ attr_reader :relative_path
+
# Initialize a new StaticFile.
#
# site - The Site.
@@ -15,6 +17,7 @@ module Jekyll
@dir = dir
@name = name
@collection = collection
+ @relative_path = File.join(*[@dir, @name].compact)
end
# Returns source file path.
@@ -22,11 +25,6 @@ module Jekyll
File.join(*[@base, @dir, @name].compact)
end
- # Returns the source file path relative to the site source
- def relative_path
- @relative_path ||= File.join(*[@dir, @name].compact)
- end
-
def extname
File.extname(path)
end
@@ -81,6 +79,7 @@ module Jekyll
FileUtils.mkdir_p(File.dirname(dest_path))
FileUtils.rm(dest_path) if File.exist?(dest_path)
FileUtils.cp(path, dest_path)
+ File.utime(@@mtimes[path], @@mtimes[path], dest_path)
true
end
diff --git a/lib/jekyll/tags/highlight.rb b/lib/jekyll/tags/highlight.rb
index b51712b6..5589d38f 100644
--- a/lib/jekyll/tags/highlight.rb
+++ b/lib/jekyll/tags/highlight.rb
@@ -42,7 +42,7 @@ eos
def render(context)
prefix = context["highlighter_prefix"] || ""
suffix = context["highlighter_suffix"] || ""
- code = super.to_s.strip
+ code = super.to_s.gsub(/\A(\n|\r)+|(\n|\r)+\z/, '')
is_safe = !!context.registers[:site].safe
@@ -75,9 +75,7 @@ eos
end
def render_pygments(code, is_safe)
- require 'pygments'
-
- @options[:encoding] = 'utf-8'
+ Jekyll::External.require_with_graceful_fail('pygments')
highlighted_code = Pygments.highlight(
code,
@@ -96,26 +94,26 @@ eos
raise ArgumentError.new("Pygments.rb returned an unacceptable value when attempting to highlight some code.")
end
- highlighted_code
+ highlighted_code.sub('
"
end
end
diff --git a/lib/jekyll/tags/include.rb b/lib/jekyll/tags/include.rb
index 3eb4d7c0..b809ffe7 100644
--- a/lib/jekyll/tags/include.rb
+++ b/lib/jekyll/tags/include.rb
@@ -101,20 +101,29 @@ eos
end
def tag_includes_dir
- '_includes'
+ '_includes'.freeze
end
def render(context)
+ site = context.registers[:site]
dir = resolved_includes_dir(context)
file = render_variable(context) || @file
validate_file_name(file)
path = File.join(dir, file)
- validate_path(path, dir, context.registers[:site].safe)
+ validate_path(path, dir, site.safe)
+
+ # Add include to dependency tree
+ if context.registers[:page] and context.registers[:page].has_key? "path"
+ site.regenerator.add_dependency(
+ site.in_source_dir(context.registers[:page]["path"]),
+ path
+ )
+ end
begin
- partial = Liquid::Template.parse(source(path, context))
+ partial = Liquid::Template.parse(read_file(path, context))
context.stack do
context['include'] = parse_params(context) if @params
@@ -126,7 +135,7 @@ eos
end
def resolved_includes_dir(context)
- File.join(File.realpath(context.registers[:site].source), @includes_dir)
+ context.registers[:site].in_source_dir(@includes_dir)
end
def validate_path(path, dir, safe)
@@ -146,14 +155,14 @@ eos
end
# This method allows to modify the file content by inheriting from the class.
- def source(file, context)
+ def read_file(file, context)
File.read(file, file_read_opts(context))
end
end
class IncludeRelativeTag < IncludeTag
def tag_includes_dir
- '.'
+ '.'.freeze
end
def page_path(context)
diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb
index 6bed8d57..14b70631 100644
--- a/lib/jekyll/url.rb
+++ b/lib/jekyll/url.rb
@@ -44,12 +44,12 @@ module Jekyll
#
# Returns the _unsanitized String URL
def generated_permalink
- (@generated_permlink ||= generate_url(@permalink)) if @permalink
+ (@generated_permalink ||= generate_url(@permalink)) if @permalink
end
# Generates a URL from the template
#
- # Returns the _unsanitized String URL
+ # Returns the unsanitized String URL
def generated_url
@generated_url ||= generate_url(@template)
end
@@ -57,11 +57,16 @@ module Jekyll
# Internal: Generate the URL by replacing all placeholders with their
# respective values in the given template
#
- # Returns the _unsanitizied_ String URL
+ # Returns the unsanitized String URL
def generate_url(template)
@placeholders.inject(template) do |result, token|
break result if result.index(':').nil?
- result.gsub(/:#{token.first}/, self.class.escape_path(token.last))
+ if token.last.nil?
+ # Remove leading '/' to avoid generating urls with `//`
+ result.gsub(/\/:#{token.first}/, '')
+ else
+ result.gsub(/:#{token.first}/, self.class.escape_path(token.last))
+ end
end
end
@@ -76,7 +81,7 @@ module Jekyll
.gsub(/\A([^\/])/, '/\1')
# Append a trailing slash to the URL if the unsanitized URL had one
- url << "/" if in_url[-1].eql?('/')
+ url << "/" if in_url.end_with?("/")
url
end
@@ -102,7 +107,7 @@ module Jekyll
# pct-encoded = "%" HEXDIG HEXDIG
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
# / "*" / "+" / "," / ";" / "="
- URI.escape(path, /[^a-zA-Z\d\-._~!$&\'()*+,;=:@\/]/).encode('utf-8')
+ URI.escape(path, /[^a-zA-Z\d\-._~!$&'()*+,;=:@\/]/).encode('utf-8')
end
# Unescapes a URL path segment
diff --git a/lib/jekyll/utils.rb b/lib/jekyll/utils.rb
index 34861593..39302790 100644
--- a/lib/jekyll/utils.rb
+++ b/lib/jekyll/utils.rb
@@ -2,6 +2,12 @@ module Jekyll
module Utils
extend self
+ # Constants for use in #slugify
+ SLUGIFY_MODES = %w{raw default pretty}
+ SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
+ SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
+ SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
+
# Merges a master hash with another hash, recursively.
#
# master_hash - the "parent" hash whose values will be overridden
@@ -90,7 +96,7 @@ module Jekyll
# Returns the parsed date if successful, throws a FatalException
# if not
def parse_date(input, msg = "Input could not be parsed.")
- Time.parse(input)
+ Time.parse(input).localtime
rescue ArgumentError
raise Errors::FatalException.new("Invalid date '#{input}': " + msg)
end
@@ -104,21 +110,92 @@ module Jekyll
# Slugify a filename or title.
#
- # name - the filename or title to slugify
+ # string - the filename or title to slugify
+ # mode - how string is slugified
#
- # Returns the given filename or title in lowercase, with every
- # sequence of spaces and non-alphanumeric characters replaced with a
- # hyphen.
- def slugify(string)
- unless string.nil?
- string \
- # Replace each non-alphanumeric character sequence with a hyphen
- .gsub(/[^a-z0-9]+/i, '-') \
- # Remove leading/trailing hyphen
- .gsub(/^\-|\-$/i, '') \
- # Downcase it
- .downcase
+ # When mode is "none", return the given string in lowercase.
+ #
+ # When mode is "raw", return the given string in lowercase,
+ # with every sequence of spaces characters replaced with a hyphen.
+ #
+ # When mode is "default" or nil, non-alphabetic characters are
+ # replaced with a hyphen too.
+ #
+ # When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@)
+ # are not replaced with hyphen.
+ #
+ # Examples:
+ # slugify("The _config.yml file")
+ # # => "the-config-yml-file"
+ #
+ # slugify("The _config.yml file", "pretty")
+ # # => "the-_config.yml-file"
+ #
+ # Returns the slugified string.
+ def slugify(string, mode=nil)
+ mode ||= 'default'
+ return nil if string.nil?
+ return string.downcase unless SLUGIFY_MODES.include?(mode)
+
+ # Replace each character sequence with a hyphen
+ re = case mode
+ when 'raw'
+ SLUGIFY_RAW_REGEXP
+ when 'default'
+ SLUGIFY_DEFAULT_REGEXP
+ when 'pretty'
+ # "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
+ # and is allowed in both extN and NTFS.
+ SLUGIFY_PRETTY_REGEXP
end
+
+ string.
+ # Strip according to the mode
+ gsub(re, '-').
+ # Remove leading/trailing hyphen
+ gsub(/^\-|\-$/i, '').
+ # Downcase
+ downcase
+ end
+
+ # Add an appropriate suffix to template so that it matches the specified
+ # permalink style.
+ #
+ # template - permalink template without trailing slash or file extension
+ # permalink_style - permalink style, either built-in or custom
+ #
+ # The returned permalink template will use the same ending style as
+ # specified in permalink_style. For example, if permalink_style contains a
+ # trailing slash (or is :pretty, which indirectly has a trailing slash),
+ # then so will the returned template. If permalink_style has a trailing
+ # ":output_ext" (or is :none, :date, or :ordinal) then so will the returned
+ # template. Otherwise, template will be returned without modification.
+ #
+ # Examples:
+ # add_permalink_suffix("/:basename", :pretty)
+ # # => "/:basename/"
+ #
+ # add_permalink_suffix("/:basename", :date)
+ # # => "/:basename:output_ext"
+ #
+ # add_permalink_suffix("/:basename", "/:year/:month/:title/")
+ # # => "/:basename/"
+ #
+ # add_permalink_suffix("/:basename", "/:year/:month/:title")
+ # # => "/:basename"
+ #
+ # Returns the updated permalink template
+ def add_permalink_suffix(template, permalink_style)
+ case permalink_style
+ when :pretty
+ template << "/"
+ when :date, :ordinal, :none
+ template << ":output_ext"
+ else
+ template << "/" if permalink_style.to_s.end_with?("/")
+ template << ":output_ext" if permalink_style.to_s.end_with?(":output_ext")
+ end
+ template
end
end
diff --git a/lib/jekyll/version.rb b/lib/jekyll/version.rb
index 4df11160..64cdaba2 100644
--- a/lib/jekyll/version.rb
+++ b/lib/jekyll/version.rb
@@ -1,3 +1,3 @@
module Jekyll
- VERSION = '2.5.2'
+ VERSION = '3.0.0.beta2'
end
diff --git a/lib/site_template/.gitignore b/lib/site_template/.gitignore
index badbc02f..45c15053 100644
--- a/lib/site_template/.gitignore
+++ b/lib/site_template/.gitignore
@@ -1,2 +1,3 @@
_site
.sass-cache
+.jekyll-metadata
diff --git a/lib/site_template/_config.yml b/lib/site_template/_config.yml
index 2f03e74d..f4151011 100644
--- a/lib/site_template/_config.yml
+++ b/lib/site_template/_config.yml
@@ -5,7 +5,7 @@ description: > # this means to ignore newlines until "baseurl:"
Write an awesome description for your new site here. You can edit this
line in _config.yml. It will appear in your document head meta (for
Google search results) and in your feed.xml site description.
-baseurl: "" # the subpath of your site, e.g. /blog/
+baseurl: "" # the subpath of your site, e.g. /blog
url: "http://yourdomain.com" # the base hostname & protocol for your site
twitter_username: jekyllrb
github_username: jekyll
diff --git a/lib/site_template/_includes/footer.html b/lib/site_template/_includes/footer.html
index be3976f7..8fa89ba9 100644
--- a/lib/site_template/_includes/footer.html
+++ b/lib/site_template/_includes/footer.html
@@ -5,19 +5,19 @@