git merge mojombo/master

This commit is contained in:
Christian Hellsten 2010-06-22 15:42:51 +03:00
commit 46a95bc036
56 changed files with 1690 additions and 655 deletions

View File

@ -1,60 +1,133 @@
== Edge
* Major Enhancements
* Proper plugin system (#19, #100)
* Add safe mode so unsafe converters/generators can be added
* Minor Enhancements
* Inclusion/exclusion of future dated posts (#59)
* Generation for a specific time (#59)
* Allocate site.time on render not per site_payload invocation (#59)
* Pages now present in the site payload and can be used through the
site.pages and site.html_pages variables
* Generate phase added to site#process and pagination is now a generator
* Switch to RakeGem for build/test process
* Only regenerate static files when they have changed (#142)
* Allow arbitrary options to Pygments (#31)
* Bug Fixes
* Render highlighted code for non markdown/textile pages (#116)
* Expand source to full path so includes work anywhere (#101)
* Fix highlighting on Ruby 1.9 (#65)
* Fix extension munging when pretty permalinks are enabled (#64)
* Stop sorting categories (#33)
* Preserve generated attributes over front matter (#119)
== 0.5.7 / 2010-01-12
* Minor Enhancements
* Allow overriding of post date in the front matter (#62, #38)
* Bug Fixes
* Categories isn't always an array (#73)
* Empty tags causes error in read_posts (#84)
* Fix pagination to adhere to read/render/write paradigm
* Test Enhancement
* cucumber features no longer use site.ports.first where a better
alternative is available
== 0.5.6 / 2010-01-08
* Bug Fixes
* Require redcloth >= 4.2.1 in tests (#92)
* Don't break on triple dashes in yaml frontmatter (#93)
* Minor Enhancements
* Allow .mkd as markdown extension
* Use $stdout/err instead of constants (#99)
* Properly wrap code blocks (#91)
* Add javascript mime type for webrick (#98)
== 0.5.5 / 2010-01-08
* Bug Fixes
* Fix pagination % 0 bug (#78)
* Ensure all posts are processed first (#71)
== NOTE
* After this point I will no longer be giving credit in the history;
that is what the commit log is for.
== 0.5.4 / 2009-08-23
* Bug Fixes
* Do not allow symlinks (security vulnerability)
== 0.5.3 / 2009-07-14
* Bug Fixes
* Solving the permalink bug where non-html files wouldn't work [github.com/jeffrydegrande]
* Solving the permalink bug where non-html files wouldn't work
[github.com/jeffrydegrande]
== 0.5.2 / 2009-06-24
* Enhancements
* Added --paginate option to the executable along with a paginator object for the payload [github.com/calavera]
* Upgraded RedCloth to 4.2.1, which makes <notextile> tags work once again.
* Configuration options set in config.yml are now available through the site payload [github.com/vilcans]
* Posts can now have an empty YAML front matter or none at all [github.com/bahuvrihi]
* Added --paginate option to the executable along with a paginator object
for the payload [github.com/calavera]
* Upgraded RedCloth to 4.2.1, which makes <notextile> tags work once
again.
* Configuration options set in config.yml are now available through the
site payload [github.com/vilcans]
* Posts can now have an empty YAML front matter or none at all
[github.com/bahuvrihi]
* Bug Fixes
* Fixing Ruby 1.9 issue that requires to_s on the err object [github.com/Chrononaut]
* Fixing Ruby 1.9 issue that requires to_s on the err object
[github.com/Chrononaut]
* Fixes for pagination and ordering posts on the same day [github.com/ujh]
* Made pages respect permalinks style and permalinks in yml front matter [github.com/eugenebolshakov]
* Index.html file should always have index.html permalink [github.com/eugenebolshakov]
* Added trailing slash to pretty permalink style so Apache is happy [github.com/eugenebolshakov]
* Bad markdown processor in config fails sooner and with better message [github.com/gcnovus]
* Made pages respect permalinks style and permalinks in yml front matter
[github.com/eugenebolshakov]
* Index.html file should always have index.html permalink
[github.com/eugenebolshakov]
* Added trailing slash to pretty permalink style so Apache is happy
[github.com/eugenebolshakov]
* Bad markdown processor in config fails sooner and with better message
[github.com/gcnovus]
* Allow CRLFs in yaml frontmatter [github.com/juretta]
* Added Date#xmlschema for Ruby versions < 1.9
== 0.5.1 / 2009-05-06
* Major Enhancements
* Next/previous posts in site payload [github.com/pantulis, github.com/tomo]
* Next/previous posts in site payload [github.com/pantulis,
github.com/tomo]
* Permalink templating system
* Moved most of the README out to the GitHub wiki
* Exclude option in configuration so specified files won't be brought over with generated site [github.com/duritong]
* Exclude option in configuration so specified files won't be brought over
with generated site [github.com/duritong]
* Bug Fixes
* Making sure config.yaml references are all gone, using only config.yml
* Fixed syntax highlighting breaking for UTF-8 code [github.com/henrik]
* Worked around RDiscount bug that prevents Markdown from getting parsed after highlight [github.com/henrik]
* Worked around RDiscount bug that prevents Markdown from getting parsed
after highlight [github.com/henrik]
* CGI escaped post titles [github.com/Chrononaut]
== 0.5.0 / 2009-04-07
* Minor Enhancements
* Ability to set post categories via YAML [github.com/qrush]
* Ability to set prevent a post from publishing via YAML [github.com/qrush]
* Ability to set prevent a post from publishing via YAML
[github.com/qrush]
* Add textilize filter [github.com/willcodeforfoo]
* Add 'pretty' permalink style for wordpress-like urls [github.com/dysinger]
* Made it possible to enter categories from YAML as an array [github.com/Chrononaut]
* Add 'pretty' permalink style for wordpress-like urls
[github.com/dysinger]
* Made it possible to enter categories from YAML as an array
[github.com/Chrononaut]
* Ignore Emacs autosave files [github.com/Chrononaut]
* Bug Fixes
* Use block syntax of popen4 to ensure that subprocesses are properly disposed [github.com/jqr]
* Use block syntax of popen4 to ensure that subprocesses are properly
disposed [github.com/jqr]
* Close open4 streams to prevent zombies [github.com/rtomayko]
* Only query required fields from the WP Database [github.com/ariejan]
* Prevent _posts from being copied to the destination directory [github.com/bdimcheff]
* Prevent _posts from being copied to the destination directory
[github.com/bdimcheff]
* Refactors
* Factored the filtering code into a method [github.com/Chrononaut]
* Fix tests and convert to Shoulda [github.com/qrush, github.com/technicalpickles]
* Add Cucumber acceptance test suite [github.com/qrush, github.com/technicalpickles]
* Fix tests and convert to Shoulda [github.com/qrush,
github.com/technicalpickles]
* Add Cucumber acceptance test suite [github.com/qrush,
github.com/technicalpickles]
== 0.4.1
* Minor Enhancements
* Changed date format on wordpress converter (zeropadding) [github.com/dysinger]
* Changed date format on wordpress converter (zeropadding)
[github.com/dysinger]
* Bug Fixes
* Add jekyll binary as executable to gemspec [github.com/dysinger]
@ -67,28 +140,35 @@
* Add array_to_sentence_string filter [github.com/mchung]
* Add a converter for textpattern [github.com/PerfectlyNormal]
* Add a working Mephisto / MySQL converter [github.com/ivey]
* Allowing .htaccess files to be copied over into the generated site [github.com/briandoll]
* Allowing .htaccess files to be copied over into the generated site
[github.com/briandoll]
* Add option to not put file date in permalink URL [github.com/mreid]
* Add line number capabilities to highlight blocks [github.com/jcon]
* Bug Fixes
* Fix permalink behavior [github.com/cavalle]
* Fixed an issue with pygments, markdown, and newlines [github.com/zpinter]
* Fixed an issue with pygments, markdown, and newlines
[github.com/zpinter]
* Ampersands need to be escaped [github.com/pufuwozu, github.com/ap]
* Test and fix the site.categories hash [github.com/zzot]
* Fix site payload available to files [github.com/matrix9180]
== 0.3.0 / 2008-12-24
* Major Enhancements
* Added --server option to start a simple WEBrick server on destination directory [github.com/johnreilly and github.com/mchung]
* Added --server option to start a simple WEBrick server on destination
directory [github.com/johnreilly and github.com/mchung]
* Minor Enhancements
* Added post categories based on directories containing _posts [github.com/mreid]
* Added post categories based on directories containing _posts
[github.com/mreid]
* Added post topics based on directories underneath _posts
* Added new date filter that shows the full month name [github.com/mreid]
* Merge Post's YAML front matter into its to_liquid payload [github.com/remi]
* Merge Post's YAML front matter into its to_liquid payload
[github.com/remi]
* Restrict includes to regular files underneath _includes
* Bug Fixes
* Change YAML delimiter matcher so as to not chew up 2nd level markdown headers [github.com/mreid]
* Fix bug that meant page data (such as the date) was not available in templates [github.com/mreid]
* Change YAML delimiter matcher so as to not chew up 2nd level markdown
headers [github.com/mreid]
* Fix bug that meant page data (such as the date) was not available in
templates [github.com/mreid]
* Properly reject directories in _layouts
== 0.2.1 / 2008-12-15
@ -111,9 +191,11 @@
* Code highlighting with Pygments if --pygments is specified
* Disable true LSI by default, enable with --lsi
* Minor Enhancements
* Output informative message if RDiscount is not available [github.com/JackDanger]
* Output informative message if RDiscount is not available
[github.com/JackDanger]
* Bug Fixes
* Prevent Jekyll from picking up the output directory as a source [github.com/JackDanger]
* Prevent Jekyll from picking up the output directory as a source
[github.com/JackDanger]
* Skip related_posts when there is only one post [github.com/JackDanger]
== 0.1.4 / 2008-12-08

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
(The MIT License)
Copyright (c) 2008 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
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
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
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
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -20,23 +20,22 @@ h2. Diving In
* Customize the "Permalinks":http://wiki.github.com/mojombo/jekyll/permalinks your posts are generated with
* Use the built-in "Liquid Extensions":http://wiki.github.com/mojombo/jekyll/liquid-extensions to make your life easier
h2. Dependencies
h2. Runtime Dependencies
* RedCloth: Textile support
* Liquid: Templating system
* Classifier: Generating related posts
* Maruku: Default markdown engine
* Directory Watcher: Auto-regeneration of sites
* Open4: Talking to pygments for syntax highlighting
* RedCloth: Textile support (Ruby)
* Liquid: Templating system (Ruby)
* Classifier: Generating related posts (Ruby)
* Maruku: Default markdown engine (Ruby)
* Directory Watcher: Auto-regeneration of sites (Ruby)
* Open4: Talking to pygments for syntax highlighting (Ruby)
* Pygments: Syntax highlighting (Python)
h2. Developer Dependencies
* Shoulda: Test framework (Ruby)
* RR: Mocking (Ruby)
* RedGreen: Nicer test output (Ruby)
h2. License
(The MIT License)
Copyright (c) 2008 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 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 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 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 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
See LICENSE.

178
Rakefile
View File

@ -1,85 +1,101 @@
require 'rubygems'
require 'rake'
require 'date'
#############################################################################
#
# Helper functions
#
#############################################################################
def name
@name ||= Dir['*.gemspec'].first.split('.').first
end
def version
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
end
def date
Date.today.to_s
end
def rubyforge_project
name
end
def gemspec_file
"#{name}.gemspec"
end
def gem_file
"#{name}-#{version}.gem"
end
def replace_header(head, header_name)
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
end
#############################################################################
#
# Standard tasks
#
#############################################################################
task :default => [:test, :features]
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
end
desc "Generate RCov test coverage and open in your browser"
task :coverage do
require 'rcov'
sh "rm -fr coverage"
sh "rcov test/test_*.rb"
sh "open coverage/index.html"
end
require 'rake/rdoctask'
begin
gem 'jeweler', '>= 0.11.0'
require 'jeweler'
Jeweler::Tasks.new do |s|
s.name = "jekyll"
s.summary = %Q{Jekyll is a simple, blog aware, static site generator.}
s.email = "tom@mojombo.com"
s.homepage = "http://github.com/mojombo/jekyll"
s.description = "Jekyll is a simple, blog aware, static site generator."
s.authors = ["Tom Preston-Werner"]
s.rubyforge_project = "jekyll"
s.files.exclude 'test/dest'
s.test_files.exclude 'test/dest'
s.add_dependency('RedCloth', '>= 4.2.1')
s.add_dependency('liquid', '>= 1.9.0')
s.add_dependency('classifier', '>= 1.3.1')
s.add_dependency('maruku', '>= 0.5.9')
s.add_dependency('directory_watcher', '>= 1.1.1')
s.add_dependency('open4', '>= 0.9.6')
end
rescue LoadError
puts "Jeweler not available. Install it with: sudo gem install jeweler --version '>= 0.11.0'"
exit(1)
end
Rake::TestTask.new do |t|
t.libs << 'lib'
t.pattern = 'test/**/test_*.rb'
t.verbose = false
end
Rake::RDocTask.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'jekyll'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.title = "#{name} #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |t|
t.libs << 'test'
t.test_files = FileList['test/**/test_*.rb']
t.verbose = true
end
rescue LoadError
end
task :default => [:test, :features]
# console
desc "Open an irb session preloaded with this library"
task :console do
sh "irb -rubygems -I lib -r jekyll.rb"
sh "irb -rubygems -r ./lib/#{name}.rb"
end
# converters
#############################################################################
#
# Custom tasks (add your own tasks here)
#
#############################################################################
namespace :convert do
namespace :migrate do
desc "Migrate from mephisto in the current directory"
task :mephisto do
sh %q(ruby -r './lib/jekyll/converters/mephisto' -e 'Jekyll::Mephisto.postgres(:database => "#{ENV["DB"]}")')
sh %q(ruby -r './lib/jekyll/migrators/mephisto' -e 'Jekyll::Mephisto.postgres(:database => "#{ENV["DB"]}")')
end
desc "Migrate from Movable Type in the current directory"
task :mt do
sh %q(ruby -r './lib/jekyll/converters/mt' -e 'Jekyll::MT.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
sh %q(ruby -r './lib/jekyll/migrators/mt' -e 'Jekyll::MT.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
end
desc "Migrate from Typo in the current directory"
task :typo do
sh %q(ruby -r './lib/jekyll/converters/typo' -e 'Jekyll::Typo.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
sh %q(ruby -r './lib/jekyll/migrators/typo' -e 'Jekyll::Typo.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
end
end
begin
require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:features) do |t|
t.cucumber_opts = "--format progress"
end
@ -89,3 +105,55 @@ rescue LoadError
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
end
end
#############################################################################
#
# Packaging tasks
#
#############################################################################
task :release => :build do
unless `git branch` =~ /^\* master$/
puts "You must be on the master branch to release!"
exit!
end
sh "git commit --allow-empty -a -m 'Release #{version}'"
sh "git tag v#{version}"
sh "git push origin master"
sh "git push v#{version}"
sh "gem push pkg/#{name}-#{version}.gem"
end
task :build => :gemspec do
sh "mkdir -p pkg"
sh "gem build #{gemspec_file}"
sh "mv #{gem_file} pkg"
end
task :gemspec do
# read spec file and split out manifest section
spec = File.read(gemspec_file)
head, manifest, tail = spec.split(" # = MANIFEST =\n")
# replace name version and date
replace_header(head, :name)
replace_header(head, :version)
replace_header(head, :date)
#comment this out if your rubyforge_project has a different name
replace_header(head, :rubyforge_project)
# determine file list from git ls-files
files = `git ls-files`.
split("\n").
sort.
reject { |file| file =~ /^\./ }.
reject { |file| file =~ /^(rdoc|pkg|coverage)/ }.
map { |file| " #{file}" }.
join("\n")
# piece file back together and write
manifest = " s.files = %w[\n#{files}\n ]\n"
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
File.open(gemspec_file, 'w') { |io| io.write(spec) }
puts "Updated #{gemspec_file}"
end

View File

@ -1,4 +0,0 @@
---
:patch: 4
:major: 0
:minor: 5

View File

@ -23,6 +23,10 @@ options = {}
opts = OptionParser.new do |opts|
opts.banner = help
opts.on("--safe", "Safe mode (default unsafe)") do
options['safe'] = true
end
opts.on("--auto", "Auto-regenerate") do
options['auto'] = true
end
@ -48,6 +52,18 @@ opts = OptionParser.new do |opts|
options['markdown'] = 'rdiscount'
end
opts.on("--time [TIME]", "Time to generate the site for") do |time|
options['time'] = Time.parse(time)
end
opts.on("--future", "Render future dated posts") do
options['future'] = true
end
opts.on("--no-future", "Do not render future dated posts") do
options['future'] = false
end
opts.on("--permalink [TYPE]", "Use 'date' (default) for YYYY/MM/DD") do |style|
options['permalink'] = style unless style.nil?
end
@ -63,7 +79,7 @@ opts = OptionParser.new do |opts|
end
opts.on("--version", "Display current version") do
puts "Jekyll " + Jekyll.version
puts "Jekyll " + Jekyll::VERSION
exit 0
end
end
@ -139,6 +155,9 @@ if options['server']
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
mime_types.store 'js', 'application/javascript'
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
mime_types.store 'js', 'application/javascript'
s = HTTPServer.new(
:Port => options['server_port'],
:DocumentRoot => destination,

1
cucumber.yml Normal file
View File

@ -0,0 +1 @@
default: --format progress

View File

@ -29,7 +29,7 @@ Feature: Create sites
Scenario: Basic site with layout and a post
Given I have a _layouts directory
And I have a _posts directory
And I have the following post:
And I have the following posts:
| title | date | layout | content |
| Wargames | 3/27/2009 | default | The only winning move is not to play. |
And I have a default layout that contains "Post Layout: {{ content }}"
@ -37,6 +37,36 @@ Feature: Create sites
Then the _site directory should exist
And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html"
Scenario: Basic site with layouts, pages, posts and files
Given I have a _layouts directory
And I have a page layout that contains "Page {{ page.title }}: {{ content }}"
And I have a post layout that contains "Post {{ page.title }}: {{ content }}"
And I have an "index.html" page with layout "page" that contains "Site contains {{ site.pages.size }} pages and {{ site.posts.size }} posts"
And I have a blog directory
And I have a "blog/index.html" page with layout "page" that contains "blog category index page"
And I have an "about.html" file that contains "No replacement {{ site.posts.size }}"
And I have an "another_file" file that contains ""
And I have a _posts directory
And I have the following posts:
| title | date | layout | content |
| entry1 | 3/27/2009 | post | content for entry1. |
| entry2 | 4/27/2009 | post | content for entry2. |
And I have a category/_posts directory
And I have the following posts in "category":
| title | date | layout | content |
| entry3 | 5/27/2009 | post | content for entry3. |
| entry4 | 6/27/2009 | post | content for entry4. |
When I run jekyll
Then the _site directory should exist
And I should see "Page : Site contains 2 pages and 4 posts" in "_site/index.html"
And I should see "No replacement \{\{ site.posts.size \}\}" in "_site/about.html"
And I should see "" in "_site/another_file"
And I should see "Page : blog category index page" in "_site/blog/index.html"
And I should see "Post entry1: <p>content for entry1.</p>" in "_site/2009/03/27/entry1.html"
And I should see "Post entry2: <p>content for entry2.</p>" in "_site/2009/04/27/entry2.html"
And I should see "Post entry3: <p>content for entry3.</p>" in "_site/category/2009/05/27/entry3.html"
And I should see "Post entry4: <p>content for entry4.</p>" in "_site/category/2009/06/27/entry4.html"
Scenario: Basic site with include tag
Given I have a _includes directory
And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
@ -44,3 +74,21 @@ Feature: Create sites
When I run jekyll
Then the _site directory should exist
And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"
Scenario: Basic site with subdir include tag
Given I have a _includes directory
And I have an "_includes/about.textile" file that contains "Generated by Jekyll"
And I have an info directory
And I have an "info/index.html" page that contains "Basic Site with subdir include tag: {% include about.textile %}"
When I run jekyll
Then the _site directory should exist
And I should see "Basic Site with subdir include tag: Generated by Jekyll" in "_site/info/index.html"
Scenario: Basic site with nested include tag
Given I have a _includes directory
And I have an "_includes/about.textile" file that contains "Generated by {% include jekyll.textile %}"
And I have an "_includes/jekyll.textile" file that contains "Jekyll"
And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
When I run jekyll
Then the _site directory should exist
And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"

View File

@ -20,7 +20,7 @@ Feature: Embed filters
And I have the following post:
| title | date | layout | content |
| Star & Wars | 3/27/2009 | default | These aren't the droids you're looking for. |
And I have a default layout that contains "{{ site.posts.first.title | xml_escape }}"
And I have a default layout that contains "{{ page.title | xml_escape }}"
When I run jekyll
Then the _site directory should exist
And I should see "Star &amp; Wars" in "_site/2009/03/27/star-wars.html"
@ -31,7 +31,7 @@ Feature: Embed filters
And I have the following post:
| title | date | layout | content |
| Star Wars | 3/27/2009 | default | These aren't the droids you're looking for. |
And I have a default layout that contains "{{ site.posts.first.content | xml_escape }}"
And I have a default layout that contains "{{ content | xml_escape }}"
When I run jekyll
Then the _site directory should exist
And I should see "7" in "_site/2009/03/27/star-wars.html"
@ -42,7 +42,7 @@ Feature: Embed filters
And I have the following post:
| title | date | layout | tags | content |
| Star Wars | 3/27/2009 | default | [scifi, movies, force] | These aren't the droids you're looking for. |
And I have a default layout that contains "{{ site.posts.first.tags | array_to_sentence_string }}"
And I have a default layout that contains "{{ page.tags | array_to_sentence_string }}"
When I run jekyll
Then the _site directory should exist
And I should see "scifi, movies, and force" in "_site/2009/03/27/star-wars.html"

30
features/markdown.feature Normal file
View File

@ -0,0 +1,30 @@
Feature: Markdown
As a hacker who likes to blog
I want to be able to make a static site
In order to share my awesome ideas with the interwebs
Scenario: Markdown in list on index
Given I have a configuration file with "paginate" set to "5"
And I have an "index.html" page that contains "Index - {% for post in site.posts %} {{ post.content }} {% endfor %}"
And I have a _posts directory
And I have the following post:
| title | date | content | type |
| Hackers | 3/27/2009 | # My Title | markdown |
When I run jekyll
Then the _site directory should exist
And I should see "Index" in "_site/index.html"
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/2009/03/27/hackers.html"
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/index.html"
Scenario: Markdown in pagination on index
Given I have a configuration file with "paginate" set to "5"
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:
| title | date | content | type |
| Hackers | 3/27/2009 | # My Title | markdown |
When I run jekyll
Then the _site directory should exist
And I should see "Index" in "_site/index.html"
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/index.html"

View File

@ -6,35 +6,22 @@ Feature: Site pagination
Scenario Outline: Paginate with N posts per page
Given I have a configuration file with "paginate" set to "<num>"
And I have a _layouts directory
And I have an "index.html" file that contains "Basic Site"
And I have an "index.html" page that contains "{{ paginator.posts.size }}"
And I have a _posts directory
And I have the following post:
| title | date | layout | content |
| Wargames | 3/27/2009 | default | The only winning move is not to play. |
| Wargames2 | 4/27/2009 | default | The only winning move is not to play2. |
| Wargames3 | 5/27/2009 | default | The only winning move is not to play2. |
| Wargames3 | 5/27/2009 | default | The only winning move is not to play3. |
| Wargames4 | 6/27/2009 | default | The only winning move is not to play4. |
When I run jekyll
Then the _site/page2 directory should exist
And the _site/page2/index.html file should exist
Then the _site/page<exist> directory should exist
And the "_site/page<exist>/index.html" file should exist
And I should see "<posts>" in "_site/page<exist>/index.html"
And the "_site/page<not_exist>/index.html" file should not exist
Examples:
| num |
| 1 |
| 2 |
Scenario: Correct liquid paginator replacements
Given I have a configuration file with "paginate" set to "1"
And I have a _layouts directory
And I have an "index.html" file that contains "{{ paginator.page }}"
And I have a _posts directory
And I have the following post:
| title | date | layout | content |
| Wargames | 3/27/2009 | default | The only winning move is not to play. |
| Wargames2 | 4/27/2009 | default | The only winning move is not to play2. |
When I run jekyll
Then the _site/index.html file should exist
And I should see "1" in "_site/index.html"
Then the _site/page2 directory should exist
And the _site/page2/index.html file should exist
And I should see "2" in "_site/page2/index.html"
| num | exist | posts | not_exist |
| 1 | 4 | 1 | 5 |
| 2 | 2 | 2 | 3 |
| 3 | 2 | 1 | 3 |

View File

@ -9,7 +9,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post title: {{ site.posts.first.title }}"
And I have a simple layout that contains "Post title: {{ page.title }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post title: Star Wars" in "_site/2009/03/27/star-wars.html"
@ -20,7 +20,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post url: {{ site.posts.first.url }}"
And I have a simple layout that contains "Post url: {{ page.url }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post url: /2009/03/27/star-wars.html" in "_site/2009/03/27/star-wars.html"
@ -31,7 +31,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post date: {{ site.posts.first.date }}"
And I have a simple layout that contains "Post date: {{ page.date }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post date: Fri Mar 27" in "_site/2009/03/27/star-wars.html"
@ -42,7 +42,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post id: {{ site.posts.first.id }}"
And I have a simple layout that contains "Post id: {{ page.id }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post id: /2009/03/27/star-wars" in "_site/2009/03/27/star-wars.html"
@ -53,7 +53,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post content: {{ site.posts.first.content }}"
And I have a simple layout that contains "Post content: {{ content }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post content: <p>Luke, I am your father.</p>" in "_site/2009/03/27/star-wars.html"
@ -65,7 +65,7 @@ Feature: Post data
And I have the following post in "movies":
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post category: {{ site.posts.first.categories }}"
And I have a simple layout that contains "Post category: {{ page.categories }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html"
@ -76,23 +76,23 @@ Feature: Post data
And I have the following post:
| title | date | layout | tag | content |
| Star Wars | 5/18/2009 | simple | twist | Luke, I am your father. |
And I have a simple layout that contains "Post tags: {{ site.posts.first.tags }}"
And I have a simple layout that contains "Post tags: {{ page.tags }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post tags: twist" in "_site/2009/05/18/star-wars.html"
Scenario: Use post.categories variable when categories are in folders
Given I have a movies directory
And I have a movies/scifi directory
And I have a movies/scifi/_posts directory
Given I have a scifi directory
And I have a scifi/movies directory
And I have a scifi/movies/_posts directory
And I have a _layouts directory
And I have the following post in "movies/scifi":
And I have the following post in "scifi/movies":
| title | date | layout | content |
| Star Wars | 3/27/2009 | simple | Luke, I am your father. |
And I have a simple layout that contains "Post categories: {{ site.posts.first.categories | array_to_sentence_string }}"
And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post categories: movies and scifi" in "_site/movies/scifi/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
@ -100,7 +100,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | category | content |
| Star Wars | 3/27/2009 | simple | movies | Luke, I am your father. |
And I have a simple layout that contains "Post category: {{ site.posts.first.categories }}"
And I have a simple layout that contains "Post category: {{ page.categories }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html"
@ -110,11 +110,11 @@ Feature: Post data
And I have a _layouts directory
And I have the following post:
| title | date | layout | categories | content |
| Star Wars | 3/27/2009 | simple | ['movies', 'scifi'] | Luke, I am your father. |
And I have a simple layout that contains "Post categories: {{ site.posts.first.categories | array_to_sentence_string }}"
| Star Wars | 3/27/2009 | simple | ['scifi', 'movies'] | Luke, I am your father. |
And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post categories: movies and scifi" in "_site/movies/scifi/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: Disable a post from being published
Given I have a _posts directory
@ -133,7 +133,7 @@ Feature: Post data
And I have the following post:
| title | date | layout | author | content |
| Star Wars | 3/27/2009 | simple | Darth Vader | Luke, I am your father. |
And I have a simple layout that contains "Post author: {{ site.posts.first.author }}"
And I have a simple layout that contains "Post author: {{ page.author }}"
When I run jekyll
Then the _site directory should exist
And I should see "Post author: Darth Vader" in "_site/2009/03/27/star-wars.html"

View File

@ -33,7 +33,7 @@ Feature: Site configuration
And I have an "README" file that contains "I want to be excluded"
And I have an "index.html" file that contains "I want to be included"
And I have a configuration file with "exclude" set to:
| Value |
| value |
| README |
| Rakefile |
When I run jekyll
@ -61,3 +61,43 @@ Feature: Site configuration
When I run jekyll
Then the _site directory should exist
And I should see "puts 'Hello world!'" in "_site/index.html"
Scenario: Set time and no future dated posts
Given I have a _layouts directory
And I have a page layout that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}"
And I have a post layout that contains "Post Layout: {{ content }}"
And I have an "index.html" page with layout "page" that contains "site index page"
And I have a configuration file with:
| key | value |
| time | 2010-01-01 |
| future | false |
And I have a _posts directory
And I have the following posts:
| title | date | layout | content |
| entry1 | 12/31/2007 | post | content for entry1. |
| entry2 | 01/31/2020 | post | content for entry2. |
When I run jekyll
Then the _site directory should exist
And I should see "Page Layout: 1 on 2010-01-01" in "_site/index.html"
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
And the "_site/2020/01/31/entry2.html" file should not exist
Scenario: Set time and future dated posts allowed
Given I have a _layouts directory
And I have a page layout that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}"
And I have a post layout that contains "Post Layout: {{ content }}"
And I have an "index.html" page with layout "page" that contains "site index page"
And I have a configuration file with:
| key | value |
| time | 2010-01-01 |
| future | true |
And I have a _posts directory
And I have the following posts:
| title | date | layout | content |
| entry1 | 12/31/2007 | post | content for entry1. |
| entry2 | 01/31/2020 | post | content for entry2. |
When I run jekyll
Then the _site directory should exist
And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html"
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html"

View File

@ -13,7 +13,7 @@ Given /^I have a blank site in "(.*)"$/ do |path|
end
# Like "I have a foo file" but gives a yaml front matter so jekyll actually processes it
Given /^I have an "(.*)" page(?: with (.*) "(.*)")? that contains "(.*)"$/ do |file, key, value, text|
Given /^I have an? "(.*)" page(?: with (.*) "(.*)")? that contains "(.*)"$/ do |file, key, value, text|
File.open(file, 'w') do |f|
f.write <<EOF
---
@ -25,7 +25,7 @@ EOF
end
end
Given /^I have an "(.*)" file that contains "(.*)"$/ do |file, text|
Given /^I have an? "(.*)" file that contains "(.*)"$/ do |file, text|
File.open(file, 'w') do |f|
f.write(text)
f.close
@ -39,13 +39,13 @@ Given /^I have a (.*) layout that contains "(.*)"$/ do |layout, text|
end
end
Given /^I have a (.*) directory$/ do |dir|
FileUtils.mkdir(dir)
Given /^I have an? (.*) directory$/ do |dir|
FileUtils.mkdir_p(dir)
end
Given /^I have the following posts?(?: (.*) "(.*)")?:$/ do |direction, folder, table|
table.hashes.each do |post|
date = Date.parse(post['date']).strftime('%Y-%m-%d')
date = Date.strptime(post['date'], '%m/%d/%Y').strftime('%Y-%m-%d')
title = post['title'].downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-')
if direction && direction == "in"
@ -81,7 +81,16 @@ end
Given /^I have a configuration file with "(.*)" set to "(.*)"$/ do |key, value|
File.open('_config.yml', 'w') do |f|
f.write("#{key}: #{value}")
f.write("#{key}: #{value}\n")
f.close
end
end
Given /^I have a configuration file with:$/ do |table|
File.open('_config.yml', 'w') do |f|
table.hashes.each do |row|
f.write("#{row["key"]}: #{row["value"]}\n")
end
f.close
end
end
@ -90,7 +99,7 @@ Given /^I have a configuration file with "([^\"]*)" set to:$/ do |key, table|
File.open('_config.yml', 'w') do |f|
f.write("#{key}:\n")
table.hashes.each do |row|
f.write("- #{row["Value"]}\n")
f.write("- #{row["value"]}\n")
end
f.close
end
@ -115,14 +124,14 @@ Then /^the (.*) directory should exist$/ do |dir|
assert File.directory?(dir)
end
Then /^the (.*) file should exist$/ do |file|
assert File.file?(file)
end
Then /^I should see "(.*)" in "(.*)"$/ do |text, file|
assert_match Regexp.new(text), File.open(file).readlines.join
end
Then /^the "(.*)" file should exist$/ do |file|
assert File.file?(file)
end
Then /^the "(.*)" file should not exist$/ do |file|
assert !File.exists?(file)
end

View File

@ -1,135 +1,127 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{jekyll}
s.version = "0.5.4"
s.specification_version = 2 if s.respond_to? :specification_version=
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Tom Preston-Werner"]
s.date = %q{2009-08-24}
s.default_executable = %q{jekyll}
s.description = %q{Jekyll is a simple, blog aware, static site generator.}
s.email = %q{tom@mojombo.com}
s.rubygems_version = '1.3.5'
s.name = 'jekyll'
s.version = '0.5.7'
s.date = '2010-04-21'
s.rubyforge_project = 'jekyll'
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 = 'http://github.com/mojombo/jekyll'
s.require_paths = %w[lib]
s.executables = ["jekyll"]
s.extra_rdoc_files = [
"README.textile"
]
s.files = [
".gitignore",
"History.txt",
"README.textile",
"Rakefile",
"VERSION.yml",
"bin/jekyll",
"features/create_sites.feature",
"features/embed_filters.feature",
"features/pagination.feature",
"features/permalinks.feature",
"features/post_data.feature",
"features/site_configuration.feature",
"features/site_data.feature",
"features/step_definitions/jekyll_steps.rb",
"features/support/env.rb",
"jekyll.gemspec",
"lib/jekyll.rb",
"lib/jekyll/albino.rb",
"lib/jekyll/converters/csv.rb",
"lib/jekyll/converters/mephisto.rb",
"lib/jekyll/converters/mt.rb",
"lib/jekyll/converters/textpattern.rb",
"lib/jekyll/converters/typo.rb",
"lib/jekyll/converters/wordpress.rb",
"lib/jekyll/convertible.rb",
"lib/jekyll/core_ext.rb",
"lib/jekyll/filters.rb",
"lib/jekyll/layout.rb",
"lib/jekyll/page.rb",
"lib/jekyll/pager.rb",
"lib/jekyll/post.rb",
"lib/jekyll/site.rb",
"lib/jekyll/tags/highlight.rb",
"lib/jekyll/tags/include.rb",
"test/helper.rb",
"test/source/_includes/sig.markdown",
"test/source/_layouts/default.html",
"test/source/_layouts/simple.html",
"test/source/_posts/2008-02-02-not-published.textile",
"test/source/_posts/2008-02-02-published.textile",
"test/source/_posts/2008-10-18-foo-bar.textile",
"test/source/_posts/2008-11-21-complex.textile",
"test/source/_posts/2008-12-03-permalinked-post.textile",
"test/source/_posts/2008-12-13-include.markdown",
"test/source/_posts/2009-01-27-array-categories.textile",
"test/source/_posts/2009-01-27-categories.textile",
"test/source/_posts/2009-01-27-category.textile",
"test/source/_posts/2009-03-12-hash-#1.markdown",
"test/source/_posts/2009-05-18-tag.textile",
"test/source/_posts/2009-05-18-tags.textile",
"test/source/_posts/2009-06-22-empty-yaml.textile",
"test/source/_posts/2009-06-22-no-yaml.textile",
"test/source/about.html",
"test/source/category/_posts/2008-9-23-categories.textile",
"test/source/contacts.html",
"test/source/css/screen.css",
"test/source/foo/_posts/bar/2008-12-12-topical-post.textile",
"test/source/index.html",
"test/source/sitemap.xml",
"test/source/win/_posts/2009-05-24-yaml-linebreak.markdown",
"test/source/z_category/_posts/2008-9-23-categories.textile",
"test/suite.rb",
"test/test_configuration.rb",
"test/test_filters.rb",
"test/test_generated_site.rb",
"test/test_page.rb",
"test/test_pager.rb",
"test/test_post.rb",
"test/test_site.rb",
"test/test_tags.rb"
]
s.homepage = %q{http://github.com/mojombo/jekyll}
s.default_executable = 'jekyll'
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{jekyll}
s.rubygems_version = %q{1.3.5}
s.summary = %q{Jekyll is a simple, blog aware, static site generator.}
s.test_files = [
"test/helper.rb",
"test/suite.rb",
"test/test_configuration.rb",
"test/test_filters.rb",
"test/test_generated_site.rb",
"test/test_page.rb",
"test/test_pager.rb",
"test/test_post.rb",
"test/test_site.rb",
"test/test_tags.rb"
s.extra_rdoc_files = %w[README.textile LICENSE]
s.add_runtime_dependency('RedCloth', [">= 4.2.1"])
s.add_runtime_dependency('liquid', [">= 1.9.0"])
s.add_runtime_dependency('classifier', [">= 1.3.1"])
s.add_runtime_dependency('maruku', [">= 0.5.9"])
s.add_runtime_dependency('directory_watcher', [">= 1.1.1"])
s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"])
# = MANIFEST =
s.files = %w[
History.txt
README.textile
Rakefile
bin/jekyll
cucumber.yml
features/create_sites.feature
features/embed_filters.feature
features/markdown.feature
features/pagination.feature
features/permalinks.feature
features/post_data.feature
features/site_configuration.feature
features/site_data.feature
features/step_definitions/jekyll_steps.rb
features/support/env.rb
jekyll.gemspec
lib/jekyll.rb
lib/jekyll/albino.rb
lib/jekyll/converter.rb
lib/jekyll/converters/identity.rb
lib/jekyll/converters/markdown.rb
lib/jekyll/converters/textile.rb
lib/jekyll/convertible.rb
lib/jekyll/core_ext.rb
lib/jekyll/extension.rb
lib/jekyll/filters.rb
lib/jekyll/generator.rb
lib/jekyll/generators/pagination.rb
lib/jekyll/layout.rb
lib/jekyll/migrators/csv.rb
lib/jekyll/migrators/mephisto.rb
lib/jekyll/migrators/mt.rb
lib/jekyll/migrators/textpattern.rb
lib/jekyll/migrators/typo.rb
lib/jekyll/migrators/wordpress.rb
lib/jekyll/page.rb
lib/jekyll/post.rb
lib/jekyll/site.rb
lib/jekyll/static_file.rb
lib/jekyll/tags/highlight.rb
lib/jekyll/tags/include.rb
test/helper.rb
test/source/_includes/sig.markdown
test/source/_layouts/default.html
test/source/_layouts/simple.html
test/source/_posts/2008-02-02-not-published.textile
test/source/_posts/2008-02-02-published.textile
test/source/_posts/2008-10-18-foo-bar.textile
test/source/_posts/2008-11-21-complex.textile
test/source/_posts/2008-12-03-permalinked-post.textile
test/source/_posts/2008-12-13-include.markdown
test/source/_posts/2009-01-27-array-categories.textile
test/source/_posts/2009-01-27-categories.textile
test/source/_posts/2009-01-27-category.textile
test/source/_posts/2009-01-27-empty-categories.textile
test/source/_posts/2009-01-27-empty-category.textile
test/source/_posts/2009-03-12-hash-#1.markdown
test/source/_posts/2009-05-18-empty-tag.textile
test/source/_posts/2009-05-18-empty-tags.textile
test/source/_posts/2009-05-18-tag.textile
test/source/_posts/2009-05-18-tags.textile
test/source/_posts/2009-06-22-empty-yaml.textile
test/source/_posts/2009-06-22-no-yaml.textile
test/source/_posts/2010-01-08-triple-dash.markdown
test/source/_posts/2010-01-09-date-override.textile
test/source/_posts/2010-01-09-time-override.textile
test/source/_posts/2010-01-09-timezone-override.textile
test/source/_posts/2010-01-16-override-data.textile
test/source/about.html
test/source/category/_posts/2008-9-23-categories.textile
test/source/contacts.html
test/source/css/screen.css
test/source/foo/_posts/bar/2008-12-12-topical-post.textile
test/source/index.html
test/source/sitemap.xml
test/source/win/_posts/2009-05-24-yaml-linebreak.markdown
test/source/z_category/_posts/2008-9-23-categories.textile
test/suite.rb
test/test_configuration.rb
test/test_core_ext.rb
test/test_filters.rb
test/test_generated_site.rb
test/test_page.rb
test/test_pager.rb
test/test_post.rb
test/test_site.rb
test/test_tags.rb
]
# = MANIFEST =
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<RedCloth>, [">= 4.2.1"])
s.add_runtime_dependency(%q<liquid>, [">= 1.9.0"])
s.add_runtime_dependency(%q<classifier>, [">= 1.3.1"])
s.add_runtime_dependency(%q<maruku>, [">= 0.5.9"])
s.add_runtime_dependency(%q<directory_watcher>, [">= 1.1.1"])
s.add_runtime_dependency(%q<open4>, [">= 0.9.6"])
else
s.add_dependency(%q<RedCloth>, [">= 4.2.1"])
s.add_dependency(%q<liquid>, [">= 1.9.0"])
s.add_dependency(%q<classifier>, [">= 1.3.1"])
s.add_dependency(%q<maruku>, [">= 0.5.9"])
s.add_dependency(%q<directory_watcher>, [">= 1.1.1"])
s.add_dependency(%q<open4>, [">= 0.9.6"])
end
else
s.add_dependency(%q<RedCloth>, [">= 4.2.1"])
s.add_dependency(%q<liquid>, [">= 1.9.0"])
s.add_dependency(%q<classifier>, [">= 1.3.1"])
s.add_dependency(%q<maruku>, [">= 0.5.9"])
s.add_dependency(%q<directory_watcher>, [">= 1.1.1"])
s.add_dependency(%q<open4>, [">= 0.9.6"])
end
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
end

View File

@ -1,43 +1,64 @@
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
# Require all of the Ruby files in the given directory.
#
# path - The String relative path from here to the directory.
#
# Returns nothing.
def require_all(path)
glob = File.join(File.dirname(__FILE__), path, '*.rb')
Dir[glob].each do |f|
require f
end
end
# rubygems
require 'rubygems'
# core
# stdlib
require 'fileutils'
require 'time'
require 'yaml'
# stdlib
# 3rd party
require 'liquid'
require 'redcloth'
# internal requires
require 'jekyll/core_ext'
require 'jekyll/pager'
require 'jekyll/site'
require 'jekyll/convertible'
require 'jekyll/layout'
require 'jekyll/page'
require 'jekyll/post'
require 'jekyll/filters'
require 'jekyll/tags/highlight'
require 'jekyll/tags/include'
require 'jekyll/albino'
require 'jekyll/static_file'
# extensions
require 'jekyll/plugin'
require 'jekyll/converter'
require 'jekyll/generator'
require_all 'jekyll/converters'
require_all 'jekyll/generators'
require_all 'jekyll/tags'
module Jekyll
VERSION = '0.5.7'
# Default options. Overriden by values in _config.yml or command-line opts.
# (Strings rather symbols used for compatability with YAML)
# (Strings rather symbols used for compatability with YAML).
DEFAULTS = {
'safe' => false,
'auto' => false,
'server' => false,
'server_port' => 4000,
'source' => Dir.pwd,
'destination' => File.join('.', '_site'),
'plugins' => File.join('.', '_plugins'),
'future' => true,
'lsi' => false,
'pygments' => false,
'markdown' => 'maruku',
@ -53,10 +74,13 @@ module Jekyll
}
# Generate a Jekyll configuration Hash by merging the default options
# with anything in _config.yml, and adding the given options on top
# +override+ is a Hash of config directives
# with anything in _config.yml, and adding the given options on top.
#
# Returns Hash
# override - A Hash of config directives that override any options in both
# the defaults and the config file. See Jekyll::DEFAULTS for a
# list of option names and their defaults.
#
# Returns the final configuration Hash.
def self.configuration(override)
# _config.yml may override default source location, but until
# then, we need to know where to look for _config.yml
@ -67,19 +91,15 @@ module Jekyll
begin
config = YAML.load_file(config_file)
raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
STDOUT.puts "Configuration from #{config_file}"
$stdout.puts "Configuration from #{config_file}"
rescue => err
STDERR.puts "WARNING: Could not read configuration. Using defaults (and options)."
STDERR.puts "\t" + err.to_s
$stderr.puts "WARNING: Could not read configuration. " +
"Using defaults (and options)."
$stderr.puts "\t" + err.to_s
config = {}
end
# Merge DEFAULTS < _config.yml < override
Jekyll::DEFAULTS.deep_merge(config).deep_merge(override)
end
def self.version
yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
"#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
end
end

View File

@ -41,7 +41,6 @@
# Chris Wanstrath // chris@ozmm.org
# GitHub // http://github.com
#
require 'open4'
class Albino
@@bin = Rails.development? ? 'pygmentize' : '/usr/bin/pygmentize' rescue 'pygmentize'
@ -61,11 +60,10 @@ class Albino
def execute(command)
output = ''
Open4.popen4(command) do |pid, stdin, stdout, stderr|
stdin.puts @target
stdin.close
output = stdout.read.strip
[stdout, stderr].each { |io| io.close }
IO.popen(command, mode='r+') do |p|
p.write @target
p.close_write
output = p.read.strip
end
output
end
@ -119,4 +117,4 @@ if $0 == __FILE__
assert_equal @syntaxer.colorize, Albino.colorize(__FILE__, :ruby)
end
end
end
end

43
lib/jekyll/converter.rb Normal file
View File

@ -0,0 +1,43 @@
module Jekyll
class Converter < Plugin
# Public: Get or set the pygments prefix. When an argument is specified,
# the prefix will be set. If no argument is specified, the current prefix
# will be returned.
#
# pygments_prefix - The String prefix (default: nil).
#
# Returns the String prefix.
def self.pygments_prefix(pygments_prefix = nil)
@pygments_prefix = pygments_prefix if pygments_prefix
@pygments_prefix
end
# Public: Get or set the pygments suffix. When an argument is specified,
# the suffix will be set. If no argument is specified, the current suffix
# will be returned.
#
# pygments_suffix - The String suffix (default: nil).
#
# Returns the String suffix.
def self.pygments_suffix(pygments_suffix = nil)
@pygments_suffix = pygments_suffix if pygments_suffix
@pygments_suffix
end
# Get the pygments prefix.
#
# Returns the String prefix.
def pygments_prefix
self.class.pygments_prefix
end
# Get the pygments suffix.
#
# Returns the String suffix.
def pygments_suffix
self.class.pygments_suffix
end
end
end

View File

@ -0,0 +1,22 @@
module Jekyll
class IdentityConverter < Converter
safe true
priority :lowest
def matches(ext)
true
end
def output_ext(ext)
ext
end
def convert(content)
content
end
end
end

View File

@ -0,0 +1,69 @@
module Jekyll
class MarkdownConverter < Converter
safe true
pygments_prefix '\n'
pygments_suffix '\n'
def initialize(config = {})
# Set the Markdown interpreter (and Maruku self.config, if necessary)
case config['markdown']
when 'rdiscount'
begin
require 'rdiscount'
def convert(content)
RDiscount.new(content).to_html
end
rescue LoadError
puts 'You must have the rdiscount gem installed first'
end
when 'maruku'
begin
require 'maruku'
def convert(content)
Maruku.new(content).to_html
end
if config['maruku']['use_divs']
require 'maruku/ext/div'
puts 'Maruku: Using extended syntax for div elements.'
end
if config['maruku']['use_tex']
require 'maruku/ext/math'
puts "Maruku: Using LaTeX extension. Images in `#{config['maruku']['png_dir']}`."
# Switch off MathML output
MaRuKu::Globals[:html_math_output_mathml] = false
MaRuKu::Globals[:html_math_engine] = 'none'
# Turn on math to PNG support with blahtex
# Resulting PNGs stored in `images/latex`
MaRuKu::Globals[:html_math_output_png] = true
MaRuKu::Globals[:html_png_engine] = config['maruku']['png_engine']
MaRuKu::Globals[:html_png_dir] = config['maruku']['png_dir']
MaRuKu::Globals[:html_png_url] = config['maruku']['png_url']
end
rescue LoadError
puts "The maruku gem is required for markdown support!"
end
else
raise "Invalid Markdown processor: '#{config['markdown']}' -- did you mean 'maruku' or 'rdiscount'?"
end
end
def matches(ext)
ext =~ /(markdown|mkdn?|md)/i
end
def output_ext(ext)
".html"
end
end
end

View File

@ -0,0 +1,23 @@
module Jekyll
class TextileConverter < Converter
safe true
pygments_prefix '<notextile>'
pygments_suffix '</notextile>'
def matches(ext)
ext =~ /textile/i
end
def output_ext(ext)
".html"
end
def convert(content)
RedCloth.new(content).to_html
end
end
end

View File

@ -3,6 +3,11 @@
#
# Requires
# self.site -> Jekyll::Site
# self.content
# self.content=
# self.data=
# self.ext=
# self.output=
module Jekyll
module Convertible
# Return the contents as a string
@ -17,42 +22,34 @@ module Jekyll
# Returns nothing
def read_yaml(base, name)
self.content = File.read(File.join(base, name))
if self.content =~ /^(---\s*\n.*?\n?)(---.*?\n)/m
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = self.content[($1.size + $2.size)..-1]
self.data = YAML.load($1)
end
self.data ||= {}
end
# Transform the contents based on the file extension.
# Transform the contents based on the content type.
#
# Returns nothing
def transform
case self.content_type
when 'textile'
self.ext = ".html"
self.content = self.site.textile(self.content)
when 'markdown'
self.ext = ".html"
self.content = self.site.markdown(self.content)
end
self.content = converter.convert(self.content)
end
# Determine which formatting engine to use based on this convertible's
# extension
# Determine the extension depending on content_type
#
# Returns one of :textile, :markdown or :unknown
def content_type
case self.ext[1..-1]
when /textile/i
return 'textile'
when /markdown/i, /mkdn/i, /md/i
return 'markdown'
end
return 'unknown'
# Returns the extensions for the output file
def output_ext
converter.output_ext(self.ext)
end
# Determine which converter to use based on this convertible's
# extension
def converter
@converter ||= self.site.converters.find { |c| c.matches(self.ext) }
end
# Add any necessary layouts to this convertible document
@ -64,7 +61,8 @@ module Jekyll
info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } }
# render and transform content (this becomes the final content of the object)
payload["content_type"] = self.content_type
payload["pygments_prefix"] = converter.pygments_prefix
payload["pygments_suffix"] = converter.pygments_suffix
self.content = Liquid::Template.parse(self.content).render(payload, info)
self.transform

View File

@ -19,6 +19,28 @@ class Hash
target
end
# Read array from the supplied hash favouring the singular key
# and then the plural key, and handling any nil entries.
# +hash+ the hash to read from
# +singular_key+ the singular key
# +plural_key+ the singular key
#
# Returns an array
def pluralized_array(singular_key, plural_key)
hash = self
if hash.has_key?(singular_key)
array = [hash[singular_key]] if hash[singular_key]
elsif hash.has_key?(plural_key)
case hash[plural_key]
when String
array = hash[plural_key].split
when Array
array = hash[plural_key].compact
end
end
array || []
end
end
# Thanks, ActiveSupport!

7
lib/jekyll/generator.rb Normal file
View File

@ -0,0 +1,7 @@
module Jekyll
class Generator < Plugin
end
end

View File

@ -0,0 +1,87 @@
module Jekyll
class Pagination < Generator
safe true
def generate(site)
site.pages.dup.each do |page|
paginate(site, page) if Pager.pagination_enabled?(site.config, page.name)
end
end
# Paginates the blog's posts. Renders the index.html file into paginated
# directories, ie: page2/index.html, page3/index.html, etc and adds more
# site-wide data.
# +page+ is the index.html Page that requires pagination
#
# {"paginator" => { "page" => <Number>,
# "per_page" => <Number>,
# "posts" => [<Post>],
# "total_posts" => <Number>,
# "total_pages" => <Number>,
# "previous_page" => <Number>,
# "next_page" => <Number> }}
def paginate(site, page)
all_posts = site.site_payload['site']['posts']
pages = Pager.calculate_pages(all_posts, site.config['paginate'].to_i)
(1..pages).each do |num_page|
pager = Pager.new(site.config, num_page, all_posts, pages)
if num_page > 1
newpage = Page.new(site, site.source, page.dir, page.name)
newpage.pager = pager
newpage.dir = File.join(page.dir, "page#{num_page}")
site.pages << newpage
else
page.pager = pager
end
end
end
end
class Pager
attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page
def self.calculate_pages(all_posts, per_page)
num_pages = all_posts.size / per_page.to_i
num_pages = num_pages + 1 if all_posts.size % per_page.to_i != 0
num_pages
end
def self.pagination_enabled?(config, file)
file == 'index.html' && !config['paginate'].nil?
end
def initialize(config, page, all_posts, num_pages = nil)
@page = page
@per_page = config['paginate'].to_i
@total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
if @page > @total_pages
raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
end
init = (@page - 1) * @per_page
offset = (init + @per_page - 1) >= all_posts.size ? all_posts.size : (init + @per_page - 1)
@total_posts = all_posts.size
@posts = all_posts[init..offset]
@previous_page = @page != 1 ? @page - 1 : nil
@next_page = @page != @total_pages ? @page + 1 : nil
end
def to_liquid
{
'page' => page,
'per_page' => per_page,
'posts' => posts,
'total_posts' => total_posts,
'total_pages' => total_pages,
'previous_page' => previous_page,
'next_page' => next_page
}
end
end
end

View File

@ -1,6 +1,7 @@
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'yaml'
# NOTE: This converter requires Sequel and the MySQL gems.
# The MySQL gem can be difficult to install on OS X. Once you have MySQL

View File

@ -3,8 +3,8 @@ module Jekyll
class Page
include Convertible
attr_accessor :site
attr_accessor :name, :ext, :basename
attr_accessor :site, :pager
attr_accessor :name, :ext, :basename, :dir
attr_accessor :data, :content, :output
# Initialize a new Page.
@ -43,10 +43,10 @@ module Jekyll
end
def template
if self.site.permalink_style == :pretty && !index?
"/:name/"
if self.site.permalink_style == :pretty && !index? && html?
"/:basename/"
else
"/:name.html"
"/:basename:output_ext"
end
end
@ -57,7 +57,12 @@ module Jekyll
def url
return permalink if permalink
@url ||= (ext == '.html') ? template.gsub(':name', basename) : "/#{name}"
@url ||= {
"basename" => self.basename,
"output_ext" => self.output_ext,
}.inject(template) { |result, token|
result.gsub(/:#{token.first}/, token.last)
}.gsub(/\/\//, "/")
end
# Extract information from the page filename
@ -75,10 +80,20 @@ module Jekyll
#
# Returns nothing
def render(layouts, site_payload)
payload = {"page" => self.data}.deep_merge(site_payload)
payload = {
"page" => self.to_liquid,
'paginator' => pager.to_liquid
}.deep_merge(site_payload)
do_layout(payload, layouts)
end
def to_liquid
self.data.deep_merge({
"url" => self.url,
"content" => self.content })
end
# Write the generated page file to the destination directory.
# +dest_prefix+ is the String path to the destination dir
# +dest_suffix+ is a suffix path to the destination dir
@ -91,7 +106,7 @@ module Jekyll
# The url needs to be unescaped in order to preserve the correct filename
path = File.join(dest, CGI.unescape(self.url))
if self.ext == '.html' && self.url[/\.html$/].nil?
if self.url =~ /\/$/
FileUtils.mkdir_p(path)
path = File.join(path, "index.html")
end
@ -101,11 +116,17 @@ module Jekyll
end
end
private
def inspect
"#<Jekyll:Page @name=#{self.name.inspect}>"
end
def index?
basename == 'index'
end
def html?
output_ext == '.html'
end
def index?
basename == 'index'
end
end

View File

@ -1,45 +0,0 @@
module Jekyll
class Pager
attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page
def self.calculate_pages(all_posts, per_page)
num_pages = all_posts.size / per_page.to_i
num_pages.abs + 1 if all_posts.size % per_page.to_i != 0
num_pages
end
def self.pagination_enabled?(config, file)
file == 'index.html' && !config['paginate'].nil?
end
def initialize(config, page, all_posts, num_pages = nil)
@page = page
@per_page = config['paginate'].to_i
@total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
if @page > @total_pages
raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
end
init = (@page - 1) * @per_page
offset = (init + @per_page - 1) >= all_posts.size ? all_posts.size : (init + @per_page - 1)
@total_posts = all_posts.size
@posts = all_posts[init..offset]
@previous_page = @page != 1 ? @page - 1 : nil
@next_page = @page != @total_pages ? @page + 1 : nil
end
def to_hash
{
'page' => page,
'per_page' => per_page,
'posts' => posts,
'total_posts' => total_posts,
'total_pages' => total_pages,
'previous_page' => previous_page,
'next_page' => next_page
}
end
end
end

76
lib/jekyll/plugin.rb Normal file
View File

@ -0,0 +1,76 @@
module Jekyll
class Plugin
PRIORITIES = { :lowest => -100,
:low => -10,
:normal => 0,
:high => 10,
:highest => 100 }
# Install a hook so that subclasses are recorded. This method is only
# ever called by Ruby itself.
#
# base - The Class subclass.
#
# Returns nothing.
def self.inherited(base)
subclasses << base
subclasses.sort!
end
# The list of Classes that have been subclassed.
#
# Returns an Array of Class objects.
def self.subclasses
@subclasses ||= []
end
# Get or set the priority of this plugin. When called without an
# argument it returns the priority. When an argument is given, it will
# set the priority.
#
# priority - The Symbol priority (default: nil). Valid options are:
# :lowest, :low, :normal, :high, :highest
#
# Returns the Symbol priority.
def self.priority(priority = nil)
if priority && PRIORITIES.has_key?(priority)
@priority = priority
end
@priority || :normal
end
# Get or set the safety of this plugin. When called without an argument
# it returns the safety. When an argument is given, it will set the
# safety.
#
# safe - The Boolean safety (default: nil).
#
# Returns the safety Boolean.
def self.safe(safe = nil)
if safe
@safe = safe
end
@safe || false
end
# Spaceship is priority [higher -> lower]
#
# other - The class to be compared.
#
# Returns -1, 0, 1.
def self.<=>(other)
PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
end
# Initialize a new plugin. This should be overridden by the subclass.
#
# config - The Hash of configuration options.
#
# Returns a new instance.
def initialize(config = {})
# no-op for default
end
end
end

View File

@ -18,12 +18,9 @@ module Jekyll
name =~ MATCHER
end
attr_accessor :site, :date, :slug, :ext, :published, :data, :content, :output, :tags
attr_writer :categories
def categories
@categories ||= []
end
attr_accessor :site
attr_accessor :data, :content, :output, :ext
attr_accessor :date, :slug, :published, :tags, :categories
# Initialize this Post instance.
# +site+ is the Site
@ -41,32 +38,23 @@ module Jekyll
self.process(name)
self.read_yaml(@base, name)
#If we've added a date and time to the yaml, use that instead of the filename date
#Means we'll sort correctly.
if self.data.has_key?('date')
# ensure Time via to_s and reparse
self.date = Time.parse(self.data["date"].to_s)
end
if self.data.has_key?('published') && self.data['published'] == false
self.published = false
else
self.published = true
end
if self.data.has_key?("tag")
self.tags = [self.data["tag"]]
elsif self.data.has_key?("tags")
self.tags = self.data['tags']
else
self.tags = []
end
self.tags = self.data.pluralized_array("tag", "tags")
if self.categories.empty?
if self.data.has_key?('category')
self.categories << self.data['category']
elsif self.data.has_key?('categories')
# Look for categories in the YAML-header, either specified as
# an array or a string.
if self.data['categories'].kind_of? String
self.categories = self.data['categories'].split
else
self.categories = self.data['categories']
end
end
self.categories = self.data.pluralized_array('category', 'categories')
end
end
@ -136,9 +124,12 @@ module Jekyll
"month" => date.strftime("%m"),
"day" => date.strftime("%d"),
"title" => CGI.escape(slug),
"categories" => categories.sort.join('/')
"i_day" => date.strftime("%d").to_i.to_s,
"i_month" => date.strftime("%m").to_i.to_s,
"categories" => categories.join('/'),
"output_ext" => self.output_ext
}.inject(template) { |result, token|
result.gsub(/:#{token.first}/, token.last)
result.gsub(/:#{Regexp.escape token.first}/, token.last)
}.gsub(/\/\//, "/")
end
@ -179,12 +170,10 @@ module Jekyll
# Returns nothing
def render(layouts, site_payload)
# construct payload
payload =
{
payload = {
"site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
"page" => self.to_liquid
}
payload = payload.deep_merge(site_payload)
}.deep_merge(site_payload)
do_layout(payload, layouts)
end
@ -213,7 +202,8 @@ module Jekyll
#
# Returns <Hash>
def to_liquid
{ "title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
self.data.deep_merge({
"title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
"url" => self.url,
"date" => self.date,
"id" => self.id,
@ -221,7 +211,7 @@ module Jekyll
"next" => self.next,
"previous" => self.previous,
"tags" => self.tags,
"content" => self.content }.deep_merge(self.data)
"content" => self.content })
end
def inspect

View File

@ -1,8 +1,10 @@
module Jekyll
class Site
attr_accessor :config, :layouts, :posts, :categories, :exclude,
:source, :dest, :lsi, :pygments, :permalink_style, :tags
attr_accessor :config, :layouts, :posts, :pages, :static_files,
:categories, :exclude, :source, :dest, :lsi, :pygments,
:permalink_style, :tags, :time, :future, :safe, :plugins
attr_accessor :converters, :generators
# Initialize the site
# +config+ is a Hash containing site configurations details
@ -11,97 +13,79 @@ module Jekyll
def initialize(config)
self.config = config.clone
self.source = config['source']
self.safe = config['safe']
self.source = File.expand_path(config['source'])
self.dest = config['destination']
self.plugins = File.expand_path(config['plugins'])
self.lsi = config['lsi']
self.pygments = config['pygments']
self.permalink_style = config['permalink'].to_sym
self.exclude = config['exclude'] || []
self.future = config['future']
self.reset
self.setup
end
def reset
self.time = Time.parse(self.config['time'].to_s) || Time.now
self.layouts = {}
self.posts = []
self.pages = []
self.static_files = []
self.categories = Hash.new { |hash, key| hash[key] = [] }
self.tags = Hash.new { |hash, key| hash[key] = [] }
end
def setup
# Check to see if LSI is enabled.
require 'classifier' if self.lsi
# Set the Markdown interpreter (and Maruku self.config, if necessary)
case self.config['markdown']
when 'rdiscount'
begin
require 'rdiscount'
# If safe mode is off, load in any ruby files under the plugins
# directory.
unless self.safe
Dir[File.join(self.plugins, "**/*.rb")].each do |f|
require f
end
end
def markdown(content)
RDiscount.new(content).to_html
end
self.converters = Jekyll::Converter.subclasses.select do |c|
!self.safe || c.safe
end.map do |c|
c.new(self.config)
end
rescue LoadError
puts 'You must have the rdiscount gem installed first'
end
when 'maruku'
begin
require 'maruku'
def markdown(content)
Maruku.new(content).to_html
end
if self.config['maruku']['use_divs']
require 'maruku/ext/div'
puts 'Maruku: Using extended syntax for div elements.'
end
if self.config['maruku']['use_tex']
require 'maruku/ext/math'
puts "Maruku: Using LaTeX extension. Images in `#{self.config['maruku']['png_dir']}`."
# Switch off MathML output
MaRuKu::Globals[:html_math_output_mathml] = false
MaRuKu::Globals[:html_math_engine] = 'none'
# Turn on math to PNG support with blahtex
# Resulting PNGs stored in `images/latex`
MaRuKu::Globals[:html_math_output_png] = true
MaRuKu::Globals[:html_png_engine] = self.config['maruku']['png_engine']
MaRuKu::Globals[:html_png_dir] = self.config['maruku']['png_dir']
MaRuKu::Globals[:html_png_url] = self.config['maruku']['png_url']
end
rescue LoadError
puts "The maruku gem is required for markdown support!"
end
else
raise "Invalid Markdown processor: '#{self.config['markdown']}' -- did you mean 'maruku' or 'rdiscount'?"
self.generators = Jekyll::Generator.subclasses.select do |c|
!self.safe || c.safe
end.map do |c|
c.new(self.config)
end
end
def textile(content)
RedCloth.new(content).to_html
end
# Do the actual work of processing the site and generating the
# real deal.
# real deal. 5 phases; reset, read, generate, render, write. This allows
# rendering to have full site payload available.
#
# Returns nothing
def process
self.reset
self.read_layouts
self.transform_pages
self.write_posts
self.read
self.generate
self.render
self.write
end
# Read all the files in <source>/_layouts into memory for later use.
def read
self.read_layouts # existing implementation did this at top level only so preserved that
self.read_directories
end
# Read all the files in <source>/<dir>/_layouts and create a new Layout
# object with each one.
#
# Returns nothing
def read_layouts
base = File.join(self.source, "_layouts")
def read_layouts(dir = '')
base = File.join(self.source, dir, "_layouts")
return unless File.exists?(base)
entries = []
Dir.chdir(base) { entries = filter_entries(Dir['*.*']) }
@ -109,24 +93,23 @@ module Jekyll
name = f.split(".")[0..-2].join(".")
self.layouts[name] = Layout.new(self, base, f)
end
rescue Errno::ENOENT => e
# ignore missing layout dir
end
# Read all the files in <base>/_posts and create a new Post object with each one.
# Read all the files in <source>/<dir>/_posts and create a new Post
# object with each one.
#
# Returns nothing
def read_posts(dir)
base = File.join(self.source, dir, '_posts')
entries = []
Dir.chdir(base) { entries = filter_entries(Dir['**/*']) }
return unless File.exists?(base)
entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
# first pass processes, but does not yet render post content
entries.each do |f|
if Post.valid?(f)
post = Post.new(self, self.source, dir, f)
if post.published
if post.published && (self.future || post.date <= self.time)
self.posts << post
post.categories.each { |c| self.categories[c] << post }
post.tags.each { |c| self.tags[c] << post }
@ -135,68 +118,70 @@ module Jekyll
end
self.posts.sort!
end
# second pass renders each post now that full site payload is available
def generate
self.generators.each do |generator|
generator.generate(self)
end
end
def render
self.posts.each do |post|
post.render(self.layouts, site_payload)
end
self.pages.each do |page|
page.render(self.layouts, site_payload)
end
self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} }
self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a} }
rescue Errno::ENOENT => e
# ignore missing layout dir
end
# Write each post to <dest>/<year>/<month>/<day>/<slug>
# Write static files, pages and posts
#
# Returns nothing
def write_posts
def write
self.posts.each do |post|
post.write(self.dest)
end
self.pages.each do |page|
page.write(self.dest)
end
self.static_files.each do |sf|
sf.write(self.dest)
end
end
# Copy all regular files from <source> to <dest>/ ignoring
# any files/directories that are hidden or backup files (start
# with "." or "#" or end with "~") or contain site content (start with "_")
# unless they are "_posts" directories or web server files such as
# '.htaccess'
# Reads the directories and finds posts, pages and static files that will
# become part of the valid site according to the rules in +filter_entries+.
# The +dir+ String is a relative path used to call this method
# recursively as it descends through directories
#
# Returns nothing
def transform_pages(dir = '')
def read_directories(dir = '')
base = File.join(self.source, dir)
entries = filter_entries(Dir.entries(base))
directories = entries.select { |e| File.directory?(File.join(base, e)) }
files = entries.reject { |e| File.directory?(File.join(base, e)) || File.symlink?(File.join(base, e)) }
# we need to make sure to process _posts *first* otherwise they
# might not be available yet to other templates as {{ site.posts }}
if directories.include?('_posts')
directories.delete('_posts')
read_posts(dir)
end
self.read_posts(dir)
[directories, files].each do |entries|
entries.each do |f|
if File.directory?(File.join(base, f))
next if self.dest.sub(/\/$/, '') == File.join(base, f)
transform_pages(File.join(dir, f))
elsif Pager.pagination_enabled?(self.config, f)
paginate_posts(f, dir)
entries.each do |f|
f_abs = File.join(base, f)
f_rel = File.join(dir, f)
if File.directory?(f_abs)
next if self.dest.sub(/\/$/, '') == f_abs
read_directories(f_rel)
elsif !File.symlink?(f_abs)
first3 = File.open(f_abs) { |fd| fd.read(3) }
if first3 == "---"
# file appears to have a YAML header so process it as a page
pages << Page.new(self, self.source, dir, f)
else
first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
if first3 == "---"
# file appears to have a YAML header so process it as a page
page = Page.new(self, self.source, dir, f)
page.render(self.layouts, site_payload)
page.write(self.dest)
else
# otherwise copy the file without transforming it
FileUtils.mkdir_p(File.join(self.dest, dir))
FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
end
# otherwise treat it as a static file
static_files << StaticFile.new(self, self.source, dir, f)
end
end
end
@ -218,48 +203,29 @@ module Jekyll
#
# Returns {"site" => {"time" => <Time>,
# "posts" => [<Post>],
# "pages" => [<Page>],
# "categories" => [<Post>]}
def site_payload
{"site" => self.config.merge({
"time" => Time.now,
"time" => self.time,
"posts" => self.posts.sort { |a,b| b <=> a },
"pages" => self.pages,
"html_pages" => self.pages.reject { |page| !page.html? },
"categories" => post_attr_hash('categories'),
"tags" => post_attr_hash('tags')})}
end
# Filter out any files/directories that are hidden or backup files (start
# with "." or "#" or end with "~") or contain site content (start with "_")
# unless they are "_posts" directories or web server files such as
# '.htaccess'
# with "." or "#" or end with "~"), or contain site content (start with "_"),
# or are excluded in the site configuration, unless they are web server
# files such as '.htaccess'
def filter_entries(entries)
entries = entries.reject do |e|
unless ['_posts', '.htaccess'].include?(e)
unless ['.htaccess'].include?(e)
['.', '_', '#'].include?(e[0..0]) || e[-1..-1] == '~' || self.exclude.include?(e)
end
end
end
# Paginates the blog's posts. Renders the index.html file into paginated directories, ie: page2, page3...
# and adds more wite-wide data
#
# {"paginator" => { "page" => <Number>,
# "per_page" => <Number>,
# "posts" => [<Post>],
# "total_posts" => <Number>,
# "total_pages" => <Number>,
# "previous_page" => <Number>,
# "next_page" => <Number> }}
def paginate_posts(file, dir)
all_posts = self.posts.sort { |a,b| b <=> a }
pages = Pager.calculate_pages(all_posts, self.config['paginate'].to_i)
pages += 1
(1..pages).each do |num_page|
pager = Pager.new(self.config, num_page, all_posts, pages)
page = Page.new(self, self.source, dir, file)
page.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
suffix = "page#{num_page}" if num_page > 1
page.write(self.dest, suffix)
end
end
end
end

76
lib/jekyll/static_file.rb Normal file
View File

@ -0,0 +1,76 @@
module Jekyll
class StaticFile
@@mtimes = Hash.new # the cache of last modification times [path] -> mtime
# Initialize a new StaticFile.
# +site+ is the Site
# +base+ is the String path to the <source>
# +dir+ is the String path between <source> and the file
# +name+ is the String filename of the file
#
# Returns <StaticFile>
def initialize(site, base, dir, name)
@site = site
@base = base
@dir = dir
@name = name
end
# Obtains source file path.
#
# Returns source file path.
def path
File.join(@base, @dir, @name)
end
# Obtain destination path.
# +dest+ is the String path to the destination dir
#
# Returns destination file path.
def destination(dest)
File.join(dest, @dir, @name)
end
# Obtain mtime of the source path.
#
# Returns last modifiaction time for this file.
def mtime
File.stat(path).mtime.to_i
end
# Is source path modified?
#
# Returns true if modified since last write.
def modified?
@@mtimes[path] != mtime
end
# Write the static file to the destination directory (if modified).
# +dest+ is the String path to the destination dir
#
# Returns false if the file was not modified since last time (no-op).
def write(dest)
dest_path = destination(dest)
dest_dir = File.join(dest, @dir)
return false if File.exist? dest_path and !modified?
@@mtimes[path] = mtime
FileUtils.mkdir_p(dest_dir)
FileUtils.cp(path, dest_path)
true
end
# Reset the mtimes cache (for testing purposes).
#
# Returns nothing.
def self.reset_cache
@@mtimes = Hash.new
nil
end
end
end

View File

@ -4,15 +4,28 @@ module Jekyll
include Liquid::StandardFilters
# we need a language, but the linenos argument is optional.
SYNTAX = /(\w+)\s?(:?linenos)?\s?/
SYNTAX = /(\w+)\s?([\w\s=]+)*/
def initialize(tag_name, markup, tokens)
super
if markup =~ SYNTAX
@lang = $1
if defined? $2
tmp_options = {}
$2.split.each do |opt|
key, value = opt.split('=')
if value.nil?
if key == 'linenos'
value = 'inline'
else
value = true
end
end
tmp_options[key] = value
end
tmp_options = tmp_options.to_a.collect { |opt| opt.join('=') }
# additional options to pass to Albino.
@options = { 'O' => 'linenos=inline' }
@options = { 'O' => tmp_options.join(',') }
else
@options = {}
end
@ -23,21 +36,17 @@ module Jekyll
def render(context)
if context.registers[:site].pygments
output = render_pygments(context, super.to_s)
render_pygments(context, super.join)
else
output = render_codehighlighter(context, super.to_s)
render_codehighlighter(context, super.join)
end
output
end
def render_pygments(context, code)
if context["content_type"] == "markdown"
return "\n" + Albino.new(code, @lang).to_s(@options) + "\n"
elsif context["content_type"] == "textile"
return "<notextile>" + Albino.new(code, @lang).to_s(@options) + "</notextile>"
else
return Albino.new(code, @lang).to_s(@options)
end
output = add_code_tags(Albino.new(code, @lang).to_s(@options), @lang)
output = context["pygments_prefix"] + output if context["pygments_prefix"]
output = output + context["pygments_suffix"] if context["pygments_suffix"]
output
end
def render_codehighlighter(context, code)
@ -50,6 +59,13 @@ module Jekyll
</div>
HTML
end
def add_code_tags(code, lang)
# Add nested <code> tags to code blocks
code = code.sub(/<pre>/,'<pre><code class="' + lang + '">')
code = code.sub(/<\/pre>/,"</code></pre>")
end
end
end

View File

@ -1,5 +1,5 @@
require 'rubygems'
gem 'RedCloth', '= 4.2.1'
gem 'RedCloth', '>= 4.2.1'
require File.join(File.dirname(__FILE__), *%w[.. lib jekyll])

View File

@ -0,0 +1,7 @@
---
layout: default
title: Category in YAML
categories:
---
Best *post* ever

View File

@ -0,0 +1,7 @@
---
layout: default
title: Category in YAML
category:
---
Best *post* ever

View File

@ -0,0 +1,6 @@
---
title: A Tag
tag:
---
Whoa.

View File

@ -0,0 +1,6 @@
---
title: Some Tags
tags:
---
Awesome!

View File

@ -0,0 +1,5 @@
---
title: Foo --- Bar
---
Triple the fun!

View File

@ -0,0 +1,7 @@
---
date: 2010-01-10
---
Post with a front matter date
{{ page.date | date_to_string }}

View File

@ -0,0 +1,7 @@
---
date: 2010-01-10 13:07:09
---
Post with a front matter time
{{ page.date | date_to_string }}

View File

@ -0,0 +1,7 @@
---
date: 2010-01-10 13:07:09 +00:00
---
Post with a front matter time with timezone
{{ page.date | date_to_string }}

View File

@ -0,0 +1,4 @@
---
date: 2010-01-10 13:07:09
tags: A string
---

View File

@ -2,22 +2,31 @@
layout: nil
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<urlset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://example.com</loc>
<lastmod>{{ site.time | date_to_xmlschema }}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
{% for post in site.posts %}
<url>
<loc>http://example.com/{{ post.url }}/</loc>
<lastmod>{{ site.time }}</lastmod>
<changefreq>monthly</changefreq>
<priority>0.2</priority>
</url>
{% endfor %}
</urlset>
<loc>http://example.com</loc>
<lastmod>{{ site.time | date: "%Y-%m-%d" }}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
{% for post in site.posts %}
<url>
<loc>http://example.com{{ post.url }}/</loc>
<lastmod>{{ post.date | date: "%Y-%m-%d" }}</lastmod>
<changefreq>monthly</changefreq>
<priority>0.2</priority>
</url>
{% endfor %}
{% for page in site.html_pages %}
<url>
<loc>http://example.com{{ page.url }}</loc>
<lastmod>{{ site.time | date: "%Y-%m-%d" }}</lastmod>
{% if page.changefreq %}<changefreq>{{ page.changefreq }}</changefreq>{% endif %}
{% if page.priority %}<priority>{{ page.priority }}</priority>{% endif %}
</url>
{% endfor %}
</urlset>

View File

@ -8,21 +8,21 @@ class TestConfiguration < Test::Unit::TestCase
should "fire warning with no _config.yml" do
mock(YAML).load_file(@path) { raise "No such file or directory - #{@path}" }
mock(STDERR).puts("WARNING: Could not read configuration. Using defaults (and options).")
mock(STDERR).puts("\tNo such file or directory - #{@path}")
mock($stderr).puts("WARNING: Could not read configuration. Using defaults (and options).")
mock($stderr).puts("\tNo such file or directory - #{@path}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end
should "load configuration as hash" do
mock(YAML).load_file(@path) { Hash.new }
mock(STDOUT).puts("Configuration from #{@path}")
mock($stdout).puts("Configuration from #{@path}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end
should "fire warning with bad config" do
mock(YAML).load_file(@path) { Array.new }
mock(STDERR).puts("WARNING: Could not read configuration. Using defaults (and options).")
mock(STDERR).puts("\tInvalid configuration - #{@path}")
mock($stderr).puts("WARNING: Could not read configuration. Using defaults (and options).")
mock($stderr).puts("\tInvalid configuration - #{@path}")
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
end
end

66
test/test_core_ext.rb Normal file
View File

@ -0,0 +1,66 @@
require File.dirname(__FILE__) + '/helper'
class TestCoreExt < Test::Unit::TestCase
context "hash" do
context "pluralized_array" do
should "return empty array with no values" do
data = {}
assert_equal [], data.pluralized_array('tag', 'tags')
end
should "return empty array with no matching values" do
data = { 'foo' => 'bar' }
assert_equal [], data.pluralized_array('tag', 'tags')
end
should "return empty array with matching nil singular" do
data = { 'foo' => 'bar', 'tag' => nil, 'tags' => ['dog', 'cat'] }
assert_equal [], data.pluralized_array('tag', 'tags')
end
should "return single value array with matching singular" do
data = { 'foo' => 'bar', 'tag' => 'dog', 'tags' => ['dog', 'cat'] }
assert_equal ['dog'], data.pluralized_array('tag', 'tags')
end
should "return single value array with matching singular with spaces" do
data = { 'foo' => 'bar', 'tag' => 'dog cat', 'tags' => ['dog', 'cat'] }
assert_equal ['dog cat'], data.pluralized_array('tag', 'tags')
end
should "return empty array with matching nil plural" do
data = { 'foo' => 'bar', 'tags' => nil }
assert_equal [], data.pluralized_array('tag', 'tags')
end
should "return empty array with matching empty array" do
data = { 'foo' => 'bar', 'tags' => [] }
assert_equal [], data.pluralized_array('tag', 'tags')
end
should "return single value array with matching plural with single string value" do
data = { 'foo' => 'bar', 'tags' => 'dog' }
assert_equal ['dog'], data.pluralized_array('tag', 'tags')
end
should "return multiple value array with matching plural with single string value with spaces" do
data = { 'foo' => 'bar', 'tags' => 'dog cat' }
assert_equal ['dog', 'cat'], data.pluralized_array('tag', 'tags')
end
should "return single value array with matching plural with single value array" do
data = { 'foo' => 'bar', 'tags' => ['dog'] }
assert_equal ['dog'], data.pluralized_array('tag', 'tags')
end
should "return multiple value array with matching plural with multiple value array" do
data = { 'foo' => 'bar', 'tags' => ['dog', 'cat'] }
assert_equal ['dog', 'cat'], data.pluralized_array('tag', 'tags')
end
end
end
end

View File

@ -13,6 +13,10 @@ class TestGeneratedSite < Test::Unit::TestCase
@index = File.read(dest_dir('index.html'))
end
should "ensure post count is as expected" do
assert_equal 26, @site.posts.size
end
should "insert site.posts into the index" do
assert @index.include?("#{@site.posts.size} Posts")
end

View File

@ -1,11 +1,38 @@
require File.dirname(__FILE__) + '/helper'
class TestPager < Test::Unit::TestCase
context "pagination enabled" do
should "calculate number of pages" do
assert_equal(0, Pager.calculate_pages([], '2'))
assert_equal(1, Pager.calculate_pages([1], '2'))
assert_equal(1, Pager.calculate_pages([1,2], '2'))
assert_equal(2, Pager.calculate_pages([1,2,3], '2'))
assert_equal(2, Pager.calculate_pages([1,2,3,4], '2'))
assert_equal(3, Pager.calculate_pages([1,2,3,4,5], '2'))
end
context "pagination disabled" do
setup do
stub(Jekyll).configuration do
Jekyll::DEFAULTS.merge({
'source' => source_dir,
'source' => source_dir,
'destination' => dest_dir
})
end
@config = Jekyll.configuration
end
should "report that pagination is disabled" do
assert !Pager.pagination_enabled?(@config, 'index.html')
end
end
context "pagination enabled for 2" do
setup do
stub(Jekyll).configuration do
Jekyll::DEFAULTS.merge({
'source' => source_dir,
'destination' => dest_dir,
'paginate' => 2
})
@ -13,35 +40,74 @@ class TestPager < Test::Unit::TestCase
@config = Jekyll.configuration
@site = Site.new(@config)
@posts = @site.read_posts('')
@site.process
@posts = @site.posts
end
should "calculate number of pages" do
assert_equal(2, Pager.calculate_pages(@posts, @config['paginate']))
end
should "create first pager" do
pager = Pager.new(@config, 1, @posts)
assert_equal(@config['paginate'].to_i, pager.posts.size)
assert_equal(2, pager.total_pages)
assert_nil(pager.previous_page)
assert_equal(2, pager.next_page)
end
should "create second pager" do
pager = Pager.new(@config, 2, @posts)
assert_equal(@posts.size - @config['paginate'].to_i, pager.posts.size)
assert_equal(2, pager.total_pages)
assert_equal(1, pager.previous_page)
assert_nil(pager.next_page)
end
should "not create third pager" do
assert_raise(RuntimeError) { Pager.new(@config, 3, @posts) }
end
should "report that pagination is enabled" do
assert Pager.pagination_enabled?(@config, 'index.html')
end
context "with 4 posts" do
setup do
@posts = @site.posts[1..4] # limit to 4
end
should "create first pager" do
pager = Pager.new(@config, 1, @posts)
assert_equal(2, pager.posts.size)
assert_equal(2, pager.total_pages)
assert_nil(pager.previous_page)
assert_equal(2, pager.next_page)
end
should "create second pager" do
pager = Pager.new(@config, 2, @posts)
assert_equal(2, pager.posts.size)
assert_equal(2, pager.total_pages)
assert_equal(1, pager.previous_page)
assert_nil(pager.next_page)
end
should "not create third pager" do
assert_raise(RuntimeError) { Pager.new(@config, 3, @posts) }
end
end
context "with 5 posts" do
setup do
@posts = @site.posts[1..5] # limit to 5
end
should "create first pager" do
pager = Pager.new(@config, 1, @posts)
assert_equal(2, pager.posts.size)
assert_equal(3, pager.total_pages)
assert_nil(pager.previous_page)
assert_equal(2, pager.next_page)
end
should "create second pager" do
pager = Pager.new(@config, 2, @posts)
assert_equal(2, pager.posts.size)
assert_equal(3, pager.total_pages)
assert_equal(1, pager.previous_page)
assert_equal(3, pager.next_page)
end
should "create third pager" do
pager = Pager.new(@config, 3, @posts)
assert_equal(1, pager.posts.size)
assert_equal(3, pager.total_pages)
assert_equal(2, pager.previous_page)
assert_nil(pager.next_page)
end
should "not create fourth pager" do
assert_raise(RuntimeError) { Pager.new(@config, 4, @posts) }
end
end
end
end

View File

@ -18,10 +18,10 @@ class TestPost < Test::Unit::TestCase
end
should "ensure valid posts are valid" do
assert Post.valid?("2008-10-19-foo-bar.textile")
assert Post.valid?("foo/bar/2008-10-19-foo-bar.textile")
assert Post.valid?("2008-09-09-foo-bar.textile")
assert Post.valid?("foo/bar/2008-09-09-foo-bar.textile")
assert !Post.valid?("lol2008-10-19-foo-bar.textile")
assert !Post.valid?("lol2008-09-09-foo-bar.textile")
assert !Post.valid?("blah")
end
@ -31,24 +31,25 @@ class TestPost < Test::Unit::TestCase
@post.site = @site
@real_file = "2008-10-18-foo-bar.textile"
@fake_file = "2008-10-19-foo-bar.textile"
@fake_file = "2008-09-09-foo-bar.textile"
@source = source_dir('_posts')
end
should "keep date, title, and markup type" do
@post.categories = []
@post.process(@fake_file)
assert_equal Time.parse("2008-10-19"), @post.date
assert_equal Time.parse("2008-09-09"), @post.date
assert_equal "foo-bar", @post.slug
assert_equal ".textile", @post.ext
assert_equal "/2008/10/19", @post.dir
assert_equal "/2008/10/19/foo-bar", @post.id
assert_equal "/2008/09/09", @post.dir
assert_equal "/2008/09/09/foo-bar", @post.id
end
should "create url based on date and title" do
@post.categories = []
@post.process(@fake_file)
assert_equal "/2008/10/19/foo-bar.html", @post.url
assert_equal "/2008/09/09/foo-bar.html", @post.url
end
should "CGI escape urls" do
@ -77,7 +78,19 @@ class TestPost < Test::Unit::TestCase
@post.read_yaml(@source, @real_file)
assert_equal({"title" => "Test title", "layout" => "post", "tag" => "Ruby"}, @post.data)
assert_equal "\r\nThis is the content", @post.content
assert_equal "This is the content", @post.content
end
end
context "with embedded triple dash" do
setup do
@real_file = "2010-01-08-triple-dash.markdown"
end
should "consume the embedded dashes" do
@post.read_yaml(@source, @real_file)
assert_equal({"title" => "Foo --- Bar"}, @post.data)
assert_equal "Triple the fun!", @post.content
end
end
@ -93,7 +106,7 @@ class TestPost < Test::Unit::TestCase
should "process the url correctly" do
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
assert_equal "/2008/10/19/foo-bar.html", @post.url
assert_equal "/2008/09/09/foo-bar.html", @post.url
end
end
@ -105,7 +118,7 @@ class TestPost < Test::Unit::TestCase
should "process the url correctly" do
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
assert_equal "/beer/2008/10/19/foo-bar.html", @post.url
assert_equal "/beer/2008/09/09/foo-bar.html", @post.url
end
end
@ -118,7 +131,7 @@ class TestPost < Test::Unit::TestCase
should "process the url correctly" do
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
assert_equal "/beer/food/2008/10/19/foo-bar.html", @post.url
assert_equal "/food/beer/2008/09/09/foo-bar.html", @post.url
end
end
@ -142,7 +155,18 @@ class TestPost < Test::Unit::TestCase
should "process the url correctly" do
assert_equal "/:categories/:year/:month/:day/:title/", @post.template
assert_equal "/2008/10/19/foo-bar/", @post.url
assert_equal "/2008/09/09/foo-bar/", @post.url
end
end
context "with custom date permalink" do
setup do
@post.site.permalink_style = '/:categories/:year/:i_month/:i_day/:title/'
@post.process(@fake_file)
end
should "process the url correctly" do
assert_equal "/2008/9/9/foo-bar/", @post.url
end
end
@ -163,7 +187,7 @@ class TestPost < Test::Unit::TestCase
@post.read_yaml(@source, @real_file)
assert_equal({"title" => "Foo Bar", "layout" => "default"}, @post.data)
assert_equal "\nh1. {{ page.title }}\n\nBest *post* ever", @post.content
assert_equal "h1. {{ page.title }}\n\nBest *post* ever", @post.content
end
should "transform textile" do
@ -212,6 +236,41 @@ class TestPost < Test::Unit::TestCase
assert_equal false, post.published
end
should "recognize date in yaml" do
post = setup_post("2010-01-09-date-override.textile")
do_render(post)
assert_equal Time, post.date.class
assert_equal Time, post.to_liquid["date"].class
assert_equal "/2010/01/10/date-override.html", post.url
assert_equal "<p>Post with a front matter date</p>\n<p>10 Jan 2010</p>", post.output
end
should "recognize time in yaml" do
post = setup_post("2010-01-09-time-override.textile")
do_render(post)
assert_equal Time, post.date.class
assert_equal Time, post.to_liquid["date"].class
assert_equal "/2010/01/10/time-override.html", post.url
assert_equal "<p>Post with a front matter time</p>\n<p>10 Jan 2010</p>", post.output
end
should "recognize time with timezone in yaml" do
post = setup_post("2010-01-09-timezone-override.textile")
do_render(post)
assert_equal Time, post.date.class
assert_equal Time, post.to_liquid["date"].class
assert_equal "/2010/01/10/timezone-override.html", post.url
assert_equal "<p>Post with a front matter time with timezone</p>\n<p>10 Jan 2010</p>", post.output
end
should "to_liquid prioritizes post attributes over data" do
post = setup_post("2010-01-16-override-data.textile")
assert_equal Array, post.tags.class
assert_equal Array, post.to_liquid["tags"].class
assert_equal Time, post.date.class
assert_equal Time, post.to_liquid["date"].class
end
should "recognize category in yaml" do
post = setup_post("2009-01-27-category.textile")
assert post.categories.include?('foo')
@ -224,6 +283,16 @@ class TestPost < Test::Unit::TestCase
assert post.categories.include?('baz')
end
should "recognize empty category in yaml" do
post = setup_post("2009-01-27-empty-category.textile")
assert_equal [], post.categories
end
should "recognize empty categories in yaml" do
post = setup_post("2009-01-27-empty-categories.textile")
assert_equal [], post.categories
end
should "recognize tag in yaml" do
post = setup_post("2009-05-18-tag.textile")
assert post.tags.include?('code')
@ -236,6 +305,16 @@ class TestPost < Test::Unit::TestCase
assert post.tags.include?('pizza')
end
should "recognize empty tag in yaml" do
post = setup_post("2009-05-18-empty-tag.textile")
assert_equal [], post.tags
end
should "recognize empty tags in yaml" do
post = setup_post("2009-05-18-empty-tags.textile")
assert_equal [], post.tags
end
should "allow no yaml" do
post = setup_post("2009-06-22-no-yaml.textile")
assert_equal "No YAML.", post.content
@ -290,6 +369,21 @@ class TestPost < Test::Unit::TestCase
assert_equal "<<< <hr />\n<p>Tom Preston-Werner github.com/mojombo</p>\n\n<p>This <em>is</em> cool</p> >>>", post.output
end
should "render date specified in front matter properly" do
post = setup_post("2010-01-09-date-override.textile")
do_render(post)
assert_equal "<p>Post with a front matter date</p>\n<p>10 Jan 2010</p>", post.output
end
should "render time specified in front matter properly" do
post = setup_post("2010-01-09-time-override.textile")
do_render(post)
assert_equal "<p>Post with a front matter time</p>\n<p>10 Jan 2010</p>", post.output
end
end
end

View File

@ -19,11 +19,77 @@ class TestSite < Test::Unit::TestCase
before_posts = @site.posts.length
before_layouts = @site.layouts.length
before_categories = @site.categories.length
before_tags = @site.tags.length
before_pages = @site.pages.length
before_static_files = @site.static_files.length
before_time = @site.time
@site.process
assert_equal before_posts, @site.posts.length
assert_equal before_layouts, @site.layouts.length
assert_equal before_categories, @site.categories.length
assert_equal before_tags, @site.tags.length
assert_equal before_pages, @site.pages.length
assert_equal before_static_files, @site.static_files.length
assert before_time <= @site.time
end
should "write only modified static files" do
clear_dest
StaticFile.reset_cache
@site.process
some_static_file = @site.static_files[0].path
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
# need to sleep because filesystem timestamps have best resolution in seconds
sleep 1
@site.process
mtime2 = File.stat(dest).mtime.to_i
assert_equal mtime1, mtime2
# simulate file modification by user
FileUtils.touch some_static_file
sleep 1
@site.process
mtime3 = File.stat(dest).mtime.to_i
assert_not_equal mtime2, mtime3 # must be regenerated!
sleep 1
@site.process
mtime4 = File.stat(dest).mtime.to_i
assert_equal mtime3, mtime4 # no modifications, so must be the same
end
should "write static files if not modified but missing in destination" do
clear_dest
StaticFile.reset_cache
@site.process
some_static_file = @site.static_files[0].path
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
# need to sleep because filesystem timestamps have best resolution in seconds
sleep 1
@site.process
mtime2 = File.stat(dest).mtime.to_i
assert_equal mtime1, mtime2
# simulate destination file deletion
File.unlink dest
sleep 1
@site.process
mtime3 = File.stat(dest).mtime.to_i
assert_not_equal mtime2, mtime3 # must be regenerated and differ!
sleep 1
@site.process
mtime4 = File.stat(dest).mtime.to_i
assert_equal mtime3, mtime4 # no modifications, so must be the same
end
should "read layouts" do
@ -52,10 +118,10 @@ class TestSite < Test::Unit::TestCase
should "filter entries" do
ent1 = %w[foo.markdown bar.markdown baz.markdown #baz.markdown#
.baz.markdow foo.markdown~]
ent2 = %w[.htaccess _posts bla.bla]
ent2 = %w[.htaccess _posts _pages bla.bla]
assert_equal %w[foo.markdown bar.markdown baz.markdown], @site.filter_entries(ent1)
assert_equal ent2, @site.filter_entries(ent2)
assert_equal %w[.htaccess bla.bla], @site.filter_entries(ent2)
end
should "filter entries with exclude" do

View File

@ -2,26 +2,18 @@ require File.dirname(__FILE__) + '/helper'
class TestTags < Test::Unit::TestCase
def create_post(content, override = {}, markdown = true)
def create_post(content, override = {}, converter_class = Jekyll::MarkdownConverter)
stub(Jekyll).configuration do
Jekyll::DEFAULTS.merge({'pygments' => true}).merge(override)
end
site = Site.new(Jekyll.configuration)
info = { :filters => [Jekyll::Filters], :registers => { :site => site } }
if markdown
payload = {"content_type" => "markdown"}
else
payload = {"content_type" => "textile"}
end
@converter = site.converters.find { |c| c.class == converter_class }
payload = { "pygments_prefix" => @converter.pygments_prefix,
"pygments_suffix" => @converter.pygments_suffix }
@result = Liquid::Template.parse(content).render(payload, info)
if markdown
@result = site.markdown(@result)
else
@result = site.textile(@result)
end
@result = @converter.convert(@result)
end
def fill_post(code, override = {})
@ -49,7 +41,7 @@ CONTENT
end
should "render markdown with pygments line handling" do
assert_match %{<pre>test\n</pre>}, @result
assert_match %{<pre><code class='text'>test\n</code></pre>}, @result
end
end
@ -59,7 +51,7 @@ CONTENT
end
should "render markdown with pygments line handling" do
assert_match %{<pre>Æ\n</pre>}, @result
assert_match %{<pre><code class='text'>Æ\n</code></pre>}, @result
end
end
@ -82,7 +74,7 @@ CONTENT
context "using Textile" do
setup do
create_post(@content, {}, false)
create_post(@content, {}, Jekyll::TextileConverter)
end
# Broken in RedCloth 4.1.9