diff --git a/.travis.yml b/.travis.yml index cd1e5f01..09b649ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ install: - script/rebund download - travis_retry bundle install --path vendor/bundle rvm: -- 2.1.2 -- 2.0.0 +- 2.1 +- 2.0 - 1.9.3 script: script/cibuild after_script: diff --git a/CONTRIBUTING.markdown b/CONTRIBUTING.markdown index 6bc1e320..94533415 100644 --- a/CONTRIBUTING.markdown +++ b/CONTRIBUTING.markdown @@ -4,10 +4,10 @@ Contribute So you've got an awesome idea to throw into Jekyll. Great! Please keep the following in mind: -* **Contributions will not be accepted without tests.** +* **Contributions will not be accepted without tests or necessary documentation updates.** * If you're creating a small fix or patch to an existing feature, just a simple test will do. Please stay in the confines of the current test suite and use - [Shoulda](http://github.com/thoughtbot/shoulda/tree/master) and + [Shoulda](https://github.com/thoughtbot/shoulda/tree/master) and [RR](https://github.com/rr/rr). * If it's a brand new feature, make sure to create a new [Cucumber](https://github.com/cucumber/cucumber/) feature and reuse steps diff --git a/History.markdown b/History.markdown index b46cc0b4..01a34e8e 100644 --- a/History.markdown +++ b/History.markdown @@ -4,20 +4,102 @@ ### Minor Enhancements + * Patch read vulnerabilities for data & confirm none for layouts (#2563) + +### Bug Fixes + +### Development Fixes + +### Site Enhancements + + * Add vertical margin to `highlight` to separate code blocks (#2558) + +## 2.1.0 / 2014-06-28 + +### Minor Enhancements + + * Bump to the latest Liquid version, 2.6.1 (#2495) * Add support for JSON files in the `_data` directory (#2369) + * Allow subclasses to override `EXCERPT_ATTRIBUTES_FOR_LIQUID` (#2408) + * Add `Jekyll.env` and `jekyll.environment` (the Liquid var) (#2417) + * Use `_config.yaml` or `_config.yml` (`.yml` takes precedence) (#2406) + * Override collection url template (#2418) + * Allow subdirectories in `_data` (#2395) + * Extract Pagination Generator into gem: `jekyll-paginate` (#2455) + * Utilize `date_to_rfc822` filter in site template (#2437) + * Add categories, last build datetime, and generator to site template + feed (#2438) + * Configurable, replaceable Logger-compliant logger (#2444) + * Extract `gist` tag into a separate gem (#2469) + * Add `collection` attribute to `Document#to_liquid` to access the + document's collection label. (#2436) + * Upgrade listen to `2.7.6 <= x < 3.0.0` (#2492) + * Allow configuration of different Twitter and GitHub usernames in site template (#2485) + * Bump Pygments to v0.6.0 (#2504) + * Front-matter defaults for documents in collections (#2419) + * Include files with a url which ends in `/` in the `site.html_pages` list (#2524) + * Make `highlight` tag use `language-` prefix in CSS class (#2511) + * Lookup item property via `item#to_liquid` before `#data` or `#[]` in filters (#2493) + * Skip initial build of site on serve with flag (#2477) + * Add support for `hl_lines` in `highlight` tag (#2532) + * Spike out `--watch` flag into a separate gem (#2550) ### Bug Fixes * Liquid `sort` filter should sort even if one of the values is `nil` (#2345) * Remove padding on `pre code` in the site template CSS (#2383) + * Set `log_level` earlier to silence info level configuration output (#2393) + * Only list pages which have `title` in site template (#2411) + * Accept `Numeric` values for dates, not `Number` values (#2377) + * Prevent code from overflowing container in site template (#2429) + * Encode URLs in UTF-8 when escaping and unescaping (#2420) + * No Layouts or Liquid for Asset Files (#2431) + * Allow front-matter defaults to set post categories (#2373) + * Fix command in subcommand deprecation warning (#2457) + * Keep all parent directories of files/dirs in `keep_files` (#2458) + * When using RedCarpet and Rouge without Rouge installed, fixed erroneous + error which stated that redcarpet was missing, not rouge. (#2464) + * Ignore *all* directories and files that merit it on auto-generation (#2459) + * Before copying file, explicitly remove the old one (#2535) + * Merge file system categories with categories from YAML. (#2531) + * Deep merge front matter defaults (#2490) + * Ensure exclude and include arrays are arrays of strings (#2542) + * Allow collections to have dots in their filenames (#2552) + * Collections shouldn't try to read in directories as files (#2552) + * Be quiet very quickly. (#2520) ### Development Fixes * Test Ruby 2.1.2 instead of 2.1.1 (#2374) * Add test for sorting UTF-8 characters (#2384) + * Use `https` for GitHub links in documentation (#2470) + * Remove coverage reporting with Coveralls (#2494) + * Fix a bit of missing TomDoc to `Jekyll::Commands::Build#build` (#2554) ### Site Enhancements + * Set `timezone` to `America/Los_Angeles` (#2394) + * Improve JavaScript in `anchor_links.html` (#2368) + * Remove note on Quickstart page about default markdown converter (#2387) + * Remove broken link in extras.md to a Maruku fork (#2401) + * Update Font Awesome to v4.1.0. (#2410) + * Fix broken link on Installation page to Templates page (#2421) + * Prevent table from extending parent width in permalink style table (#2424) + * Add collections to info about pagination support (#2389) + * Add `jekyll_github_sample` plugin to list of third-party plugins (#2463) + * Clarify documentation around front-matter defaults and add details + about defaults for collections. (#2439) + * Add Jekyll Project Version Tag to list of third-party plugins (#2468) + * Use `https` for GitHub links across whole site (#2470) + * Add StickerMule + Jekyll post (#2476) + * Add Jekyll Asset Pipeline Reborn to list of third-party plugins (#2479) + * Add link to jekyll-compress-html to list of third-party plugins (#2514) + * Add Piwigo Gallery to list of third-party plugins (#2526) + * Set `show_drafts` to `false` in default configuration listing (#2536) + * Provide an updated link for Windows installation instructions (#2544) + * Remove `url` from configuration docs (#2547) + * Documentation for Continuous Integration for your Jekyll Site (#2432) + ## 2.0.3 / 2014-05-08 ### Bug Fixes diff --git a/README.markdown b/README.markdown index 704a66b9..124004f1 100644 --- a/README.markdown +++ b/README.markdown @@ -1,11 +1,9 @@ # [Jekyll](http://jekyllrb.com/) -[![Gem Version](https://badge.fury.io/rb/jekyll.svg)](http://badge.fury.io/rb/jekyll) - +[![Gem Version](https://badge.fury.io/rb/jekyll.svg)](https://rubygems.org/gems/jekyll) [![Build Status](https://secure.travis-ci.org/jekyll/jekyll.svg?branch=master)](https://travis-ci.org/jekyll/jekyll) -[![Code Climate](https://codeclimate.com/github/jekyll/jekyll.png)](https://codeclimate.com/github/jekyll/jekyll) +[![Code Climate](http://img.shields.io/codeclimate/github/jekyll/jekyll.svg)](https://codeclimate.com/github/jekyll/jekyll) [![Dependency Status](https://gemnasium.com/jekyll/jekyll.svg)](https://gemnasium.com/jekyll/jekyll) -[![Coverage Status](https://coveralls.io/repos/jekyll/jekyll/badge.png)](https://coveralls.io/r/jekyll/jekyll) By Tom Preston-Werner, Nick Quaranto, and many [awesome contributors](https://github.com/jekyll/jekyll/graphs/contributors)! @@ -19,42 +17,19 @@ Jekyll does what you tell it to do — no more, no less. It doesn't try to outs * [Install](http://jekyllrb.com/docs/installation/) the gem * Read up about its [Usage](http://jekyllrb.com/docs/usage/) and [Configuration](http://jekyllrb.com/docs/configuration/) -* Take a gander at some existing [Sites](http://wiki.github.com/jekyll/jekyll/sites) +* Take a gander at some existing [Sites](https://wiki.github.com/jekyll/jekyll/sites) * Fork and [Contribute](http://jekyllrb.com/docs/contributing/) your own modifications -* Have questions? Check out `#jekyll` on irc.freenode.net. +* Have questions? Check out [`#jekyll` on irc.freenode.net](https://botbot.me/freenode/jekyll/). ## Diving In -* [Migrate](http://jekyllrb.com/docs/migrations/) from your previous system +* [Migrate](http://import.jekyllrb.com/docs/home/) from your previous system * Learn how the [YAML Front Matter](http://jekyllrb.com/docs/frontmatter/) works * Put information on your site with [Variables](http://jekyllrb.com/docs/variables/) * Customize the [Permalinks](http://jekyllrb.com/docs/permalinks/) your posts are generated with * Use the built-in [Liquid Extensions](http://jekyllrb.com/docs/templates/) to make your life easier * Use custom [Plugins](http://jekyllrb.com/docs/plugins/) to generate content specific to your site -## Runtime Dependencies - -* Commander: Command-line interface constructor (Ruby) -* Colorator: Colorizes command line output (Ruby) -* Classifier: Generating related posts (Ruby) -* Directory Watcher: Auto-regeneration of sites (Ruby) -* Kramdown: Default Markdown engine (Ruby) -* Liquid: Templating system (Ruby) -* Pygments.rb: Syntax highlighting (Ruby/Python) -* RedCarpet: Markdown engine (Ruby) -* Safe YAML: YAML Parser built for security (Ruby) - -## Developer Dependencies - -* Launchy: Cross-platform file launcher (Ruby) -* Maruku: Markdown-superset interpreter (Ruby) -* RDiscount: Discount Markdown Processor (Ruby) -* RedCloth: Textile support (Ruby) -* RedGreen: Nicer test output (Ruby) -* RR: Mocking (Ruby) -* Shoulda: Test framework (Ruby) -* SimpleCov: Coverage framework (Ruby) - ## License See [LICENSE](https://github.com/jekyll/jekyll/blob/master/LICENSE). diff --git a/Rakefile b/Rakefile index 8c38b205..e5eb2e8c 100644 --- a/Rakefile +++ b/Rakefile @@ -14,7 +14,7 @@ require 'jekyll/version' ############################################################################# def name - @name ||= Dir['*.gemspec'].first.split('.').first + @name ||= File.basename(Dir['*.gemspec'].first, ".*") end def version @@ -68,14 +68,7 @@ end # ############################################################################# -if ENV["TRAVIS"] == "true" - require 'coveralls/rake/task' - Coveralls::RakeTask.new - - task :default => [:test, :features, 'coveralls:push'] -else - task :default => [:test, :features] -end +task :default => [:test, :features] require 'rake/testtask' Rake::TestTask.new(:test) do |test| @@ -235,6 +228,7 @@ end # ############################################################################# +desc "Release #{name} v#{version}" task :release => :build do unless `git branch` =~ /^\* master$/ puts "You must be on the master branch to release!" @@ -247,6 +241,7 @@ task :release => :build do sh "gem push pkg/#{name}-#{version}.gem" end +desc "Build #{name} v#{version} into pkg/" task :build do mkdir_p "pkg" sh "gem build #{gemspec_file}" diff --git a/docs/jp/CONTRIBUTING.jp.markdown b/docs/jp/CONTRIBUTING.jp.markdown index 1c66d01a..fa8e71cb 100644 --- a/docs/jp/CONTRIBUTING.jp.markdown +++ b/docs/jp/CONTRIBUTING.jp.markdown @@ -7,8 +7,8 @@ * **テストなしではコントリビュートはできません。** * もし、既存の機能への小さな修正やパッチを作成したなら、シンプルなテストを行います。 現在のテストスイートの範囲にとどまり、そして - [Shoulda](http://github.com/thoughtbot/shoulda/tree/master) や - [RR](http://github.com/btakita/rr/tree/master) を使用してください。 + [Shoulda](https://github.com/thoughtbot/shoulda/tree/master) や + [RR](https://github.com/btakita/rr/tree/master) を使用してください。 * もし、それが新しい機能の場合は、必ず新しい [Cucumber](https://github.com/cucumber/cucumber/) の機能を作成し、 必要に応じて手順を再利用します。 diff --git a/docs/jp/README.jp.markdown b/docs/jp/README.jp.markdown index 0085c3f1..2bf7e814 100644 --- a/docs/jp/README.jp.markdown +++ b/docs/jp/README.jp.markdown @@ -5,7 +5,6 @@ [![Build Status](https://secure.travis-ci.org/jekyll/jekyll.png?branch=master)](https://travis-ci.org/jekyll/jekyll) [![Code Climate](https://codeclimate.com/github/jekyll/jekyll.png)](https://codeclimate.com/github/jekyll/jekyll) [![Dependency Status](https://gemnasium.com/jekyll/jekyll.png)](https://gemnasium.com/jekyll/jekyll) -[![Coverage Status](https://coveralls.io/repos/jekyll/jekyll/badge.png)](https://coveralls.io/r/jekyll/jekyll) Tom Preston-Werner, Nick Quaranto や多くの[素晴らしいコントリビュータ](https://github.com/jekyll/jekyll/graphs/contributors)によって作成されています! @@ -28,7 +27,7 @@ Jekyll あなたがするように伝えたことをします ― それ以上 * gem を[インストール](http://jekyllrb.com/docs/installation/)します * [使用方法](http://jekyllrb.com/docs/usage/) と [設定方法](http://jekyllrb.com/docs/configuration/) を読みます -* 既存の [Jekyll で作られたサイト](http://wiki.github.com/jekyll/jekyll/sites) をチラッと見ます +* 既存の [Jekyll で作られたサイト](https://wiki.github.com/jekyll/jekyll/sites) をチラッと見ます * Fork し、あなたの変更を [コントリビュート](http://jekyllrb.com/docs/contributing/) します * 質問があったら? irc.freenode.net の `#jekyll` チャンネルをチェックしてください diff --git a/features/collections.feature b/features/collections.feature index 5c75677f..4f69e8b9 100644 --- a/features/collections.feature +++ b/features/collections.feature @@ -29,6 +29,20 @@ Feature: Collections And I should see "Methods metadata: bar" in "_site/collection_metadata.html" And I should see "

Whatever: foo.bar

" in "_site/methods/configuration.html" + Scenario: Rendered collection at a custom URL + Given I have an "index.html" page that contains "Collections: {{ site.collections }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + methods: + output: true + permalink: /:collection/:path/ + """ + When I run jekyll build + Then the _site directory should exist + And I should see "

Whatever: foo.bar

" in "_site/methods/configuration/index.html" + Scenario: Rendered document in a layout Given I have an "index.html" page that contains "Collections: {{ site.collections }}" And I have a default layout that contains "
Tom Preston-Werner
{{content}}" diff --git a/features/data.feature b/features/data.feature index 45f5554e..4f0e32ed 100644 --- a/features/data.feature +++ b/features/data.feature @@ -60,6 +60,35 @@ Feature: Data And I should see "Jack" in "_site/index.html" And I should see "Leon" in "_site/index.html" + Scenario: autoload *.yaml files in subdirectories in _data directory + Given I have a _data directory + And I have a _data/categories directory + And I have a "_data/categories/dairy.yaml" file with content: + """ + name: Dairy Products + """ + And I have an "index.html" page that contains "{{ site.data.categories.dairy.name }}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Dairy Products" in "_site/index.html" + + Scenario: folders should have precedence over files with the same name + Given I have a _data directory + And I have a _data/categories directory + And I have a "_data/categories/dairy.yaml" file with content: + """ + name: Dairy Products + """ + And I have a "_data/categories.yaml" file with content: + """ + dairy: + name: Should not display this + """ + And I have an "index.html" page that contains "{{ site.data.categories.dairy.name }}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Dairy Products" in "_site/index.html" + Scenario: should be backward compatible with site.data in _config.yml Given I have a "_config.yml" file with content: """ diff --git a/features/frontmatter_defaults.feature b/features/frontmatter_defaults.feature index 3f2d3cd5..41a7a35e 100644 --- a/features/frontmatter_defaults.feature +++ b/features/frontmatter_defaults.feature @@ -77,3 +77,60 @@ Feature: frontmatter defaults Then I should see "a blog by some guy" in "_site/frontmatter.html" And I should see "nothing" in "_site/override.html" But the "_site/perma.html" file should not exist + + Scenario: Use frontmatter defaults in collections + Given I have a _slides directory + And I have a "index.html" file that contains "nothing" + And I have a "_slides/slide1.html" file with content: + """ + Value: {{ page.myval }} + """ + And I have a "_config.yml" file with content: + """ + collections: + slides: + output: true + defaults: + - + scope: + path: "" + type: slides + values: + myval: "Test" + """ + When I run jekyll build + Then the _site directory should exist + And I should see "Value: Test" in "_site/slides/slide1.html" + + Scenario: Override frontmatter defaults inside a collection + Given I have a _slides directory + And I have a "index.html" file that contains "nothing" + And I have a "_slides/slide2.html" file with content: + """ + --- + myval: Override + --- + Value: {{ page.myval }} + """ + And I have a "_config.yml" file with content: + """ + collections: + slides: + output: true + defaults: + - + scope: + path: "" + type: slides + values: + myval: "Test" + """ + When I run jekyll build + Then the _site directory should exist + And I should see "Value: Override" in "_site/slides/slide2.html" + + Scenario: Deep merge frontmatter defaults + Given I have an "index.html" page with fruit "{orange: 1}" that contains "Fruits: {{ page.fruit.orange | plus: page.fruit.apple }}" + And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {fruit: {apple: 2}}}]" + When I run jekyll build + Then I should see "Fruits: 3" in "_site/index.html" diff --git a/features/post_data.feature b/features/post_data.feature index 6d92b19e..c72f52fa 100644 --- a/features/post_data.feature +++ b/features/post_data.feature @@ -70,6 +70,18 @@ Feature: Post data Then the _site directory should exist And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + Scenario: Use post.categories variable when category is in a folder and has categories in YAML + Given I have a movies directory + And I have a movies/_posts directory + And I have a _layouts directory + And I have the following post in "movies": + | title | date | layout | categories | content | + | Star Wars | 2009-03-27 | simple | [film] | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then the _site directory should exist + And I should see "Post category: movies" in "_site/movies/film/2009/03/27/star-wars.html" + Scenario: Use post.tags variable Given I have a _posts directory And I have a _layouts directory diff --git a/features/rendering.feature b/features/rendering.feature new file mode 100644 index 00000000..5f71e2cd --- /dev/null +++ b/features/rendering.feature @@ -0,0 +1,34 @@ +Feature: Rendering + 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 + But I want to make it as simply as possible + So render with Liquid and place in Layouts + + Scenario: Render Liquid and place in layout + Given I have a "index.html" page with layout "simple" that contains "Hi there, Jekyll {{ jekyll.environment }}!" + And I have a simple layout that contains "{{ content }}Ahoy, indeed!" + When I run jekyll build + Then the _site directory should exist + And I should see "Hi there, Jekyll development!\nAhoy, indeed" in "_site/index.html" + + Scenario: Don't place asset files in layout + Given I have an "index.scss" page with layout "simple" that contains ".foo-bar { color:black; }" + And I have an "index.coffee" page with layout "simple" that contains "whatever()" + And I have a simple layout that contains "{{ content }}Ahoy, indeed!" + When I run jekyll build + Then the _site directory should exist + And I should not see "Ahoy, indeed!" in "_site/index.css" + And I should not see "Ahoy, indeed!" in "_site/index.js" + + Scenario: Don't render liquid in Sass + Given I have an "index.scss" page that contains ".foo-bar { color:{{site.color}}; }" + When I run jekyll build + Then the _site directory should not exist + And I should see "Invalid CSS after" in the build output + + Scenario: Don't render liquid in CoffeeScript + Given I have an "index.coffee" page that contains "hey='for {{site.animal}}'" + When I run jekyll build + Then the _site directory should exist + And I should see "hey = 'for {{site.animal}}';" in "_site/index.js" diff --git a/features/site_configuration.feature b/features/site_configuration.feature index f42f3833..8aaa9e1d 100644 --- a/features/site_configuration.feature +++ b/features/site_configuration.feature @@ -273,3 +273,11 @@ Feature: Site configuration And I should see "Whatever" in "_site/index.html" And the "_site/test.txt" file should exist And I should see "this is a test" in "_site/test.txt" + + Scenario: arbitrary file reads via layouts + Given I have an "index.html" page with layout "page" that contains "FOO" + And I have a "_config.yml" file that contains "layouts: '../../../../../../../../../../../../../../usr/include'" + When I run jekyll build + Then the _site directory should exist + And I should see "FOO" in "_site/index.html" + And I should not see " " in "_site/index.html" diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb index 4fe7edd2..b0cb4c87 100644 --- a/features/step_definitions/jekyll_steps.rb +++ b/features/step_definitions/jekyll_steps.rb @@ -141,7 +141,7 @@ end When /^I run jekyll(.*)$/ do |args| status = run_jekyll(args) - if !status || args.include?("--verbose") + if args.include?("--verbose") || ENV['DEBUG'] puts jekyll_run_output end end @@ -195,3 +195,7 @@ end Then /^I should see today's date in "(.*)"$/ do |file| assert_match Regexp.new(Date.today.to_s), file_contents(file) end + +Then /^I should see "(.*)" in the build output$/ do |text| + assert_match Regexp.new(text), jekyll_run_output +end diff --git a/features/support/env.rb b/features/support/env.rb index 29875629..2f5f5e5f 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,6 +1,3 @@ -require 'coveralls' -Coveralls.wear_merged! - require 'fileutils' require 'rr' require 'test/unit' diff --git a/jekyll.gemspec b/jekyll.gemspec index 71929103..c6617308 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.authors = ["Tom Preston-Werner"] s.email = 'tom@mojombo.com' - s.homepage = 'http://github.com/jekyll/jekyll' + s.homepage = 'https://github.com/jekyll/jekyll' s.files = `git ls-files`.split($/) s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } @@ -28,18 +28,20 @@ Gem::Specification.new do |s| s.rdoc_options = ["--charset=UTF-8"] s.extra_rdoc_files = %w[README.markdown LICENSE] - s.add_runtime_dependency('liquid', "~> 2.5.5") + s.add_runtime_dependency('liquid', "~> 2.6.1") s.add_runtime_dependency('classifier', "~> 1.3") - s.add_runtime_dependency('listen', "~> 2.5") s.add_runtime_dependency('kramdown', "~> 1.3") - s.add_runtime_dependency('pygments.rb', "~> 0.5.0") + s.add_runtime_dependency('pygments.rb', "~> 0.6.0") s.add_runtime_dependency('mercenary', "~> 0.3.3") s.add_runtime_dependency('safe_yaml', "~> 1.0") s.add_runtime_dependency('colorator', "~> 0.1") s.add_runtime_dependency('redcarpet', "~> 3.1") s.add_runtime_dependency('toml', '~> 0.1.0') + s.add_runtime_dependency('jekyll-paginate', '~> 1.0') + s.add_runtime_dependency('jekyll-gist', '~> 1.0') s.add_runtime_dependency('jekyll-coffeescript', '~> 1.0') s.add_runtime_dependency('jekyll-sass-converter', '~> 1.0') + s.add_runtime_dependency('jekyll-watch', '~> 1.0') s.add_development_dependency('rake', "~> 10.1") s.add_development_dependency('rdoc', "~> 3.11") @@ -53,7 +55,6 @@ Gem::Specification.new do |s| s.add_development_dependency('launchy', "~> 2.3") s.add_development_dependency('simplecov', "~> 0.7") s.add_development_dependency('simplecov-gem-adapter', "~> 1.0.1") - s.add_development_dependency('coveralls', "~> 0.7.0") s.add_development_dependency('mime-types', "~> 1.5") s.add_development_dependency('activesupport', '~> 3.2.13') s.add_development_dependency('jekyll_test_plugin') diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 941084d5..b36569b3 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -21,6 +21,7 @@ require 'time' require 'safe_yaml/load' require 'English' require 'pathname' +require 'logger' # 3rd party require 'liquid' @@ -31,6 +32,7 @@ require 'toml' # internal requires require 'jekyll/version' require 'jekyll/utils' +require 'jekyll/log_adapter' require 'jekyll/stevenson' require 'jekyll/deprecator' require 'jekyll/configuration' @@ -72,10 +74,21 @@ require_all 'jekyll/tags' # plugins require 'jekyll-coffeescript' require 'jekyll-sass-converter' +require 'jekyll-paginate' +require 'jekyll-gist' SafeYAML::OPTIONS[:suppress_warnings] = true module Jekyll + + # Public: Tells you which Jekyll environment you are building in so you can skip tasks + # if you need to. This is useful when doing expensive compression tasks on css and + # images and allows you to skip that when working in development. + + def self.env + ENV["JEKYLL_ENV"] || "development" + end + # Public: Generate a Jekyll configuration Hash by merging the default # options with anything in _config.yml, and adding the given options on top. # @@ -106,7 +119,11 @@ module Jekyll end def self.logger - @logger ||= Stevenson.new + @logger ||= LogAdapter.new(Stevenson.new) + end + + def self.logger=(writer) + @logger = LogAdapter.new(writer) end # Public: File system root diff --git a/lib/jekyll/cleaner.rb b/lib/jekyll/cleaner.rb index 583fc844..0aa3cdfd 100644 --- a/lib/jekyll/cleaner.rb +++ b/lib/jekyll/cleaner.rb @@ -30,7 +30,7 @@ module Jekyll def existing_files files = Set.new Dir.glob(File.join(site.dest, "**", "*"), File::FNM_DOTMATCH) do |file| - files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex + files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex || keep_dirs.include?(file) end files end @@ -49,7 +49,19 @@ module Jekyll # # Returns a Set with the directory paths def new_dirs - new_files.map { |file| File.dirname(file) }.to_set + new_files.map { |file| parent_dirs(file) }.flatten.to_set + end + + # Private: The list of parent directories of a given file + # + # Returns an Array with the directory paths + def parent_dirs(file) + parent_dir = File.dirname(file) + if parent_dir == site.dest + [] + else + [parent_dir] + parent_dirs(parent_dir) + end end # Private: The list of existing files that will be replaced by a directory during build @@ -59,6 +71,14 @@ module Jekyll new_dirs.select { |dir| File.file?(dir) }.to_set end + # Private: The list of directories that need to be kept because they are parent directories + # of files specified in keep_files + # + # Returns a Set with the directory paths + def keep_dirs + site.keep_files.map{|file| parent_dirs(File.join(site.dest, file))}.flatten.to_set + end + # Private: Creates a regular expression from the config's keep_files array # # Examples diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb index dcb7b50d..8db6c54c 100644 --- a/lib/jekyll/collection.rb +++ b/lib/jekyll/collection.rb @@ -52,7 +52,7 @@ module Jekyll def filtered_entries return Array.new unless exists? Dir.chdir(directory) do - entry_filter.filter(entries) + entry_filter.filter(entries).reject { |f| File.directory?(f) } end end @@ -105,7 +105,7 @@ module Jekyll # # Returns a sanitized version of the label. def sanitize_label(label) - label.gsub(/[^a-z0-9_\-]/i, '') + label.gsub(/[^a-z0-9_\-\.]/i, '') end # Produce a representation of this Collection for use in Liquid. @@ -132,6 +132,13 @@ module Jekyll !!metadata['output'] end + # The URL template to render collection's documents at. + # + # Returns the URL template to render collection's documents at. + def url_template + metadata.fetch('permalink', "/:collection/:path:output_ext") + end + # Extract options for this collection from the site configuration. # # Returns the metadata for this collection diff --git a/lib/jekyll/command.rb b/lib/jekyll/command.rb index 62a532f6..654f0ae3 100644 --- a/lib/jekyll/command.rb +++ b/lib/jekyll/command.rb @@ -19,19 +19,29 @@ module Jekyll super(base) end - # Listing of all directories (globbed to include subfiles and folders) + # Paths to ignore for the watch option # - # source - the source path - # destination - the destination path + # options - A Hash of options passed to the command # - # Returns an Array of directory globs in the source, excluding the destination - def globs(source, destination) - Dir.chdir(source) do - dirs = Dir['*'].select { |x| File.directory?(x) } - dirs -= [destination, File.expand_path(destination), File.basename(destination)] - dirs = dirs.map { |x| "#{x}/**/*" } - dirs += ['*'] + # Returns a list of relative paths from source that should be ignored + def ignore_paths(options) + source = options['source'] + destination = options['destination'] + config_files = Configuration[options].config_files(options) + paths = config_files + Array(destination) + ignored = [] + + source_abs = Pathname.new(source).expand_path + paths.each do |p| + path_abs = Pathname.new(p).expand_path + begin + rel_path = path_abs.relative_path_from(source_abs).to_s + ignored << Regexp.new(Regexp.escape(rel_path)) unless rel_path.start_with?('../') + rescue ArgumentError + # Could not find a relative path + end end + ignored end # Run Site#process and catch errors diff --git a/lib/jekyll/commands/build.rb b/lib/jekyll/commands/build.rb index 54c774ab..3f187601 100644 --- a/lib/jekyll/commands/build.rb +++ b/lib/jekyll/commands/build.rb @@ -22,19 +22,23 @@ module Jekyll # Build your jekyll site # Continuously watch if `watch` is set to true in the config. def process(options) + Jekyll.logger.log_level = :error if options['quiet'] + options = configuration_from_options(options) site = Jekyll::Site.new(options) - Jekyll.logger.log_level = :error if options['quiet'] - - build(site, options) + if options.fetch('skip_initial_build', false) + Jekyll.logger.warn "Build Warning:", "Skipping the initial build. This may result in an out-of-date site." + else + build(site, options) + end watch(site, options) if options['watch'] end # Build your Jekyll site. # # site - the Jekyll::Site instance to build - # options - the + # options - A Hash of options passed to the command # # Returns nothing. def build(site, options) @@ -54,49 +58,8 @@ module Jekyll # # Returns nothing. def watch(site, options) - require 'listen' - - source = options['source'] - destination = options['destination'] - - begin - dest = Pathname.new(destination).relative_path_from(Pathname.new(source)).to_s - ignored = Regexp.new(Regexp.escape(dest)) - rescue ArgumentError - # Destination is outside the source, no need to ignore it. - ignored = nil - end - - listener = Listen.to( - source, - :ignore => ignored, - :force_polling => options['force_polling'] - ) do |modified, added, removed| - t = Time.now.strftime("%Y-%m-%d %H:%M:%S") - n = modified.length + added.length + removed.length - print Jekyll.logger.formatted_topic("Regenerating:") + "#{n} files at #{t} " - begin - process_site(site) - puts "...done." - rescue => e - puts "...error:" - Jekyll.logger.warn "Error:", e.message - Jekyll.logger.warn "Error:", "Run jekyll build --trace for more information." - end - end - listener.start - - Jekyll.logger.info "Auto-regeneration:", "enabled" - - unless options['serving'] - trap("INT") do - listener.stop - puts " Halting auto-regeneration." - exit 0 - end - - loop { sleep 1000 } - end + require 'jekyll-watch' + Jekyll::Commands::Watch.watch(site, options) end end # end of class << self diff --git a/lib/jekyll/commands/serve.rb b/lib/jekyll/commands/serve.rb index 0f6c6d37..2fbfd74c 100644 --- a/lib/jekyll/commands/serve.rb +++ b/lib/jekyll/commands/serve.rb @@ -17,6 +17,7 @@ module Jekyll c.option 'port', '-P', '--port [PORT]', 'Port to listen on' c.option 'host', '-H', '--host [HOST]', 'Host to bind to' c.option 'baseurl', '-b', '--baseurl [URL]', 'Base URL' + c.option 'skip_initial_build', '--skip-initial-build', 'Skips the initial site build which occurs before the server is started.' c.action do |args, options| options["serving"] ||= true diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb index 0c68365a..6f5cbc15 100644 --- a/lib/jekyll/configuration.rb +++ b/lib/jekyll/configuration.rb @@ -40,6 +40,7 @@ module Jekyll 'markdown_ext' => 'markdown,mkdown,mkdn,mkd,md', 'textile_ext' => 'textile', + 'quiet' => false, 'port' => '4000', 'host' => '0.0.0.0', @@ -103,6 +104,10 @@ module Jekyll override['source'] || self['source'] || DEFAULTS['source'] end + def quiet?(override = {}) + override['quiet'] || self['quiet'] || DEFAULTS['quiet'] + end + def safe_load_file(filename) case File.extname(filename) when '.toml' @@ -120,10 +125,16 @@ module Jekyll # # Returns an Array of config files def config_files(override) + # Be quiet quickly. + Jekyll.logger.log_level = :error if quiet?(override) + # Get configuration from /_config.yml or / config_files = override.delete('config') if config_files.to_s.empty? - config_files = File.join(source(override), "_config.yml") + default = %w[yml yaml].find(Proc.new { 'yml' }) do |ext| + File.exists? Jekyll.sanitized_path(source(override), "_config.#{ext}") + end + config_files = Jekyll.sanitized_path(source(override), "_config.#{default}") @default_config_file = true end config_files = [config_files] unless config_files.is_a? Array @@ -231,6 +242,7 @@ module Jekyll " as a list of comma-separated values." config[option] = csv_to_array(config[option]) end + config[option].map!(&:to_s) end if config.fetch('markdown', 'kramdown').to_s.downcase.eql?("maruku") diff --git a/lib/jekyll/converters/markdown/redcarpet_parser.rb b/lib/jekyll/converters/markdown/redcarpet_parser.rb index df18327b..2c5bb2f8 100644 --- a/lib/jekyll/converters/markdown/redcarpet_parser.rb +++ b/lib/jekyll/converters/markdown/redcarpet_parser.rb @@ -67,8 +67,15 @@ module Jekyll end when 'rouge' Class.new(Redcarpet::Render::HTML) do - require 'rouge' - require 'rouge/plugins/redcarpet' + begin + require 'rouge' + require 'rouge/plugins/redcarpet' + rescue LoadError => e + Jekyll.logger.error "You are missing the 'rouge' gem. Please run:" + Jekyll.logger.error " $ [sudo] gem install rouge" + Jekyll.logger.error "Or add 'rouge' to your Gemfile." + raise FatalException.new("Missing dependency: rouge") + end if Rouge.version < '1.3.0' abort "Please install Rouge 1.3.0 or greater and try running Jekyll again." diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index 9d00d703..8e9700b2 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -43,7 +43,7 @@ module Jekyll # Returns nothing. def read_yaml(base, name, opts = {}) begin - self.content = File.read(File.join(base, name), + self.content = File.read(Jekyll.sanitized_path(base, name), merged_file_read_opts(opts)) if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m self.content = $POSTMATCH @@ -114,6 +114,10 @@ module Jekyll Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data) end + # The type of a document, + # i.e., its classname downcase'd and to_sym'd. + # + # Returns the type of self. def type if is_a?(Post) :post @@ -124,6 +128,31 @@ module Jekyll end end + # Determine whether the document is an asset file. + # Asset files include CoffeeScript files and Sass/SCSS files. + # + # Returns true if the extname belongs to the set of extensions + # that asset files use. + def asset_file? + %w[.sass .scss .coffee].include?(ext) + end + + # Determine whether the file should be rendered with Liquid. + # + # Returns false if the document is either an asset file or a yaml file, + # true otherwise. + def render_with_liquid? + !asset_file? + end + + # Determine whether the file should be placed into layouts. + # + # Returns false if the document is either an asset file or a yaml file, + # true otherwise. + def place_in_layout? + !asset_file? + end + # Recursively render layouts # # layouts - a list of the layouts @@ -167,13 +196,13 @@ module Jekyll payload["highlighter_prefix"] = converter.highlighter_prefix payload["highlighter_suffix"] = converter.highlighter_suffix - self.content = render_liquid(content, payload, info) + self.content = render_liquid(content, payload, info) if render_with_liquid? transform # output keeps track of what will finally be written self.output = content - render_all_layouts(layouts, payload, info) + render_all_layouts(layouts, payload, info) if place_in_layout? end # Write the generated page file to the destination directory. diff --git a/lib/jekyll/deprecator.rb b/lib/jekyll/deprecator.rb index 7600cd29..47f03b70 100644 --- a/lib/jekyll/deprecator.rb +++ b/lib/jekyll/deprecator.rb @@ -19,7 +19,7 @@ module Jekyll def self.no_subcommand(args) if args.size > 0 && args.first =~ /^--/ && !%w[--help --version].include?(args.first) Jekyll.logger.error "Deprecation:", "Jekyll now uses subcommands instead of just \ - switches. Run `jekyll help' to find out more." + switches. Run `jekyll --help' to find out more." end end diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb index be4ead20..f1fcdf2a 100644 --- a/lib/jekyll/document.rb +++ b/lib/jekyll/document.rb @@ -88,11 +88,19 @@ module Jekyll !(asset_file? || yaml_file?) end + # Determine whether the file should be placed into layouts. + # + # Returns false if the document is either an asset file or a yaml file, + # true otherwise. + def place_in_layout? + !(asset_file? || yaml_file?) + end + # The URL template where the document would be accessible. # # Returns the URL template for the document. def url_template - "/:collection/:path:output_ext" + collection.url_template end # Construct a Hash of key-value pairs which contain a mapping between @@ -168,6 +176,8 @@ module Jekyll end # Read in the file and assign the content and data based on the file contents. + # Merge the frontmatter of the file with the frontmatter default + # values # # Returns nothing. def read(opts = {}) @@ -175,10 +185,17 @@ module Jekyll @data = SafeYAML.load_file(path) else begin + defaults = @site.frontmatter_defaults.all(url, collection.label.to_sym) + unless defaults.empty? + @data = defaults + end @content = File.read(path, merged_file_read_opts(opts)) if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m @content = $POSTMATCH - @data = SafeYAML.load($1) + data_file = SafeYAML.load($1) + unless data_file.nil? + @data = Utils.deep_merge_hashes(defaults, data_file) + end end rescue SyntaxError => e puts "YAML Exception reading #{path}: #{e.message}" @@ -198,7 +215,8 @@ module Jekyll "content" => content, "path" => path, "relative_path" => relative_path, - "url" => url + "url" => url, + "collection" => collection.label } else data diff --git a/lib/jekyll/excerpt.rb b/lib/jekyll/excerpt.rb index 61327eb8..130b2880 100644 --- a/lib/jekyll/excerpt.rb +++ b/lib/jekyll/excerpt.rb @@ -26,7 +26,7 @@ module Jekyll end def to_liquid - post.to_liquid(Post::EXCERPT_ATTRIBUTES_FOR_LIQUID) + post.to_liquid(post.class::EXCERPT_ATTRIBUTES_FOR_LIQUID) end # Fetch YAML front-matter data from related post, without layout key diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb index 5e96061f..002d926f 100644 --- a/lib/jekyll/filters.rb +++ b/lib/jekyll/filters.rb @@ -207,9 +207,8 @@ module Jekyll when nils == "last" order = + 1 else - Jekyll.logger.error "Invalid nils order:", - "'#{nils}' is not a valid nils order. It must be 'first' or 'last'." - exit(1) + raise ArgumentError.new("Invalid nils order: " + + "'#{nils}' is not a valid nils order. It must be 'first' or 'last'.") end input.sort { |apple, orange| @@ -234,7 +233,7 @@ module Jekyll input when String Time.parse(input) rescue Time.at(input.to_i) - when Number + when Numeric Time.at(input) else Jekyll.logger.error "Invalid Date:", "'#{input}' is not a valid datetime." @@ -247,7 +246,9 @@ module Jekyll end def item_property(item, property) - if item.respond_to?(:data) + if item.respond_to?(:to_liquid) + item.to_liquid[property.to_s] + elsif item.respond_to?(:data) item.data[property.to_s] else item[property.to_s] diff --git a/lib/jekyll/frontmatter_defaults.rb b/lib/jekyll/frontmatter_defaults.rb index 2c5755b5..42b85ed5 100644 --- a/lib/jekyll/frontmatter_defaults.rb +++ b/lib/jekyll/frontmatter_defaults.rb @@ -41,10 +41,10 @@ module Jekyll old_scope = nil matching_sets(path, type).each do |set| if has_precedence?(old_scope, set['scope']) - defaults.merge! set['values'] + defaults = Utils.deep_merge_hashes(defaults, set['values']) old_scope = set['scope'] else - defaults = set['values'].merge(defaults) + defaults = Utils.deep_merge_hashes(set['values'], defaults) end end defaults @@ -145,4 +145,4 @@ module Jekyll end end end -end \ No newline at end of file +end diff --git a/lib/jekyll/generators/pagination.rb b/lib/jekyll/generators/pagination.rb deleted file mode 100644 index 72dc5292..00000000 --- a/lib/jekyll/generators/pagination.rb +++ /dev/null @@ -1,217 +0,0 @@ -module Jekyll - module Generators - class Pagination < Generator - # This generator is safe from arbitrary code execution. - safe true - - # This generator should be passive with regard to its execution - priority :lowest - - # Generate paginated pages if necessary. - # - # site - The Site. - # - # Returns nothing. - def generate(site) - if Pager.pagination_enabled?(site) - if template = template_page(site) - paginate(site, template) - else - Jekyll.logger.warn "Pagination:", "Pagination is enabled, but I couldn't find " + - "an index.html page to use as the pagination template. Skipping pagination." - end - end - end - - # Paginates the blog's posts. Renders the index.html file into paginated - # directories, e.g.: page2/index.html, page3/index.html, etc and adds more - # site-wide data. - # - # site - The Site. - # page - 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, num_page, all_posts, pages) - if num_page > 1 - newpage = Page.new(site, site.source, page.dir, page.name) - newpage.pager = pager - newpage.dir = Pager.paginate_path(site, num_page) - site.pages << newpage - else - page.pager = pager - end - end - end - - # Static: Fetch the URL of the template page. Used to determine the - # path to the first pager in the series. - # - # site - the Jekyll::Site object - # - # Returns the url of the template page - def self.first_page_url(site) - if page = Pagination.new.template_page(site) - page.url - else - nil - end - end - - # Public: Find the Jekyll::Page which will act as the pager template - # - # site - the Jekyll::Site object - # - # Returns the Jekyll::Page which will act as the pager template - def template_page(site) - site.pages.dup.select do |page| - Pager.pagination_candidate?(site.config, page) - end.sort do |one, two| - two.path.size <=> one.path.size - end.first - end - end - end - - class Pager - attr_reader :page, :per_page, :posts, :total_posts, :total_pages, - :previous_page, :previous_page_path, :next_page, :next_page_path - - # Calculate the number of pages. - # - # all_posts - The Array of all Posts. - # per_page - The Integer of entries per page. - # - # Returns the Integer number of pages. - def self.calculate_pages(all_posts, per_page) - (all_posts.size.to_f / per_page.to_i).ceil - end - - # Determine if pagination is enabled the site. - # - # site - the Jekyll::Site object - # - # Returns true if pagination is enabled, false otherwise. - def self.pagination_enabled?(site) - !site.config['paginate'].nil? && - site.pages.size > 0 - end - - # Static: Determine if a page is a possible candidate to be a template page. - # Page's name must be `index.html` and exist in any of the directories - # between the site source and `paginate_path`. - # - # config - the site configuration hash - # page - the Jekyll::Page about which we're inquiring - # - # Returns true if the - def self.pagination_candidate?(config, page) - page_dir = File.dirname(File.expand_path(remove_leading_slash(page.path), config['source'])) - paginate_path = remove_leading_slash(config['paginate_path']) - paginate_path = File.expand_path(paginate_path, config['source']) - page.name == 'index.html' && - in_hierarchy(config['source'], page_dir, File.dirname(paginate_path)) - end - - # Determine if the subdirectories of the two paths are the same relative to source - # - # source - the site source - # page_dir - the directory of the Jekyll::Page - # paginate_path - the absolute paginate path (from root of FS) - # - # Returns whether the subdirectories are the same relative to source - def self.in_hierarchy(source, page_dir, paginate_path) - return false if paginate_path == File.dirname(paginate_path) - return false if paginate_path == Pathname.new(source).parent - page_dir == paginate_path || - in_hierarchy(source, page_dir, File.dirname(paginate_path)) - end - - # Static: Return the pagination path of the page - # - # site - the Jekyll::Site object - # num_page - the pagination page number - # - # Returns the pagination path as a string - def self.paginate_path(site, num_page) - return nil if num_page.nil? - return Generators::Pagination.first_page_url(site) if num_page <= 1 - format = site.config['paginate_path'] - format = format.sub(':num', num_page.to_s) - ensure_leading_slash(format) - end - - # Static: Return a String version of the input which has a leading slash. - # If the input already has a forward slash in position zero, it will be - # returned unchanged. - # - # path - a String path - # - # Returns the path with a leading slash - def self.ensure_leading_slash(path) - path[0..0] == "/" ? path : "/#{path}" - end - - # Static: Return a String version of the input without a leading slash. - # - # path - a String path - # - # Returns the input without the leading slash - def self.remove_leading_slash(path) - ensure_leading_slash(path)[1..-1] - end - - # Initialize a new Pager. - # - # site - the Jekyll::Site object - # page - The Integer page number. - # all_posts - The Array of all the site's Posts. - # num_pages - The Integer number of pages or nil if you'd like the number - # of pages calculated. - def initialize(site, page, all_posts, num_pages = nil) - @page = page - @per_page = site.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 - @previous_page_path = Pager.paginate_path(site, @previous_page) - @next_page = @page != @total_pages ? @page + 1 : nil - @next_page_path = Pager.paginate_path(site, @next_page) - end - - # Convert this Pager's data to a Hash suitable for use by Liquid. - # - # Returns the Hash representation of this Pager. - def to_liquid - { - 'page' => page, - 'per_page' => per_page, - 'posts' => posts, - 'total_posts' => total_posts, - 'total_pages' => total_pages, - 'previous_page' => previous_page, - 'previous_page_path' => previous_page_path, - 'next_page' => next_page, - 'next_page_path' => next_page_path - } - end - end -end diff --git a/lib/jekyll/log_adapter.rb b/lib/jekyll/log_adapter.rb new file mode 100644 index 00000000..63e23fcd --- /dev/null +++ b/lib/jekyll/log_adapter.rb @@ -0,0 +1,102 @@ +module Jekyll + class LogAdapter + attr_reader :writer + + LOG_LEVELS = { + :debug => ::Logger::DEBUG, + :info => ::Logger::INFO, + :warn => ::Logger::WARN, + :error => ::Logger::ERROR + } + + # Public: Create a new instance of Jekyll's log writer + # + # writer - Logger compatible instance + # log_level - (optional, symbol) the log level + # + # Returns nothing + def initialize(writer, level = :info) + @writer = writer + self.log_level = level + end + + # Public: Set the log level on the writer + # + # level - (symbol) the log level + # + # Returns nothing + def log_level=(level) + writer.level = LOG_LEVELS.fetch(level) + end + + # Public: Print a jekyll debug message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def debug(topic, message = nil) + writer.debug(message(topic, message)) + end + + # Public: Print a jekyll message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def info(topic, message = nil) + writer.info(message(topic, message)) + end + + # Public: Print a jekyll message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def warn(topic, message = nil) + writer.warn(message(topic, message)) + end + + # Public: Print a jekyll error message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def error(topic, message = nil) + writer.error(message(topic, message)) + end + + # Public: Print a Jekyll error message and immediately abort the process + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail (can be omitted) + # + # Returns nothing + def abort_with(topic, message = nil) + error(topic, message) + abort + end + + # Internal: Build a Jekyll topic method + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns the formatted message + def message(topic, message) + formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ') + end + + # Internal: Format the topic + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # + # Returns the formatted topic statement + def formatted_topic(topic) + "#{topic} ".rjust(20) + end + end +end diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb index 13217bf0..f13d8b2c 100644 --- a/lib/jekyll/post.rb +++ b/lib/jekyll/post.rb @@ -77,10 +77,10 @@ module Jekyll end def populate_categories - if categories.empty? - self.categories = Utils.pluralized_array_from_hash(data, 'category', 'categories').map {|c| c.to_s.downcase} - end - categories.flatten! + categories_from_data = Utils.pluralized_array_from_hash(data, 'category', 'categories') + self.categories = ( + Array(categories) + categories_from_data + ).map {|c| c.to_s.downcase}.flatten.uniq end def populate_tags @@ -256,7 +256,7 @@ module Jekyll # construct payload payload = Utils.deep_merge_hashes({ "site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) }, - "page" => to_liquid(EXCERPT_ATTRIBUTES_FOR_LIQUID) + "page" => to_liquid(self.class::EXCERPT_ATTRIBUTES_FOR_LIQUID) }, site_payload) if generate_excerpt? diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb index e3d233df..f918cc0d 100644 --- a/lib/jekyll/renderer.rb +++ b/lib/jekyll/renderer.rb @@ -47,11 +47,15 @@ module Jekyll output = render_liquid(output, payload, info) end - place_in_layouts( - convert(output), - payload, - info - ) + if document.place_in_layout? + place_in_layouts( + convert(output), + payload, + info + ) + else + convert(output) + end end # Convert the given content using the converters which match this renderer's document. diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 5f1580bb..1015d5d9 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -195,18 +195,34 @@ module Jekyll # # Returns nothing def read_data(dir) - base = File.join(source, dir) - return unless File.directory?(base) && (!safe || !File.symlink?(base)) + base = Jekyll.sanitized_path(source, dir) + read_data_to(base, self.data) + end - entries = Dir.chdir(base) { Dir['*.{yaml,yml,json}'] } - entries.delete_if { |e| File.directory?(File.join(base, e)) } + # Read and parse all yaml files under and add them to the + # variable. + # + # dir - The string absolute path of the directory to read. + # data - The variable to which data will be added. + # + # Returns nothing + def read_data_to(dir, data) + return unless File.directory?(dir) && (!safe || !File.symlink?(dir)) + + entries = Dir.chdir(dir) do + Dir['*.{yaml,yml,json}'] + Dir['*'].select { |fn| File.directory?(fn) } + end entries.each do |entry| - path = File.join(source, dir, entry) + path = Jekyll.sanitized_path(dir, entry) next if File.symlink?(path) && safe key = sanitize_filename(File.basename(entry, '.*')) - self.data[key] = SafeYAML.load_file(path) + if File.directory?(path) + read_data_to(path, data[key] = {}) + else + data[key] = SafeYAML.load_file(path) + end end end @@ -314,19 +330,23 @@ module Jekyll # "tags" - The Hash of tag values and Posts. # See Site#post_attr_hash for type info. def site_payload - {"jekyll" => { "version" => Jekyll::VERSION }, - "site" => Utils.deep_merge_hashes(config, - Utils.deep_merge_hashes(Hash[collections.map{|label, coll| [label, coll.docs]}], { - "time" => time, - "posts" => posts.sort { |a, b| b <=> a }, - "pages" => pages, - "static_files" => static_files.sort { |a, b| a.relative_path <=> b.relative_path }, - "html_pages" => pages.reject { |page| !page.html? }, - "categories" => post_attr_hash('categories'), - "tags" => post_attr_hash('tags'), - "collections" => collections, - "documents" => documents, - "data" => site_data + { + "jekyll" => { + "version" => Jekyll::VERSION, + "environment" => Jekyll.env + }, + "site" => Utils.deep_merge_hashes(config, + Utils.deep_merge_hashes(Hash[collections.map{|label, coll| [label, coll.docs]}], { + "time" => time, + "posts" => posts.sort { |a, b| b <=> a }, + "pages" => pages, + "static_files" => static_files.sort { |a, b| a.relative_path <=> b.relative_path }, + "html_pages" => pages.select { |page| page.html? || page.url.end_with?("/") }, + "categories" => post_attr_hash('categories'), + "tags" => post_attr_hash('tags'), + "collections" => collections, + "documents" => documents, + "data" => site_data })) } end diff --git a/lib/jekyll/static_file.rb b/lib/jekyll/static_file.rb index dfbc9fba..2382d03c 100644 --- a/lib/jekyll/static_file.rb +++ b/lib/jekyll/static_file.rb @@ -59,6 +59,7 @@ module Jekyll @@mtimes[path] = mtime FileUtils.mkdir_p(File.dirname(dest_path)) + FileUtils.rm(dest_path) if File.exist?(dest_path) FileUtils.cp(path, dest_path) true diff --git a/lib/jekyll/stevenson.rb b/lib/jekyll/stevenson.rb index b50368c1..9a9f412e 100644 --- a/lib/jekyll/stevenson.rb +++ b/lib/jekyll/stevenson.rb @@ -1,102 +1,58 @@ module Jekyll - class Stevenson - attr_accessor :log_level - - LOG_LEVELS = { - debug: 0, - info: 1, - warn: 2, - error: 3 - } - - # Public: Create a new instance of Stevenson, Jekyll's logger - # - # level - (optional, symbol) the log level - # - # Returns nothing - def initialize(level = :info) - @log_level = level + class Stevenson < ::Logger + def initialize + @progname = nil + @level = DEBUG + @default_formatter = Formatter.new + @logdev = $stdout + @formatter = proc do |severity, datetime, progname, msg| + "#{msg}" + end end - # Public: Print a jekyll debug message to stdout - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def debug(topic, message = nil) - $stdout.puts(message(topic, message)) if should_log(:debug) + def add(severity, message = nil, progname = nil, &block) + severity ||= UNKNOWN + @logdev = set_logdevice(severity) + + if @logdev.nil? or severity < @level + return true + end + progname ||= @progname + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + @logdev.puts( + format_message(format_severity(severity), Time.now, progname, message)) + true end - # Public: Print a jekyll message to stdout - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def info(topic, message = nil) - $stdout.puts(message(topic, message)) if should_log(:info) + # Log a +WARN+ message + def warn(progname = nil, &block) + add(WARN, nil, progname.yellow, &block) end - # Public: Print a jekyll message to stderr - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def warn(topic, message = nil) - $stderr.puts(message(topic, message).yellow) if should_log(:warn) + # Log an +ERROR+ message + def error(progname = nil, &block) + add(ERROR, nil, progname.red, &block) end - # Public: Print a jekyll error message to stderr - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns nothing - def error(topic, message = nil) - $stderr.puts(message(topic, message).red) if should_log(:error) + def close + # No LogDevice in use end - # Public: Print a Jekyll error message to stderr and immediately abort the process - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail (can be omitted) - # - # Returns nothing - def abort_with(topic, message = nil) - error(topic, message) - abort - end + private - # Public: Build a Jekyll topic method - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # message - the message detail - # - # Returns the formatted message - def message(topic, message) - formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ') - end - - # Public: Format the topic - # - # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. - # - # Returns the formatted topic statement - def formatted_topic(topic) - "#{topic} ".rjust(20) - end - - # Public: Determine whether the current log level warrants logging at the - # proposed level. - # - # level_of_message - the log level of the message (symbol) - # - # Returns true if the log level of the message is greater than or equal to - # this logger's log level. - def should_log(level_of_message) - LOG_LEVELS.fetch(log_level) <= LOG_LEVELS.fetch(level_of_message) + def set_logdevice(severity) + if severity > INFO + $stderr + else + $stdout + end end end end diff --git a/lib/jekyll/tags/gist.rb b/lib/jekyll/tags/gist.rb deleted file mode 100644 index 977933dc..00000000 --- a/lib/jekyll/tags/gist.rb +++ /dev/null @@ -1,47 +0,0 @@ -# Gist Liquid Tag -# -# Example: -# {% gist username/1234567 %} -# {% gist username/1234567 file.rb %} - -module Jekyll - class GistTag < Liquid::Tag - - def render(context) - if tag_contents = determine_arguments(@markup.strip) - gist_id, filename = tag_contents[0], tag_contents[1] - gist_script_tag(gist_id, filename) - else - raise ArgumentError.new <<-eos -Syntax error in tag 'gist' while parsing the following markup: - - #{@markup} - -Valid syntax: - for all gists: {% gist user/1234567 %} -eos - end - end - - private - - def determine_arguments(input) - matched = if input.include?("/") - input.match(/\A([a-zA-Z0-9\/\-_]+) ?(\S*)\Z/) - else - input.match(/\A(\d+) ?(\S*)\Z/) - end - [matched[1].strip, matched[2].strip] if matched && matched.length >= 3 - end - - def gist_script_tag(gist_id, filename = nil) - if filename.empty? - "" - else - "" - end - end - end -end - -Liquid::Template.register_tag('gist', Jekyll::GistTag) diff --git a/lib/jekyll/tags/highlight.rb b/lib/jekyll/tags/highlight.rb index 29a6ea0a..f7ca2dcd 100644 --- a/lib/jekyll/tags/highlight.rb +++ b/lib/jekyll/tags/highlight.rb @@ -4,9 +4,11 @@ module Jekyll include Liquid::StandardFilters # The regular expression syntax checker. Start with the language specifier. - # Follow that by zero or more space separated options that take one of two - # forms: name or name=value - SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=\w+)?)*)$/ + # Follow that by zero or more space separated options that take one of three + # forms: name, name=value, or name="" + # + # is a space-separated list of numbers + SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$/ def initialize(tag_name, markup, tokens) super @@ -14,8 +16,14 @@ module Jekyll @lang = $1.downcase @options = {} if defined?($2) && $2 != '' - $2.split.each do |opt| + # Split along 3 possible forms -- key="", key=value, or key + $2.scan(/(?:\w="[^"]*"|\w=\w|\w)+/) do |opt| key, value = opt.split('=') + # If a quoted list, convert to array + if value && value.include?("\"") + value.gsub!(/"/, "") + value = value.split + end @options[key.to_sym] = value || true end end @@ -83,7 +91,7 @@ eos def add_code_tag(code) # Add nested tags to code blocks - code = code.sub(/
\n*/,'
')
+        code = code.sub(/
\n*/,'
')
         code = code.sub(/\n*<\/pre>/,"
") code.strip end diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb index 8cd47242..f4ea4d33 100644 --- a/lib/jekyll/url.rb +++ b/lib/jekyll/url.rb @@ -89,7 +89,7 @@ module Jekyll # pct-encoded = "%" HEXDIG HEXDIG # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" # / "*" / "+" / "," / ";" / "=" - URI.escape(path, /[^a-zA-Z\d\-._~!$&\'()*+,;=:@\/]/) + URI.escape(path, /[^a-zA-Z\d\-._~!$&\'()*+,;=:@\/]/).encode('utf-8') end # Unescapes a URL path segment @@ -103,7 +103,7 @@ module Jekyll # # Returns the unescaped path. def self.unescape_path(path) - URI.unescape(path) + URI.unescape(path.encode('utf-8')) end end end diff --git a/lib/jekyll/utils.rb b/lib/jekyll/utils.rb index dc19a3e4..e1b4704d 100644 --- a/lib/jekyll/utils.rb +++ b/lib/jekyll/utils.rb @@ -35,17 +35,25 @@ module Jekyll # # Returns an array def pluralized_array_from_hash(hash, singular_key, plural_key) - if hash.has_key?(singular_key) - array = [hash[singular_key]] if hash[singular_key] - elsif hash.has_key?(plural_key) - case hash[plural_key] + [].tap do |array| + array << (value_from_singular_key(hash, singular_key) || value_from_plural_key(hash, plural_key)) + end.flatten.compact + end + + def value_from_singular_key(hash, key) + hash[key] if (hash.has_key?(key) || (hash.default_proc && hash[key])) + end + + def value_from_plural_key(hash, key) + if hash.has_key?(key) || (hash.default_proc && hash[key]) + val = hash[key] + case val when String - array = hash[plural_key].split + val.split when Array - array = hash[plural_key].compact + val.compact end end - array || [] end def transform_keys(hash) diff --git a/lib/jekyll/version.rb b/lib/jekyll/version.rb index 18840a56..b61af28b 100644 --- a/lib/jekyll/version.rb +++ b/lib/jekyll/version.rb @@ -1,3 +1,3 @@ module Jekyll - VERSION = '2.0.3' + VERSION = '2.1.0' end diff --git a/lib/site_template/_config.yml b/lib/site_template/_config.yml index 2c0d7379..ae45638b 100644 --- a/lib/site_template/_config.yml +++ b/lib/site_template/_config.yml @@ -4,6 +4,8 @@ email: your-email@domain.com description: "Write an awesome description for your new site here. You can edit this line in _config.yml. It will appear in your document head meta (for Google search results) and in your feed.xml site description." baseurl: "" url: "http://yourdomain.com" +twitter_username: jekyllrb +github_username: jekyll # Build settings markdown: kramdown diff --git a/lib/site_template/_includes/footer.html b/lib/site_template/_includes/footer.html index e9082174..0026c4c5 100644 --- a/lib/site_template/_includes/footer.html +++ b/lib/site_template/_includes/footer.html @@ -13,8 +13,8 @@ diff --git a/lib/site_template/_includes/header.html b/lib/site_template/_includes/header.html index 36ff6cc6..e5e6f26a 100644 --- a/lib/site_template/_includes/header.html +++ b/lib/site_template/_includes/header.html @@ -18,7 +18,7 @@
{% for page in site.pages %} - {{ page.title }} + {% if page.title %}{{ page.title }}{% endif %} {% endfor %}
diff --git a/lib/site_template/css/main.css b/lib/site_template/css/main.css index b94ca8fb..88090a3e 100644 --- a/lib/site_template/css/main.css +++ b/lib/site_template/css/main.css @@ -234,6 +234,7 @@ a:visited { color: #205caa; } -moz-border-radius: 3px; border-radius: 3px; font-size: 15px; + overflow:scroll; } .post code { padding: 1px 5px; } diff --git a/lib/site_template/feed.xml b/lib/site_template/feed.xml index 234d1c0b..4d7f8a4c 100644 --- a/lib/site_template/feed.xml +++ b/lib/site_template/feed.xml @@ -8,13 +8,22 @@ layout: none {{ site.description | xml_escape }} {{ site.url }}{{ site.baseurl }}/ + {{ site.time | date_to_rfc822 }} + {{ site.time | date_to_rfc822 }} + Jekyll v{{ jekyll.version }} {% for post in site.posts limit:10 %} {{ post.title | xml_escape }} {{ post.content | xml_escape }} - {{ post.date | date: "%a, %d %b %Y %H:%M:%S %z" }} + {{ post.date | date_to_rfc822 }} {{ post.url | prepend: site.baseurl | prepend: site.url }} {{ post.url | prepend: site.baseurl | prepend: site.url }} + {% for tag in post.tags %} + {{ tag | xml_escape }} + {% endfor %} + {% for cat in post.categories %} + {{ cat | xml_escape }} + {% endfor %} {% endfor %} diff --git a/site/_config.yml b/site/_config.yml index 44c2772e..0d4dacfd 100644 --- a/site/_config.yml +++ b/site/_config.yml @@ -7,3 +7,4 @@ excerpt_separator: noifniof3nioaniof3nioafafinoafnoif repository: https://github.com/jekyll/jekyll help_url: https://github.com/jekyll/jekyll-help google_analytics_id: UA-50755011-1 +timezone: America/Los_Angeles diff --git a/site/_data/docs.yml b/site/_data/docs.yml index 7d0a83f3..474f73fa 100644 --- a/site/_data/docs.yml +++ b/site/_data/docs.yml @@ -31,6 +31,7 @@ docs: - github-pages - deployment-methods + - continuous-integration - title: Miscellaneous docs: diff --git a/site/_includes/anchor_links.html b/site/_includes/anchor_links.html index e9802071..08e37b5b 100644 --- a/site/_includes/anchor_links.html +++ b/site/_includes/anchor_links.html @@ -1,27 +1,32 @@ - diff --git a/site/_includes/css/font-awesome.css b/site/_includes/css/font-awesome.css index f864c27c..86435ecd 100644 --- a/site/_includes/css/font-awesome.css +++ b/site/_includes/css/font-awesome.css @@ -1,11 +1,11 @@ /*! - * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ @font-face { font-family: 'FontAwesome'; - src: url('../fonts/fontawesome-webfont.eot?v=4.0.3'); - src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg'); + src: url('../fonts/fontawesome-webfont.eot?v=4.1.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } diff --git a/site/_includes/css/style.css b/site/_includes/css/style.css index e65c9065..18bc8330 100644 --- a/site/_includes/css/style.css +++ b/site/_includes/css/style.css @@ -590,6 +590,10 @@ article h2:first-child { filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9e2812', endColorstr='#6f0d0d',GradientType=0 ); } +.post-content img { + max-width: 100% +} + .label { float: left; text-transform: uppercase; @@ -695,6 +699,7 @@ pre, code { } .highlight { + margin: 1em 0; padding: 10px 0; width: 100%; overflow: auto; diff --git a/site/_includes/news_contents.html b/site/_includes/news_contents.html index f22a1a1a..5c545211 100644 --- a/site/_includes/news_contents.html +++ b/site/_includes/news_contents.html @@ -10,7 +10,7 @@

Recent Releases

+

Other News

+
    + {% for post in site.posts %} + {% unless post.categories contains 'release' %} +
  • + {{ post.title }} +
  • + {% endunless %} + {% endfor %} +
diff --git a/site/_includes/top.html b/site/_includes/top.html index ffeddd9d..35ba2f26 100644 --- a/site/_includes/top.html +++ b/site/_includes/top.html @@ -10,7 +10,6 @@ - {% include anchor_links.html %}