diff --git a/History.txt b/History.txt index 2cbaa68c..1fe095f8 100644 --- a/History.txt +++ b/History.txt @@ -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 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 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 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..c01df2b7 --- /dev/null +++ b/LICENSE @@ -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. \ No newline at end of file diff --git a/README.textile b/README.textile index d5165711..ebc4bf63 100644 --- a/README.textile +++ b/README.textile @@ -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. diff --git a/Rakefile b/Rakefile index ceea1cea..ce61601e 100644 --- a/Rakefile +++ b/Rakefile @@ -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 \ No newline at end of file diff --git a/VERSION.yml b/VERSION.yml deleted file mode 100644 index 66ebe587..00000000 --- a/VERSION.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -:patch: 4 -:major: 0 -:minor: 5 diff --git a/bin/jekyll b/bin/jekyll index dc4bc3cf..9c47d48c 100755 --- a/bin/jekyll +++ b/bin/jekyll @@ -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, diff --git a/cucumber.yml b/cucumber.yml new file mode 100644 index 00000000..9692830c --- /dev/null +++ b/cucumber.yml @@ -0,0 +1 @@ +default: --format progress \ No newline at end of file diff --git a/features/create_sites.feature b/features/create_sites.feature index d10f1625..8517e28d 100644 --- a/features/create_sites.feature +++ b/features/create_sites.feature @@ -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:

The only winning move is not to play.

" 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:

content for entry1.

" in "_site/2009/03/27/entry1.html" + And I should see "Post entry2:

content for entry2.

" in "_site/2009/04/27/entry2.html" + And I should see "Post entry3:

content for entry3.

" in "_site/category/2009/05/27/entry3.html" + And I should see "Post entry4:

content for entry4.

" 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" \ No newline at end of file diff --git a/features/embed_filters.feature b/features/embed_filters.feature index f71dfb8f..61bd8da0 100644 --- a/features/embed_filters.feature +++ b/features/embed_filters.feature @@ -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 & 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" diff --git a/features/markdown.feature b/features/markdown.feature new file mode 100644 index 00000000..859e6f7e --- /dev/null +++ b/features/markdown.feature @@ -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 "

My Title

" in "_site/2009/03/27/hackers.html" + And I should see "

My Title

" in "_site/index.html" + + Scenario: Markdown in pagination on index + Given I have a configuration file with "paginate" set to "5" + 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 "

My Title

" in "_site/index.html" + \ No newline at end of file diff --git a/features/pagination.feature b/features/pagination.feature index b52ddcf2..90b10578 100644 --- a/features/pagination.feature +++ b/features/pagination.feature @@ -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 "" 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 directory should exist + And the "_site/page/index.html" file should exist + And I should see "" in "_site/page/index.html" + And the "_site/page/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" - \ No newline at end of file + | num | exist | posts | not_exist | + | 1 | 4 | 1 | 5 | + | 2 | 2 | 2 | 3 | + | 3 | 2 | 1 | 3 | diff --git a/features/post_data.feature b/features/post_data.feature index 87f5d851..36fe9382 100644 --- a/features/post_data.feature +++ b/features/post_data.feature @@ -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:

Luke, I am your father.

" 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" diff --git a/features/site_configuration.feature b/features/site_configuration.feature index 31b8c71e..6f1c600e 100644 --- a/features/site_configuration.feature +++ b/features/site_configuration.feature @@ -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:

content for entry1.

" 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:

content for entry1.

" in "_site/2007/12/31/entry1.html" + And I should see "Post Layout:

content for entry2.

" in "_site/2020/01/31/entry2.html" diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb index 3bf34736..27239bd5 100644 --- a/features/step_definitions/jekyll_steps.rb +++ b/features/step_definitions/jekyll_steps.rb @@ -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 <= 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, [">= 4.2.1"]) - s.add_runtime_dependency(%q, [">= 1.9.0"]) - s.add_runtime_dependency(%q, [">= 1.3.1"]) - s.add_runtime_dependency(%q, [">= 0.5.9"]) - s.add_runtime_dependency(%q, [">= 1.1.1"]) - s.add_runtime_dependency(%q, [">= 0.9.6"]) - else - s.add_dependency(%q, [">= 4.2.1"]) - s.add_dependency(%q, [">= 1.9.0"]) - s.add_dependency(%q, [">= 1.3.1"]) - s.add_dependency(%q, [">= 0.5.9"]) - s.add_dependency(%q, [">= 1.1.1"]) - s.add_dependency(%q, [">= 0.9.6"]) - end - else - s.add_dependency(%q, [">= 4.2.1"]) - s.add_dependency(%q, [">= 1.9.0"]) - s.add_dependency(%q, [">= 1.3.1"]) - s.add_dependency(%q, [">= 0.5.9"]) - s.add_dependency(%q, [">= 1.1.1"]) - s.add_dependency(%q, [">= 0.9.6"]) - end + s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ } end diff --git a/lib/jekyll.rb b/lib/jekyll.rb index b3ee35e1..a3151e05 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -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 diff --git a/lib/jekyll/albino.rb b/lib/jekyll/albino.rb index 2497cb6d..10d0ffb9 100644 --- a/lib/jekyll/albino.rb +++ b/lib/jekyll/albino.rb @@ -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 \ No newline at end of file +end diff --git a/lib/jekyll/converter.rb b/lib/jekyll/converter.rb new file mode 100644 index 00000000..61398bf1 --- /dev/null +++ b/lib/jekyll/converter.rb @@ -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 \ No newline at end of file diff --git a/lib/jekyll/converters/identity.rb b/lib/jekyll/converters/identity.rb new file mode 100644 index 00000000..7d9628ca --- /dev/null +++ b/lib/jekyll/converters/identity.rb @@ -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 diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb new file mode 100644 index 00000000..0210c786 --- /dev/null +++ b/lib/jekyll/converters/markdown.rb @@ -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 diff --git a/lib/jekyll/converters/textile.rb b/lib/jekyll/converters/textile.rb new file mode 100644 index 00000000..60f2153b --- /dev/null +++ b/lib/jekyll/converters/textile.rb @@ -0,0 +1,23 @@ +module Jekyll + + class TextileConverter < Converter + safe true + + pygments_prefix '' + pygments_suffix '' + + def matches(ext) + ext =~ /textile/i + end + + def output_ext(ext) + ".html" + end + + def convert(content) + RedCloth.new(content).to_html + end + + end + +end diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index f0348ab5..b76157ec 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -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 diff --git a/lib/jekyll/core_ext.rb b/lib/jekyll/core_ext.rb index ead448a4..fc40cb38 100644 --- a/lib/jekyll/core_ext.rb +++ b/lib/jekyll/core_ext.rb @@ -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! diff --git a/lib/jekyll/generator.rb b/lib/jekyll/generator.rb new file mode 100644 index 00000000..f1cd1a58 --- /dev/null +++ b/lib/jekyll/generator.rb @@ -0,0 +1,7 @@ +module Jekyll + + class Generator < Plugin + + end + +end \ No newline at end of file diff --git a/lib/jekyll/generators/pagination.rb b/lib/jekyll/generators/pagination.rb new file mode 100644 index 00000000..59d71093 --- /dev/null +++ b/lib/jekyll/generators/pagination.rb @@ -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" => , + # "per_page" => , + # "posts" => [], + # "total_posts" => , + # "total_pages" => , + # "previous_page" => , + # "next_page" => }} + 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 diff --git a/lib/jekyll/converters/csv.rb b/lib/jekyll/migrators/csv.rb similarity index 100% rename from lib/jekyll/converters/csv.rb rename to lib/jekyll/migrators/csv.rb diff --git a/lib/jekyll/converters/mephisto.rb b/lib/jekyll/migrators/mephisto.rb similarity index 100% rename from lib/jekyll/converters/mephisto.rb rename to lib/jekyll/migrators/mephisto.rb diff --git a/lib/jekyll/converters/mt.rb b/lib/jekyll/migrators/mt.rb similarity index 100% rename from lib/jekyll/converters/mt.rb rename to lib/jekyll/migrators/mt.rb diff --git a/lib/jekyll/converters/textpattern.rb b/lib/jekyll/migrators/textpattern.rb similarity index 100% rename from lib/jekyll/converters/textpattern.rb rename to lib/jekyll/migrators/textpattern.rb diff --git a/lib/jekyll/converters/typo.rb b/lib/jekyll/migrators/typo.rb similarity index 100% rename from lib/jekyll/converters/typo.rb rename to lib/jekyll/migrators/typo.rb diff --git a/lib/jekyll/converters/wordpress.rb b/lib/jekyll/migrators/wordpress.rb similarity index 99% rename from lib/jekyll/converters/wordpress.rb rename to lib/jekyll/migrators/wordpress.rb index b570ded9..bd80daee 100644 --- a/lib/jekyll/converters/wordpress.rb +++ b/lib/jekyll/migrators/wordpress.rb @@ -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 diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index 20463903..20e52f5f 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -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 + "#" + end - def index? - basename == 'index' - end + def html? + output_ext == '.html' + end + + def index? + basename == 'index' + end end diff --git a/lib/jekyll/pager.rb b/lib/jekyll/pager.rb deleted file mode 100644 index 883579a4..00000000 --- a/lib/jekyll/pager.rb +++ /dev/null @@ -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 diff --git a/lib/jekyll/plugin.rb b/lib/jekyll/plugin.rb new file mode 100644 index 00000000..208472f0 --- /dev/null +++ b/lib/jekyll/plugin.rb @@ -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 diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb index 914cfd59..b1a2a63d 100644 --- a/lib/jekyll/post.rb +++ b/lib/jekyll/post.rb @@ -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 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 diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index be275d88..3d4137a3 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -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 /_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 //_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 /_posts and create a new Post object with each one. + # Read all the files in //_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 //// + # 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 to / 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" =>