mirror of
https://github.com/jekyll/jekyll.git
synced 2026-04-28 03:01:03 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf71631c34 | ||
|
|
6c0e4b37b9 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -6,10 +6,3 @@ pkg/
|
||||
*~
|
||||
_site/
|
||||
.bundle/
|
||||
.DS_Store
|
||||
bbin/
|
||||
gh-pages/
|
||||
site/_site/
|
||||
coverage
|
||||
.ruby-version
|
||||
.sass-cache
|
||||
|
||||
27
.travis.yml
27
.travis.yml
@@ -1,27 +0,0 @@
|
||||
language: ruby
|
||||
cache: bundler
|
||||
install:
|
||||
- script/rebund download
|
||||
- travis_retry bundle install --path vendor/bundle
|
||||
rvm:
|
||||
- 2.1.1
|
||||
- 2.0.0
|
||||
- 1.9.3
|
||||
script: script/cibuild
|
||||
after_script:
|
||||
- script/rebund upload
|
||||
notifications:
|
||||
irc:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
channels:
|
||||
- irc.freenode.org#jekyll
|
||||
template:
|
||||
- '%{repository}#%{build_number} (%{branch}) %{message} %{build_url}'
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: never
|
||||
env:
|
||||
global:
|
||||
- secure: bt5nglPTdsc0N5fB1dOJz2WbM81dGpDuVD8PnhEsxgUfoo6xavhU4+pNrUADlSUqQ1aJrdU+MKW4x+JZ2ZnJS8vOpNzRymuMZSbFaljK4pgFGiKFgBdMKxVikvoYcxKCjLAl7NJZ11W6hUw+JtJScClDZwrJJAQB6I7Isp/LsdM=
|
||||
- secure: Ym8nx7nbfGYGo47my92M+deJykaiMkdZdb615EO51liv/xy/0aQ919Jpfieugc9d3zVnm+zFGPbpv4YzRpsik6OlVBNa4lP+BnQ27ptf5YcLWD8Hksi7845WFLecXMoaTCoYer/TvYZsIWJb2nSDMH9qbfZhnd1YZKuvUpK0rEU=
|
||||
@@ -1,91 +0,0 @@
|
||||
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.**
|
||||
* 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
|
||||
[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
|
||||
where appropriate. Also, whipping up some documentation in your fork's `site`
|
||||
would be appreciated, and once merged it will be transferred over to the main
|
||||
`site`, jekyllrb.com.
|
||||
* If your contribution changes any Jekyll behavior, make sure to update the
|
||||
documentation. It lives in `site/docs`. If the docs are missing information,
|
||||
please feel free to add it in. Great docs make a great project!
|
||||
* Please follow the [GitHub Ruby Styleguide](https://github.com/styleguide/ruby)
|
||||
when modifying Ruby code.
|
||||
* Please do your best to submit **small pull requests**. The easier the proposed
|
||||
change is to review, the more likely it will be merged.
|
||||
* When submitting a pull request, please make judicious use of the pull request
|
||||
body. A description of what changes were made, the motivations behind the
|
||||
changes and [any tasks completed or left to complete](http://git.io/gfm-tasks)
|
||||
will also speed up review time.
|
||||
|
||||
Test Dependencies
|
||||
-----------------
|
||||
|
||||
To run the test suite and build the gem you'll need to install Jekyll's
|
||||
dependencies. Jekyll uses Bundler, so a quick run of the bundle command and
|
||||
you're all set!
|
||||
|
||||
$ bundle
|
||||
|
||||
Before you start, run the tests and make sure that they pass (to confirm your
|
||||
environment is configured properly):
|
||||
|
||||
$ bundle exec rake test
|
||||
$ bundle exec rake features
|
||||
|
||||
Workflow
|
||||
--------
|
||||
|
||||
Here's the most direct way to get your work merged into the project:
|
||||
|
||||
* Fork the project.
|
||||
* Clone down your fork ( `git clone git@github.com:<username>/jekyll.git` ).
|
||||
* Create a topic branch to contain your change ( `git checkout -b my_awesome_feature` ).
|
||||
* Hack away, add tests. Not necessarily in that order.
|
||||
* Make sure everything still passes by running `rake`.
|
||||
* If necessary, rebase your commits into logical chunks, without errors.
|
||||
* Push the branch up ( `git push origin my_awesome_feature` ).
|
||||
* Create a pull request against jekyll/jekyll and describe what your change
|
||||
does and the why you think it should be merged.
|
||||
|
||||
Updating Documentation
|
||||
----------------------
|
||||
|
||||
We want the Jekyll documentation to be the best it can be. We've
|
||||
open-sourced our docs and we welcome any pull requests if you find it
|
||||
lacking.
|
||||
|
||||
You can find the documentation for jekyllrb.com in the
|
||||
[site](https://github.com/jekyll/jekyll/tree/master/site) directory of
|
||||
Jekyll's repo on GitHub.com.
|
||||
|
||||
All documentation pull requests should be directed at `master`. Pull
|
||||
requests directed at another branch will not be accepted.
|
||||
|
||||
The [Jekyll wiki](https://github.com/jekyll/jekyll/wiki) on GitHub
|
||||
can be freely updated without a pull request as all GitHub users have access.
|
||||
|
||||
Gotchas
|
||||
-------
|
||||
|
||||
* If you want to bump the gem version, please put that in a separate commit.
|
||||
This way, the maintainers can control when the gem gets released.
|
||||
* Try to keep your patch(es) based from the latest commit on jekyll/jekyll.
|
||||
The easier it is to apply your work, the less work the maintainers have to do,
|
||||
which is always a good thing.
|
||||
* Please don't tag your GitHub issue with [fix], [feature], etc. The maintainers
|
||||
actively read the issues and will label it once they come across it.
|
||||
|
||||
Finally...
|
||||
----------
|
||||
|
||||
Thanks! Hacking on Jekyll should be fun. If you find any of this hard to figure
|
||||
out, let us know so we can improve our process or documentation!
|
||||
1176
History.markdown
1176
History.markdown
File diff suppressed because it is too large
Load Diff
294
History.txt
Normal file
294
History.txt
Normal file
@@ -0,0 +1,294 @@
|
||||
== HEAD
|
||||
* Major Enhancements
|
||||
* Add command line importer functionality (#253)
|
||||
* Minor Enhancements
|
||||
* Switch to Albino gem
|
||||
* Bundler support
|
||||
* Use English library to avoid hoops (#292)
|
||||
* Add Posterous importer (#254)
|
||||
* Fixes for Wordpress importer (#274, #252, #271)
|
||||
* Better error message for invalid post date (#291)
|
||||
* Print formatted fatal exceptions to stdout on build failure
|
||||
* Bug Fixes
|
||||
* Secure additional path exploits
|
||||
|
||||
== 0.10.0 / 2010-12-16
|
||||
* Bug Fixes
|
||||
* Add --no-server option.
|
||||
|
||||
== 0.9.0 / 2010-12-15
|
||||
* Minor Enhancements
|
||||
* Use OptionParser's [no-] functionality for better boolean parsing.
|
||||
* Add Drupal migrator (#245)
|
||||
* Complain about YAML and Liquid errors (#249)
|
||||
* Remove orphaned files during regeneration (#247)
|
||||
* Add Marley migrator (#28)
|
||||
|
||||
== 0.8.0 / 2010-11-22
|
||||
* Minor Enhancements
|
||||
* Add wordpress.com importer (#207)
|
||||
* Add --limit-posts cli option (#212)
|
||||
* Add uri_escape filter (#234)
|
||||
* Add --base-url cli option (#235)
|
||||
* Improve MT migrator (#238)
|
||||
* Add kramdown support (#239)
|
||||
* Bug Fixes
|
||||
* Fixed filename basename generation (#208)
|
||||
* Set mode to UTF8 on Sequel connections (#237)
|
||||
* Prevent _includes dir from being a symlink
|
||||
|
||||
== 0.7.0 / 2010-08-24
|
||||
* Minor Enhancements
|
||||
* Add support for rdiscount extensions (#173)
|
||||
* Bug Fixes
|
||||
* Highlight should not be able to render local files
|
||||
* The site configuration may not always provide a 'time' setting (#184)
|
||||
|
||||
== 0.6.2 / 2010-06-25
|
||||
* Bug Fixes
|
||||
* Fix Rakefile 'release' task (tag pushing was missing origin)
|
||||
* Ensure that RedCloth is loaded when textilize filter is used (#183)
|
||||
* Expand source, destination, and plugin paths (#180)
|
||||
* Fix page.url to include full relative path (#181)
|
||||
|
||||
== 0.6.1 / 2010-06-24
|
||||
* Bug Fixes
|
||||
* Fix Markdown Pygments prefix and suffix (#178)
|
||||
|
||||
== 0.6.0 / 2010-06-23
|
||||
* Major Enhancements
|
||||
* Proper plugin system (#19, #100)
|
||||
* Add safe mode so unsafe converters/generators can be added
|
||||
* Maruku is now the only processor dependency installed by default.
|
||||
Other processors will be lazy-loaded when necessary (and prompt the
|
||||
user to install them when necessary) (#57)
|
||||
* 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)
|
||||
* Allow URL to be set via command line option (#147)
|
||||
* Bug Fixes
|
||||
* Render highlighted code for non markdown/textile pages (#116)
|
||||
* 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)
|
||||
* Fix source directory binding using Dir.pwd (#75)
|
||||
|
||||
== 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.posts.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]
|
||||
|
||||
== 0.5.2 / 2009-06-24
|
||||
* Enhancements
|
||||
* Added --paginate option to the executable along with a paginator object
|
||||
for the payload [github.com/calavera]
|
||||
* Upgraded RedCloth to 4.2.1, which makes <notextile> tags work once
|
||||
again.
|
||||
* Configuration options set in config.yml are now available through the
|
||||
site payload [github.com/vilcans]
|
||||
* Posts can now have an empty YAML front matter or none at all
|
||||
[github.com/bahuvrihi]
|
||||
* Bug Fixes
|
||||
* 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]
|
||||
* 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]
|
||||
* 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]
|
||||
* 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]
|
||||
* 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]
|
||||
* 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]
|
||||
* Ignore Emacs autosave files [github.com/Chrononaut]
|
||||
* Bug Fixes
|
||||
* 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]
|
||||
* 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]
|
||||
|
||||
== 0.4.1
|
||||
* Minor Enhancements
|
||||
* Changed date format on wordpress converter (zeropadding)
|
||||
[github.com/dysinger]
|
||||
* Bug Fixes
|
||||
* Add jekyll binary as executable to gemspec [github.com/dysinger]
|
||||
|
||||
== 0.4.0 / 2009-02-03
|
||||
* Major Enhancements
|
||||
* Switch to Jeweler for packaging tasks
|
||||
* Minor Enhancements
|
||||
* Type importer [github.com/codeslinger]
|
||||
* site.topics accessor [github.com/baz]
|
||||
* 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]
|
||||
* 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]
|
||||
* 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]
|
||||
* Minor Enhancements
|
||||
* 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]
|
||||
* 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]
|
||||
* Properly reject directories in _layouts
|
||||
|
||||
== 0.2.1 / 2008-12-15
|
||||
* Major Changes
|
||||
* Use Maruku (pure Ruby) for Markdown by default [github.com/mreid]
|
||||
* Allow use of RDiscount with --rdiscount flag
|
||||
* Minor Enhancements
|
||||
* Don't load directory_watcher unless it's needed [github.com/pjhyett]
|
||||
|
||||
== 0.2.0 / 2008-12-14
|
||||
* Major Changes
|
||||
* related_posts is now found in site.related_posts
|
||||
|
||||
== 0.1.6 / 2008-12-13
|
||||
* Major Features
|
||||
* Include files in _includes with {% include x.textile %}
|
||||
|
||||
== 0.1.5 / 2008-12-12
|
||||
* Major Features
|
||||
* 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]
|
||||
* Bug Fixes
|
||||
* 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
|
||||
* Bug Fixes
|
||||
* DATA does not work properly with rubygems
|
||||
|
||||
== 0.1.3 / 2008-12-06
|
||||
* Major Features
|
||||
* Markdown support [github.com/vanpelt]
|
||||
* Mephisto and CSV converters [github.com/vanpelt]
|
||||
* Code hilighting [github.com/vanpelt]
|
||||
* Autobuild
|
||||
* Bug Fixes
|
||||
* Accept both \r\n and \n in YAML header [github.com/vanpelt]
|
||||
|
||||
== 0.1.2 / 2008-11-22
|
||||
* Major Features
|
||||
* Add a real "related posts" implementation using Classifier
|
||||
* Command Line Changes
|
||||
* Allow cli to be called with 0, 1, or 2 args intuiting dir paths
|
||||
if they are omitted
|
||||
|
||||
== 0.1.1 / 2008-11-22
|
||||
* Minor Additions
|
||||
* Posts now support introspectional data e.g. {{ page.url }}
|
||||
|
||||
== 0.1.0 / 2008-11-05
|
||||
* First release
|
||||
* Converts posts written in Textile
|
||||
* Converts regular site pages
|
||||
* Simple copy of binary files
|
||||
|
||||
== 0.0.0 / 2008-10-19
|
||||
* Birthday!
|
||||
|
||||
4
LICENSE
4
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2008-2014 Tom Preston-Werner
|
||||
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
|
||||
@@ -18,4 +18,4 @@ 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.
|
||||
SOFTWARE.
|
||||
@@ -1,60 +0,0 @@
|
||||
# [Jekyll](http://jekyllrb.com/)
|
||||
|
||||
[](http://badge.fury.io/rb/jekyll)
|
||||
|
||||
[](https://travis-ci.org/jekyll/jekyll)
|
||||
[](https://codeclimate.com/github/jekyll/jekyll)
|
||||
[](https://gemnasium.com/jekyll/jekyll)
|
||||
[](https://coveralls.io/r/jekyll/jekyll)
|
||||
|
||||
By Tom Preston-Werner, Nick Quaranto, and many [awesome contributors](https://github.com/jekyll/jekyll/graphs/contributors)!
|
||||
|
||||
Jekyll is a simple, blog-aware, static site generator perfect for personal, project, or organization sites. Think of it like a file-based CMS, without all the complexity. Jekyll takes your content, renders Markdown and Liquid templates, and spits out a complete, static website ready to be served by Apache, Nginx or another web server. Jekyll is the engine behind [GitHub Pages](http://pages.github.com), which you can use to host sites right from your GitHub repositories.
|
||||
|
||||
## Philosophy
|
||||
|
||||
Jekyll does what you tell it to do — no more, no less. It doesn't try to outsmart users by making bold assumptions, nor does it burden them with needless complexity and configuration. Put simply, Jekyll gets out of your way and allows you to concentrate on what truly matters: your content.
|
||||
|
||||
## Getting Started
|
||||
|
||||
* [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)
|
||||
* Fork and [Contribute](http://jekyllrb.com/docs/contributing/) your own modifications
|
||||
* Have questions? Check out `#jekyll` on irc.freenode.net.
|
||||
|
||||
## Diving In
|
||||
|
||||
* [Migrate](http://jekyllrb.com/docs/migrations/) 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).
|
||||
40
README.textile
Normal file
40
README.textile
Normal file
@@ -0,0 +1,40 @@
|
||||
h1. Jekyll
|
||||
|
||||
By Tom Preston-Werner, Nick Quaranto, and many awesome contributors!
|
||||
|
||||
Jekyll is a simple, blog aware, static site generator. It takes a template directory (representing the raw form of a website), runs it through Textile or Markdown and Liquid converters, and spits out a complete, static website suitable for serving with Apache or your favorite web server. This is also the engine behind "GitHub Pages":http://pages.github.com, which you can use to host your project's page or blog right here from GitHub.
|
||||
|
||||
h2. Getting Started
|
||||
|
||||
* "Install":http://wiki.github.com/mojombo/jekyll/install the gem
|
||||
* Read up about its "Usage":http://wiki.github.com/mojombo/jekyll/usage and "Configuration":http://wiki.github.com/mojombo/jekyll/configuration
|
||||
* Take a gander at some existing "Sites":http://wiki.github.com/mojombo/jekyll/sites
|
||||
* Fork and "Contribute":http://wiki.github.com/mojombo/jekyll/contribute your own modifications
|
||||
* Have questions? Post them on the "Mailing List":http://groups.google.com/group/jekyll-rb
|
||||
|
||||
h2. Diving In
|
||||
|
||||
* "Migrate":http://wiki.github.com/mojombo/jekyll/blog-migrations from your previous system
|
||||
* Learn how the "YAML Front Matter":http://wiki.github.com/mojombo/jekyll/yaml-front-matter works
|
||||
* Put information on your site with "Template Data":http://wiki.github.com/mojombo/jekyll/template-data
|
||||
* Customize the "Permalinks":http://wiki.github.com/mojombo/jekyll/permalinks your posts are generated with
|
||||
* Use the built-in "Liquid Extensions":http://wiki.github.com/mojombo/jekyll/liquid-extensions to make your life easier
|
||||
|
||||
h2. Runtime Dependencies
|
||||
|
||||
* 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)
|
||||
* Pygments: Syntax highlighting (Python)
|
||||
|
||||
h2. Developer Dependencies
|
||||
|
||||
* Shoulda: Test framework (Ruby)
|
||||
* RR: Mocking (Ruby)
|
||||
* RedGreen: Nicer test output (Ruby)
|
||||
|
||||
h2. License
|
||||
|
||||
See LICENSE.
|
||||
241
Rakefile
241
Rakefile
@@ -1,11 +1,6 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rdoc'
|
||||
require 'date'
|
||||
require 'yaml'
|
||||
|
||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[lib]))
|
||||
require 'jekyll/version'
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
@@ -18,7 +13,16 @@ def name
|
||||
end
|
||||
|
||||
def version
|
||||
Jekyll::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
|
||||
@@ -29,37 +33,8 @@ def gem_file
|
||||
"#{name}-#{version}.gem"
|
||||
end
|
||||
|
||||
def normalize_bullets(markdown)
|
||||
markdown.gsub(/\n\s{2}\*{1}/, "\n-")
|
||||
end
|
||||
|
||||
def linkify_prs(markdown)
|
||||
markdown.gsub(/#(\d+)/) do |word|
|
||||
"[#{word}]({{ site.repository }}/issues/#{word.delete("#")})"
|
||||
end
|
||||
end
|
||||
|
||||
def linkify_users(markdown)
|
||||
markdown.gsub(/(@\w+)/) do |username|
|
||||
"[#{username}](https://github.com/#{username.delete("@")})"
|
||||
end
|
||||
end
|
||||
|
||||
def linkify(markdown)
|
||||
linkify_users(linkify_prs(markdown))
|
||||
end
|
||||
|
||||
def liquid_escape(markdown)
|
||||
markdown.gsub(/(`{[{%].+[}%]}`)/, "{% raw %}\\1{% endraw %}")
|
||||
end
|
||||
|
||||
def remove_head_from_history(markdown)
|
||||
index = markdown =~ /^##\s+\d+\.\d+\.\d+/
|
||||
markdown[index..-1]
|
||||
end
|
||||
|
||||
def converted_history(markdown)
|
||||
remove_head_from_history(liquid_escape(linkify(normalize_bullets(markdown))))
|
||||
def replace_header(head, header_name)
|
||||
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
||||
end
|
||||
|
||||
#############################################################################
|
||||
@@ -68,14 +43,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|
|
||||
@@ -84,7 +52,15 @@ Rake::TestTask.new(:test) do |test|
|
||||
test.verbose = true
|
||||
end
|
||||
|
||||
require 'rdoc/task'
|
||||
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'
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = "#{name} #{version}"
|
||||
@@ -92,21 +68,6 @@ Rake::RDocTask.new do |rdoc|
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
Cucumber::Rake::Task.new(:features) do |t|
|
||||
t.profile = "travis"
|
||||
end
|
||||
Cucumber::Rake::Task.new(:"features:html", "Run Cucumber features and produce HTML output") do |t|
|
||||
t.profile = "html_report"
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'Cucumber rake task not available'
|
||||
task :features do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
||||
desc "Open an irb session preloaded with this library"
|
||||
task :console do
|
||||
sh "irb -rubygems -r ./lib/#{name}.rb"
|
||||
@@ -114,118 +75,34 @@ end
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Site tasks - http://jekyllrb.com
|
||||
# Custom tasks (add your own tasks here)
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
namespace :site do
|
||||
desc "Generate and view the site locally"
|
||||
task :preview do
|
||||
require "launchy"
|
||||
|
||||
# Yep, it's a hack! Wait a few seconds for the Jekyll site to generate and
|
||||
# then open it in a browser. Someday we can do better than this, I hope.
|
||||
Thread.new do
|
||||
sleep 4
|
||||
puts "Opening in browser..."
|
||||
Launchy.open("http://localhost:4000")
|
||||
end
|
||||
|
||||
# Generate the site in server mode.
|
||||
puts "Running Jekyll..."
|
||||
Dir.chdir("site") do
|
||||
sh "#{File.expand_path('bin/jekyll', File.dirname(__FILE__))} serve --watch"
|
||||
end
|
||||
namespace :migrate do
|
||||
desc "Migrate from mephisto in the current directory"
|
||||
task :mephisto do
|
||||
sh %q(ruby -r './lib/jekyll/migrators/mephisto' -e 'Jekyll::Mephisto.postgres(:database => "#{ENV["DB"]}")')
|
||||
end
|
||||
|
||||
desc "Update normalize.css library to the latest version and minify"
|
||||
task :update_normalize_css do
|
||||
Dir.chdir("site/_includes/css") do
|
||||
sh 'curl "http://necolas.github.io/normalize.css/latest/normalize.css" -o "normalize.scss"'
|
||||
sh 'sass "normalize.scss":"normalize.css" --style compressed'
|
||||
sh 'rm "normalize.scss"'
|
||||
end
|
||||
desc "Migrate from Movable Type in the current directory"
|
||||
task :mt do
|
||||
sh %q(ruby -r './lib/jekyll/migrators/mt' -e 'Jekyll::MT.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
|
||||
end
|
||||
|
||||
desc "Commit the local site to the gh-pages branch and publish to GitHub Pages"
|
||||
task :publish => [:history] do
|
||||
# Ensure the gh-pages dir exists so we can generate into it.
|
||||
puts "Checking for gh-pages dir..."
|
||||
unless File.exist?("./gh-pages")
|
||||
puts "No gh-pages directory found. Run the following commands first:"
|
||||
puts " `git clone git@github.com:jekyll/jekyll gh-pages"
|
||||
puts " `cd gh-pages"
|
||||
puts " `git checkout gh-pages`"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Ensure gh-pages branch is up to date.
|
||||
Dir.chdir('gh-pages') do
|
||||
sh "git pull origin gh-pages"
|
||||
end
|
||||
|
||||
# Copy to gh-pages dir.
|
||||
puts "Copying site to gh-pages branch..."
|
||||
Dir.glob("site/*") do |path|
|
||||
next if path.include? "_site"
|
||||
sh "cp -R #{path} gh-pages/"
|
||||
end
|
||||
|
||||
# Commit and push.
|
||||
puts "Committing and pushing to GitHub Pages..."
|
||||
sha = `git log`.match(/[a-z0-9]{40}/)[0]
|
||||
Dir.chdir('gh-pages') do
|
||||
sh "git add ."
|
||||
sh "git commit --allow-empty -m 'Updating to #{sha}.'"
|
||||
sh "git push origin gh-pages"
|
||||
end
|
||||
puts 'Done.'
|
||||
desc "Migrate from Typo in the current directory"
|
||||
task :typo do
|
||||
sh %q(ruby -r './lib/jekyll/migrators/typo' -e 'Jekyll::Typo.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
|
||||
end
|
||||
end
|
||||
|
||||
desc "Create a nicely formatted history page for the jekyll site based on the repo history."
|
||||
task :history do
|
||||
if File.exist?("History.markdown")
|
||||
history_file = File.read("History.markdown")
|
||||
front_matter = {
|
||||
"layout" => "docs",
|
||||
"title" => "History",
|
||||
"permalink" => "/docs/history/",
|
||||
"prev_section" => "contributing"
|
||||
}
|
||||
Dir.chdir('site/docs/') do
|
||||
File.open("history.md", "w") do |file|
|
||||
file.write("#{front_matter.to_yaml}---\n\n")
|
||||
file.write(converted_history(history_file))
|
||||
end
|
||||
end
|
||||
else
|
||||
abort "You seem to have misplaced your History.markdown file. I can haz?"
|
||||
end
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
Cucumber::Rake::Task.new(:features) do |t|
|
||||
t.cucumber_opts = "--format progress"
|
||||
end
|
||||
|
||||
namespace :releases do
|
||||
desc "Create new release post"
|
||||
task :new, :version do |t, args|
|
||||
raise "Specify a version: rake site:releases:new['1.2.3']" unless args.version
|
||||
today = Time.new.strftime('%Y-%m-%d')
|
||||
release = args.version.to_s
|
||||
filename = "site/_posts/#{today}-jekyll-#{release.split('.').join('-')}-released.markdown"
|
||||
|
||||
File.open(filename, "wb") do |post|
|
||||
post.puts("---")
|
||||
post.puts("layout: news_item")
|
||||
post.puts("title: 'Jekyll #{release} Released'")
|
||||
post.puts("date: #{Time.new.strftime('%Y-%m-%d %H:%M:%S %z')}")
|
||||
post.puts("author: ")
|
||||
post.puts("version: #{release}")
|
||||
post.puts("categories: [release]")
|
||||
post.puts("---")
|
||||
post.puts
|
||||
post.puts
|
||||
end
|
||||
|
||||
puts "Created #{filename}"
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'Cucumber rake task not available'
|
||||
task :features do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -240,15 +117,43 @@ task :release => :build do
|
||||
puts "You must be on the master branch to release!"
|
||||
exit!
|
||||
end
|
||||
sh "git commit --allow-empty -m 'Release #{version}'"
|
||||
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
||||
sh "git tag v#{version}"
|
||||
sh "git push origin master"
|
||||
sh "git push origin v#{version}"
|
||||
sh "gem push pkg/#{name}-#{version}.gem"
|
||||
end
|
||||
|
||||
task :build do
|
||||
mkdir_p "pkg"
|
||||
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
|
||||
288
bin/jekyll
288
bin/jekyll
@@ -1,40 +1,276 @@
|
||||
#!/usr/bin/env ruby
|
||||
STDOUT.sync = true
|
||||
|
||||
$:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
|
||||
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
||||
|
||||
help = <<HELP
|
||||
Jekyll is a blog-aware, static site generator.
|
||||
|
||||
Basic Command Line Usage:
|
||||
jekyll # . -> ./_site
|
||||
jekyll <path to write generated site> # . -> <path>
|
||||
jekyll <path to source> <path to write generated site> # <path> -> <path>
|
||||
jekyll import <importer name> <options> # imports posts using named import script
|
||||
|
||||
Configuration is read from '<source>/_config.yml' but can be overriden
|
||||
using the following options:
|
||||
|
||||
HELP
|
||||
|
||||
require 'optparse'
|
||||
require 'jekyll'
|
||||
require 'mercenary'
|
||||
|
||||
%w[jekyll-import].each do |blessed_gem|
|
||||
begin
|
||||
require blessed_gem
|
||||
rescue LoadError
|
||||
|
||||
exec = {}
|
||||
options = {}
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = help
|
||||
|
||||
opts.on("--file [PATH]", "File to import from") do |import_file|
|
||||
options['file'] = import_file
|
||||
end
|
||||
end
|
||||
|
||||
opts.on("--dbname [TEXT]", "DB to import from") do |import_dbname|
|
||||
options['dbname'] = import_dbname
|
||||
end
|
||||
|
||||
opts.on("--user [TEXT]", "Username to use when importing") do |import_user|
|
||||
options['user'] = import_user
|
||||
end
|
||||
|
||||
opts.on("--pass [TEXT]", "Password to use when importing") do |import_pass|
|
||||
options['pass'] = import_pass
|
||||
end
|
||||
|
||||
opts.on("--host [HOST ADDRESS]", "Host to import from") do |import_host|
|
||||
options['host'] = import_host
|
||||
end
|
||||
|
||||
opts.on("--site [SITE NAME]", "Site to import from") do |import_site|
|
||||
options['site'] = import_site
|
||||
end
|
||||
|
||||
|
||||
Jekyll::Deprecator.process(ARGV)
|
||||
opts.on("--[no-]safe", "Safe mode (default unsafe)") do |safe|
|
||||
options['safe'] = safe
|
||||
end
|
||||
|
||||
Mercenary.program(:jekyll) do |p|
|
||||
p.version Jekyll::VERSION
|
||||
p.description 'Jekyll is a blog-aware, static site generator in Ruby'
|
||||
p.syntax 'jekyll <subcommand> [options]'
|
||||
opts.on("--[no-]auto", "Auto-regenerate") do |auto|
|
||||
options['auto'] = auto
|
||||
end
|
||||
|
||||
p.option 'source', '-s', '--source [DIR]', 'Source directory (defaults to ./)'
|
||||
p.option 'destination', '-d', '--destination [DIR]', 'Destination directory (defaults to ./_site)'
|
||||
p.option 'safe', '--safe', 'Safe mode (defaults to false)'
|
||||
p.option 'plugins', '-p', '--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]', Array, 'Plugins directory (defaults to ./_plugins)'
|
||||
p.option 'layouts', '--layouts DIR', String, 'Layouts directory (defaults to ./_layouts)'
|
||||
opts.on("--server [PORT]", "Start web server (default port 4000)") do |port|
|
||||
options['server'] = true
|
||||
options['server_port'] = port unless port.nil?
|
||||
end
|
||||
|
||||
Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }
|
||||
opts.on("--no-server", "Do not start a web server") do |part|
|
||||
options['server'] = false
|
||||
end
|
||||
|
||||
p.action do |args, options|
|
||||
if args.empty?
|
||||
puts p
|
||||
else
|
||||
unless p.has_command?(args.first)
|
||||
Jekyll.logger.abort_with "Invalid command. Use --help for more information"
|
||||
end
|
||||
opts.on("--base-url [BASE_URL]", "Serve website from a given base URL (default '/'") do |baseurl|
|
||||
options['baseurl'] = baseurl
|
||||
end
|
||||
|
||||
opts.on("--[no-]lsi", "Use LSI for better related posts") do |lsi|
|
||||
options['lsi'] = lsi
|
||||
end
|
||||
|
||||
opts.on("--[no-]pygments", "Use pygments to highlight code") do |pygments|
|
||||
options['pygments'] = pygments
|
||||
end
|
||||
|
||||
opts.on("--rdiscount", "Use rdiscount gem for Markdown") do
|
||||
options['markdown'] = 'rdiscount'
|
||||
end
|
||||
|
||||
opts.on("--kramdown", "Use kramdown gem for Markdown") do
|
||||
options['markdown'] = 'kramdown'
|
||||
end
|
||||
|
||||
opts.on("--time [TIME]", "Time to generate the site for") do |time|
|
||||
options['time'] = Time.parse(time)
|
||||
end
|
||||
|
||||
opts.on("--[no-]future", "Render future dated posts") do |future|
|
||||
options['future'] = future
|
||||
end
|
||||
|
||||
opts.on("--permalink [TYPE]", "Use 'date' (default) for YYYY/MM/DD") do |style|
|
||||
options['permalink'] = style unless style.nil?
|
||||
end
|
||||
|
||||
opts.on("--paginate [POSTS_PER_PAGE]", "Paginate a blog's posts") do |per_page|
|
||||
begin
|
||||
options['paginate'] = per_page.to_i
|
||||
raise ArgumentError if options['paginate'] == 0
|
||||
rescue
|
||||
puts 'you must specify a number of posts by page bigger than 0'
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
opts.on("--limit_posts [MAX_POSTS]", "Limit the number of posts to publish") do |limit_posts|
|
||||
begin
|
||||
options['limit_posts'] = limit_posts.to_i
|
||||
raise ArgumentError if options['limit_posts'] < 1
|
||||
rescue
|
||||
puts 'you must specify a number of posts by page bigger than 0'
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
opts.on("--url [URL]", "Set custom site.url") do |url|
|
||||
options['url'] = url
|
||||
end
|
||||
|
||||
opts.on("--version", "Display current version") do
|
||||
puts "Jekyll " + Jekyll::VERSION
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
# Read command line options into `options` hash
|
||||
opts.parse!
|
||||
|
||||
|
||||
# Check for import stuff
|
||||
if ARGV.size > 0
|
||||
if ARGV[0] == 'import'
|
||||
migrator = ARGV[1]
|
||||
|
||||
if migrator.nil?
|
||||
puts "Invalid options. Run `jekyll --help` for assistance."
|
||||
exit(1)
|
||||
else
|
||||
migrator = migrator.downcase
|
||||
end
|
||||
|
||||
cmd_options = []
|
||||
['file', 'dbname', 'user', 'pass', 'host', 'site'].each do |p|
|
||||
cmd_options << "\"#{options[p]}\"" unless options[p].nil?
|
||||
end
|
||||
|
||||
# It's import time
|
||||
puts "Importing..."
|
||||
|
||||
# Ideally, this shouldn't be necessary. Maybe parse the actual
|
||||
# src files for the migrator name?
|
||||
migrators = {
|
||||
:posterous => 'Posterous',
|
||||
:wordpressdotcom => 'WordpressDotCom',
|
||||
:wordpress => 'Wordpress',
|
||||
:csv => 'CSV',
|
||||
:drupal => 'Drupal',
|
||||
:mephisto => 'Mephisto',
|
||||
:mt => 'MT',
|
||||
:textpattern => 'TextPattern',
|
||||
:typo => 'Typo'
|
||||
}
|
||||
|
||||
app_root = File.join(File.dirname(__FILE__), '..')
|
||||
|
||||
require "#{app_root}/lib/jekyll/migrators/#{migrator}"
|
||||
|
||||
if Jekyll.const_defined?(migrators[migrator.to_sym])
|
||||
migrator_class = Jekyll.const_get(migrators[migrator.to_sym])
|
||||
migrator_class.process(*cmd_options)
|
||||
else
|
||||
puts "Invalid migrator. Run `jekyll --help` for assistance."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
exit(0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Get source and destintation from command line
|
||||
case ARGV.size
|
||||
when 0
|
||||
when 1
|
||||
options['destination'] = ARGV[0]
|
||||
when 2
|
||||
options['source'] = ARGV[0]
|
||||
options['destination'] = ARGV[1]
|
||||
else
|
||||
puts "Invalid options. Run `jekyll --help` for assistance."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
options = Jekyll.configuration(options)
|
||||
|
||||
# Get source and destination directories (possibly set by config file)
|
||||
source = options['source']
|
||||
destination = options['destination']
|
||||
|
||||
# Files to watch
|
||||
def globs(source)
|
||||
Dir.chdir(source) do
|
||||
dirs = Dir['*'].select { |x| File.directory?(x) }
|
||||
dirs -= ['_site']
|
||||
dirs = dirs.map { |x| "#{x}/**/*" }
|
||||
dirs += ['*']
|
||||
end
|
||||
end
|
||||
|
||||
# Create the Site
|
||||
site = Jekyll::Site.new(options)
|
||||
|
||||
# Run the directory watcher for auto-generation, if required
|
||||
if options['auto']
|
||||
require 'directory_watcher'
|
||||
|
||||
puts "Auto-regenerating enabled: #{source} -> #{destination}"
|
||||
|
||||
dw = DirectoryWatcher.new(source)
|
||||
dw.interval = 1
|
||||
dw.glob = globs(source)
|
||||
|
||||
dw.add_observer do |*args|
|
||||
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
puts "[#{t}] regeneration: #{args.size} files changed"
|
||||
site.process
|
||||
end
|
||||
|
||||
dw.start
|
||||
|
||||
unless options['server']
|
||||
loop { sleep 1000 }
|
||||
end
|
||||
else
|
||||
puts "Building site: #{source} -> #{destination}"
|
||||
begin
|
||||
site.process
|
||||
rescue Jekyll::FatalException => e
|
||||
puts
|
||||
puts "ERROR: YOUR SITE COULD NOT BE BUILT:"
|
||||
puts "------------------------------------"
|
||||
puts e.message
|
||||
exit(1)
|
||||
end
|
||||
puts "Successfully generated site: #{source} -> #{destination}"
|
||||
end
|
||||
|
||||
# Run the server on the specified port, if required
|
||||
if options['server']
|
||||
require 'webrick'
|
||||
include WEBrick
|
||||
|
||||
FileUtils.mkdir_p(destination)
|
||||
|
||||
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
|
||||
mime_types.store 'js', 'application/javascript'
|
||||
|
||||
s = HTTPServer.new(
|
||||
:Port => options['server_port'],
|
||||
:MimeTypes => mime_types
|
||||
)
|
||||
s.mount(options['baseurl'], HTTPServlet::FileHandler, destination)
|
||||
t = Thread.new {
|
||||
s.start
|
||||
}
|
||||
|
||||
trap("INT") { s.shutdown }
|
||||
t.join()
|
||||
end
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
default: --format pretty
|
||||
travis: --format progress
|
||||
html_report: --format progress --format html --out=features_report.html
|
||||
default: --format progress
|
||||
1
doc/.gitignore
vendored
Normal file
1
doc/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
output
|
||||
7
doc/.gitscribe
Normal file
7
doc/.gitscribe
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
publish: true
|
||||
edition: 0.1
|
||||
language: en
|
||||
version: 1.0
|
||||
author: Your Name
|
||||
cover: image/cover.jpg
|
||||
9
doc/README.asciidoc
Normal file
9
doc/README.asciidoc
Normal file
@@ -0,0 +1,9 @@
|
||||
This Book
|
||||
=========
|
||||
|
||||
This book is written using using the git-scribe toolchain, which can be found at:
|
||||
|
||||
http://github.com/schacon/git-scribe
|
||||
|
||||
Instructions on how to install the tool and use it for things like editing this book,
|
||||
submitting errata and providing translations can be found at that site.
|
||||
10
doc/book/book.asc
Normal file
10
doc/book/book.asc
Normal file
@@ -0,0 +1,10 @@
|
||||
Jekyll
|
||||
======
|
||||
:Author: Tom Preston-Werner
|
||||
:Email: <tom@mojombo.com>
|
||||
|
||||
include::ch00-preface.asc[]
|
||||
|
||||
include::ch01-quick-start.asc[]
|
||||
|
||||
include::ch02-directory-layout.asc[]
|
||||
41
doc/book/ch00-preface.asc
Normal file
41
doc/book/ch00-preface.asc
Normal file
@@ -0,0 +1,41 @@
|
||||
== Preface
|
||||
|
||||
Jekyll was born out the desire to create a blog engine that would make it
|
||||
possible to write posts in my local text editor, version those posts with Git,
|
||||
and keep up with my desire to tweak the styles and layout of my site.
|
||||
|
||||
In other words, I wanted something that fit into my existing software
|
||||
development workflow and toolchain. Jekyll handles not only this case, but a
|
||||
wide variety of other situations that call for static site generation based on
|
||||
converted content and layout templates.
|
||||
|
||||
At its core, Jekyll is a text transformation engine. The concept behind the
|
||||
system is this: you give it text written in your favorite markup language, be
|
||||
that Markdown, Textile, or just plain HTML, and it churns that through a
|
||||
layout or series of layout files. Throughout that process you can tweak how
|
||||
you want the site URLs to look, what data gets displayed on the layout and
|
||||
much more.
|
||||
|
||||
If you're looking for a simple, yet powerful solution to your blogging or
|
||||
static site needs, Jekyll may be just what you've been looking for.
|
||||
|
||||
|
||||
=== What this book covers
|
||||
|
||||
_Chapter 1, Quick Start_ covers installation, introduces the Jekyll command
|
||||
line interface, and runs through a quick example demonstrating the site
|
||||
generator, post generator and how to convert your Jekyll site into a static
|
||||
site.
|
||||
|
||||
_Chapter 2, Directory Layout_ covers the various files and directories that
|
||||
comprise a Jekyll site.
|
||||
|
||||
_Chapter 3, Tags and Filters_
|
||||
|
||||
_Chapter X, Deploying your Jekyll Site_
|
||||
|
||||
_Chapter X, Customizing Jekyll with Plugins_
|
||||
|
||||
_Chapter X, Migrating to Jekyll from your Existing Blog_
|
||||
|
||||
_Chapter X, Configuration Reference_
|
||||
153
doc/book/ch01-quick-start.asc
Normal file
153
doc/book/ch01-quick-start.asc
Normal file
@@ -0,0 +1,153 @@
|
||||
== Chapter 1: Quick Start
|
||||
|
||||
This chapter is designed to get you up and running with Jekyll as quickly as
|
||||
possible.
|
||||
|
||||
|
||||
=== Installation
|
||||
|
||||
The best way to install Jekyll is via RubyGems:
|
||||
|
||||
----
|
||||
gem install jekyll
|
||||
----
|
||||
|
||||
This is all you need in order to get started with a basic Jekyll site. Some
|
||||
options require additional packages to be installed.
|
||||
|
||||
If you encounter errors during gem installation, you may need to install the
|
||||
header files for compiling extension modules for ruby 1.8:
|
||||
|
||||
.Debian
|
||||
----
|
||||
sudo apt-get install ruby1.8-dev
|
||||
----
|
||||
|
||||
.Red Hat / CentOS / Fedora systems
|
||||
----
|
||||
sudo yum install ruby-devel
|
||||
----
|
||||
|
||||
.NearlyFreeSpeech
|
||||
----
|
||||
RB_USER_INSTALL=true gem install jekyll
|
||||
----
|
||||
|
||||
If you encounter errors like +Failed to build gem native extension+ on Windows
|
||||
you may need to install http://wiki.github.com/oneclick/rubyinstaller/development-kit[RubyInstaller
|
||||
DevKit].
|
||||
|
||||
==== LaTeX to PNG
|
||||
|
||||
Maruku comes with optional support for LaTeX to PNG rendering via blahtex
|
||||
(Version 0.6) which must be in your $PATH along with @dvips@.
|
||||
|
||||
(NOTE: "remi's fork of Maruku":http://github.com/remi/maruku/tree/master does
|
||||
not assume a fixed location for @dvips@ if you need that fixed)
|
||||
|
||||
==== RDiscount
|
||||
|
||||
If you prefer to use
|
||||
http://github.com/rtomayko/rdiscount/tree/master[RDiscount] instead of
|
||||
http://maruku.rubyforge.org/[Maruku] for markdown, just make sure it's
|
||||
installed:
|
||||
|
||||
----
|
||||
sudo gem install rdiscount
|
||||
----
|
||||
|
||||
And run Jekyll with the following option:
|
||||
|
||||
----
|
||||
jekyll --rdiscount
|
||||
----
|
||||
|
||||
Or, in your @_config.yml@ file put the following so you don't have to specify the flag:
|
||||
|
||||
----
|
||||
markdown: rdiscount
|
||||
----
|
||||
|
||||
==== Pygments
|
||||
|
||||
If you want syntax highlighting via the @{% highlight %}@ tag in your posts,
|
||||
you'll need to install http://pygments.org/[Pygments].
|
||||
|
||||
.On OSX with Homebrew
|
||||
----
|
||||
brew install pip && pip install pygments
|
||||
----
|
||||
|
||||
.On OSX with MacPorts
|
||||
----
|
||||
sudo port install python25 py25-pygments
|
||||
----
|
||||
|
||||
.Bare OS X Leopard
|
||||
----
|
||||
sudo easy_install Pygments
|
||||
----
|
||||
|
||||
.Archlinux
|
||||
----
|
||||
sudo pacman -S python-pygments
|
||||
----
|
||||
|
||||
.Archlinux python2 for Pygments
|
||||
----
|
||||
$ sudo pacman -S python2-pygments
|
||||
----
|
||||
|
||||
NOTE: python2 pygments version creates a `pygmentize2` executable, while
|
||||
Jekyll tries to find `pygmentize`. Either create a symlink `# ln -s
|
||||
/usr/bin/pygmentize2 /usr/bin/pygmentize` or use the python3 version.
|
||||
|
||||
.Ubuntu and Debian
|
||||
----
|
||||
sudo apt-get install python-pygments
|
||||
----
|
||||
|
||||
.Gentoo
|
||||
----
|
||||
$ sudo emerge -av dev-python/pygments
|
||||
----
|
||||
|
||||
|
||||
=== Creating your First Site
|
||||
|
||||
Jekyll comes with a handy generator that will create a barebones skeleton site
|
||||
to help you get up and running in no time. Simply create an empty directory to
|
||||
contain your site, navigate to it, and run the generator command:
|
||||
|
||||
----
|
||||
$ mkdir mysite
|
||||
$ cd mysite
|
||||
$ jekyll gen
|
||||
----
|
||||
|
||||
Make sure the directory is empty or Jekyll will refuse to run. If everything
|
||||
was successful, you'll be left with a complete, valid Jekyll site that's ready
|
||||
to be converted into a static site.
|
||||
|
||||
To perform the conversion, make sure you're in the root of your Jekyll site
|
||||
directory and run:
|
||||
|
||||
----
|
||||
$ jekyll --server
|
||||
----
|
||||
|
||||
If all goes well, you should get a few lines with information about config
|
||||
file detection, source and destination paths, and a success message.
|
||||
|
||||
The `--server` command line option fires up a simple web server that will
|
||||
serve the static site we just generated so that we can easily preview what it
|
||||
will look like once we deploy it to a production environment.
|
||||
|
||||
Open up your favorite web browser and navigate to:
|
||||
|
||||
----
|
||||
http://localhost:4000
|
||||
----
|
||||
|
||||
Congratulations! You have now successfully created and converted your first
|
||||
Jekyll site!
|
||||
90
doc/book/ch02-directory-layout.asc
Normal file
90
doc/book/ch02-directory-layout.asc
Normal file
@@ -0,0 +1,90 @@
|
||||
== Chapter 2: Directory Layout
|
||||
|
||||
If you followed the Quick Start in the last chapter, you have a Jekyll site on
|
||||
your local machine. Let's take a closer look at it and see what makes it tick.
|
||||
The file layout should look something like this:
|
||||
|
||||
----
|
||||
.
|
||||
|-- _config.yml
|
||||
|-- _layouts
|
||||
| |-- default.html
|
||||
| `-- post.html
|
||||
|-- _posts
|
||||
| |-- 2007-10-29-why-every-programmer-should-play-nethack.textile
|
||||
| `-- 2009-04-26-barcamp-boston-4-roundup.textile
|
||||
|-- _site
|
||||
|-- images
|
||||
| `-- logo.png
|
||||
`-- index.html
|
||||
----
|
||||
|
||||
Notice that some of the files and directories begin with an underscore. These
|
||||
have special meaning to Jekyll. The underscore ensures that they will not
|
||||
interfere with the rest of your site's normal content. It also means that if
|
||||
any of your normal files start with an underscore, they will cause problems,
|
||||
so try to avoid this.
|
||||
|
||||
=== _config.yml
|
||||
|
||||
This file stores configuration data. A majority of these options can be
|
||||
specified from the command line executable but it's easier to throw them in
|
||||
here so you don't have to type them out every time. Detailed explanations of
|
||||
configuration directives can be found in Chapter X.
|
||||
|
||||
=== _layouts
|
||||
|
||||
Files in this directory represent templates that can be used to wrap converted
|
||||
pages. Layouts are defined on a page-by-page basis in the YAML front matter.
|
||||
The liquid tag +{{ content }}+ specifies where the content will be placed
|
||||
during the conversion process.
|
||||
|
||||
=== _posts
|
||||
|
||||
If you're using Jekyll as a blog engine, this is where you'll place your blog
|
||||
posts. A post's filename contains several pieces of data, so you must be very
|
||||
careful about how these files are named. The filename format is:
|
||||
+YEAR-MONTH-DATE-SLUG.MARKUP+. The YEAR must be four numbers and the MONTH and
|
||||
DATE must be two numbers each. The SLUG is what will appear in the URL. The
|
||||
MARKUP tells Jekyll the format of the post. The date and slug will be used
|
||||
along with any permalink options you specify (See Chapter X) to construct the
|
||||
final URL of the post.
|
||||
|
||||
=== _site
|
||||
|
||||
This is where the generated site will be placed (by default) once Jekyll is
|
||||
done transforming it. If you're using version control, you'll want to add this
|
||||
directory to the list of files to be ignored.
|
||||
|
||||
=== Normal Files with YAML Front Matter
|
||||
|
||||
All files outside of the special underscore directories and that do not
|
||||
themselves begin with an underscore will be scanned by Jekyll and subjected to
|
||||
conversion if they contain any YAML front matter.
|
||||
|
||||
=== Everything Else
|
||||
|
||||
Any files and directories that do not fall into one of the above categories
|
||||
will be copied to the static site as-is without modification. In this example,
|
||||
+images/logo.png+ will be copied to the same location in the generated site.
|
||||
|
||||
|
||||
|
||||
|
||||
h2. Running Jekyll
|
||||
|
||||
Usually this is done through the @jekyll@ executable, which is installed with
|
||||
the gem. In order to get a server up and running with your Jekyll site, run:
|
||||
|
||||
@jekyll --server@
|
||||
|
||||
and then browse to http://0.0.0.0:4000. There's plenty of [[configuration
|
||||
options|Configuration]] available to you as well.
|
||||
|
||||
On Debian or Ubuntu, you may need to add @/var/lib/gems/1.8/bin/@ to your path.
|
||||
|
||||
h2. Deployment
|
||||
|
||||
Since Jekyll simply generates a folder filled with HTML files, it can be
|
||||
served using practically any available web server out there. Please check the
|
||||
[[Deployment]] page for more information regarding specific scenarios.
|
||||
@@ -1,93 +0,0 @@
|
||||
コントリビュート
|
||||
==========
|
||||
|
||||
あなたは Jekyll に投じるすばらしいアイディアを持っています。
|
||||
すばらしいことです!次の事柄を心に留めてください。
|
||||
|
||||
* **テストなしではコントリビュートはできません。**
|
||||
* もし、既存の機能への小さな修正やパッチを作成したなら、シンプルなテストを行います。
|
||||
現在のテストスイートの範囲にとどまり、そして
|
||||
[Shoulda](http://github.com/thoughtbot/shoulda/tree/master) や
|
||||
[RR](http://github.com/btakita/rr/tree/master) を使用してください。
|
||||
* もし、それが新しい機能の場合は、必ず新しい
|
||||
[Cucumber](https://github.com/cucumber/cucumber/) の機能を作成し、
|
||||
必要に応じて手順を再利用します。
|
||||
また、あなたがフォークした `site` に
|
||||
急ぎいくつかのドキュメントを用意し、一度マージを行い
|
||||
メイン `site` の jekyllrb.com に転送していただければ幸いです。
|
||||
* あなたのコントリビュートによって Jekyll の振る舞いが変わった場合、ドキュメントを更新すべきです。
|
||||
それは `site/docs` にあります。
|
||||
もし、 docs に情報の誤りがあった場合、遠慮なく追加してください。
|
||||
すばらしいドキュメントはすばらしいプロジェクトを作ります!
|
||||
* Ruby のコードを変更するときは、 [GitHub Ruby Styleguide](https://github.com/styleguide/ruby)
|
||||
に従ってください。
|
||||
* **小さなプルリクエスト** に最善を尽くしてください。
|
||||
簡単に提案された変更はレビューされ、マージされる可能性が高いです。
|
||||
* プルリクエストを送信するとき、プルリクエストのボディを賢明に使用してください。
|
||||
変更されたかどうかの記述、変更の背後にある動機、 [完了したかどうかのタスクリスト](http://git.io/gfm-tasks)
|
||||
もレビュー時間を早めます。
|
||||
|
||||
テストの依存関係
|
||||
-----------------
|
||||
|
||||
テストスイートの実行や gem のビルドのために、
|
||||
Jekyll の依存ツールをインストールする必要があります。
|
||||
Jekyll は Bundler を使用しており、 `bundle` コマンドを実行すると全ての設定が迅速に行われます!
|
||||
|
||||
$ bundle
|
||||
|
||||
はじめる前に、テストを実行し、必ずテストが通ることを
|
||||
確認してください(あなたの環境が適切に設定されているかを確認するために):
|
||||
|
||||
$ bundle exec rake test
|
||||
$ bundle exec rake features
|
||||
|
||||
ワークフロー
|
||||
--------
|
||||
|
||||
これは、あなたの作業がプロジェクトにマージされるもっとも直接的な方法です:
|
||||
|
||||
* プロジェクトをフォークします。
|
||||
* あなたのフォークプロジェクトをクローンします ( `git clone git@github.com:<username>/jekyll.git` )。
|
||||
* トピックブランチを作成し、あなたの変更を含んでください ( `git checkout -b my_awesome_feature` )。
|
||||
* ハックし、テストを追加します。必ずしもこの順番でなくてかまいません
|
||||
* `rake` を実行し、テストが必ず全て通ることを確認してください
|
||||
* 必要に応じて、エラーがないようにコミットを論理的な塊にリベースしてください
|
||||
* ブランチをプッシュしてください ( `git push origin my_awesome_feature` ).
|
||||
* jekyll/jekyll プロジェクトの master ブランチに対してプルリクエストを作成し、
|
||||
あなたの変更内容と、なぜそれをマージすべきかを記述してください
|
||||
|
||||
ドキュメントの更新
|
||||
----------------------
|
||||
|
||||
私たちは Jekyll のドキュメントについて最善を尽くしたいです。
|
||||
私たちはドキュメントをオープンソース化しました、そして
|
||||
あなたが Jekyll に欠けているものを見つけた場合、私たちはプルリクエストを歓迎しています。
|
||||
|
||||
あなたは、 GitHub.com 上の Jekyll リポジトリの [site]({{ site.repository }}/tree/master/site) で
|
||||
jekyllrb.comのドキュメントを見つけることができます。
|
||||
|
||||
全てのドキュメントのプルリクエストは `master` に向けられる必要があります。
|
||||
他のブランチに向けたプルリクエストは受け入れられません。
|
||||
|
||||
GitHub の [Jekyll wiki](https://github.com/jekyll/jekyll/wiki) は、
|
||||
自由に更新することができるように、プルリクエストなしで
|
||||
全ての GitHub ユーザがアクセス権を持つことができます。
|
||||
|
||||
落とし穴
|
||||
-------
|
||||
|
||||
* もし、 gem のバージョンがかちあった場合、コミットを分けてください。
|
||||
この方法だと、メンテナが gem をリリースするときに制御できます。
|
||||
* jekyll/jekyll の最新コミットに基づいて(複数の)パッチを維持してください。
|
||||
それは適用するためのあなたの仕事で、メンテナがしなければならないことを少なくするのは
|
||||
とてもよいことです。
|
||||
* あなたの GitHub issue で [fix], [feature] などのタグをつけないでください。
|
||||
メンテナは積極的に issue を読み、彼らが問題に出くわしたらラベルをつけるでしょう。
|
||||
|
||||
最後に…
|
||||
----------
|
||||
|
||||
ありがとう! Jekyll のハックは楽しいものでなければなりません。
|
||||
もし、あなたがこのハードを理解するための何かを発見した場合、知らせてください。
|
||||
我々のプロセスやドキュメントを改善することができます!
|
||||
@@ -1,69 +0,0 @@
|
||||
# [Jekyll](http://jekyllrb.com/)
|
||||
|
||||
[](http://badge.fury.io/rb/jekyll)
|
||||
|
||||
[](https://travis-ci.org/jekyll/jekyll)
|
||||
[](https://codeclimate.com/github/jekyll/jekyll)
|
||||
[](https://gemnasium.com/jekyll/jekyll)
|
||||
[](https://coveralls.io/r/jekyll/jekyll)
|
||||
|
||||
Tom Preston-Werner, Nick Quaranto や多くの[素晴らしいコントリビュータ](https://github.com/jekyll/jekyll/graphs/contributors)によって作成されています!
|
||||
|
||||
Jekyll は個人プロジェクトや組織のサイトに最適な、シンプルで、ブログを意識した静的サイトジェネレータです。
|
||||
複雑さを排除したファイルベースのCMSのようなものと考えてください。
|
||||
Jekyll はコンテンツを受け取り、 Markdown や Liquid テンプレート をレンダリングし、
|
||||
Apache や Nginx やその他の Web サーバに提供する準備ができた静的な Web サイトを完全に出力してくれます。
|
||||
Jekyll は [GitHub Pages](http://pages.github.com) の背後にあるエンジンなので、
|
||||
あなたの GitHub リポジトリからサイトをホストするために使用する事ができます。
|
||||
|
||||
## 原理
|
||||
|
||||
Jekyll あなたがするように伝えたことをします ― それ以上でもそれ以下でもありません。
|
||||
それは、大胆な仮定によってユーザの裏をかこうとせず、
|
||||
また、不必要な複雑さや設定をユーザに負担しません。
|
||||
簡単に言えば、 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) をチラッと見ます
|
||||
* Fork し、あなたの変更を [コントリビュート](http://jekyllrb.com/docs/contributing/) します
|
||||
* 質問があったら? irc.freenode.net の `#jekyll` チャンネルをチェックしてください
|
||||
|
||||
## より深く
|
||||
|
||||
* 以前のシステムからの[移行](http://jekyllrb.com/docs/migrations/)
|
||||
* [YAML Front Matter](http://jekyllrb.com/docs/frontmatter/) がどのように働くかを学ぶ
|
||||
* [変数](http://jekyllrb.com/docs/variables/)を使ってサイトに情報を表示する
|
||||
* posts が生成される時の[パーマリンク](http://jekyllrb.com/docs/permalinks/)をカスタマイズ
|
||||
* 人生を容易にするために、組み込みの [Liquid 拡張](http://jekyllrb.com/docs/templates/)を使用する
|
||||
* あなたのサイト固有のコンテンツを生成するために、カスタム[プラグイン](http://jekyllrb.com/docs/plugins/)を使用する
|
||||
|
||||
## 実行時の依存関係
|
||||
|
||||
* Commander: コマンドラインインターフェース構築 (Ruby)
|
||||
* Colorator: コマンドライン出力に色付け (Ruby)
|
||||
* Classifier: posts の関連を生成 (Ruby)
|
||||
* Directory Watcher: サイトの自動再生成 (Ruby)
|
||||
* Kramdown: デフォルトの Markdown エンジン (Ruby)
|
||||
* Liquid: テンプレートシステム (Ruby)
|
||||
* Pygments.rb: シンタックスハイライト (Ruby/Python)
|
||||
* RedCarpet: Markdown エンジン (Ruby)
|
||||
* Safe YAML: セキュリティのために構築された YAML パーサ (Ruby)
|
||||
|
||||
## 開発時の依存関係
|
||||
|
||||
* Launchy: クロスプラットフォーム ファイルランチャ (Ruby)
|
||||
* Maruku: Markdown スーパーセット インタプリタ (Ruby)
|
||||
* RDiscount: Discount Markdown プロセッサ (Ruby)
|
||||
* RedCloth: Textile サポート (Ruby)
|
||||
* RedGreen: よりよいテスト出力 (Ruby)
|
||||
* RR: モック (Ruby)
|
||||
* Shoulda: テストフレームワーク (Ruby)
|
||||
* SimpleCov: カバレッジフレームワーク (Ruby)
|
||||
|
||||
## ライセンス
|
||||
|
||||
[ライセンス](https://github.com/jekyll/jekyll/blob/master/LICENSE)を見てください。
|
||||
@@ -1,108 +0,0 @@
|
||||
Feature: Collections
|
||||
As a hacker who likes to structure content
|
||||
I want to be able to create collections of similar information
|
||||
And render them
|
||||
|
||||
Scenario: Unrendered collection
|
||||
Given I have an "index.html" page that contains "Collections: {{ site.methods }}"
|
||||
And I have fixture collections
|
||||
And I have a configuration file with "collections" set to "['methods']"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Collections: <p>Use <code>Jekyll.configuration</code> to build a full configuration for use w/Jekyll.</p>\n\n<p>Whatever: foo.bar</p>\n<p><code>Jekyll.sanitized_path</code> is used to make sure your path is in your source.</p>\n<p>Run your generators! default</p>\n<p>Create dat site.</p>\n<p>Run your generators! default</p>" in "_site/index.html"
|
||||
And the "_site/methods/configuration.html" file should not exist
|
||||
|
||||
Scenario: Rendered collection
|
||||
Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
|
||||
And I have an "collection_metadata.html" page that contains "Methods metadata: {{ site.collections.methods.foo }} {{ site.collections.methods }}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
methods:
|
||||
output: true
|
||||
foo: bar
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Collections: {\"methods" in "_site/index.html"
|
||||
And I should see "Methods metadata: bar" in "_site/collection_metadata.html"
|
||||
And I should see "<p>Whatever: foo.bar</p>" in "_site/methods/configuration.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 "<div class='title'>Tom Preston-Werner</div> {{content}}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
methods:
|
||||
output: true
|
||||
foo: bar
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Collections: {\"methods" in "_site/index.html"
|
||||
And I should see "<p>Run your generators! default</p>" in "_site/methods/site/generate.html"
|
||||
And I should see "<div class='title'>Tom Preston-Werner</div>" in "_site/methods/site/generate.html"
|
||||
|
||||
Scenario: Collections specified as an array
|
||||
Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
- methods
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Collections: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
|
||||
|
||||
Scenario: Collections specified as an hash
|
||||
Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
methods:
|
||||
baz: bin
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Collections: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
|
||||
|
||||
Scenario: All the documents
|
||||
Given I have an "index.html" page that contains "All documents: {% for doc in site.documents %}{{ doc.relative_path }} {% endfor %}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
- methods
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "All documents: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
|
||||
|
||||
Scenario: Documents have an output attribute, which is the converted HTML
|
||||
Given I have an "index.html" page that contains "First document's output: {{ site.documents.first.output }}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
- methods
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "First document's output: <p>Use <code>Jekyll.configuration</code> to build a full configuration for use w/Jekyll.</p>\n\n<p>Whatever: foo.bar</p>" in "_site/index.html"
|
||||
|
||||
Scenario: Filter documents by where
|
||||
Given I have an "index.html" page that contains "{% assign items = site.methods | where: 'whatever','foo.bar' %}Item count: {{ items.size }}"
|
||||
And I have fixture collections
|
||||
And I have a "_config.yml" file with content:
|
||||
"""
|
||||
collections:
|
||||
- methods
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Item count: 1" in "_site/index.html"
|
||||
@@ -3,25 +3,18 @@ Feature: Create sites
|
||||
I want to be able to make a static site
|
||||
In order to share my awesome ideas with the interwebs
|
||||
|
||||
Scenario: Blank site
|
||||
Given I do not have a "test_blank" directory
|
||||
When I run jekyll new test_blank --blank
|
||||
Then the test_blank/_layouts directory should exist
|
||||
And the test_blank/_posts directory should exist
|
||||
And the "test_blank/index.html" file should exist
|
||||
|
||||
Scenario: Basic site
|
||||
Given I have an "index.html" file that contains "Basic Site"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Basic Site" in "_site/index.html"
|
||||
|
||||
Scenario: Basic site with a post
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| Hackers | 2009-03-27 | My First Exploit |
|
||||
When I run jekyll build
|
||||
| title | date | content |
|
||||
| Hackers | 3/27/2009 | My First Exploit |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "My First Exploit" in "_site/2009/03/27/hackers.html"
|
||||
|
||||
@@ -29,7 +22,7 @@ Feature: Create sites
|
||||
Given I have a _layouts directory
|
||||
And I have an "index.html" page with layout "default" that contains "Basic Site with Layout"
|
||||
And I have a default layout that contains "Page Layout: {{ content }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: Basic Site with Layout" in "_site/index.html"
|
||||
|
||||
@@ -37,21 +30,10 @@ Feature: Create sites
|
||||
Given I have a _layouts directory
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| Wargames | 2009-03-27 | default | The only winning move is not to play. |
|
||||
| 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 }}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html"
|
||||
|
||||
Scenario: Basic site with layout inside a subfolder and a post
|
||||
Given I have a _layouts directory
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| Wargames | 2009-03-27 | post/simple | The only winning move is not to play. |
|
||||
And I have a post/simple layout that contains "Post Layout: {{ content }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html"
|
||||
|
||||
@@ -66,15 +48,15 @@ Feature: Create sites
|
||||
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 | 2009-03-27 | post | content for entry1. |
|
||||
| entry2 | 2009-04-27 | post | content for entry2. |
|
||||
| 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 | 2009-05-27 | post | content for entry3. |
|
||||
| entry4 | 2009-06-27 | post | content for entry4. |
|
||||
When I run jekyll build
|
||||
| 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"
|
||||
@@ -89,7 +71,7 @@ Feature: Create sites
|
||||
Given I have a _includes directory
|
||||
And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
|
||||
And I have an "_includes/about.textile" file that contains "Generated by Jekyll"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -98,7 +80,7 @@ Feature: Create sites
|
||||
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 build
|
||||
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"
|
||||
|
||||
@@ -107,52 +89,6 @@ Feature: Create sites
|
||||
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 build
|
||||
When I debug 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 internal post linking
|
||||
Given I have an "index.html" page that contains "URL: {% post_url 2020-01-31-entry2 %}"
|
||||
And I have a configuration file with "permalink" set to "pretty"
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
| entry2 | 2020-01-31 | post | content for entry2. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "URL: /2020/01/31/entry2/" in "_site/index.html"
|
||||
|
||||
Scenario: Basic site with whitelisted dotfile
|
||||
Given I have an ".htaccess" file that contains "SomeDirective"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "SomeDirective" in "_site/.htaccess"
|
||||
|
||||
Scenario: File was replaced by a directory
|
||||
Given I have a "test" file that contains "some stuff"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
When I delete the file "test"
|
||||
Given I have a test directory
|
||||
And I have a "test/index.html" file that contains "some other stuff"
|
||||
When I run jekyll build
|
||||
Then the _site/test directory should exist
|
||||
And I should see "some other stuff" in "_site/test/index.html"
|
||||
|
||||
Scenario: Basic site with unpublished page
|
||||
Given I have an "index.html" page with title "index" that contains "Published page"
|
||||
And I have a "public.html" page with published "true" that contains "Explicitly published page"
|
||||
And I have a "secret.html" page with published "false" that contains "Unpublished page"
|
||||
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And the "_site/index.html" file should exist
|
||||
And the "_site/public.html" file should exist
|
||||
But the "_site/secret.html" file should not exist
|
||||
|
||||
When I run jekyll build --unpublished
|
||||
Then the _site directory should exist
|
||||
And the "_site/index.html" file should exist
|
||||
And the "_site/public.html" file should exist
|
||||
And the "_site/secret.html" file should exist
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
Feature: Data
|
||||
In order to use well-formatted data in my blog
|
||||
As a blog's user
|
||||
I want to use _data directory in my site
|
||||
|
||||
Scenario: autoload *.yaml files in _data directory
|
||||
Given I have a _data directory
|
||||
And I have a "_data/products.yaml" file with content:
|
||||
"""
|
||||
- name: sugar
|
||||
price: 5.3
|
||||
- name: salt
|
||||
price: 2.5
|
||||
"""
|
||||
And I have an "index.html" page that contains "{% for product in site.data.products %}{{product.name}}{% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the "_site/index.html" file should exist
|
||||
And I should see "sugar" in "_site/index.html"
|
||||
And I should see "salt" in "_site/index.html"
|
||||
|
||||
Scenario: autoload *.yml files in _data directory
|
||||
Given I have a _data directory
|
||||
And I have a "_data/members.yml" file with content:
|
||||
"""
|
||||
- name: Jack
|
||||
age: 28
|
||||
- name: Leon
|
||||
age: 34
|
||||
"""
|
||||
And I have an "index.html" page that contains "{% for member in site.data.members %}{{member.name}}{% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the "_site/index.html" file should exist
|
||||
And I should see "Jack" in "_site/index.html"
|
||||
And I should see "Leon" in "_site/index.html"
|
||||
|
||||
Scenario: autoload *.yml files in _data directory with space in file name
|
||||
Given I have a _data directory
|
||||
And I have a "_data/team members.yml" file with content:
|
||||
"""
|
||||
- name: Jack
|
||||
age: 28
|
||||
- name: Leon
|
||||
age: 34
|
||||
"""
|
||||
And I have an "index.html" page that contains "{% for member in site.data.team_members %}{{member.name}}{% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the "_site/index.html" file should exist
|
||||
And I should see "Jack" in "_site/index.html"
|
||||
And I should see "Leon" in "_site/index.html"
|
||||
|
||||
Scenario: should be backward compatible with site.data in _config.yml
|
||||
Given I have a "_config.yml" file with content:
|
||||
"""
|
||||
data:
|
||||
- name: Jack
|
||||
age: 28
|
||||
- name: Leon
|
||||
age: 34
|
||||
"""
|
||||
And I have an "index.html" page that contains "{% for member in site.data %}{{member.name}}{% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the "_site/index.html" file should exist
|
||||
And I should see "Jack" in "_site/index.html"
|
||||
And I should see "Leon" in "_site/index.html"
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
Feature: Draft Posts
|
||||
As a hacker who likes to blog
|
||||
I want to be able to preview drafts locally
|
||||
In order to see if they look alright before publishing
|
||||
|
||||
Scenario: Preview a draft
|
||||
Given I have a configuration file with "permalink" set to "none"
|
||||
And I have a _drafts directory
|
||||
And I have the following draft:
|
||||
| title | date | layout | content |
|
||||
| Recipe | 2009-03-27 | default | Not baked yet. |
|
||||
When I run jekyll build --drafts
|
||||
Then the _site directory should exist
|
||||
And I should see "Not baked yet." in "_site/recipe.html"
|
||||
|
||||
Scenario: Don't preview a draft
|
||||
Given I have a configuration file with "permalink" set to "none"
|
||||
And I have an "index.html" page that contains "Totally index"
|
||||
And I have a _drafts directory
|
||||
And I have the following draft:
|
||||
| title | date | layout | content |
|
||||
| Recipe | 2009-03-27 | default | Not baked yet. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And the "_site/recipe.html" file should not exist
|
||||
|
||||
Scenario: Don't preview a draft that is not published
|
||||
Given I have a configuration file with "permalink" set to "none"
|
||||
And I have an "index.html" page that contains "Totally index"
|
||||
And I have a _drafts directory
|
||||
And I have the following draft:
|
||||
| title | date | layout | published | content |
|
||||
| Recipe | 2009-03-27 | default | false | Not baked yet. |
|
||||
When I run jekyll build --drafts
|
||||
Then the _site directory should exist
|
||||
And the "_site/recipe.html" file should not exist
|
||||
|
||||
Scenario: Use page.path variable
|
||||
Given I have a configuration file with "permalink" set to "none"
|
||||
And I have a _drafts directory
|
||||
And I have the following draft:
|
||||
| title | date | layout | content |
|
||||
| Recipe | 2009-03-27 | simple | Post path: {{ page.path }} |
|
||||
When I run jekyll build --drafts
|
||||
Then the _site directory should exist
|
||||
And I should see "Post path: _drafts/recipe.textile" in "_site/recipe.html"
|
||||
@@ -7,10 +7,10 @@ Feature: Embed filters
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
|
||||
| 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.time | date_to_xmlschema }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see today's date in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
@@ -18,12 +18,10 @@ Feature: Embed filters
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star & Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
|
||||
|
||||
|
||||
| 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 "{{ page.title | xml_escape }}"
|
||||
When I run jekyll build
|
||||
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,10 +29,10 @@ Feature: Embed filters
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
|
||||
| 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 "{{ content | xml_escape }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "7" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
@@ -42,10 +40,10 @@ Feature: Embed filters
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | tags | content |
|
||||
| Star Wars | 2009-03-27 | default | [scifi, movies, force] | These aren't the droids you're looking for. |
|
||||
| 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 "{{ page.tags | array_to_sentence_string }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -53,55 +51,10 @@ Feature: Embed filters
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. |
|
||||
| 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 "By {{ '_Obi-wan_' | textilize }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "By <p><em>Obi-wan</em></p>" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Sort by an arbitrary variable
|
||||
Given I have a _layouts directory
|
||||
And I have the following page:
|
||||
| title | layout | value | content |
|
||||
| Page-1 | default | 8 | Something |
|
||||
And I have the following page:
|
||||
| title | layout | value | content |
|
||||
| Page-2 | default | 6 | Something |
|
||||
And I have a default layout that contains "{{ site.pages | sort:'value' | map:'title' | join:', ' }}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see exactly "Page-2, Page-1" in "_site/page-1.html"
|
||||
And I should see exactly "Page-2, Page-1" in "_site/page-2.html"
|
||||
|
||||
Scenario: Sort pages by the title
|
||||
Given I have a _layouts directory
|
||||
And I have the following page:
|
||||
| title | layout | content |
|
||||
| Dog | default | Run |
|
||||
And I have the following page:
|
||||
| title | layout | content |
|
||||
| Bird | default | Fly |
|
||||
And I have the following page:
|
||||
| layout | content |
|
||||
| default | Jump |
|
||||
And I have a default layout that contains "{% assign sorted_pages = site.pages | sort: 'title' %}The rule of {{ sorted_pages.size }}: {% for p in sorted_pages %}{{ p.content | strip_html | strip_newlines }}, {% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see exactly "The rule of 3: Jump, Fly, Run," in "_site/bird.html"
|
||||
|
||||
Scenario: Sort pages by the title ordering pages without title last
|
||||
Given I have a _layouts directory
|
||||
And I have the following page:
|
||||
| title | layout | content |
|
||||
| Dog | default | Run |
|
||||
And I have the following page:
|
||||
| title | layout | content |
|
||||
| Bird | default | Fly |
|
||||
And I have the following page:
|
||||
| layout | content |
|
||||
| default | Jump |
|
||||
And I have a default layout that contains "{% assign sorted_pages = site.pages | sort: 'title', 'last' %}The rule of {{ sorted_pages.size }}: {% for p in sorted_pages %}{{ p.content | strip_html | strip_newlines }}, {% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see exactly "The rule of 3: Fly, Run, Jump," in "_site/bird.html"
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
Feature: frontmatter defaults
|
||||
Scenario: Use default for frontmatter variables internally
|
||||
Given I have a _layouts directory
|
||||
And I have a pretty layout that contains "THIS IS THE LAYOUT: {{content}}"
|
||||
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| default layout | 2013-09-11 | just some post |
|
||||
And I have an "index.html" page with title "some title" that contains "just some page"
|
||||
|
||||
And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {layout: "pretty"}}]"
|
||||
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "THIS IS THE LAYOUT: <p>just some post</p>" in "_site/2013/09/11/default-layout.html"
|
||||
And I should see "THIS IS THE LAYOUT: just some page" in "_site/index.html"
|
||||
|
||||
Scenario: Use default for frontmatter variables in Liquid
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| default data | 2013-09-11 | <p>{{page.custom}}</p><div>{{page.author}}</div> |
|
||||
And I have an "index.html" page that contains "just {{page.custom}} by {{page.author}}"
|
||||
And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {custom: "some special data", author: "Ben"}}]"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "<p>some special data</p><div>Ben</div>" in "_site/2013/09/11/default-data.html"
|
||||
And I should see "just some special data by Ben" in "_site/index.html"
|
||||
|
||||
Scenario: Override frontmatter defaults by path
|
||||
Given I have a _layouts directory
|
||||
And I have a root layout that contains "root: {{ content }}"
|
||||
And I have a subfolder layout that contains "subfolder: {{ content }}"
|
||||
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| about | 2013-10-14 | info on {{page.description}} |
|
||||
And I have a special/_posts directory
|
||||
And I have the following post in "special":
|
||||
| title | date | path | content |
|
||||
| about | 2013-10-14 | local | info on {{page.description}} |
|
||||
|
||||
And I have an "index.html" page with title "overview" that contains "Overview for {{page.description}}"
|
||||
And I have an "special/index.html" page with title "section overview" that contains "Overview for {{page.description}}"
|
||||
|
||||
And I have a configuration file with "defaults" set to "[{scope: {path: "special"}, values: {layout: "subfolder", description: "the special section"}}, {scope: {path: ""}, values: {layout: "root", description: "the webpage"}}]"
|
||||
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "root: <p>info on the webpage</p>" in "_site/2013/10/14/about.html"
|
||||
And I should see "subfolder: <p>info on the special section</p>" in "_site/special/2013/10/14/about.html"
|
||||
And I should see "root: Overview for the webpage" in "_site/index.html"
|
||||
And I should see "subfolder: Overview for the special section" in "_site/special/index.html"
|
||||
|
||||
Scenario: Override frontmatter defaults by type
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| this is a post | 2013-10-14 | blabla |
|
||||
And I have an "index.html" page that contains "interesting stuff"
|
||||
And I have a configuration file with "defaults" set to "[{scope: {path: "", type: "post"}, values: {permalink: "/post.html"}}, {scope: {path: "", type: "page"}, values: {permalink: "/page.html"}}, {scope: {path: ""}, values: {permalink: "/perma.html"}}]"
|
||||
When I run jekyll build
|
||||
Then I should see "blabla" in "_site/post.html"
|
||||
And I should see "interesting stuff" in "_site/page.html"
|
||||
But the "_site/perma.html" file should not exist
|
||||
|
||||
Scenario: Actual frontmatter overrides defaults
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | permalink | author | content |
|
||||
| override | 2013-10-14 | /frontmatter.html | some guy | a blog by {{page.author}} |
|
||||
And I have an "index.html" page with permalink "override.html" that contains "nothing"
|
||||
And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {permalink: "/perma.html", author: "Chris"}}]"
|
||||
When I run jekyll build
|
||||
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
|
||||
@@ -1,68 +0,0 @@
|
||||
Feature: Include tags
|
||||
In order to share their content across several pages
|
||||
As a hacker who likes to blog
|
||||
I want to be able to include files in my blog posts
|
||||
|
||||
Scenario: Include a file with parameters
|
||||
Given I have an _includes directory
|
||||
And I have an "_includes/header.html" file that contains "<header>My awesome blog header: {{include.param}}</header>"
|
||||
And I have an "_includes/params.html" file that contains "Parameters:<ul>{% for param in include %}<li>{{param[0]}} = {{param[1]}}</li>{% endfor %}</ul>"
|
||||
And I have an "_includes/ignore.html" file that contains "<footer>My blog footer</footer>"
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Include Files | 2013-03-21 | default | {% include header.html param="myparam" %} |
|
||||
| Ignore params if unused | 2013-03-21 | default | {% include ignore.html date="today" %} |
|
||||
| List multiple parameters | 2013-03-21 | default | {% include params.html date="today" start="tomorrow" %} |
|
||||
| Dont keep parameters | 2013-03-21 | default | {% include ignore.html param="test" %}\n{% include header.html %} |
|
||||
| Allow params with spaces and quotes | 2013-04-07 | default | {% include params.html cool="param with spaces" super="\"quoted\"" single='has "quotes"' escaped='\'single\' quotes' %} |
|
||||
| Parameter syntax | 2013-04-12 | default | {% include params.html param1_or_2="value" %} |
|
||||
| Pass a variable | 2013-06-22 | default | {% assign var = 'some text' %}{% include params.html local=var layout=page.layout %} |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "<header>My awesome blog header: myparam</header>" in "_site/2013/03/21/include-files.html"
|
||||
And I should not see "myparam" in "_site/2013/03/21/ignore-params-if-unused.html"
|
||||
And I should see "<li>date = today</li>" in "_site/2013/03/21/list-multiple-parameters.html"
|
||||
And I should see "<li>start = tomorrow</li>" in "_site/2013/03/21/list-multiple-parameters.html"
|
||||
And I should not see "<header>My awesome blog header: myparam</header>" in "_site/2013/03/21/dont-keep-parameters.html"
|
||||
But I should see "<header>My awesome blog header: </header>" in "_site/2013/03/21/dont-keep-parameters.html"
|
||||
And I should see "<li>cool = param with spaces</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
|
||||
And I should see "<li>super = “quoted”</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
|
||||
And I should see "<li>single = has “quotes”</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
|
||||
And I should see "<li>escaped = ‘single’ quotes</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
|
||||
And I should see "<li>param1_or_2 = value</li>" in "_site/2013/04/12/parameter-syntax.html"
|
||||
And I should see "<li>local = some text</li>" in "_site/2013/06/22/pass-a-variable.html"
|
||||
And I should see "<li>layout = default</li>" in "_site/2013/06/22/pass-a-variable.html"
|
||||
|
||||
Scenario: Include a file from a variable
|
||||
Given I have an _includes directory
|
||||
And I have an "_includes/snippet.html" file that contains "a snippet"
|
||||
And I have an "_includes/parametrized.html" file that contains "works with {{include.what}}"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| include_file1 | snippet.html |
|
||||
| include_file2 | parametrized.html |
|
||||
And I have an "index.html" page that contains "{% include {{site.include_file1}} %} that {% include {{site.include_file2}} what='parameters' %}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "a snippet that works with parameters" in "_site/index.html"
|
||||
|
||||
Scenario: Include a variable file in a loop
|
||||
Given I have an _includes directory
|
||||
And I have an "_includes/one.html" file that contains "one"
|
||||
And I have an "_includes/two.html" file that contains "two"
|
||||
And I have an "index.html" page with files "[one.html, two.html]" that contains "{% for file in page.files %}{% include {{file}} %} {% endfor %}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "one two" in "_site/index.html"
|
||||
|
||||
Scenario: Include a file with variables and filters
|
||||
Given I have an _includes directory
|
||||
And I have an "_includes/one.html" file that contains "one included"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| include_file | one |
|
||||
And I have an "index.html" page that contains "{% include {{ site.include_file | append: '.html' }} %}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "one included" in "_site/index.html"
|
||||
@@ -8,60 +8,23 @@ Feature: Markdown
|
||||
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 | 2009-03-27 | # My Title | markdown |
|
||||
When I run jekyll build
|
||||
| title | date | content | type |
|
||||
| Hackers | 3/27/2009 | # My Title | markdown |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Index" in "_site/index.html"
|
||||
And I should see "<h1 id=\"my-title\">My Title</h1>" in "_site/2009/03/27/hackers.html"
|
||||
And I should see "<h1 id=\"my-title\">My Title</h1>" in "_site/index.html"
|
||||
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/2009/03/27/hackers.html"
|
||||
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/index.html"
|
||||
|
||||
Scenario: Markdown in pagination on index
|
||||
Given I have a configuration file with "paginate" set to "5"
|
||||
And I have an "index.html" page that contains "Index - {% for post in paginator.posts %} {{ post.content }} {% endfor %}"
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content | type |
|
||||
| Hackers | 2009-03-27 | # My Title | markdown |
|
||||
When I run jekyll build
|
||||
| title | date | content | type |
|
||||
| Hackers | 3/27/2009 | # My Title | markdown |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Index" in "_site/index.html"
|
||||
And I should see "<h1 id=\"my-title\">My Title</h1>" in "_site/index.html"
|
||||
|
||||
Scenario: Maruku fenced codeblocks
|
||||
Given I have a configuration file with "markdown" set to "maruku"
|
||||
And I have an "index.markdown" file with content:
|
||||
"""
|
||||
---
|
||||
title: My title
|
||||
---
|
||||
|
||||
# My title
|
||||
|
||||
```
|
||||
My awesome code
|
||||
```
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "My awesome code" in "_site/index.html"
|
||||
And I should see "<pre><code>\nMy awesome code\n</code></pre>" in "_site/index.html"
|
||||
|
||||
Scenario: Maruku fenced codeblocks
|
||||
Given I have a configuration file with "markdown" set to "maruku"
|
||||
And I have an "index.markdown" file with content:
|
||||
"""
|
||||
---
|
||||
title: My title
|
||||
---
|
||||
|
||||
# My title
|
||||
|
||||
```ruby
|
||||
puts "My awesome string"
|
||||
```
|
||||
"""
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "My awesome string" in "_site/index.html"
|
||||
And I should see "<pre class="ruby"><code class="ruby">\nputs "My awesome string"\n</code></pre>" in "_site/index.html"
|
||||
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/index.html"
|
||||
|
||||
@@ -2,81 +2,26 @@ Feature: Site pagination
|
||||
In order to paginate my blog
|
||||
As a blog's user
|
||||
I want divide the posts in several pages
|
||||
|
||||
|
||||
Scenario Outline: Paginate with N posts per page
|
||||
Given I have a configuration file with "paginate" set to "<num>"
|
||||
And I have a _layouts directory
|
||||
And I have an "index.html" page that contains "{{ paginator.posts.size }}"
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| Wargames | 2009-03-27 | default | The only winning move is not to play. |
|
||||
| Wargames2 | 2009-04-27 | default | The only winning move is not to play2. |
|
||||
| Wargames3 | 2009-05-27 | default | The only winning move is not to play3. |
|
||||
| Wargames4 | 2009-06-27 | default | The only winning move is not to play4. |
|
||||
When I run jekyll build
|
||||
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 play3. |
|
||||
| Wargames4 | 6/27/2009 | default | The only winning move is not to play4. |
|
||||
When I run jekyll
|
||||
Then the _site/page<exist> directory should exist
|
||||
And the "_site/page<exist>/index.html" file should exist
|
||||
And I should see "<posts>" in "_site/page<exist>/index.html"
|
||||
And the "_site/page<not_exist>/index.html" file should not exist
|
||||
|
||||
|
||||
Examples:
|
||||
| num | exist | posts | not_exist |
|
||||
| 1 | 4 | 1 | 5 |
|
||||
| 2 | 2 | 2 | 3 |
|
||||
| 3 | 2 | 1 | 3 |
|
||||
|
||||
Scenario Outline: Setting a custom pagination path
|
||||
Given I have a configuration file with:
|
||||
| key | value |
|
||||
| paginate | 1 |
|
||||
| paginate_path | /blog/page-:num |
|
||||
| permalink | /blog/:year/:month/:day/:title |
|
||||
And I have a blog directory
|
||||
And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}"
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| Wargames | 2009-03-27 | default | The only winning move is not to play. |
|
||||
| Wargames2 | 2009-04-27 | default | The only winning move is not to play2. |
|
||||
| Wargames3 | 2009-05-27 | default | The only winning move is not to play3. |
|
||||
| Wargames4 | 2009-06-27 | default | The only winning move is not to play4. |
|
||||
When I run jekyll build
|
||||
Then the _site/blog/page-<exist> directory should exist
|
||||
And the "_site/blog/page-<exist>/index.html" file should exist
|
||||
And I should see "<posts>" in "_site/blog/page-<exist>/index.html"
|
||||
And the "_site/blog/page-<not_exist>/index.html" file should not exist
|
||||
|
||||
Examples:
|
||||
| exist | posts | not_exist |
|
||||
| 2 | 1 | 5 |
|
||||
| 3 | 1 | 6 |
|
||||
| 4 | 1 | 7 |
|
||||
|
||||
Scenario Outline: Setting a custom pagination path without an index.html in it
|
||||
Given I have a configuration file with:
|
||||
| key | value |
|
||||
| paginate | 1 |
|
||||
| paginate_path | /blog/page/:num |
|
||||
| permalink | /blog/:year/:month/:day/:title |
|
||||
And I have a blog directory
|
||||
And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}"
|
||||
And I have an "index.html" page that contains "Don't pick me!"
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| Wargames | 2009-03-27 | default | The only winning move is not to play. |
|
||||
| Wargames2 | 2009-04-27 | default | The only winning move is not to play2. |
|
||||
| Wargames3 | 2009-05-27 | default | The only winning move is not to play3. |
|
||||
| Wargames4 | 2009-06-27 | default | The only winning move is not to play4. |
|
||||
When I run jekyll build
|
||||
Then the _site/blog/page/<exist> directory should exist
|
||||
And the "_site/blog/page/<exist>/index.html" file should exist
|
||||
And I should see "<posts>" in "_site/blog/page/<exist>/index.html"
|
||||
And the "_site/blog/page/<not_exist>/index.html" file should not exist
|
||||
|
||||
Examples:
|
||||
| exist | posts | not_exist |
|
||||
| 2 | 1 | 5 |
|
||||
| 3 | 1 | 6 |
|
||||
| 4 | 1 | 7 |
|
||||
|
||||
@@ -6,20 +6,20 @@ Feature: Fancy permalinks
|
||||
Scenario: Use none permalink schema
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| None Permalink Schema | 2009-03-27 | Totally nothing. |
|
||||
| title | date | content |
|
||||
| None Permalink Schema | 3/27/2009 | Totally nothing. |
|
||||
And I have a configuration file with "permalink" set to "none"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Totally nothing." in "_site/none-permalink-schema.html"
|
||||
|
||||
Scenario: Use pretty permalink schema
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content |
|
||||
| Pretty Permalink Schema | 2009-03-27 | Totally wordpress. |
|
||||
| title | date | content |
|
||||
| Pretty Permalink Schema | 3/27/2009 | Totally wordpress. |
|
||||
And I have a configuration file with "permalink" set to "pretty"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Totally wordpress." in "_site/2009/03/27/pretty-permalink-schema/index.html"
|
||||
|
||||
@@ -28,7 +28,7 @@ Feature: Fancy permalinks
|
||||
And I have an "awesome.html" page that contains "Totally awesome"
|
||||
And I have an "sitemap.xml" page that contains "Totally uhm, sitemap"
|
||||
And I have a configuration file with "permalink" set to "pretty"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Totally index" in "_site/index.html"
|
||||
And I should see "Totally awesome" in "_site/awesome/index.html"
|
||||
@@ -37,49 +37,29 @@ Feature: Fancy permalinks
|
||||
Scenario: Use custom permalink schema with prefix
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | category | date | content |
|
||||
| Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. |
|
||||
| title | category | date | content |
|
||||
| Custom Permalink Schema | stuff | 3/27/2009 | Totally custom. |
|
||||
And I have a configuration file with "permalink" set to "/blog/:year/:month/:day/:title"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Totally custom." in "_site/blog/2009/03/27/custom-permalink-schema/index.html"
|
||||
|
||||
Scenario: Use custom permalink schema with category
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | category | date | content |
|
||||
| Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. |
|
||||
| title | category | date | content |
|
||||
| Custom Permalink Schema | stuff | 3/27/2009 | Totally custom. |
|
||||
And I have a configuration file with "permalink" set to "/:categories/:title.html"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Totally custom." in "_site/stuff/custom-permalink-schema.html"
|
||||
|
||||
Scenario: Use custom permalink schema with squished date
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | category | date | content |
|
||||
| Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. |
|
||||
| title | category | date | content |
|
||||
| Custom Permalink Schema | stuff | 3/27/2009 | Totally custom. |
|
||||
And I have a configuration file with "permalink" set to "/:month-:day-:year/:title.html"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Totally custom." in "_site/03-27-2009/custom-permalink-schema.html"
|
||||
|
||||
Scenario: Use per-post permalink
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | permalink | content |
|
||||
| Some post | 2013-04-14 | /custom/posts/1 | bla bla |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And the _site/custom/posts/1 directory should exist
|
||||
And I should see "bla bla" in "_site/custom/posts/1/index.html"
|
||||
|
||||
Scenario: Use per-post ending in .html
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | permalink | content |
|
||||
| Some post | 2013-04-14 | /custom/posts/some.html | bla bla |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And the _site/custom/posts directory should exist
|
||||
And I should see "bla bla" in "_site/custom/posts/some.html"
|
||||
|
||||
@@ -7,10 +7,10 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
| 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: {{ page.title }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -18,10 +18,10 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
| 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: {{ page.url }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -29,21 +29,21 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
And I have a simple layout that contains "Post date: {{ page.date | date_to_string }}"
|
||||
When I run jekyll build
|
||||
| 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: {{ page.date }}"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Post date: 27 Mar 2009" in "_site/2009/03/27/star-wars.html"
|
||||
And I should see "Post date: Fri Mar 27" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Use post.id variable
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
| 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: {{ page.id }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -51,10 +51,10 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
| 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: {{ content }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Post content: <p>Luke, I am your father.</p>" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
@@ -63,10 +63,10 @@ Feature: Post data
|
||||
And I have a movies/_posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post in "movies":
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
| 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: {{ page.categories }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -74,10 +74,10 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | tag | content |
|
||||
| Star Wars | 2009-05-18 | simple | twist | Luke, I am your father. |
|
||||
| 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: {{ page.tags }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -87,23 +87,10 @@ Feature: Post data
|
||||
And I have a scifi/movies/_posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post in "scifi/movies":
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | Luke, I am your father. |
|
||||
| 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: {{ page.categories | array_to_sentence_string }}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Use post.categories variable when categories are in folders with mixed case
|
||||
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 "scifi/Movies":
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 2009-03-27 | simple | 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 build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
|
||||
|
||||
@@ -111,79 +98,31 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | category | content |
|
||||
| Star Wars | 2009-03-27 | simple | movies | Luke, I am your father. |
|
||||
| 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: {{ page.categories }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
Scenario: Use post.categories variable when category is in YAML and is mixed-case
|
||||
Scenario: Use post.categories variable when categories are in YAML
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | category | content |
|
||||
| Star Wars | 2009-03-27 | simple | Movies | 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/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Use post.categories variable when category is in YAML
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | category | content |
|
||||
| Star Wars | 2009-03-27 | simple | movies | 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/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Use post.categories variable when categories are in YAML with mixed case
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | categories | content |
|
||||
| Star Wars | 2009-03-27 | simple | ['scifi', 'Movies'] | Luke, I am your father. |
|
||||
| Star Trek | 2013-03-17 | simple | ['SciFi', 'movies'] | Jean Luc, I am your father. |
|
||||
| title | date | layout | categories | content |
|
||||
| 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 build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
|
||||
And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2013/03/17/star-trek.html"
|
||||
|
||||
Scenario Outline: Use page.path variable
|
||||
Given I have a <dir>/_posts directory
|
||||
And I have the following post in "<dir>":
|
||||
| title | type | date | content |
|
||||
| my-post | html | 2013-04-12 | Source path: {{ page.path }} |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Source path: <path_prefix>_posts/2013-04-12-my-post.html" in "_site/<dir>/2013/04/12/my-post.html"
|
||||
|
||||
Examples:
|
||||
| dir | path_prefix |
|
||||
| . | |
|
||||
| dir | dir/ |
|
||||
| dir/nested | dir/nested/ |
|
||||
|
||||
Scenario: Override page.path variable
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | path | content |
|
||||
| override | 2013-04-12 | override-path.html | Custom path: {{ page.path }} |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Custom path: override-path.html" in "_site/2013/04/12/override.html"
|
||||
|
||||
Scenario: Disable a post from being published
|
||||
Given I have a _posts directory
|
||||
And I have an "index.html" file that contains "Published!"
|
||||
And I have the following post:
|
||||
| title | date | layout | published | content |
|
||||
| Star Wars | 2009-03-27 | simple | false | Luke, I am your father. |
|
||||
When I run jekyll build
|
||||
| title | date | layout | published | content |
|
||||
| Star Wars | 3/27/2009 | simple | false | Luke, I am your father. |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And the "_site/2009/03/27/star-wars.html" file should not exist
|
||||
And I should see "Published!" in "_site/index.html"
|
||||
@@ -192,10 +131,10 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | author | content |
|
||||
| Star Wars | 2009-03-27 | simple | Darth Vader | Luke, I am your father. |
|
||||
| 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: {{ page.author }}"
|
||||
When I run jekyll build
|
||||
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"
|
||||
|
||||
@@ -203,12 +142,12 @@ Feature: Post data
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | author | content |
|
||||
| Star Wars | 2009-03-27 | ordered | Darth Vader | Luke, I am your father. |
|
||||
| Some like it hot | 2009-04-27 | ordered | Osgood | Nobody is perfect. |
|
||||
| Terminator | 2009-05-27 | ordered | Arnold | Sayonara, baby |
|
||||
| title | date | layout | author | content |
|
||||
| Star Wars | 3/27/2009 | ordered | Darth Vader | Luke, I am your father. |
|
||||
| Some like it hot | 4/27/2009 | ordered | Osgood | Nobody is perfect. |
|
||||
| Terminator | 5/27/2009 | ordered | Arnold | Sayonara, baby |
|
||||
And I have a ordered layout that contains "Previous post: {{ page.previous.title }} and next post: {{ page.next.title }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "next post: Some like it hot" in "_site/2009/03/27/star-wars.html"
|
||||
And I should see "Previous post: Some like it hot" in "_site/2009/05/27/terminator.html"
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
Feature: Post excerpts
|
||||
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 some people can only focus for a few moments
|
||||
So just give them a taste
|
||||
|
||||
Scenario: An excerpt without a layout
|
||||
Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}"
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see exactly "<p>content for entry1.</p>" in "_site/index.html"
|
||||
|
||||
Scenario: An excerpt from a post with a layout
|
||||
Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}"
|
||||
And I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have a post layout that contains "{{ page.excerpt }}"
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And the _site/2007 directory should exist
|
||||
And the _site/2007/12 directory should exist
|
||||
And the _site/2007/12/31 directory should exist
|
||||
And the "_site/2007/12/31/entry1.html" file should exist
|
||||
And I should see exactly "<p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
|
||||
And I should see exactly "<p>content for entry1.</p>" in "_site/index.html"
|
||||
|
||||
Scenario: An excerpt from a post with a layout which has context
|
||||
Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}"
|
||||
And I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have a post layout that contains "<html><head></head><body>{{ page.excerpt }}</body></html>"
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And the _site/2007 directory should exist
|
||||
And the _site/2007/12 directory should exist
|
||||
And the _site/2007/12/31 directory should exist
|
||||
And the "_site/2007/12/31/entry1.html" file should exist
|
||||
And I should see exactly "<p>content for entry1.</p>" in "_site/index.html"
|
||||
And I should see exactly "<html><head></head><body><p>content for entry1.</p></body></html>" in "_site/2007/12/31/entry1.html"
|
||||
@@ -3,48 +3,27 @@ Feature: Site configuration
|
||||
I want to be able to configure jekyll
|
||||
In order to make setting up a site easier
|
||||
|
||||
Scenario: Change source directory
|
||||
Scenario: Change destination directory
|
||||
Given I have a blank site in "_sourcedir"
|
||||
And I have an "_sourcedir/index.html" file that contains "Changing source directory"
|
||||
And I have a configuration file with "source" set to "_sourcedir"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Changing source directory" in "_site/index.html"
|
||||
|
||||
Scenario: Change destination directory
|
||||
Given I have an "index.html" file that contains "Changing destination directory"
|
||||
And I have a configuration file with "destination" set to "_mysite"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _mysite directory should exist
|
||||
And I should see "Changing destination directory" in "_mysite/index.html"
|
||||
|
||||
Scenario Outline: Similarly named source and destination
|
||||
Given I have a blank site in "<source>"
|
||||
And I have an "<source>/index.md" page that contains "markdown"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| source | <source> |
|
||||
| destination | <dest> |
|
||||
When I run jekyll build
|
||||
Then the <source> directory should exist
|
||||
And the "<dest>/index.html" file should <file_exist> exist
|
||||
And I should see "markdown" in "<source>/index.md"
|
||||
|
||||
Examples:
|
||||
| source | dest | file_exist |
|
||||
| mysite_source | mysite | |
|
||||
| mysite | mysite_dest | |
|
||||
| mysite/ | mysite | not |
|
||||
| mysite | ./mysite | not |
|
||||
| mysite/source | mysite | not |
|
||||
| mysite | mysite/dest | |
|
||||
|
||||
Scenario: Exclude files inline
|
||||
Given I have an "Rakefile" file that contains "I want to be excluded"
|
||||
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 "['Rakefile', 'README']"
|
||||
When I run jekyll build
|
||||
And I have a configuration file with "exclude" set to "Rakefile", "README"
|
||||
When I run jekyll
|
||||
Then I should see "I want to be included" in "_site/index.html"
|
||||
And the "_site/Rakefile" file should not exist
|
||||
And the "_site/README" file should not exist
|
||||
@@ -57,7 +36,7 @@ Feature: Site configuration
|
||||
| value |
|
||||
| README |
|
||||
| Rakefile |
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then I should see "I want to be included" in "_site/index.html"
|
||||
And the "_site/Rakefile" file should not exist
|
||||
And the "_site/README" file should not exist
|
||||
@@ -65,54 +44,30 @@ Feature: Site configuration
|
||||
Scenario: Use RDiscount for markup
|
||||
Given I have an "index.markdown" page that contains "[Google](http://google.com)"
|
||||
And I have a configuration file with "markdown" set to "rdiscount"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "<a href=\"http://google.com\">Google</a>" in "_site/index.html"
|
||||
And I should see "<a href="http://google.com">Google</a>" in "_site/index.html"
|
||||
|
||||
Scenario: Use Kramdown for markup
|
||||
Given I have an "index.markdown" page that contains "[Google](http://google.com)"
|
||||
And I have a configuration file with "markdown" set to "kramdown"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "<a href=\"http://google.com\">Google</a>" in "_site/index.html"
|
||||
|
||||
Scenario: Use Redcarpet for markup
|
||||
Given I have an "index.markdown" page that contains "[Google](http://google.com)"
|
||||
And I have a configuration file with "markdown" set to "redcarpet"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "<a href=\"http://google.com\">Google</a>" in "_site/index.html"
|
||||
And I should see "<a href="http://google.com">Google</a>" in "_site/index.html"
|
||||
|
||||
Scenario: Use Maruku for markup
|
||||
Given I have an "index.markdown" page that contains "[Google](http://google.com)"
|
||||
And I have a configuration file with "markdown" set to "maruku"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "<a href=\"http://google.com\">Google</a>" in "_site/index.html"
|
||||
And I should see "<a href='http://google.com'>Google</a>" in "_site/index.html"
|
||||
|
||||
Scenario: Highlight code with pygments
|
||||
Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}"
|
||||
When I run jekyll build
|
||||
Given I have an "index.html" file that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}"
|
||||
And I have a configuration file with "pygments" set to "true"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Hello world!" in "_site/index.html"
|
||||
And I should see "class=\"highlight\"" in "_site/index.html"
|
||||
|
||||
Scenario: Highlight code with rouge
|
||||
Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}"
|
||||
And I have a configuration file with "highlighter" set to "rouge"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Hello world!" in "_site/index.html"
|
||||
And I should see "class=\"highlight\"" in "_site/index.html"
|
||||
|
||||
Scenario: Rouge renders code block once
|
||||
Given I have a configuration file with "highlighter" set to "rouge"
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| foo | 2014-04-27 11:34 | default | {% highlight text %} test {% endhighlight %} |
|
||||
When I run jekyll build
|
||||
Then I should not see "highlight(.*)highlight" in "_site/2014/04/27/foo.html"
|
||||
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
|
||||
@@ -125,10 +80,10 @@ Feature: Site configuration
|
||||
| future | false |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
| entry2 | 2020-01-31 | post | content for entry2. |
|
||||
When I run jekyll build
|
||||
| title | date | layout | content |
|
||||
| entry1 | 12/31/2007 | post | content for entry1. |
|
||||
| entry2 | 01/31/2020 | post | content for entry2. |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 1 on 2010-01-01" in "_site/index.html"
|
||||
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
|
||||
@@ -145,131 +100,27 @@ Feature: Site configuration
|
||||
| future | true |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
| entry2 | 2020-01-31 | post | content for entry2. |
|
||||
When I run jekyll build
|
||||
| title | date | layout | content |
|
||||
| entry1 | 12/31/2007 | post | content for entry1. |
|
||||
| entry2 | 01/31/2020 | post | content for entry2. |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html"
|
||||
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
|
||||
And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html"
|
||||
|
||||
Scenario: Generate proper dates with explicitly set timezone (same as posts' time)
|
||||
Given I have a _layouts directory
|
||||
And I have a page layout that contains "Page Layout: {{ site.posts.size }}"
|
||||
And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}"
|
||||
And I have an "index.html" page with layout "page" that contains "site index page"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| timezone | America/New_York |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2013-04-09 23:22 -0400 | post | content for entry1. |
|
||||
| entry2 | 2013-04-10 03:14 -0400 | post | content for entry2. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 2" in "_site/index.html"
|
||||
And I should see "Post Layout: <p>content for entry1.</p> built at 2013-04-09T23:22:00-04:00" in "_site/2013/04/09/entry1.html"
|
||||
And I should see "Post Layout: <p>content for entry2.</p> built at 2013-04-10T03:14:00-04:00" in "_site/2013/04/10/entry2.html"
|
||||
|
||||
Scenario: Generate proper dates with explicitly set timezone (different than posts' time)
|
||||
Given I have a _layouts directory
|
||||
And I have a page layout that contains "Page Layout: {{ site.posts.size }}"
|
||||
And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}"
|
||||
And I have an "index.html" page with layout "page" that contains "site index page"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| timezone | Australia/Melbourne |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2013-04-09 23:22 -0400 | post | content for entry1. |
|
||||
| entry2 | 2013-04-10 03:14 -0400 | post | content for entry2. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 2" in "_site/index.html"
|
||||
And the "_site/2013/04/10/entry1.html" file should exist
|
||||
And the "_site/2013/04/10/entry2.html" file should exist
|
||||
And I should see escaped "Post Layout: <p>content for entry1.</p> built at 2013-04-10T13:22:00+10:00" in "_site/2013/04/10/entry1.html"
|
||||
And I should see escaped "Post Layout: <p>content for entry2.</p> built at 2013-04-10T17:14:00+10:00" in "_site/2013/04/10/entry2.html"
|
||||
|
||||
Scenario: Limit the number of posts generated by most recent date
|
||||
Given I have a _posts directory
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| limit_posts | 2 |
|
||||
And I have the following posts:
|
||||
| title | date | content |
|
||||
| Apples | 2009-03-27 | An article about apples |
|
||||
| Oranges | 2009-04-01 | An article about oranges |
|
||||
| Bananas | 2009-04-05 | An article about bananas |
|
||||
When I run jekyll build
|
||||
| title | date | content |
|
||||
| Apples | 3/27/2009 | An article about apples |
|
||||
| Oranges | 4/1/2009 | An article about oranges |
|
||||
| Bananas | 4/5/2009 | An article about bananas |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And the "_site/2009/04/05/bananas.html" file should exist
|
||||
And the "_site/2009/04/01/oranges.html" file should exist
|
||||
And the "_site/2009/03/27/apples.html" file should not exist
|
||||
|
||||
Scenario: Copy over normally excluded files when they are explicitly included
|
||||
Given I have a ".gitignore" file that contains ".DS_Store"
|
||||
And I have an ".htaccess" file that contains "SomeDirective"
|
||||
And I have a configuration file with "include" set to:
|
||||
| value |
|
||||
| .gitignore |
|
||||
| .foo |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see ".DS_Store" in "_site/.gitignore"
|
||||
And the "_site/.htaccess" file should not exist
|
||||
|
||||
Scenario: Using a different layouts directory
|
||||
Given I have a _theme directory
|
||||
And I have a page theme that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}"
|
||||
And I have a post theme 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 |
|
||||
| layouts | _theme |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 2007-12-31 | post | content for entry1. |
|
||||
| entry2 | 2020-01-31 | post | content for entry2. |
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html"
|
||||
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
|
||||
And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html"
|
||||
|
||||
Scenario: Add a gem-based plugin
|
||||
Given I have an "index.html" file that contains "Whatever"
|
||||
And I have a configuration file with "gems" set to "[jekyll_test_plugin]"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Whatever" in "_site/index.html"
|
||||
And I should see "this is a test" in "_site/test.txt"
|
||||
|
||||
Scenario: Add an empty whitelist to restrict all gems
|
||||
Given I have an "index.html" file that contains "Whatever"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| gems | [jekyll_test_plugin] |
|
||||
| whitelist | [] |
|
||||
When I run jekyll build --safe
|
||||
Then the _site directory should exist
|
||||
And I should see "Whatever" in "_site/index.html"
|
||||
And the "_site/test.txt" file should not exist
|
||||
|
||||
Scenario: Add a whitelist to restrict some gems but allow others
|
||||
Given I have an "index.html" file that contains "Whatever"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| gems | [jekyll_test_plugin, jekyll_test_plugin_malicious] |
|
||||
| whitelist | [jekyll_test_plugin] |
|
||||
When I run jekyll build --safe
|
||||
Then the _site directory should exist
|
||||
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"
|
||||
|
||||
@@ -4,33 +4,14 @@ Feature: Site data
|
||||
In order to make the site slightly dynamic
|
||||
|
||||
Scenario: Use page variable in a page
|
||||
Given I have an "contact.html" page with title "Contact" that contains "{{ page.title }}: email@example.com"
|
||||
When I run jekyll build
|
||||
Given I have an "contact.html" page with title "Contact" that contains "{{ page.title }}: email@me.com"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Contact: email@example.com" in "_site/contact.html"
|
||||
|
||||
Scenario Outline: Use page.path variable in a page
|
||||
Given I have a <dir> directory
|
||||
And I have a "<path>" page that contains "Source path: {{ page.path }}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Source path: <path>" in "_site/<path>"
|
||||
|
||||
Examples:
|
||||
| dir | path |
|
||||
| . | index.html |
|
||||
| dir | dir/about.html |
|
||||
| dir/nested | dir/nested/page.html |
|
||||
|
||||
Scenario: Override page.path
|
||||
Given I have an "override.html" page with path "custom-override.html" that contains "Custom path: {{ page.path }}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "Custom path: custom-override.html" in "_site/override.html"
|
||||
And I should see "Contact: email@me.com" in "_site/contact.html"
|
||||
|
||||
Scenario: Use site.time variable
|
||||
Given I have an "index.html" page that contains "{{ site.time }}"
|
||||
When I run jekyll build
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see today's time in "_site/index.html"
|
||||
|
||||
@@ -38,11 +19,11 @@ Feature: Site data
|
||||
Given I have a _posts directory
|
||||
And I have an "index.html" page that contains "{{ site.posts.first.title }}: {{ site.posts.first.url }}"
|
||||
And I have the following posts:
|
||||
| title | date | content |
|
||||
| First Post | 2009-03-25 | My First Post |
|
||||
| Second Post | 2009-03-26 | My Second Post |
|
||||
| Third Post | 2009-03-27 | My Third Post |
|
||||
When I run jekyll build
|
||||
| title | date | content |
|
||||
| First Post | 3/25/2009 | My First Post |
|
||||
| Second Post | 3/26/2009 | My Second Post |
|
||||
| Third Post | 3/27/2009 | My Third Post |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Third Post: /2009/03/27/third-post.html" in "_site/index.html"
|
||||
|
||||
@@ -50,11 +31,11 @@ Feature: Site data
|
||||
Given I have a _posts directory
|
||||
And I have an "index.html" page that contains "{% for post in site.posts %} {{ post.title }} {% endfor %}"
|
||||
And I have the following posts:
|
||||
| title | date | content |
|
||||
| First Post | 2009-03-25 | My First Post |
|
||||
| Second Post | 2009-03-26 | My Second Post |
|
||||
| Third Post | 2009-03-27 | My Third Post |
|
||||
When I run jekyll build
|
||||
| title | date | content |
|
||||
| First Post | 3/25/2009 | My First Post |
|
||||
| Second Post | 3/26/2009 | My Second Post |
|
||||
| Third Post | 3/27/2009 | My Third Post |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Third Post Second Post First Post" in "_site/index.html"
|
||||
|
||||
@@ -62,10 +43,10 @@ Feature: Site data
|
||||
Given I have a _posts directory
|
||||
And I have an "index.html" page that contains "{% for post in site.categories.code %} {{ post.title }} {% endfor %}"
|
||||
And I have the following posts:
|
||||
| title | date | category | content |
|
||||
| Awesome Hack | 2009-03-26 | code | puts 'Hello World' |
|
||||
| Delicious Beer | 2009-03-26 | food | 1) Yuengling |
|
||||
When I run jekyll build
|
||||
| title | date | category | content |
|
||||
| Awesome Hack | 3/26/2009 | code | puts 'Hello World' |
|
||||
| Delicious Beer | 3/26/2009 | food | 1) Yuengling |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Awesome Hack" in "_site/index.html"
|
||||
|
||||
@@ -73,9 +54,9 @@ Feature: Site data
|
||||
Given I have a _posts directory
|
||||
And I have an "index.html" page that contains "{% for post in site.tags.beer %} {{ post.content }} {% endfor %}"
|
||||
And I have the following posts:
|
||||
| title | date | tag | content |
|
||||
| Delicious Beer | 2009-03-26 | beer | 1) Yuengling |
|
||||
When I run jekyll build
|
||||
| title | date | tag | content |
|
||||
| Delicious Beer | 3/26/2009 | beer | 1) Yuengling |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Yuengling" in "_site/index.html"
|
||||
|
||||
@@ -83,25 +64,19 @@ Feature: Site data
|
||||
Given I have a _posts directory
|
||||
And I have an "index.html" page that contains "{% for post in site.posts %}{{ post.title }}:{{ post.previous.title}},{{ post.next.title}} {% endfor %}"
|
||||
And I have the following posts:
|
||||
| title | date | content |
|
||||
| first | 2009-02-26 | first |
|
||||
| A | 2009-03-26 | A |
|
||||
| B | 2009-03-26 | B |
|
||||
| C | 2009-03-26 | C |
|
||||
| last | 2009-04-26 | last |
|
||||
When I run jekyll build
|
||||
| title | date | content |
|
||||
| first | 2/26/2009 | first |
|
||||
| A | 3/26/2009 | A |
|
||||
| B | 3/26/2009 | B |
|
||||
| C | 3/26/2009 | C |
|
||||
| last | 4/26/2009 | last |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "last:C, C:B,last B:A,C A:first,B first:,A" in "_site/index.html"
|
||||
|
||||
Scenario: Use configuration date in site payload
|
||||
Given I have an "index.html" page that contains "{{ site.url }}"
|
||||
And I have a configuration file with "url" set to "http://example.com"
|
||||
When I run jekyll build
|
||||
And I have a configuration file with "url" set to "http://mysite.com"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "http://example.com" in "_site/index.html"
|
||||
|
||||
Scenario: Access Jekyll version via jekyll.version
|
||||
Given I have an "index.html" page that contains "{{ jekyll.version }}"
|
||||
When I run jekyll build
|
||||
Then the _site directory should exist
|
||||
And I should see "\d+\.\d+\.\d+" in "_site/index.html"
|
||||
And I should see "http://mysite.com" in "_site/index.html"
|
||||
|
||||
@@ -1,39 +1,15 @@
|
||||
def file_content_from_hash(input_hash)
|
||||
matter_hash = input_hash.reject { |k, v| k == "content" }
|
||||
matter = matter_hash.map { |k, v| "#{k}: #{v}\n" }.join.chomp
|
||||
|
||||
content = if input_hash['input'] && input_hash['filter']
|
||||
"{{ #{input_hash['input']} | #{input_hash['filter']} }}"
|
||||
else
|
||||
input_hash['content']
|
||||
end
|
||||
|
||||
<<EOF
|
||||
---
|
||||
#{matter}
|
||||
---
|
||||
#{content}
|
||||
EOF
|
||||
end
|
||||
|
||||
Before do
|
||||
FileUtils.mkdir_p(TEST_DIR) unless File.exist?(TEST_DIR)
|
||||
FileUtils.mkdir(TEST_DIR)
|
||||
Dir.chdir(TEST_DIR)
|
||||
end
|
||||
|
||||
After do
|
||||
FileUtils.rm_rf(TEST_DIR) if File.exists?(TEST_DIR)
|
||||
FileUtils.rm(JEKYLL_COMMAND_OUTPUT_FILE) if File.exists?(JEKYLL_COMMAND_OUTPUT_FILE)
|
||||
Dir.chdir(TEST_DIR)
|
||||
FileUtils.rm_rf(TEST_DIR)
|
||||
end
|
||||
|
||||
World(Test::Unit::Assertions)
|
||||
|
||||
Given /^I have a blank site in "(.*)"$/ do |path|
|
||||
FileUtils.mkdir_p(path) unless File.exist?(path)
|
||||
end
|
||||
|
||||
Given /^I do not have a "(.*)" directory$/ do |path|
|
||||
File.directory?("#{TEST_DIR}/#{path}")
|
||||
FileUtils.mkdir(path)
|
||||
end
|
||||
|
||||
# Like "I have a foo file" but gives a yaml front matter so jekyll actually processes it
|
||||
@@ -45,34 +21,21 @@ Given /^I have an? "(.*)" page(?: with (.*) "(.*)")? that contains "(.*)"$/ do |
|
||||
---
|
||||
#{text}
|
||||
EOF
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have an? "(.*)" file that contains "(.*)"$/ do |file, text|
|
||||
File.open(file, 'w') do |f|
|
||||
f.write(text)
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have an? (.*) (layout|theme) that contains "(.*)"$/ do |name, type, text|
|
||||
folder = if type == 'layout'
|
||||
'_layouts'
|
||||
else
|
||||
'_theme'
|
||||
end
|
||||
destination_file = File.join(folder, name + '.html')
|
||||
destination_path = File.dirname(destination_file)
|
||||
unless File.exist?(destination_path)
|
||||
FileUtils.mkdir_p(destination_path)
|
||||
end
|
||||
File.open(destination_file, 'w') do |f|
|
||||
f.write(text)
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have an? "(.*)" file with content:$/ do |file, text|
|
||||
File.open(file, 'w') do |f|
|
||||
Given /^I have a (.*) layout that contains "(.*)"$/ do |layout, text|
|
||||
File.open(File.join('_layouts', layout + '.html'), 'w') do |f|
|
||||
f.write(text)
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,28 +43,38 @@ Given /^I have an? (.*) directory$/ do |dir|
|
||||
FileUtils.mkdir_p(dir)
|
||||
end
|
||||
|
||||
Given /^I have the following (draft|page|post)s?(?: (in|under) "([^"]+)")?:$/ do |status, direction, folder, table|
|
||||
table.hashes.each do |input_hash|
|
||||
title = slug(input_hash['title'])
|
||||
ext = input_hash['type'] || 'textile'
|
||||
before, after = location(folder, direction)
|
||||
Given /^I have the following posts?(?: (.*) "(.*)")?:$/ do |direction, folder, table|
|
||||
table.hashes.each do |post|
|
||||
date = Date.strptime(post['date'], '%m/%d/%Y').strftime('%Y-%m-%d')
|
||||
title = post['title'].downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-')
|
||||
|
||||
case status
|
||||
when "draft"
|
||||
dest_folder = '_drafts'
|
||||
filename = "#{title}.#{ext}"
|
||||
when "page"
|
||||
dest_folder = ''
|
||||
filename = "#{title}.#{ext}"
|
||||
when "post"
|
||||
parsed_date = Time.xmlschema(input_hash['date']) rescue Time.parse(input_hash['date'])
|
||||
dest_folder = '_posts'
|
||||
filename = "#{parsed_date.strftime('%Y-%m-%d')}-#{title}.#{ext}"
|
||||
if direction && direction == "in"
|
||||
before = folder || '.'
|
||||
elsif direction && direction == "under"
|
||||
after = folder || '.'
|
||||
end
|
||||
|
||||
path = File.join(before || '.', '_posts', after || '.', "#{date}-#{title}.#{post['type'] || 'textile'}")
|
||||
|
||||
matter_hash = {}
|
||||
%w(title layout tag tags category categories published author).each do |key|
|
||||
matter_hash[key] = post[key] if post[key]
|
||||
end
|
||||
matter = matter_hash.map { |k, v| "#{k}: #{v}\n" }.join.chomp
|
||||
|
||||
content = post['content']
|
||||
if post['input'] && post['filter']
|
||||
content = "{{ #{post['input']} | #{post['filter']} }}"
|
||||
end
|
||||
|
||||
path = File.join(before, dest_folder, after, filename)
|
||||
File.open(path, 'w') do |f|
|
||||
f.write file_content_from_hash(input_hash)
|
||||
f.write <<EOF
|
||||
---
|
||||
#{matter}
|
||||
---
|
||||
#{content}
|
||||
EOF
|
||||
f.close
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -109,6 +82,7 @@ end
|
||||
Given /^I have a configuration file with "(.*)" set to "(.*)"$/ do |key, value|
|
||||
File.open('_config.yml', 'w') do |f|
|
||||
f.write("#{key}: #{value}\n")
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
@@ -117,6 +91,7 @@ Given /^I have a configuration file with:$/ do |table|
|
||||
table.hashes.each do |row|
|
||||
f.write("#{row["key"]}: #{row["value"]}\n")
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
@@ -126,24 +101,17 @@ Given /^I have a configuration file with "([^\"]*)" set to:$/ do |key, table|
|
||||
table.hashes.each do |row|
|
||||
f.write("- #{row["value"]}\n")
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have fixture collections$/ do
|
||||
FileUtils.cp_r File.join(JEKYLL_SOURCE_DIR, "test", "source", "_methods"), source_dir
|
||||
|
||||
When /^I run jekyll$/ do
|
||||
run_jekyll
|
||||
end
|
||||
|
||||
##################
|
||||
#
|
||||
# Changing stuff
|
||||
#
|
||||
##################
|
||||
|
||||
When /^I run jekyll(.*)$/ do |args|
|
||||
status = run_jekyll(args)
|
||||
if !status || args.include?("--verbose")
|
||||
puts jekyll_run_output
|
||||
end
|
||||
When /^I debug jekyll$/ do
|
||||
run_jekyll(:debug => true)
|
||||
end
|
||||
|
||||
When /^I change "(.*)" to contain "(.*)"$/ do |file, text|
|
||||
@@ -152,46 +120,26 @@ When /^I change "(.*)" to contain "(.*)"$/ do |file, text|
|
||||
end
|
||||
end
|
||||
|
||||
When /^I delete the file "(.*)"$/ do |file|
|
||||
File.delete(file)
|
||||
end
|
||||
|
||||
Then /^the (.*) directory should +exist$/ do |dir|
|
||||
assert File.directory?(dir), "The directory \"#{dir}\" does not exist"
|
||||
end
|
||||
|
||||
Then /^the (.*) directory should not exist$/ do |dir|
|
||||
assert !File.directory?(dir), "The directory \"#{dir}\" exists"
|
||||
Then /^the (.*) directory should exist$/ do |dir|
|
||||
assert File.directory?(dir)
|
||||
end
|
||||
|
||||
Then /^I should see "(.*)" in "(.*)"$/ do |text, file|
|
||||
assert_match Regexp.new(text, Regexp::MULTILINE), file_contents(file)
|
||||
assert_match Regexp.new(text), File.open(file).readlines.join
|
||||
end
|
||||
|
||||
Then /^I should see exactly "(.*)" in "(.*)"$/ do |text, file|
|
||||
assert_equal text, file_contents(file).strip
|
||||
end
|
||||
|
||||
Then /^I should not see "(.*)" in "(.*)"$/ do |text, file|
|
||||
assert_no_match Regexp.new(text, Regexp::MULTILINE), file_contents(file)
|
||||
end
|
||||
|
||||
Then /^I should see escaped "(.*)" in "(.*)"$/ do |text, file|
|
||||
assert_match Regexp.new(Regexp.escape(text)), file_contents(file)
|
||||
end
|
||||
|
||||
Then /^the "(.*)" file should +exist$/ do |file|
|
||||
assert File.file?(file), "The file \"#{file}\" does not exist"
|
||||
Then /^the "(.*)" file should exist$/ do |file|
|
||||
assert File.file?(file)
|
||||
end
|
||||
|
||||
Then /^the "(.*)" file should not exist$/ do |file|
|
||||
assert !File.exist?(file), "The file \"#{file}\" exists"
|
||||
assert !File.exists?(file)
|
||||
end
|
||||
|
||||
Then /^I should see today's time in "(.*)"$/ do |file|
|
||||
assert_match Regexp.new(seconds_agnostic_time(Time.now)), file_contents(file)
|
||||
assert_match Regexp.new(Regexp.escape(Time.now.to_s)), File.open(file).readlines.join
|
||||
end
|
||||
|
||||
Then /^I should see today's date in "(.*)"$/ do |file|
|
||||
assert_match Regexp.new(Date.today.to_s), file_contents(file)
|
||||
assert_match Regexp.new(Date.today.to_s), File.open(file).readlines.join
|
||||
end
|
||||
|
||||
@@ -1,68 +1,19 @@
|
||||
require 'coveralls'
|
||||
Coveralls.wear_merged!
|
||||
|
||||
require 'fileutils'
|
||||
require 'rr'
|
||||
require 'test/unit'
|
||||
require 'time'
|
||||
|
||||
JEKYLL_SOURCE_DIR = File.dirname(File.dirname(File.dirname(__FILE__)))
|
||||
TEST_DIR = File.expand_path(File.join('..', '..', 'tmp', 'jekyll'), File.dirname(__FILE__))
|
||||
JEKYLL_PATH = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'jekyll')
|
||||
JEKYLL_COMMAND_OUTPUT_FILE = File.join(File.dirname(TEST_DIR), 'jekyll_output.txt')
|
||||
|
||||
def source_dir(*files)
|
||||
File.join(TEST_DIR, *files)
|
||||
World do
|
||||
include Test::Unit::Assertions
|
||||
end
|
||||
|
||||
def jekyll_output_file
|
||||
JEKYLL_COMMAND_OUTPUT_FILE
|
||||
TEST_DIR = File.join('/', 'tmp', 'jekyll')
|
||||
JEKYLL_PATH = File.join(ENV['PWD'], 'bin', 'jekyll')
|
||||
|
||||
def run_jekyll(opts = {})
|
||||
command = JEKYLL_PATH
|
||||
command << " >> /dev/null 2>&1" if opts[:debug].nil?
|
||||
system command
|
||||
end
|
||||
|
||||
def jekyll_run_output
|
||||
File.read(jekyll_output_file)
|
||||
end
|
||||
|
||||
def run_jekyll(args)
|
||||
system "#{JEKYLL_PATH} #{args} --trace > #{jekyll_output_file} 2>&1"
|
||||
end
|
||||
|
||||
def slug(title)
|
||||
if title
|
||||
title.downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-')
|
||||
else
|
||||
Time.now.strftime("%s%9N") # nanoseconds since the Epoch
|
||||
end
|
||||
end
|
||||
|
||||
def location(folder, direction)
|
||||
if folder
|
||||
before = folder if direction == "in"
|
||||
after = folder if direction == "under"
|
||||
end
|
||||
[before || '.', after || '.']
|
||||
end
|
||||
|
||||
def file_contents(path)
|
||||
File.open(path) do |file|
|
||||
file.readlines.join # avoid differences with \n and \r\n line endings
|
||||
end
|
||||
end
|
||||
|
||||
def seconds_agnostic_datetime(datetime = Time.now)
|
||||
date, time, zone = datetime.to_s.split(" ")
|
||||
time = seconds_agnostic_time(time)
|
||||
[
|
||||
Regexp.escape(date),
|
||||
"#{time}:\\d{2}",
|
||||
Regexp.escape(zone)
|
||||
].join("\\ ")
|
||||
end
|
||||
|
||||
def seconds_agnostic_time(time)
|
||||
if time.is_a? Time
|
||||
time = time.strftime("%H:%M:%S")
|
||||
end
|
||||
hour, minutes, _ = time.split(":")
|
||||
"#{hour}:#{minutes}"
|
||||
end
|
||||
# work around "invalid option: --format" cucumber bug (see #296)
|
||||
Test::Unit.run = true
|
||||
|
||||
170
jekyll.gemspec
170
jekyll.gemspec
@@ -1,62 +1,142 @@
|
||||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'jekyll/version'
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.specification_version = 2 if s.respond_to? :specification_version=
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.rubygems_version = '2.2.2'
|
||||
s.required_ruby_version = '>= 1.9.3'
|
||||
s.rubygems_version = '1.3.5'
|
||||
|
||||
s.name = 'jekyll'
|
||||
s.version = Jekyll::VERSION
|
||||
s.license = 'MIT'
|
||||
s.version = '0.10.0'
|
||||
s.date = '2010-12-16'
|
||||
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/jekyll/jekyll'
|
||||
s.homepage = 'http://github.com/mojombo/jekyll'
|
||||
|
||||
s.files = `git ls-files`.split($/)
|
||||
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
||||
s.require_paths = ["lib"]
|
||||
s.require_paths = %w[lib]
|
||||
|
||||
s.executables = ["jekyll"]
|
||||
s.default_executable = 'jekyll'
|
||||
|
||||
s.rdoc_options = ["--charset=UTF-8"]
|
||||
s.extra_rdoc_files = %w[README.markdown LICENSE]
|
||||
s.extra_rdoc_files = %w[README.textile LICENSE]
|
||||
|
||||
s.add_runtime_dependency('liquid', "~> 2.5.5")
|
||||
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('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-coffeescript', '~> 1.0')
|
||||
s.add_runtime_dependency('jekyll-sass-converter', '~> 1.0')
|
||||
s.add_runtime_dependency('liquid', ">= 1.9.0")
|
||||
s.add_runtime_dependency('classifier', ">= 1.3.1")
|
||||
s.add_runtime_dependency('directory_watcher', ">= 1.1.1")
|
||||
s.add_runtime_dependency('maruku', ">= 0.5.9")
|
||||
s.add_runtime_dependency('kramdown', ">= 0.13.2")
|
||||
s.add_runtime_dependency('albino', ">= 1.3.2")
|
||||
|
||||
s.add_development_dependency('rake', "~> 10.1")
|
||||
s.add_development_dependency('rdoc', "~> 3.11")
|
||||
s.add_development_dependency('redgreen', "~> 1.2")
|
||||
s.add_development_dependency('shoulda', "~> 3.5")
|
||||
s.add_development_dependency('rr', "~> 1.1")
|
||||
s.add_development_dependency('cucumber', "1.3.11")
|
||||
s.add_development_dependency('RedCloth', "~> 4.2")
|
||||
s.add_development_dependency('maruku', "0.7.0")
|
||||
s.add_development_dependency('rdiscount', "~> 1.6")
|
||||
s.add_development_dependency('launchy', "~> 2.3")
|
||||
s.add_development_dependency('simplecov', "~> 0.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')
|
||||
s.add_development_dependency('jekyll_test_plugin_malicious')
|
||||
s.add_development_dependency('rouge', '~> 1.3')
|
||||
s.add_development_dependency('redgreen', ">= 1.2.2")
|
||||
s.add_development_dependency('shoulda', ">= 2.11.3")
|
||||
s.add_development_dependency('rr', ">= 1.0.2")
|
||||
s.add_development_dependency('cucumber', ">= 0.10.0")
|
||||
s.add_development_dependency('RedCloth', ">= 4.2.1")
|
||||
s.add_development_dependency('rdiscount', ">= 1.6.5")
|
||||
|
||||
# = MANIFEST =
|
||||
s.files = %w[
|
||||
History.txt
|
||||
LICENSE
|
||||
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/errors.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/drupal.rb
|
||||
lib/jekyll/migrators/marley.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.com.rb
|
||||
lib/jekyll/migrators/wordpress.rb
|
||||
lib/jekyll/page.rb
|
||||
lib/jekyll/plugin.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/.htaccess
|
||||
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/deal.with.dots.html
|
||||
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_kramdown.rb
|
||||
test/test_page.rb
|
||||
test/test_pager.rb
|
||||
test/test_post.rb
|
||||
test/test_rdiscount.rb
|
||||
test/test_site.rb
|
||||
test/test_tags.rb
|
||||
]
|
||||
# = MANIFEST =
|
||||
|
||||
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
|
||||
end
|
||||
|
||||
146
lib/jekyll.rb
146
lib/jekyll.rb
@@ -18,111 +18,109 @@ require 'rubygems'
|
||||
# stdlib
|
||||
require 'fileutils'
|
||||
require 'time'
|
||||
require 'safe_yaml/load'
|
||||
require 'yaml'
|
||||
require 'English'
|
||||
require 'pathname'
|
||||
|
||||
# 3rd party
|
||||
require 'liquid'
|
||||
require 'kramdown'
|
||||
require 'colorator'
|
||||
require 'toml'
|
||||
require 'maruku'
|
||||
require 'albino'
|
||||
|
||||
# internal requires
|
||||
require 'jekyll/version'
|
||||
require 'jekyll/utils'
|
||||
require 'jekyll/stevenson'
|
||||
require 'jekyll/deprecator'
|
||||
require 'jekyll/configuration'
|
||||
require 'jekyll/document'
|
||||
require 'jekyll/collection'
|
||||
require 'jekyll/plugin_manager'
|
||||
require 'jekyll/frontmatter_defaults'
|
||||
require 'jekyll/core_ext'
|
||||
require 'jekyll/site'
|
||||
require 'jekyll/convertible'
|
||||
require 'jekyll/url'
|
||||
require 'jekyll/layout'
|
||||
require 'jekyll/page'
|
||||
require 'jekyll/post'
|
||||
require 'jekyll/excerpt'
|
||||
require 'jekyll/draft'
|
||||
require 'jekyll/filters'
|
||||
require 'jekyll/static_file'
|
||||
require 'jekyll/errors'
|
||||
require 'jekyll/related_posts'
|
||||
require 'jekyll/cleaner'
|
||||
require 'jekyll/entry_filter'
|
||||
require 'jekyll/layout_reader'
|
||||
require 'jekyll/publisher'
|
||||
require 'jekyll/renderer'
|
||||
|
||||
# extensions
|
||||
require 'jekyll/plugin'
|
||||
require 'jekyll/converter'
|
||||
require 'jekyll/generator'
|
||||
require 'jekyll/command'
|
||||
require 'jekyll/liquid_extensions'
|
||||
|
||||
require_all 'jekyll/commands'
|
||||
require_all 'jekyll/converters'
|
||||
require_all 'jekyll/converters/markdown'
|
||||
require_all 'jekyll/generators'
|
||||
require_all 'jekyll/tags'
|
||||
|
||||
# plugins
|
||||
require 'jekyll-coffeescript'
|
||||
require 'jekyll-sass-converter'
|
||||
|
||||
SafeYAML::OPTIONS[:suppress_warnings] = true
|
||||
|
||||
module Jekyll
|
||||
# Public: Generate a Jekyll configuration Hash by merging the default
|
||||
# options with anything in _config.yml, and adding the given options on top.
|
||||
VERSION = '0.10.0'
|
||||
|
||||
# Default options. Overriden by values in _config.yml or command-line opts.
|
||||
# (Strings rather symbols used for compatability with YAML).
|
||||
DEFAULTS = {
|
||||
'safe' => false,
|
||||
'auto' => false,
|
||||
'server' => false,
|
||||
'server_port' => 4000,
|
||||
|
||||
'source' => Dir.pwd,
|
||||
'destination' => File.join(Dir.pwd, '_site'),
|
||||
'plugins' => File.join(Dir.pwd, '_plugins'),
|
||||
|
||||
'future' => true,
|
||||
'lsi' => false,
|
||||
'pygments' => false,
|
||||
'markdown' => 'maruku',
|
||||
'permalink' => 'date',
|
||||
|
||||
'maruku' => {
|
||||
'use_tex' => false,
|
||||
'use_divs' => false,
|
||||
'png_engine' => 'blahtex',
|
||||
'png_dir' => 'images/latex',
|
||||
'png_url' => '/images/latex'
|
||||
},
|
||||
'rdiscount' => {
|
||||
'extensions' => []
|
||||
},
|
||||
'kramdown' => {
|
||||
'auto_ids' => true,
|
||||
'footnote_nr' => 1,
|
||||
'entity_output' => 'as_char',
|
||||
'toc_levels' => '1..6',
|
||||
'use_coderay' => false,
|
||||
|
||||
'coderay' => {
|
||||
'coderay_wrap' => 'div',
|
||||
'coderay_line_numbers' => 'inline',
|
||||
'coderay_line_number_start' => 1,
|
||||
'coderay_tab_width' => 4,
|
||||
'coderay_bold_every' => 10,
|
||||
'coderay_css' => 'style'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate a Jekyll configuration Hash by merging the default options
|
||||
# with anything in _config.yml, and adding the given options on top.
|
||||
#
|
||||
# override - A Hash of config directives that override any options in both
|
||||
# the defaults and the config file. See Jekyll::Configuration::DEFAULTS for a
|
||||
# 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 = Configuration[Configuration::DEFAULTS]
|
||||
override = Configuration[override].stringify_keys
|
||||
config = config.read_config_files(config.config_files(override))
|
||||
# _config.yml may override default source location, but until
|
||||
# then, we need to know where to look for _config.yml
|
||||
source = override['source'] || Jekyll::DEFAULTS['source']
|
||||
|
||||
# Get configuration from <source>/_config.yml
|
||||
config_file = File.join(source, '_config.yml')
|
||||
begin
|
||||
config = YAML.load_file(config_file)
|
||||
raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
|
||||
$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
|
||||
config = {}
|
||||
end
|
||||
|
||||
# Merge DEFAULTS < _config.yml < override
|
||||
config = Utils.deep_merge_hashes(config, override).stringify_keys
|
||||
set_timezone(config['timezone']) if config['timezone']
|
||||
|
||||
config
|
||||
end
|
||||
|
||||
# Static: Set the TZ environment variable to use the timezone specified
|
||||
#
|
||||
# timezone - the IANA Time Zone
|
||||
#
|
||||
# Returns nothing
|
||||
def self.set_timezone(timezone)
|
||||
ENV['TZ'] = timezone
|
||||
end
|
||||
|
||||
def self.logger
|
||||
@logger ||= Stevenson.new
|
||||
end
|
||||
|
||||
# Public: File system root
|
||||
#
|
||||
# Returns the root of the filesystem as a Pathname
|
||||
def self.fs_root
|
||||
@fs_root ||= "/"
|
||||
end
|
||||
|
||||
def self.sanitized_path(base_directory, questionable_path)
|
||||
clean_path = File.expand_path(questionable_path, fs_root)
|
||||
clean_path.gsub!(/\A\w\:\//, '/')
|
||||
unless clean_path.start_with?(base_directory)
|
||||
File.join(base_directory, clean_path)
|
||||
else
|
||||
clean_path
|
||||
end
|
||||
Jekyll::DEFAULTS.deep_merge(config).deep_merge(override)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
require 'set'
|
||||
|
||||
module Jekyll
|
||||
class Site
|
||||
# Handles the cleanup of a site's destination before it is built.
|
||||
class Cleaner
|
||||
attr_reader :site
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
# Cleans up the site's destination directory
|
||||
def cleanup!
|
||||
FileUtils.rm_rf(obsolete_files)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Private: The list of files and directories to be deleted during cleanup process
|
||||
#
|
||||
# Returns an Array of the file and directory paths
|
||||
def obsolete_files
|
||||
(existing_files - new_files - new_dirs + replaced_files).to_a
|
||||
end
|
||||
|
||||
# Private: The list of existing files, apart from those included in keep_files and hidden files.
|
||||
#
|
||||
# Returns a Set with the file paths
|
||||
def existing_files
|
||||
files = Set.new
|
||||
Dir.glob(File.join(site.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
|
||||
files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex
|
||||
end
|
||||
files
|
||||
end
|
||||
|
||||
# Private: The list of files to be created when site is built.
|
||||
#
|
||||
# Returns a Set with the file paths
|
||||
def new_files
|
||||
files = Set.new
|
||||
site.each_site_file { |item| files << item.destination(site.dest) }
|
||||
files
|
||||
end
|
||||
|
||||
# Private: The list of directories to be created when site is built.
|
||||
# These are the parent directories of the files in #new_files.
|
||||
#
|
||||
# Returns a Set with the directory paths
|
||||
def new_dirs
|
||||
new_files.map { |file| File.dirname(file) }.to_set
|
||||
end
|
||||
|
||||
# Private: The list of existing files that will be replaced by a directory during build
|
||||
#
|
||||
# Returns a Set with the file paths
|
||||
def replaced_files
|
||||
new_dirs.select { |dir| File.file?(dir) }.to_set
|
||||
end
|
||||
|
||||
# Private: Creates a regular expression from the config's keep_files array
|
||||
#
|
||||
# Examples
|
||||
# ['.git','.svn'] creates the following regex: /\/(\.git|\/.svn)/
|
||||
#
|
||||
# Returns the regular expression
|
||||
def keep_file_regex
|
||||
or_list = site.keep_files.join("|")
|
||||
pattern = "\/(#{or_list.gsub(".", "\.")})"
|
||||
Regexp.new pattern
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,147 +0,0 @@
|
||||
module Jekyll
|
||||
class Collection
|
||||
attr_reader :site, :label, :metadata
|
||||
|
||||
# Create a new Collection.
|
||||
#
|
||||
# site - the site to which this collection belongs.
|
||||
# label - the name of the collection
|
||||
#
|
||||
# Returns nothing.
|
||||
def initialize(site, label)
|
||||
@site = site
|
||||
@label = sanitize_label(label)
|
||||
@metadata = extract_metadata
|
||||
end
|
||||
|
||||
# Fetch the Documents in this collection.
|
||||
# Defaults to an empty array if no documents have been read in.
|
||||
#
|
||||
# Returns an array of Jekyll::Document objects.
|
||||
def docs
|
||||
@docs ||= []
|
||||
end
|
||||
|
||||
# Read the allowed documents into the collection's array of docs.
|
||||
#
|
||||
# Returns the sorted array of docs.
|
||||
def read
|
||||
filtered_entries.each do |file_path|
|
||||
doc = Jekyll::Document.new(Jekyll.sanitized_path(directory, file_path), { site: site, collection: self })
|
||||
doc.read
|
||||
docs << doc
|
||||
end
|
||||
docs.sort!
|
||||
end
|
||||
|
||||
# All the entries in this collection.
|
||||
#
|
||||
# Returns an Array of file paths to the documents in this collection
|
||||
# relative to the collection's directory
|
||||
def entries
|
||||
return Array.new unless exists?
|
||||
Dir.glob(File.join(directory, "**", "*.*")).map do |entry|
|
||||
entry[File.join(directory, "")] = ''; entry
|
||||
end
|
||||
end
|
||||
|
||||
# Filtered version of the entries in this collection.
|
||||
# See `Jekyll::EntryFilter#filter` for more information.
|
||||
#
|
||||
# Returns a list of filtered entry paths.
|
||||
def filtered_entries
|
||||
return Array.new unless exists?
|
||||
Dir.chdir(directory) do
|
||||
entry_filter.filter(entries)
|
||||
end
|
||||
end
|
||||
|
||||
# The directory for this Collection, relative to the site source.
|
||||
#
|
||||
# Returns a String containing the directory name where the collection
|
||||
# is stored on the filesystem.
|
||||
def relative_directory
|
||||
"_#{label}"
|
||||
end
|
||||
|
||||
# The full path to the directory containing the
|
||||
#
|
||||
# Returns a String containing th directory name where the collection
|
||||
# is stored on the filesystem.
|
||||
def directory
|
||||
Jekyll.sanitized_path(site.source, relative_directory)
|
||||
end
|
||||
|
||||
# Checks whether the directory "exists" for this collection.
|
||||
# The directory must exist on the filesystem and must not be a symlink
|
||||
# if in safe mode.
|
||||
#
|
||||
# Returns false if the directory doesn't exist or if it's a symlink
|
||||
# and we're in safe mode.
|
||||
def exists?
|
||||
File.directory?(directory) && !(File.symlink?(directory) && site.safe)
|
||||
end
|
||||
|
||||
# The entry filter for this collection.
|
||||
# Creates an instance of Jekyll::EntryFilter.
|
||||
#
|
||||
# Returns the instance of Jekyll::EntryFilter for this collection.
|
||||
def entry_filter
|
||||
@entry_filter ||= Jekyll::EntryFilter.new(site, relative_directory)
|
||||
end
|
||||
|
||||
# An inspect string.
|
||||
#
|
||||
# Returns the inspect string
|
||||
def inspect
|
||||
"#<Jekyll::Collection @label=#{label} docs=#{docs}>"
|
||||
end
|
||||
|
||||
# Produce a sanitized label name
|
||||
# Label names may not contain anything but alphanumeric characters,
|
||||
# underscores, and hyphens.
|
||||
#
|
||||
# label - the possibly-unsafe label
|
||||
#
|
||||
# Returns a sanitized version of the label.
|
||||
def sanitize_label(label)
|
||||
label.gsub(/[^a-z0-9_\-]/i, '')
|
||||
end
|
||||
|
||||
# Produce a representation of this Collection for use in Liquid.
|
||||
# Exposes two attributes:
|
||||
# - label
|
||||
# - docs
|
||||
#
|
||||
# Returns a representation of this collection for use in Liquid.
|
||||
def to_liquid
|
||||
metadata.merge({
|
||||
"label" => label,
|
||||
"docs" => docs,
|
||||
"directory" => directory,
|
||||
"output" => write?,
|
||||
"relative_directory" => relative_directory
|
||||
})
|
||||
end
|
||||
|
||||
# Whether the collection's documents ought to be written as individual
|
||||
# files in the output.
|
||||
#
|
||||
# Returns true if the 'write' metadata is true, false otherwise.
|
||||
def write?
|
||||
!!metadata['output']
|
||||
end
|
||||
|
||||
# Extract options for this collection from the site configuration.
|
||||
#
|
||||
# Returns the metadata for this collection
|
||||
def extract_metadata
|
||||
if site.config['collections'].is_a?(Hash)
|
||||
site.config['collections'][label] || Hash.new
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,81 +0,0 @@
|
||||
module Jekyll
|
||||
class Command
|
||||
|
||||
class << self
|
||||
|
||||
# A list of subclasses of Jekyll::Command
|
||||
def subclasses
|
||||
@subclasses ||= []
|
||||
end
|
||||
|
||||
# Keep a list of subclasses of Jekyll::Command every time it's inherited
|
||||
# Called automatically.
|
||||
#
|
||||
# base - the subclass
|
||||
#
|
||||
# Returns nothing
|
||||
def inherited(base)
|
||||
subclasses << base
|
||||
super(base)
|
||||
end
|
||||
|
||||
# Listing of all directories (globbed to include subfiles and folders)
|
||||
#
|
||||
# source - the source path
|
||||
# destination - the destination path
|
||||
#
|
||||
# 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 += ['*']
|
||||
end
|
||||
end
|
||||
|
||||
# Run Site#process and catch errors
|
||||
#
|
||||
# site - the Jekyll::Site object
|
||||
#
|
||||
# Returns nothing
|
||||
def process_site(site)
|
||||
site.process
|
||||
rescue Jekyll::FatalException => e
|
||||
Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:"
|
||||
Jekyll.logger.error "", "------------------------------------"
|
||||
Jekyll.logger.error "", e.message
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Create a full Jekyll configuration with the options passed in as overrides
|
||||
#
|
||||
# options - the configuration overrides
|
||||
#
|
||||
# Returns a full Jekyll configuration
|
||||
def configuration_from_options(options)
|
||||
Jekyll.configuration(options)
|
||||
end
|
||||
|
||||
# Add common options to a command for building configuration
|
||||
#
|
||||
# c - the Jekyll::Command to add these options to
|
||||
#
|
||||
# Returns nothing
|
||||
def add_build_options(c)
|
||||
c.option 'config', '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
|
||||
c.option 'future', '--future', 'Publishes posts with a future date'
|
||||
c.option 'limit_posts', '--limit_posts MAX_POSTS', Integer, 'Limits the number of posts to parse and publish'
|
||||
c.option 'watch', '-w', '--watch', 'Watch for changes and rebuild'
|
||||
c.option 'force_polling', '--force_polling', 'Force watch to use polling'
|
||||
c.option 'lsi', '--lsi', 'Use LSI for improved related posts'
|
||||
c.option 'show_drafts', '-D', '--drafts', 'Render posts in the _drafts folder'
|
||||
c.option 'unpublished', '--unpublished', 'Render posts that were marked as unpublished'
|
||||
c.option 'quiet', '-q', '--quiet', 'Silence output.'
|
||||
c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,106 +0,0 @@
|
||||
module Jekyll
|
||||
module Commands
|
||||
class Build < Command
|
||||
|
||||
class << self
|
||||
|
||||
# Create the Mercenary command for the Jekyll CLI for this Command
|
||||
def init_with_program(prog)
|
||||
prog.command(:build) do |c|
|
||||
c.syntax 'build [options]'
|
||||
c.description 'Build your site'
|
||||
|
||||
add_build_options(c)
|
||||
|
||||
c.action do |args, options|
|
||||
options["serving"] = false
|
||||
Jekyll::Commands::Build.process(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Build your jekyll site
|
||||
# Continuously watch if `watch` is set to true in the config.
|
||||
def process(options)
|
||||
options = configuration_from_options(options)
|
||||
site = Jekyll::Site.new(options)
|
||||
|
||||
Jekyll.logger.log_level = :error if options['quiet']
|
||||
|
||||
build(site, options)
|
||||
watch(site, options) if options['watch']
|
||||
end
|
||||
|
||||
# Build your Jekyll site.
|
||||
#
|
||||
# site - the Jekyll::Site instance to build
|
||||
# options - the
|
||||
#
|
||||
# Returns nothing.
|
||||
def build(site, options)
|
||||
source = options['source']
|
||||
destination = options['destination']
|
||||
Jekyll.logger.info "Source:", source
|
||||
Jekyll.logger.info "Destination:", destination
|
||||
Jekyll.logger.info "Generating..."
|
||||
process_site(site)
|
||||
Jekyll.logger.info "", "done."
|
||||
end
|
||||
|
||||
# Private: Watch for file changes and rebuild the site.
|
||||
#
|
||||
# site - A Jekyll::Site instance
|
||||
# options - A Hash of options passed to the command
|
||||
#
|
||||
# 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
|
||||
end
|
||||
|
||||
end # end of class << self
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,30 +0,0 @@
|
||||
module Jekyll
|
||||
module Commands
|
||||
class Docs < Command
|
||||
|
||||
class << self
|
||||
|
||||
def init_with_program(prog)
|
||||
prog.command(:docs) do |c|
|
||||
c.syntax 'docs'
|
||||
c.description "Launch local server with docs for Jekyll v#{Jekyll::VERSION}"
|
||||
|
||||
c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
|
||||
c.option 'host', '-H', '--host [HOST]', 'Host to bind to'
|
||||
|
||||
c.action do |args, options|
|
||||
options.merge!({
|
||||
'source' => File.expand_path("../../../site", File.dirname(__FILE__)),
|
||||
'destination' => File.expand_path("../../../site/_site", File.dirname(__FILE__))
|
||||
})
|
||||
Jekyll::Commands::Build.process(options)
|
||||
Jekyll::Commands::Serve.process(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,84 +0,0 @@
|
||||
module Jekyll
|
||||
module Commands
|
||||
class Doctor < Command
|
||||
class << self
|
||||
|
||||
def init_with_program(prog)
|
||||
prog.command(:doctor) do |c|
|
||||
c.syntax 'doctor'
|
||||
c.description 'Search site and print specific deprecation warnings'
|
||||
c.alias(:hyde)
|
||||
|
||||
c.option '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
|
||||
|
||||
c.action do |args, options|
|
||||
Jekyll::Commands::Doctor.process(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process(options)
|
||||
site = Jekyll::Site.new(configuration_from_options(options))
|
||||
site.read
|
||||
|
||||
if healthy?(site)
|
||||
Jekyll.logger.info "Your test results", "are in. Everything looks fine."
|
||||
else
|
||||
abort
|
||||
end
|
||||
end
|
||||
|
||||
def healthy?(site)
|
||||
[
|
||||
!deprecated_relative_permalinks(site),
|
||||
!conflicting_urls(site)
|
||||
].all?
|
||||
end
|
||||
|
||||
def deprecated_relative_permalinks(site)
|
||||
contains_deprecated_pages = false
|
||||
site.pages.each do |page|
|
||||
if page.uses_relative_permalinks
|
||||
Jekyll.logger.warn "Deprecation:", "'#{page.path}' uses relative" +
|
||||
" permalinks which will be deprecated in" +
|
||||
" Jekyll v2.0.0 and beyond."
|
||||
contains_deprecated_pages = true
|
||||
end
|
||||
end
|
||||
contains_deprecated_pages
|
||||
end
|
||||
|
||||
def conflicting_urls(site)
|
||||
conflicting_urls = false
|
||||
urls = {}
|
||||
urls = collect_urls(urls, site.pages, site.dest)
|
||||
urls = collect_urls(urls, site.posts, site.dest)
|
||||
urls.each do |url, paths|
|
||||
if paths.size > 1
|
||||
conflicting_urls = true
|
||||
Jekyll.logger.warn "Conflict:", "The URL '#{url}' is the destination" +
|
||||
" for the following pages: #{paths.join(", ")}"
|
||||
end
|
||||
end
|
||||
conflicting_urls
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collect_urls(urls, things, destination)
|
||||
things.each do |thing|
|
||||
dest = thing.destination(destination)
|
||||
if urls[dest]
|
||||
urls[dest] << thing.path
|
||||
else
|
||||
urls[dest] = [thing.path]
|
||||
end
|
||||
end
|
||||
urls
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,80 +0,0 @@
|
||||
require 'erb'
|
||||
|
||||
module Jekyll
|
||||
module Commands
|
||||
class New < Command
|
||||
def self.init_with_program(prog)
|
||||
prog.command(:new) do |c|
|
||||
c.syntax 'new PATH'
|
||||
c.description 'Creates a new Jekyll site scaffold in PATH'
|
||||
|
||||
c.option 'force', '--force', 'Force creation even if PATH already exists'
|
||||
c.option 'blank', '--blank', 'Creates scaffolding but with empty files'
|
||||
|
||||
c.action do |args, options|
|
||||
Jekyll::Commands::New.process(args, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.process(args, options = {})
|
||||
raise ArgumentError.new('You must specify a path.') if args.empty?
|
||||
|
||||
new_blog_path = File.expand_path(args.join(" "), Dir.pwd)
|
||||
FileUtils.mkdir_p new_blog_path
|
||||
if preserve_source_location?(new_blog_path, options)
|
||||
Jekyll.logger.abort_with "Conflict:", "#{new_blog_path} exists and is not empty."
|
||||
end
|
||||
|
||||
if options["blank"]
|
||||
create_blank_site new_blog_path
|
||||
else
|
||||
create_sample_files new_blog_path
|
||||
|
||||
File.open(File.expand_path(initialized_post_name, new_blog_path), "w") do |f|
|
||||
f.write(scaffold_post_content)
|
||||
end
|
||||
end
|
||||
|
||||
Jekyll.logger.info "New jekyll site installed in #{new_blog_path}."
|
||||
end
|
||||
|
||||
def self.create_blank_site(path)
|
||||
Dir.chdir(path) do
|
||||
FileUtils.mkdir(%w(_layouts _posts _drafts))
|
||||
FileUtils.touch("index.html")
|
||||
end
|
||||
end
|
||||
|
||||
def self.scaffold_post_content
|
||||
ERB.new(File.read(File.expand_path(scaffold_path, site_template))).result
|
||||
end
|
||||
|
||||
# Internal: Gets the filename of the sample post to be created
|
||||
#
|
||||
# Returns the filename of the sample post, as a String
|
||||
def self.initialized_post_name
|
||||
"_posts/#{Time.now.strftime('%Y-%m-%d')}-welcome-to-jekyll.markdown"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.preserve_source_location?(path, options)
|
||||
!options["force"] && !Dir["#{path}/**/*"].empty?
|
||||
end
|
||||
|
||||
def self.create_sample_files(path)
|
||||
FileUtils.cp_r site_template + '/.', path
|
||||
FileUtils.rm File.expand_path(scaffold_path, path)
|
||||
end
|
||||
|
||||
def self.site_template
|
||||
File.expand_path("../../site_template", File.dirname(__FILE__))
|
||||
end
|
||||
|
||||
def self.scaffold_path
|
||||
"_posts/0000-00-00-welcome-to-jekyll.markdown.erb"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,128 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
module Jekyll
|
||||
module Commands
|
||||
class Serve < Command
|
||||
|
||||
class << self
|
||||
|
||||
def init_with_program(prog)
|
||||
prog.command(:serve) do |c|
|
||||
c.syntax 'serve [options]'
|
||||
c.description 'Serve your site locally'
|
||||
c.alias :server
|
||||
|
||||
add_build_options(c)
|
||||
|
||||
c.option 'detach', '-B', '--detach', 'Run the server in the background (detach)'
|
||||
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.action do |args, options|
|
||||
options["serving"] ||= true
|
||||
Jekyll::Commands::Build.process(options)
|
||||
Jekyll::Commands::Serve.process(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Boot up a WEBrick server which points to the compiled site's root.
|
||||
def process(options)
|
||||
options = configuration_from_options(options)
|
||||
destination = options['destination']
|
||||
setup(destination)
|
||||
|
||||
s = WEBrick::HTTPServer.new(webrick_options(options))
|
||||
s.unmount("")
|
||||
|
||||
s.mount(
|
||||
options['baseurl'],
|
||||
WEBrick::HTTPServlet::FileHandler,
|
||||
destination,
|
||||
file_handler_options
|
||||
)
|
||||
|
||||
Jekyll.logger.info "Server address:", server_address(s, options)
|
||||
|
||||
if options['detach'] # detach the server
|
||||
pid = Process.fork { s.start }
|
||||
Process.detach(pid)
|
||||
Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `kill -9 #{pid}' to stop the server."
|
||||
else # create a new server thread, then join it with current terminal
|
||||
t = Thread.new { s.start }
|
||||
trap("INT") { s.shutdown }
|
||||
t.join
|
||||
end
|
||||
end
|
||||
|
||||
def setup(destination)
|
||||
require 'webrick'
|
||||
|
||||
FileUtils.mkdir_p(destination)
|
||||
|
||||
# monkey patch WEBrick using custom 404 page (/404.html)
|
||||
if File.exist?(File.join(destination, '404.html'))
|
||||
WEBrick::HTTPResponse.class_eval do
|
||||
def create_error_page
|
||||
@header['content-type'] = "text/html; charset=UTF-8"
|
||||
@body = IO.read(File.join(@config[:DocumentRoot], '404.html'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def webrick_options(config)
|
||||
opts = {
|
||||
:DocumentRoot => config['destination'],
|
||||
:Port => config['port'],
|
||||
:BindAddress => config['host'],
|
||||
:MimeTypes => mime_types,
|
||||
:DoNotReverseLookup => true,
|
||||
:StartCallback => start_callback(config['detach']),
|
||||
:DirectoryIndex => %w(index.html index.htm index.cgi index.rhtml index.xml)
|
||||
}
|
||||
|
||||
if !config['verbose']
|
||||
opts.merge!({
|
||||
:AccessLog => [],
|
||||
:Logger => WEBrick::Log.new([], WEBrick::Log::WARN)
|
||||
})
|
||||
end
|
||||
|
||||
opts
|
||||
end
|
||||
|
||||
def start_callback(detached)
|
||||
unless detached
|
||||
Proc.new { Jekyll.logger.info "Server running...", "press ctrl-c to stop." }
|
||||
end
|
||||
end
|
||||
|
||||
def mime_types
|
||||
mime_types_file = File.expand_path('../mime.types', File.dirname(__FILE__))
|
||||
WEBrick::HTTPUtils::load_mime_types(mime_types_file)
|
||||
end
|
||||
|
||||
def server_address(server, options)
|
||||
baseurl = "#{options['baseurl']}/" if options['baseurl']
|
||||
[
|
||||
"http://",
|
||||
server.config[:BindAddress],
|
||||
":",
|
||||
server.config[:Port],
|
||||
baseurl || ""
|
||||
].map(&:to_s).join("")
|
||||
end
|
||||
|
||||
# recreate NondisclosureName under utf-8 circumstance
|
||||
def file_handler_options
|
||||
fh_option = WEBrick::Config::FileHandler
|
||||
fh_option[:NondisclosureName] = ['.ht*','~*']
|
||||
fh_option
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,256 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
module Jekyll
|
||||
class Configuration < Hash
|
||||
|
||||
# Default options. Overridden by values in _config.yml.
|
||||
# Strings rather than symbols are used for compatibility with YAML.
|
||||
DEFAULTS = {
|
||||
'source' => Dir.pwd,
|
||||
'destination' => File.join(Dir.pwd, '_site'),
|
||||
'plugins' => '_plugins',
|
||||
'layouts' => '_layouts',
|
||||
'data_source' => '_data',
|
||||
'keep_files' => ['.git','.svn'],
|
||||
'gems' => [],
|
||||
'collections' => nil,
|
||||
|
||||
'timezone' => nil, # use the local timezone
|
||||
|
||||
'encoding' => 'utf-8', # always use utf-8 encoding. NEVER FORGET
|
||||
|
||||
'safe' => false,
|
||||
'detach' => false, # default to not detaching the server
|
||||
'show_drafts' => nil,
|
||||
'limit_posts' => 0,
|
||||
'lsi' => false,
|
||||
'future' => true, # remove and make true just default
|
||||
'unpublished' => false,
|
||||
|
||||
'relative_permalinks' => false,
|
||||
|
||||
'markdown' => 'kramdown',
|
||||
'highlighter' => 'pygments',
|
||||
'permalink' => 'date',
|
||||
'baseurl' => '',
|
||||
'include' => ['.htaccess'],
|
||||
'exclude' => [],
|
||||
'paginate_path' => '/page:num',
|
||||
|
||||
'markdown_ext' => 'markdown,mkdown,mkdn,mkd,md',
|
||||
'textile_ext' => 'textile',
|
||||
|
||||
'port' => '4000',
|
||||
'host' => '0.0.0.0',
|
||||
|
||||
'excerpt_separator' => "\n\n",
|
||||
|
||||
'defaults' => [],
|
||||
|
||||
'maruku' => {
|
||||
'use_tex' => false,
|
||||
'use_divs' => false,
|
||||
'png_engine' => 'blahtex',
|
||||
'png_dir' => 'images/latex',
|
||||
'png_url' => '/images/latex',
|
||||
'fenced_code_blocks' => true
|
||||
},
|
||||
|
||||
'rdiscount' => {
|
||||
'extensions' => []
|
||||
},
|
||||
|
||||
'redcarpet' => {
|
||||
'extensions' => []
|
||||
},
|
||||
|
||||
'kramdown' => {
|
||||
'auto_ids' => true,
|
||||
'footnote_nr' => 1,
|
||||
'entity_output' => 'as_char',
|
||||
'toc_levels' => '1..6',
|
||||
'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
|
||||
'use_coderay' => false,
|
||||
|
||||
'coderay' => {
|
||||
'coderay_wrap' => 'div',
|
||||
'coderay_line_numbers' => 'inline',
|
||||
'coderay_line_number_start' => 1,
|
||||
'coderay_tab_width' => 4,
|
||||
'coderay_bold_every' => 10,
|
||||
'coderay_css' => 'style'
|
||||
}
|
||||
},
|
||||
|
||||
'redcloth' => {
|
||||
'hard_breaks' => true
|
||||
}
|
||||
}
|
||||
|
||||
# Public: Turn all keys into string
|
||||
#
|
||||
# Return a copy of the hash where all its keys are strings
|
||||
def stringify_keys
|
||||
reduce({}) { |hsh,(k,v)| hsh.merge(k.to_s => v) }
|
||||
end
|
||||
|
||||
# Public: Directory of the Jekyll source folder
|
||||
#
|
||||
# override - the command-line options hash
|
||||
#
|
||||
# Returns the path to the Jekyll source directory
|
||||
def source(override)
|
||||
override['source'] || self['source'] || DEFAULTS['source']
|
||||
end
|
||||
|
||||
def safe_load_file(filename)
|
||||
case File.extname(filename)
|
||||
when '.toml'
|
||||
TOML.load_file(filename)
|
||||
when /\.y(a)?ml/
|
||||
SafeYAML.load_file(filename)
|
||||
else
|
||||
raise ArgumentError, "No parser for '#{filename}' is available. Use a .toml or .y(a)ml file instead."
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Generate list of configuration files from the override
|
||||
#
|
||||
# override - the command-line options hash
|
||||
#
|
||||
# Returns an Array of config files
|
||||
def config_files(override)
|
||||
# Get configuration from <source>/_config.yml or <source>/<config_file>
|
||||
config_files = override.delete('config')
|
||||
if config_files.to_s.empty?
|
||||
config_files = File.join(source(override), "_config.yml")
|
||||
@default_config_file = true
|
||||
end
|
||||
config_files = [config_files] unless config_files.is_a? Array
|
||||
config_files
|
||||
end
|
||||
|
||||
# Public: Read configuration and return merged Hash
|
||||
#
|
||||
# file - the path to the YAML file to be read in
|
||||
#
|
||||
# Returns this configuration, overridden by the values in the file
|
||||
def read_config_file(file)
|
||||
next_config = safe_load_file(file)
|
||||
raise ArgumentError.new("Configuration file: (INVALID) #{file}".yellow) unless next_config.is_a?(Hash)
|
||||
Jekyll.logger.info "Configuration file:", file
|
||||
next_config
|
||||
rescue SystemCallError
|
||||
if @default_config_file
|
||||
Jekyll.logger.warn "Configuration file:", "none"
|
||||
{}
|
||||
else
|
||||
Jekyll.logger.error "Fatal:", "The configuration file '#{file}' could not be found."
|
||||
raise LoadError, "The Configuration file '#{file}' could not be found."
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Read in a list of configuration files and merge with this hash
|
||||
#
|
||||
# files - the list of configuration file paths
|
||||
#
|
||||
# Returns the full configuration, with the defaults overridden by the values in the
|
||||
# configuration files
|
||||
def read_config_files(files)
|
||||
configuration = clone
|
||||
|
||||
begin
|
||||
files.each do |config_file|
|
||||
new_config = read_config_file(config_file)
|
||||
configuration = Utils.deep_merge_hashes(configuration, new_config)
|
||||
end
|
||||
rescue ArgumentError => err
|
||||
Jekyll.logger.warn "WARNING:", "Error reading configuration. " +
|
||||
"Using defaults (and options)."
|
||||
$stderr.puts "#{err}"
|
||||
end
|
||||
|
||||
configuration.fix_common_issues.backwards_compatibilize
|
||||
end
|
||||
|
||||
# Public: Split a CSV string into an array containing its values
|
||||
#
|
||||
# csv - the string of comma-separated values
|
||||
#
|
||||
# Returns an array of the values contained in the CSV
|
||||
def csv_to_array(csv)
|
||||
csv.split(",").map(&:strip)
|
||||
end
|
||||
|
||||
# Public: Ensure the proper options are set in the configuration to allow for
|
||||
# backwards-compatibility with Jekyll pre-1.0
|
||||
#
|
||||
# Returns the backwards-compatible configuration
|
||||
def backwards_compatibilize
|
||||
config = clone
|
||||
# Provide backwards-compatibility
|
||||
if config.has_key?('auto') || config.has_key?('watch')
|
||||
Jekyll.logger.warn "Deprecation:", "Auto-regeneration can no longer" +
|
||||
" be set from your configuration file(s). Use the"+
|
||||
" --watch/-w command-line option instead."
|
||||
config.delete('auto')
|
||||
config.delete('watch')
|
||||
end
|
||||
|
||||
if config.has_key? 'server'
|
||||
Jekyll.logger.warn "Deprecation:", "The 'server' configuration option" +
|
||||
" is no longer accepted. Use the 'jekyll serve'" +
|
||||
" subcommand to serve your site with WEBrick."
|
||||
config.delete('server')
|
||||
end
|
||||
|
||||
if config.has_key? 'server_port'
|
||||
Jekyll.logger.warn "Deprecation:", "The 'server_port' configuration option" +
|
||||
" has been renamed to 'port'. Please update your config" +
|
||||
" file accordingly."
|
||||
# copy but don't overwrite:
|
||||
config['port'] = config['server_port'] unless config.has_key?('port')
|
||||
config.delete('server_port')
|
||||
end
|
||||
|
||||
if config.has_key? 'pygments'
|
||||
Jekyll.logger.warn "Deprecation:", "The 'pygments' configuration option" +
|
||||
" has been renamed to 'highlighter'. Please update your" +
|
||||
" config file accordingly. The allowed values are 'rouge', " +
|
||||
"'pygments' or null."
|
||||
|
||||
config['highlighter'] = 'pygments' if config['pygments']
|
||||
config.delete('pygments')
|
||||
end
|
||||
|
||||
%w[include exclude].each do |option|
|
||||
if config.fetch(option, []).is_a?(String)
|
||||
Jekyll.logger.warn "Deprecation:", "The '#{option}' configuration option" +
|
||||
" must now be specified as an array, but you specified" +
|
||||
" a string. For now, we've treated the string you provided" +
|
||||
" as a list of comma-separated values."
|
||||
config[option] = csv_to_array(config[option])
|
||||
end
|
||||
end
|
||||
|
||||
if config.fetch('markdown', 'kramdown').to_s.downcase.eql?("maruku")
|
||||
Jekyll::Deprecator.deprecation_message "You're using the 'maruku' " +
|
||||
"Markdown processor. Maruku support has been deprecated and will " +
|
||||
"be removed in 3.0.0. We recommend you switch to Kramdown."
|
||||
end
|
||||
config
|
||||
end
|
||||
|
||||
def fix_common_issues
|
||||
config = clone
|
||||
|
||||
if config.has_key?('paginate') && (!config['paginate'].is_a?(Integer) || config['paginate'] < 1)
|
||||
Jekyll.logger.warn "Config Warning:", "The `paginate` key must be a" +
|
||||
" positive integer or nil. It's currently set to '#{config['paginate'].inspect}'."
|
||||
config['paginate'] = nil
|
||||
end
|
||||
|
||||
config
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,27 +1,28 @@
|
||||
module Jekyll
|
||||
|
||||
class Converter < Plugin
|
||||
# Public: Get or set the highlighter prefix. When an argument is specified,
|
||||
# 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.
|
||||
#
|
||||
# highlighter_prefix - The String prefix (default: nil).
|
||||
# pygments_prefix - The String prefix (default: nil).
|
||||
#
|
||||
# Returns the String prefix.
|
||||
def self.highlighter_prefix(highlighter_prefix = nil)
|
||||
@highlighter_prefix = highlighter_prefix if highlighter_prefix
|
||||
@highlighter_prefix
|
||||
def self.pygments_prefix(pygments_prefix = nil)
|
||||
@pygments_prefix = pygments_prefix if pygments_prefix
|
||||
@pygments_prefix
|
||||
end
|
||||
|
||||
# Public: Get or set the highlighter suffix. When an argument is specified,
|
||||
# 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.
|
||||
#
|
||||
# highlighter_suffix - The String suffix (default: nil).
|
||||
# pygments_suffix - The String suffix (default: nil).
|
||||
#
|
||||
# Returns the String suffix.
|
||||
def self.highlighter_suffix(highlighter_suffix = nil)
|
||||
@highlighter_suffix = highlighter_suffix if highlighter_suffix
|
||||
@highlighter_suffix
|
||||
def self.pygments_suffix(pygments_suffix = nil)
|
||||
@pygments_suffix = pygments_suffix if pygments_suffix
|
||||
@pygments_suffix
|
||||
end
|
||||
|
||||
# Initialize the converter.
|
||||
@@ -31,18 +32,19 @@ module Jekyll
|
||||
@config = config
|
||||
end
|
||||
|
||||
# Get the highlighter prefix.
|
||||
# Get the pygments prefix.
|
||||
#
|
||||
# Returns the String prefix.
|
||||
def highlighter_prefix
|
||||
self.class.highlighter_prefix
|
||||
def pygments_prefix
|
||||
self.class.pygments_prefix
|
||||
end
|
||||
|
||||
# Get the highlighter suffix.
|
||||
# Get the pygments suffix.
|
||||
#
|
||||
# Returns the String suffix.
|
||||
def highlighter_suffix
|
||||
self.class.highlighter_suffix
|
||||
def pygments_suffix
|
||||
self.class.pygments_suffix
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,21 +1,22 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Identity < Converter
|
||||
safe true
|
||||
|
||||
priority :lowest
|
||||
class IdentityConverter < Converter
|
||||
safe true
|
||||
|
||||
def matches(ext)
|
||||
true
|
||||
end
|
||||
priority :lowest
|
||||
|
||||
def output_ext(ext)
|
||||
ext
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
content
|
||||
end
|
||||
def matches(ext)
|
||||
true
|
||||
end
|
||||
|
||||
def output_ext(ext)
|
||||
ext
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
content
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,58 +1,113 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Markdown < Converter
|
||||
safe true
|
||||
|
||||
highlighter_prefix "\n"
|
||||
highlighter_suffix "\n"
|
||||
class MarkdownConverter < Converter
|
||||
safe true
|
||||
|
||||
def setup
|
||||
return if @setup
|
||||
@parser =
|
||||
case @config['markdown'].downcase
|
||||
when 'redcarpet' then RedcarpetParser.new(@config)
|
||||
when 'kramdown' then KramdownParser.new(@config)
|
||||
when 'rdiscount' then RDiscountParser.new(@config)
|
||||
when 'maruku' then MarukuParser.new(@config)
|
||||
else
|
||||
# So they can't try some tricky bullshit or go down the ancestor chain, I hope.
|
||||
if allowed_custom_class?(@config['markdown'])
|
||||
self.class.const_get(@config['markdown']).new(@config)
|
||||
else
|
||||
Jekyll.logger.error "Invalid Markdown Processor:", "#{@config['markdown']}"
|
||||
Jekyll.logger.error "", "Valid options are [ maruku | rdiscount | kramdown | redcarpet ]"
|
||||
raise FatalException, "Invalid Markdown Processor: #{@config['markdown']}"
|
||||
end
|
||||
pygments_prefix "\n"
|
||||
pygments_suffix "\n"
|
||||
|
||||
def setup
|
||||
return if @setup
|
||||
# Set the Markdown interpreter (and Maruku self.config, if necessary)
|
||||
case @config['markdown']
|
||||
when 'kramdown'
|
||||
begin
|
||||
require 'kramdown'
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install kramdown'
|
||||
raise FatalException.new("Missing dependency: kramdown")
|
||||
end
|
||||
@setup = true
|
||||
when 'rdiscount'
|
||||
begin
|
||||
require 'rdiscount'
|
||||
|
||||
# Load rdiscount extensions
|
||||
@rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym }
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install rdiscount'
|
||||
raise FatalException.new("Missing dependency: rdiscount")
|
||||
end
|
||||
when 'maruku'
|
||||
begin
|
||||
require 'maruku'
|
||||
|
||||
if @config['maruku']['use_divs']
|
||||
require 'maruku/ext/div'
|
||||
STDERR.puts 'Maruku: Using extended syntax for div elements.'
|
||||
end
|
||||
|
||||
if @config['maruku']['use_tex']
|
||||
require 'maruku/ext/math'
|
||||
STDERR.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
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install maruku'
|
||||
raise FatalException.new("Missing dependency: maruku")
|
||||
end
|
||||
else
|
||||
STDERR.puts "Invalid Markdown processor: #{@config['markdown']}"
|
||||
STDERR.puts " Valid options are [ maruku | rdiscount | kramdown ]"
|
||||
raise FatalException.new("Invalid Markdown process: #{@config['markdown']}")
|
||||
end
|
||||
@setup = true
|
||||
end
|
||||
|
||||
def matches(ext)
|
||||
rgx = '^\.(' + @config['markdown_ext'].gsub(',','|') +')$'
|
||||
ext =~ Regexp.new(rgx, Regexp::IGNORECASE)
|
||||
end
|
||||
def matches(ext)
|
||||
ext =~ /(markdown|mkdn?|md)/i
|
||||
end
|
||||
|
||||
def output_ext(ext)
|
||||
".html"
|
||||
end
|
||||
def output_ext(ext)
|
||||
".html"
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
setup
|
||||
@parser.convert(content)
|
||||
end
|
||||
def convert(content)
|
||||
setup
|
||||
case @config['markdown']
|
||||
when 'kramdown'
|
||||
# Check for use of coderay
|
||||
if @config['kramdown']['use_coderay']
|
||||
Kramdown::Document.new(content, {
|
||||
:auto_ids => @config['kramdown']['auto_ids'],
|
||||
:footnote_nr => @config['kramdown']['footnote_nr'],
|
||||
:entity_output => @config['kramdown']['entity_output'],
|
||||
:toc_levels => @config['kramdown']['toc_levels'],
|
||||
|
||||
private
|
||||
|
||||
# Private: Determine whether a class name is an allowed custom markdown
|
||||
# class name
|
||||
#
|
||||
# parser_name - the name of the parser class
|
||||
#
|
||||
# Returns true if the parser name contains only alphanumeric characters
|
||||
# and is defined within Jekyll::Converters::Markdown
|
||||
def allowed_custom_class?(parser_name)
|
||||
parser_name !~ /[^A-Za-z0-9]/ && self.class.constants.include?(parser_name.to_sym)
|
||||
:coderay_wrap => @config['kramdown']['coderay']['coderay_wrap'],
|
||||
:coderay_line_numbers => @config['kramdown']['coderay']['coderay_line_numbers'],
|
||||
:coderay_line_number_start => @config['kramdown']['coderay']['coderay_line_number_start'],
|
||||
:coderay_tab_width => @config['kramdown']['coderay']['coderay_tab_width'],
|
||||
:coderay_bold_every => @config['kramdown']['coderay']['coderay_bold_every'],
|
||||
:coderay_css => @config['kramdown']['coderay']['coderay_css']
|
||||
}).to_html
|
||||
else
|
||||
# not using coderay
|
||||
Kramdown::Document.new(content, {
|
||||
:auto_ids => @config['kramdown']['auto_ids'],
|
||||
:footnote_nr => @config['kramdown']['footnote_nr'],
|
||||
:entity_output => @config['kramdown']['entity_output'],
|
||||
:toc_levels => @config['kramdown']['toc_levels']
|
||||
}).to_html
|
||||
end
|
||||
when 'rdiscount'
|
||||
RDiscount.new(content, *@rdiscount_extensions).to_html
|
||||
when 'maruku'
|
||||
Maruku.new(content).to_html
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Markdown
|
||||
class KramdownParser
|
||||
def initialize(config)
|
||||
require 'kramdown'
|
||||
@config = config
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install kramdown'
|
||||
raise FatalException.new("Missing dependency: kramdown")
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
# Check for use of coderay
|
||||
if @config['kramdown']['use_coderay']
|
||||
%w[wrap line_numbers line_numbers_start tab_width bold_every css default_lang].each do |opt|
|
||||
key = "coderay_#{opt}"
|
||||
@config['kramdown'][key] = @config['kramdown']['coderay'][key] unless @config['kramdown'].has_key?(key)
|
||||
end
|
||||
end
|
||||
|
||||
Kramdown::Document.new(content, Utils.symbolize_hash_keys(@config["kramdown"])).to_html
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,55 +0,0 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Markdown
|
||||
class MarukuParser
|
||||
def initialize(config)
|
||||
require 'maruku'
|
||||
@config = config
|
||||
@errors = []
|
||||
load_divs_library if @config['maruku']['use_divs']
|
||||
load_blahtext_library if @config['maruku']['use_tex']
|
||||
|
||||
# allow fenced code blocks (new in Maruku 0.7.0)
|
||||
MaRuKu::Globals[:fenced_code_blocks] = !!@config['maruku']['fenced_code_blocks']
|
||||
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install maruku'
|
||||
raise FatalException.new("Missing dependency: maruku")
|
||||
end
|
||||
|
||||
def load_divs_library
|
||||
require 'maruku/ext/div'
|
||||
STDERR.puts 'Maruku: Using extended syntax for div elements.'
|
||||
end
|
||||
|
||||
def load_blahtext_library
|
||||
require 'maruku/ext/math'
|
||||
STDERR.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
|
||||
|
||||
def print_errors_and_fail
|
||||
print @errors.join
|
||||
raise MaRuKu::Exception, "MaRuKu encountered problem(s) while converting your markup."
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
converted = Maruku.new(content, :error_stream => @errors).to_html.strip
|
||||
print_errors_and_fail unless @errors.empty?
|
||||
converted
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,37 +0,0 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Markdown
|
||||
class RDiscountParser
|
||||
def initialize(config)
|
||||
require 'rdiscount'
|
||||
@config = config
|
||||
@rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym }
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install rdiscount'
|
||||
raise FatalException.new("Missing dependency: rdiscount")
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
rd = RDiscount.new(content, *@rdiscount_extensions)
|
||||
html = rd.to_html
|
||||
if @config['rdiscount']['toc_token']
|
||||
html = replace_generated_toc(rd, html, @config['rdiscount']['toc_token'])
|
||||
end
|
||||
html
|
||||
end
|
||||
|
||||
private
|
||||
def replace_generated_toc(rd, html, toc_token)
|
||||
if rd.generate_toc && html.include?(toc_token)
|
||||
utf8_toc = rd.toc_content
|
||||
utf8_toc.force_encoding('utf-8') if utf8_toc.respond_to?(:force_encoding)
|
||||
html.gsub(toc_token, utf8_toc)
|
||||
else
|
||||
html
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,101 +0,0 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Markdown
|
||||
class RedcarpetParser
|
||||
|
||||
module CommonMethods
|
||||
def add_code_tags(code, lang)
|
||||
code = code.to_s
|
||||
code = code.sub(/<pre>/, "<pre><code class=\"language-#{lang}\" data-lang=\"#{lang}\">")
|
||||
code = code.sub(/<\/pre>/,"</code></pre>")
|
||||
end
|
||||
end
|
||||
|
||||
module WithPygments
|
||||
include CommonMethods
|
||||
def block_code(code, lang)
|
||||
require 'pygments'
|
||||
lang = lang && lang.split.first || "text"
|
||||
add_code_tags(
|
||||
Pygments.highlight(code, :lexer => lang, :options => { :encoding => 'utf-8' }),
|
||||
lang
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
module WithoutHighlighting
|
||||
require 'cgi'
|
||||
|
||||
include CommonMethods
|
||||
|
||||
def code_wrap(code)
|
||||
"<div class=\"highlight\"><pre>#{CGI::escapeHTML(code)}</pre></div>"
|
||||
end
|
||||
|
||||
def block_code(code, lang)
|
||||
lang = lang && lang.split.first || "text"
|
||||
add_code_tags(code_wrap(code), lang)
|
||||
end
|
||||
end
|
||||
|
||||
module WithRouge
|
||||
def block_code(code, lang)
|
||||
code = "<pre>#{super}</pre>"
|
||||
|
||||
output = "<div class=\"highlight\">"
|
||||
output << add_code_tags(code, lang)
|
||||
output << "</div>"
|
||||
end
|
||||
|
||||
protected
|
||||
def rouge_formatter(opts = {})
|
||||
Rouge::Formatters::HTML.new(opts.merge(wrap: false))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(config)
|
||||
require 'redcarpet'
|
||||
@config = config
|
||||
@redcarpet_extensions = {}
|
||||
@config['redcarpet']['extensions'].each { |e| @redcarpet_extensions[e.to_sym] = true }
|
||||
|
||||
@renderer ||= case @config['highlighter']
|
||||
when 'pygments'
|
||||
Class.new(Redcarpet::Render::HTML) do
|
||||
include WithPygments
|
||||
end
|
||||
when 'rouge'
|
||||
Class.new(Redcarpet::Render::HTML) do
|
||||
require 'rouge'
|
||||
require 'rouge/plugins/redcarpet'
|
||||
|
||||
if Rouge.version < '1.3.0'
|
||||
abort "Please install Rouge 1.3.0 or greater and try running Jekyll again."
|
||||
end
|
||||
|
||||
include Rouge::Plugins::Redcarpet
|
||||
include CommonMethods
|
||||
include WithRouge
|
||||
end
|
||||
else
|
||||
Class.new(Redcarpet::Render::HTML) do
|
||||
include WithoutHighlighting
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Markdown. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install redcarpet'
|
||||
raise FatalException.new("Missing dependency: redcarpet")
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
@redcarpet_extensions[:fenced_code_blocks] = !@redcarpet_extensions[:no_fenced_code_blocks]
|
||||
@renderer.send :include, Redcarpet::Render::SmartyPants if @redcarpet_extensions[:smart]
|
||||
markdown = Redcarpet::Markdown.new(@renderer.new(@redcarpet_extensions), @redcarpet_extensions)
|
||||
markdown.render(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,50 +1,33 @@
|
||||
module Jekyll
|
||||
module Converters
|
||||
class Textile < Converter
|
||||
safe true
|
||||
|
||||
highlighter_prefix '<notextile>'
|
||||
highlighter_suffix '</notextile>'
|
||||
class TextileConverter < Converter
|
||||
safe true
|
||||
|
||||
def setup
|
||||
return if @setup
|
||||
require 'redcloth'
|
||||
@setup = true
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Textile. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install RedCloth'
|
||||
raise FatalException.new("Missing dependency: RedCloth")
|
||||
end
|
||||
pygments_prefix '<notextile>'
|
||||
pygments_suffix '</notextile>'
|
||||
|
||||
def matches(ext)
|
||||
rgx = '(' + @config['textile_ext'].gsub(',','|') +')'
|
||||
ext =~ Regexp.new(rgx, Regexp::IGNORECASE)
|
||||
end
|
||||
def setup
|
||||
return if @setup
|
||||
require 'redcloth'
|
||||
@setup = true
|
||||
rescue LoadError
|
||||
STDERR.puts 'You are missing a library required for Textile. Please run:'
|
||||
STDERR.puts ' $ [sudo] gem install RedCloth'
|
||||
raise FatalException.new("Missing dependency: RedCloth")
|
||||
end
|
||||
|
||||
def output_ext(ext)
|
||||
".html"
|
||||
end
|
||||
def matches(ext)
|
||||
ext =~ /textile/i
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
setup
|
||||
def output_ext(ext)
|
||||
".html"
|
||||
end
|
||||
|
||||
# Shortcut if config doesn't contain RedCloth section
|
||||
return RedCloth.new(content).to_html if @config['redcloth'].nil?
|
||||
|
||||
# List of attributes defined on RedCloth
|
||||
# (from http://redcloth.rubyforge.org/classes/RedCloth/TextileDoc.html)
|
||||
attrs = ['filter_classes', 'filter_html', 'filter_ids', 'filter_styles',
|
||||
'hard_breaks', 'lite_mode', 'no_span_caps', 'sanitize_html']
|
||||
|
||||
r = RedCloth.new(content)
|
||||
|
||||
# Set attributes in r if they are NOT nil in the config
|
||||
attrs.each do |attr|
|
||||
r.instance_variable_set("@#{attr}".to_sym, @config['redcloth'][attr]) unless @config['redcloth'][attr].nil?
|
||||
end
|
||||
|
||||
r.to_html
|
||||
end
|
||||
def convert(content)
|
||||
setup
|
||||
RedCloth.new(content).to_html
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'set'
|
||||
|
||||
# Convertible provides methods for converting a pagelike item
|
||||
# from a certain type of markup into actual content
|
||||
#
|
||||
@@ -12,47 +8,30 @@ require 'set'
|
||||
# self.data=
|
||||
# self.ext=
|
||||
# self.output=
|
||||
# self.name
|
||||
# self.path
|
||||
# self.type -> :page, :post or :draft
|
||||
|
||||
module Jekyll
|
||||
module Convertible
|
||||
# Returns the contents as a String.
|
||||
def to_s
|
||||
content || ''
|
||||
end
|
||||
|
||||
# Whether the file is published or not, as indicated in YAML front-matter
|
||||
def published?
|
||||
!(data.has_key?('published') && data['published'] == false)
|
||||
end
|
||||
|
||||
# Returns merged option hash for File.read of self.site (if exists)
|
||||
# and a given param
|
||||
def merged_file_read_opts(opts)
|
||||
(site ? site.file_read_opts : {}).merge(opts)
|
||||
self.content || ''
|
||||
end
|
||||
|
||||
# Read the YAML frontmatter.
|
||||
#
|
||||
# base - The String path to the dir containing the file.
|
||||
# name - The String filename of the file.
|
||||
# opts - optional parameter to File.read, default at site configs
|
||||
#
|
||||
# Returns nothing.
|
||||
def read_yaml(base, name, opts = {})
|
||||
begin
|
||||
self.content = File.read(File.join(base, name),
|
||||
merged_file_read_opts(opts))
|
||||
if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
|
||||
self.content = $POSTMATCH
|
||||
self.data = SafeYAML.load($1)
|
||||
def read_yaml(base, name)
|
||||
self.content = File.read(File.join(base, name))
|
||||
|
||||
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
||||
self.content = $POSTMATCH
|
||||
|
||||
begin
|
||||
self.data = YAML.load($1)
|
||||
rescue => e
|
||||
puts "YAML Exception: #{e.message}"
|
||||
end
|
||||
rescue SyntaxError => e
|
||||
Jekyll.logger.warn "YAML Exception reading #{File.join(base, name)}: #{e.message}"
|
||||
rescue Exception => e
|
||||
Jekyll.logger.warn "Error reading file #{File.join(base, name)}: #{e.message}"
|
||||
end
|
||||
|
||||
self.data ||= {}
|
||||
@@ -62,11 +41,7 @@ module Jekyll
|
||||
#
|
||||
# Returns nothing.
|
||||
def transform
|
||||
self.content = converter.convert(content)
|
||||
rescue => e
|
||||
Jekyll.logger.error "Conversion error:", "There was an error converting" +
|
||||
" '#{path}'."
|
||||
raise e
|
||||
self.content = converter.convert(self.content)
|
||||
end
|
||||
|
||||
# Determine the extension depending on content_type.
|
||||
@@ -74,7 +49,7 @@ module Jekyll
|
||||
# Returns the String extension for the output file.
|
||||
# e.g. ".html" for an HTML output file.
|
||||
def output_ext
|
||||
converter.output_ext(ext)
|
||||
converter.output_ext(self.ext)
|
||||
end
|
||||
|
||||
# Determine which converter to use based on this convertible's
|
||||
@@ -82,76 +57,7 @@ module Jekyll
|
||||
#
|
||||
# Returns the Converter instance.
|
||||
def converter
|
||||
@converter ||= site.converters.find { |c| c.matches(ext) }
|
||||
end
|
||||
|
||||
# Render Liquid in the content
|
||||
#
|
||||
# content - the raw Liquid content to render
|
||||
# payload - the payload for Liquid
|
||||
# info - the info for Liquid
|
||||
#
|
||||
# Returns the converted content
|
||||
def render_liquid(content, payload, info, path = nil)
|
||||
Liquid::Template.parse(content).render!(payload, info)
|
||||
rescue Tags::IncludeTagError => e
|
||||
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || self.path}"
|
||||
raise e
|
||||
rescue Exception => e
|
||||
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || self.path}"
|
||||
raise e
|
||||
end
|
||||
|
||||
# Convert this Convertible's data to a Hash suitable for use by Liquid.
|
||||
#
|
||||
# Returns the Hash representation of this Convertible.
|
||||
def to_liquid(attrs = nil)
|
||||
further_data = Hash[(attrs || self.class::ATTRIBUTES_FOR_LIQUID).map { |attribute|
|
||||
[attribute, send(attribute)]
|
||||
}]
|
||||
|
||||
defaults = site.frontmatter_defaults.all(relative_path, type)
|
||||
Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data)
|
||||
end
|
||||
|
||||
def type
|
||||
if is_a?(Post)
|
||||
:post
|
||||
elsif is_a?(Page)
|
||||
:page
|
||||
elsif is_a?(Draft)
|
||||
:draft
|
||||
end
|
||||
end
|
||||
|
||||
# Recursively render layouts
|
||||
#
|
||||
# layouts - a list of the layouts
|
||||
# payload - the payload for Liquid
|
||||
# info - the info for Liquid
|
||||
#
|
||||
# Returns nothing
|
||||
def render_all_layouts(layouts, payload, info)
|
||||
# recursively render layouts
|
||||
layout = layouts[data["layout"]]
|
||||
used = Set.new([layout])
|
||||
|
||||
while layout
|
||||
payload = Utils.deep_merge_hashes(payload, {"content" => output, "page" => layout.data})
|
||||
|
||||
self.output = render_liquid(layout.content,
|
||||
payload,
|
||||
info,
|
||||
File.join(site.config['layouts'], layout.name))
|
||||
|
||||
if layout = layouts[layout.data["layout"]]
|
||||
if used.include?(layout)
|
||||
layout = nil # avoid recursive chain
|
||||
else
|
||||
used << layout
|
||||
end
|
||||
end
|
||||
end
|
||||
@converter ||= self.site.converters.find { |c| c.matches(self.ext) }
|
||||
end
|
||||
|
||||
# Add any necessary layouts to this convertible document.
|
||||
@@ -161,44 +67,35 @@ module Jekyll
|
||||
#
|
||||
# Returns nothing.
|
||||
def do_layout(payload, layouts)
|
||||
info = { :filters => [Jekyll::Filters], :registers => { :site => site, :page => payload['page'] } }
|
||||
info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } }
|
||||
|
||||
# render and transform content (this becomes the final content of the object)
|
||||
payload["highlighter_prefix"] = converter.highlighter_prefix
|
||||
payload["highlighter_suffix"] = converter.highlighter_suffix
|
||||
payload["pygments_prefix"] = converter.pygments_prefix
|
||||
payload["pygments_suffix"] = converter.pygments_suffix
|
||||
|
||||
self.content = render_liquid(content, payload, info)
|
||||
transform
|
||||
begin
|
||||
self.content = Liquid::Template.parse(self.content).render(payload, info)
|
||||
rescue => e
|
||||
puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
|
||||
end
|
||||
|
||||
self.transform
|
||||
|
||||
# output keeps track of what will finally be written
|
||||
self.output = content
|
||||
self.output = self.content
|
||||
|
||||
render_all_layouts(layouts, payload, info)
|
||||
end
|
||||
# recursively render layouts
|
||||
layout = layouts[self.data["layout"]]
|
||||
while layout
|
||||
payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
|
||||
|
||||
# Write the generated page file to the destination directory.
|
||||
#
|
||||
# dest - The String path to the destination dir.
|
||||
#
|
||||
# Returns nothing.
|
||||
def write(dest)
|
||||
path = destination(dest)
|
||||
FileUtils.mkdir_p(File.dirname(path))
|
||||
File.open(path, 'wb') do |f|
|
||||
f.write(output)
|
||||
end
|
||||
end
|
||||
begin
|
||||
self.output = Liquid::Template.parse(layout.content).render(payload, info)
|
||||
rescue => e
|
||||
puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
|
||||
end
|
||||
|
||||
# Accessor for data properties by Liquid.
|
||||
#
|
||||
# property - The String name of the property to retrieve.
|
||||
#
|
||||
# Returns the String value or nil if the property isn't included.
|
||||
def [](property)
|
||||
if self.class::ATTRIBUTES_FOR_LIQUID.include?(property)
|
||||
send(property)
|
||||
else
|
||||
data[property]
|
||||
layout = layouts[layout.data["layout"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
52
lib/jekyll/core_ext.rb
Normal file
52
lib/jekyll/core_ext.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
class Hash
|
||||
# Merges self with another hash, recursively.
|
||||
#
|
||||
# This code was lovingly stolen from some random gem:
|
||||
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
||||
#
|
||||
# Thanks to whoever made it.
|
||||
def deep_merge(hash)
|
||||
target = dup
|
||||
|
||||
hash.keys.each do |key|
|
||||
if hash[key].is_a? Hash and self[key].is_a? Hash
|
||||
target[key] = target[key].deep_merge(hash[key])
|
||||
next
|
||||
end
|
||||
|
||||
target[key] = hash[key]
|
||||
end
|
||||
|
||||
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!
|
||||
class Date
|
||||
# Converts datetime to an appropriate format for use in XML
|
||||
def xmlschema
|
||||
strftime("%Y-%m-%dT%H:%M:%S%Z")
|
||||
end if RUBY_VERSION < '1.9'
|
||||
end
|
||||
@@ -1,36 +0,0 @@
|
||||
module Jekyll
|
||||
class Deprecator
|
||||
def self.process(args)
|
||||
no_subcommand(args)
|
||||
arg_is_present? args, "--server", "The --server command has been replaced by the \
|
||||
'serve' subcommand."
|
||||
arg_is_present? args, "--no-server", "To build Jekyll without launching a server, \
|
||||
use the 'build' subcommand."
|
||||
arg_is_present? args, "--auto", "The switch '--auto' has been replaced with '--watch'."
|
||||
arg_is_present? args, "--no-auto", "To disable auto-replication, simply leave off \
|
||||
the '--watch' switch."
|
||||
arg_is_present? args, "--pygments", "The 'pygments'settings has been removed in \
|
||||
favour of 'highlighter'."
|
||||
arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in your \
|
||||
config files."
|
||||
arg_is_present? args, "--url", "The 'url' setting can only be set in your config files."
|
||||
end
|
||||
|
||||
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."
|
||||
end
|
||||
end
|
||||
|
||||
def self.arg_is_present?(args, deprecated_argument, message)
|
||||
if args.include?(deprecated_argument)
|
||||
deprecation_message(message)
|
||||
end
|
||||
end
|
||||
|
||||
def self.deprecation_message(message)
|
||||
Jekyll.logger.error "Deprecation:", message
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,242 +0,0 @@
|
||||
module Jekyll
|
||||
class Document
|
||||
include Comparable
|
||||
|
||||
attr_reader :path, :site
|
||||
attr_accessor :content, :collection, :output
|
||||
|
||||
# Create a new Document.
|
||||
#
|
||||
# site - the Jekyll::Site instance to which this Document belongs
|
||||
# path - the path to the file
|
||||
#
|
||||
# Returns nothing.
|
||||
def initialize(path, relations)
|
||||
@site = relations[:site]
|
||||
@path = path
|
||||
@collection = relations[:collection]
|
||||
end
|
||||
|
||||
# Fetch the Document's data.
|
||||
#
|
||||
# Returns a Hash containing the data. An empty hash is returned if
|
||||
# no data was read.
|
||||
def data
|
||||
@data ||= Hash.new
|
||||
end
|
||||
|
||||
# The path to the document, relative to the site source.
|
||||
#
|
||||
# Returns a String path which represents the relative path
|
||||
# from the site source to this document
|
||||
def relative_path
|
||||
Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
|
||||
end
|
||||
|
||||
# The base filename of the document.
|
||||
#
|
||||
# suffix - (optional) the suffix to be removed from the end of the filename
|
||||
#
|
||||
# Returns the base filename of the document.
|
||||
def basename(suffix = "")
|
||||
File.basename(path, suffix)
|
||||
end
|
||||
|
||||
# The extension name of the document.
|
||||
#
|
||||
# Returns the extension name of the document.
|
||||
def extname
|
||||
File.extname(path)
|
||||
end
|
||||
|
||||
# Produces a "cleaned" relative path.
|
||||
# The "cleaned" relative path is the relative path without the extname
|
||||
# and with the collection's directory removed as well.
|
||||
# This method is useful when building the URL of the document.
|
||||
#
|
||||
# Examples:
|
||||
# When relative_path is "_methods/site/generate.md":
|
||||
# cleaned_relative_path
|
||||
# # => "/site/generate"
|
||||
#
|
||||
# Returns the cleaned relative path of the document.
|
||||
def cleaned_relative_path
|
||||
relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
|
||||
end
|
||||
|
||||
# Determine whether the document is a YAML file.
|
||||
#
|
||||
# Returns true if the extname is either .yml or .yaml, false otherwise.
|
||||
def yaml_file?
|
||||
%w[.yaml .yml].include?(extname)
|
||||
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?(extname)
|
||||
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? || 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"
|
||||
end
|
||||
|
||||
# Construct a Hash of key-value pairs which contain a mapping between
|
||||
# a key in the URL template and the corresponding value for this document.
|
||||
#
|
||||
# Returns the Hash of key-value pairs for replacement in the URL.
|
||||
def url_placeholders
|
||||
{
|
||||
collection: collection.label,
|
||||
path: cleaned_relative_path,
|
||||
output_ext: Jekyll::Renderer.new(site, self).output_ext
|
||||
}
|
||||
end
|
||||
|
||||
# The permalink for this Document.
|
||||
# Permalink is set via the data Hash.
|
||||
#
|
||||
# Returns the permalink or nil if no permalink was set in the data.
|
||||
def permalink
|
||||
data && data.is_a?(Hash) && data['permalink']
|
||||
end
|
||||
|
||||
# The computed URL for the document. See `Jekyll::URL#to_s` for more details.
|
||||
#
|
||||
# Returns the computed URL for the document.
|
||||
def url
|
||||
@url ||= URL.new({
|
||||
template: url_template,
|
||||
placeholders: url_placeholders,
|
||||
permalink: permalink
|
||||
}).to_s
|
||||
end
|
||||
|
||||
# The full path to the output file.
|
||||
#
|
||||
# base_directory - the base path of the output directory
|
||||
#
|
||||
# Returns the full path to the output file of this document.
|
||||
def destination(base_directory)
|
||||
path = Jekyll.sanitized_path(base_directory, url)
|
||||
path = File.join(path, "index.html") if url =~ /\/$/
|
||||
path
|
||||
end
|
||||
|
||||
# Write the generated Document file to the destination directory.
|
||||
#
|
||||
# dest - The String path to the destination dir.
|
||||
#
|
||||
# Returns nothing.
|
||||
def write(dest)
|
||||
path = destination(dest)
|
||||
FileUtils.mkdir_p(File.dirname(path))
|
||||
File.open(path, 'wb') do |f|
|
||||
f.write(output)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns merged option hash for File.read of self.site (if exists)
|
||||
# and a given param
|
||||
#
|
||||
# opts - override options
|
||||
#
|
||||
# Return the file read options hash.
|
||||
def merged_file_read_opts(opts)
|
||||
site ? site.file_read_opts.merge(opts) : opts
|
||||
end
|
||||
|
||||
# Whether the file is published or not, as indicated in YAML front-matter
|
||||
#
|
||||
# Returns true if the 'published' key is specified in the YAML front-matter and not `false`.
|
||||
def published?
|
||||
!(data.has_key?('published') && data['published'] == false)
|
||||
end
|
||||
|
||||
# Read in the file and assign the content and data based on the file contents.
|
||||
#
|
||||
# Returns nothing.
|
||||
def read(opts = {})
|
||||
if yaml_file?
|
||||
@data = SafeYAML.load_file(path)
|
||||
else
|
||||
begin
|
||||
@content = File.read(path, merged_file_read_opts(opts))
|
||||
if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
||||
@content = $POSTMATCH
|
||||
@data = SafeYAML.load($1)
|
||||
end
|
||||
rescue SyntaxError => e
|
||||
puts "YAML Exception reading #{path}: #{e.message}"
|
||||
rescue Exception => e
|
||||
puts "Error reading file #{path}: #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Create a Liquid-understandable version of this Document.
|
||||
#
|
||||
# Returns a Hash representing this Document's data.
|
||||
def to_liquid
|
||||
if data.is_a?(Hash)
|
||||
Utils.deep_merge_hashes data, {
|
||||
"output" => output,
|
||||
"content" => content,
|
||||
"path" => path,
|
||||
"relative_path" => relative_path,
|
||||
"url" => url
|
||||
}
|
||||
else
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
# The inspect string for this document.
|
||||
# Includes the relative path and the collection label.
|
||||
#
|
||||
# Returns the inspect string for this document.
|
||||
def inspect
|
||||
"#<Jekyll::Document #{relative_path} collection=#{collection.label}>"
|
||||
end
|
||||
|
||||
# The string representation for this document.
|
||||
#
|
||||
# Returns the content of the document
|
||||
def to_s
|
||||
output || content
|
||||
end
|
||||
|
||||
# Compare this document against another document.
|
||||
# Comparison is a comparison between the 2 paths of the documents.
|
||||
#
|
||||
# Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
|
||||
# equal or greater than the other doc's path. See String#<=> for more details.
|
||||
def <=>(anotherDocument)
|
||||
path <=> anotherDocument.path
|
||||
end
|
||||
|
||||
# Determine whether this document should be written.
|
||||
# Based on the Collection to which it belongs.
|
||||
#
|
||||
# True if the document has a collection and if that collection's #write?
|
||||
# method returns true, otherwise false.
|
||||
def write?
|
||||
collection && collection.write?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,40 +0,0 @@
|
||||
module Jekyll
|
||||
|
||||
class Draft < Post
|
||||
|
||||
# Valid post name regex (no date)
|
||||
MATCHER = /^(.*)(\.[^.]+)$/
|
||||
|
||||
# Draft name validator. Draft filenames must be like:
|
||||
# my-awesome-post.textile
|
||||
#
|
||||
# Returns true if valid, false if not.
|
||||
def self.valid?(name)
|
||||
name =~ MATCHER
|
||||
end
|
||||
|
||||
# Get the full path to the directory containing the draft files
|
||||
def containing_dir(source, dir)
|
||||
File.join(source, dir, '_drafts')
|
||||
end
|
||||
|
||||
# The path to the draft source file, relative to the site source
|
||||
def relative_path
|
||||
File.join(@dir, '_drafts', @name)
|
||||
end
|
||||
|
||||
# Extract information from the post filename.
|
||||
#
|
||||
# name - The String filename of the post file.
|
||||
#
|
||||
# Returns nothing.
|
||||
def process(name)
|
||||
m, slug, ext = *name.match(MATCHER)
|
||||
self.date = File.mtime(File.join(@base, name))
|
||||
self.slug = slug
|
||||
self.ext = ext
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,72 +0,0 @@
|
||||
module Jekyll
|
||||
class EntryFilter
|
||||
SPECIAL_LEADING_CHARACTERS = ['.', '_', '#'].freeze
|
||||
|
||||
attr_reader :site
|
||||
|
||||
def initialize(site, base_directory = nil)
|
||||
@site = site
|
||||
@base_directory = derive_base_directory(@site, base_directory.to_s.dup)
|
||||
end
|
||||
|
||||
def base_directory
|
||||
@base_directory.to_s
|
||||
end
|
||||
|
||||
def derive_base_directory(site, base_dir)
|
||||
if base_dir.start_with?(site.source)
|
||||
base_dir[site.source] = ""
|
||||
end
|
||||
base_dir
|
||||
end
|
||||
|
||||
def relative_to_source(entry)
|
||||
File.join(base_directory, entry)
|
||||
end
|
||||
|
||||
def filter(entries)
|
||||
entries.reject do |e|
|
||||
unless included?(e)
|
||||
special?(e) || backup?(e) || excluded?(e) || symlink?(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def included?(entry)
|
||||
glob_include?(site.include, entry)
|
||||
end
|
||||
|
||||
def special?(entry)
|
||||
SPECIAL_LEADING_CHARACTERS.include?(entry[0..0]) ||
|
||||
SPECIAL_LEADING_CHARACTERS.include?(File.basename(entry)[0..0])
|
||||
end
|
||||
|
||||
def backup?(entry)
|
||||
entry[-1..-1] == '~'
|
||||
end
|
||||
|
||||
def excluded?(entry)
|
||||
excluded = glob_include?(site.exclude, relative_to_source(entry))
|
||||
Jekyll.logger.debug "excluded?(#{relative_to_source(entry)}) ==> #{excluded}"
|
||||
excluded
|
||||
end
|
||||
|
||||
def symlink?(entry)
|
||||
File.symlink?(entry) && site.safe
|
||||
end
|
||||
|
||||
def ensure_leading_slash(path)
|
||||
path[0..0] == "/" ? path : "/#{path}"
|
||||
end
|
||||
|
||||
# Returns true if path matches against any glob pattern.
|
||||
# Look for more detail about glob pattern in method File::fnmatch.
|
||||
def glob_include?(enum, e)
|
||||
entry = ensure_leading_slash(e)
|
||||
enum.any? do |exp|
|
||||
item = ensure_leading_slash(exp)
|
||||
File.fnmatch?(item, entry) || entry.start_with?(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,6 @@
|
||||
module Jekyll
|
||||
|
||||
class FatalException < StandardError
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,115 +0,0 @@
|
||||
require 'jekyll/convertible'
|
||||
require 'forwardable'
|
||||
|
||||
module Jekyll
|
||||
class Excerpt
|
||||
include Convertible
|
||||
extend Forwardable
|
||||
|
||||
attr_accessor :post
|
||||
attr_accessor :content, :output, :ext
|
||||
|
||||
def_delegator :@post, :site, :site
|
||||
def_delegator :@post, :name, :name
|
||||
def_delegator :@post, :ext, :ext
|
||||
|
||||
# Initialize this Post instance.
|
||||
#
|
||||
# site - The Site.
|
||||
# base - The String path to the dir containing the post file.
|
||||
# name - The String filename of the post file.
|
||||
#
|
||||
# Returns the new Post.
|
||||
def initialize(post)
|
||||
self.post = post
|
||||
self.content = extract_excerpt(post.content)
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
post.to_liquid(Post::EXCERPT_ATTRIBUTES_FOR_LIQUID)
|
||||
end
|
||||
|
||||
# Fetch YAML front-matter data from related post, without layout key
|
||||
#
|
||||
# Returns Hash of post data
|
||||
def data
|
||||
@data ||= post.data.dup
|
||||
@data.delete("layout")
|
||||
@data
|
||||
end
|
||||
|
||||
# 'Path' of the excerpt.
|
||||
#
|
||||
# Returns the path for the post this excerpt belongs to with #excerpt appended
|
||||
def path
|
||||
File.join(post.path, "#excerpt")
|
||||
end
|
||||
|
||||
# Check if excerpt includes a string
|
||||
#
|
||||
# Returns true if the string passed in
|
||||
def include?(something)
|
||||
(output && output.include?(something)) || content.include?(something)
|
||||
end
|
||||
|
||||
# The UID for this post (useful in feeds).
|
||||
# e.g. /2008/11/05/my-awesome-post
|
||||
#
|
||||
# Returns the String UID.
|
||||
def id
|
||||
File.join(post.dir, post.slug, "#excerpt")
|
||||
end
|
||||
|
||||
def to_s
|
||||
output || content
|
||||
end
|
||||
|
||||
# Returns the shorthand String identifier of this Post.
|
||||
def inspect
|
||||
"<Excerpt: #{self.id}>"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Internal: Extract excerpt from the content
|
||||
#
|
||||
# By default excerpt is your first paragraph of a post: everything before
|
||||
# the first two new lines:
|
||||
#
|
||||
# ---
|
||||
# title: Example
|
||||
# ---
|
||||
#
|
||||
# First paragraph with [link][1].
|
||||
#
|
||||
# Second paragraph.
|
||||
#
|
||||
# [1]: http://example.com/
|
||||
#
|
||||
# This is fairly good option for Markdown and Textile files. But might cause
|
||||
# problems for HTML posts (which is quite unusual for Jekyll). If default
|
||||
# excerpt delimiter is not good for you, you might want to set your own via
|
||||
# configuration option `excerpt_separator`. For example, following is a good
|
||||
# alternative for HTML posts:
|
||||
#
|
||||
# # file: _config.yml
|
||||
# excerpt_separator: "<!-- more -->"
|
||||
#
|
||||
# Notice that all markdown-style link references will be appended to the
|
||||
# excerpt. So the example post above will have this excerpt source:
|
||||
#
|
||||
# First paragraph with [link][1].
|
||||
#
|
||||
# [1]: http://example.com/
|
||||
#
|
||||
# Excerpts are rendered same time as content is rendered.
|
||||
#
|
||||
# Returns excerpt String
|
||||
def extract_excerpt(post_content)
|
||||
separator = site.config['excerpt_separator']
|
||||
head, _, tail = post_content.partition(separator)
|
||||
|
||||
"" << head << "\n\n" << tail.scan(/^\[[^\]]+\]:.+$/).join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,140 +1,40 @@
|
||||
require 'uri'
|
||||
require 'json'
|
||||
|
||||
module Jekyll
|
||||
|
||||
module Filters
|
||||
# Convert a Textile string into HTML output.
|
||||
#
|
||||
# input - The Textile String to convert.
|
||||
#
|
||||
# Returns the HTML formatted String.
|
||||
def textilize(input)
|
||||
site = @context.registers[:site]
|
||||
converter = site.getConverterImpl(Jekyll::Converters::Textile)
|
||||
converter.convert(input)
|
||||
TextileConverter.new.convert(input)
|
||||
end
|
||||
|
||||
# Convert a Markdown string into HTML output.
|
||||
#
|
||||
# input - The Markdown String to convert.
|
||||
#
|
||||
# Returns the HTML formatted String.
|
||||
def markdownify(input)
|
||||
site = @context.registers[:site]
|
||||
converter = site.getConverterImpl(Jekyll::Converters::Markdown)
|
||||
converter.convert(input)
|
||||
end
|
||||
|
||||
# Format a date in short format e.g. "27 Jan 2011".
|
||||
#
|
||||
# date - the Time to format.
|
||||
#
|
||||
# Returns the formatting String.
|
||||
def date_to_string(date)
|
||||
time(date).strftime("%d %b %Y")
|
||||
date.strftime("%d %b %Y")
|
||||
end
|
||||
|
||||
# Format a date in long format e.g. "27 January 2011".
|
||||
#
|
||||
# date - The Time to format.
|
||||
#
|
||||
# Returns the formatted String.
|
||||
def date_to_long_string(date)
|
||||
time(date).strftime("%d %B %Y")
|
||||
date.strftime("%d %B %Y")
|
||||
end
|
||||
|
||||
# Format a date for use in XML.
|
||||
#
|
||||
# date - The Time to format.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# date_to_xmlschema(Time.now)
|
||||
# # => "2011-04-24T20:34:46+08:00"
|
||||
#
|
||||
# Returns the formatted String.
|
||||
def date_to_xmlschema(date)
|
||||
time(date).xmlschema
|
||||
date.xmlschema
|
||||
end
|
||||
|
||||
# Format a date according to RFC-822
|
||||
#
|
||||
# date - The Time to format.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# date_to_rfc822(Time.now)
|
||||
# # => "Sun, 24 Apr 2011 12:34:46 +0000"
|
||||
#
|
||||
# Returns the formatted String.
|
||||
def date_to_rfc822(date)
|
||||
time(date).rfc822
|
||||
end
|
||||
|
||||
# XML escape a string for use. Replaces any special characters with
|
||||
# appropriate HTML entity replacements.
|
||||
#
|
||||
# input - The String to escape.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# xml_escape('foo "bar" <baz>')
|
||||
# # => "foo "bar" <baz>"
|
||||
#
|
||||
# Returns the escaped String.
|
||||
def xml_escape(input)
|
||||
CGI.escapeHTML(input.to_s)
|
||||
CGI.escapeHTML(input)
|
||||
end
|
||||
|
||||
# CGI escape a string for use in a URL. Replaces any special characters
|
||||
# with appropriate %XX replacements.
|
||||
#
|
||||
# input - The String to escape.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# cgi_escape('foo,bar;baz?')
|
||||
# # => "foo%2Cbar%3Bbaz%3F"
|
||||
#
|
||||
# Returns the escaped String.
|
||||
def cgi_escape(input)
|
||||
CGI::escape(input)
|
||||
end
|
||||
|
||||
# URI escape a string.
|
||||
#
|
||||
# input - The String to escape.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# uri_escape('foo, bar \\baz?')
|
||||
# # => "foo,%20bar%20%5Cbaz?"
|
||||
#
|
||||
# Returns the escaped String.
|
||||
def uri_escape(input)
|
||||
URI.escape(input)
|
||||
end
|
||||
|
||||
# Count the number of words in the input string.
|
||||
#
|
||||
# input - The String on which to operate.
|
||||
#
|
||||
# Returns the Integer word count.
|
||||
def number_of_words(input)
|
||||
input.split.length
|
||||
end
|
||||
|
||||
# Join an array of things into a string by separating with commas and the
|
||||
# word "and" for the last one.
|
||||
#
|
||||
# array - The Array of Strings to join.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# array_to_sentence_string(["apples", "oranges", "grapes"])
|
||||
# # => "apples, oranges, and grapes"
|
||||
#
|
||||
# Returns the formatted String.
|
||||
def array_to_sentence_string(array)
|
||||
connector = "and"
|
||||
case array.length
|
||||
@@ -149,106 +49,5 @@ module Jekyll
|
||||
end
|
||||
end
|
||||
|
||||
# Convert the input into json string
|
||||
#
|
||||
# input - The Array or Hash to be converted
|
||||
#
|
||||
# Returns the converted json string
|
||||
def jsonify(input)
|
||||
input.to_json
|
||||
end
|
||||
|
||||
# Group an array of items by a property
|
||||
#
|
||||
# input - the inputted Enumerable
|
||||
# property - the property
|
||||
#
|
||||
# Returns an array of Hashes, each looking something like this:
|
||||
# {"name" => "larry"
|
||||
# "items" => [...] } # all the items where `property` == "larry"
|
||||
def group_by(input, property)
|
||||
if groupable?(input)
|
||||
input.group_by do |item|
|
||||
item_property(item, property).to_s
|
||||
end.inject([]) do |memo, i|
|
||||
memo << {"name" => i.first, "items" => i.last}
|
||||
end
|
||||
else
|
||||
input
|
||||
end
|
||||
end
|
||||
|
||||
# Filter an array of objects
|
||||
#
|
||||
# input - the object array
|
||||
# key - key within each object to filter by
|
||||
# value - desired value
|
||||
#
|
||||
# Returns the filtered array of objects
|
||||
def where(input, property, value)
|
||||
return input unless input.is_a?(Array)
|
||||
input.select { |object| item_property(object, property) == value }
|
||||
end
|
||||
|
||||
# Sort an array of objects
|
||||
#
|
||||
# input - the object array
|
||||
# key - key within each object to filter by
|
||||
# nils ('first' | 'last') - nils appear before or after non-nil values
|
||||
#
|
||||
# Returns the filtered array of objects
|
||||
def sort(input, key = nil, nils = "first")
|
||||
if key.nil?
|
||||
input.sort
|
||||
else
|
||||
case
|
||||
when nils == "first"
|
||||
order = - 1
|
||||
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)
|
||||
end
|
||||
|
||||
input.sort { |a, b|
|
||||
if !a[key].nil? && b[key].nil?
|
||||
- order
|
||||
elsif a[key].nil? && !b[key].nil?
|
||||
+ order
|
||||
else
|
||||
a[key] <=> b[key]
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def time(input)
|
||||
case input
|
||||
when Time
|
||||
input
|
||||
when String
|
||||
Time.parse(input) rescue Time.at(input.to_i)
|
||||
when Number
|
||||
Time.at(input)
|
||||
else
|
||||
Jekyll.logger.error "Invalid Date:", "'#{input}' is not a valid datetime."
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
def groupable?(element)
|
||||
element.respond_to?(:group_by)
|
||||
end
|
||||
|
||||
def item_property(item, property)
|
||||
if item.respond_to?(:data)
|
||||
item.data[property.to_s]
|
||||
else
|
||||
item[property.to_s]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
module Jekyll
|
||||
class Configuration
|
||||
# This class handles custom defaults for YAML frontmatter settings.
|
||||
# These are set in _config.yml and apply both to internal use (e.g. layout)
|
||||
# and the data available to liquid.
|
||||
#
|
||||
# It is exposed via the frontmatter_defaults method on the site class.
|
||||
class FrontmatterDefaults
|
||||
# Initializes a new instance.
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
# Finds a default value for a given setting, filtered by path and type
|
||||
#
|
||||
# path - the path (relative to the source) of the page, post or :draft the default is used in
|
||||
# type - a symbol indicating whether a :page, a :post or a :draft calls this method
|
||||
#
|
||||
# Returns the default value or nil if none was found
|
||||
def find(path, type, setting)
|
||||
value = nil
|
||||
old_scope = nil
|
||||
|
||||
matching_sets(path, type).each do |set|
|
||||
if set['values'].has_key?(setting) && has_precedence?(old_scope, set['scope'])
|
||||
value = set['values'][setting]
|
||||
old_scope = set['scope']
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
# Collects a hash with all default values for a page or post
|
||||
#
|
||||
# path - the relative path of the page or post
|
||||
# type - a symbol indicating the type (:post, :page or :draft)
|
||||
#
|
||||
# Returns a hash with all default values (an empty hash if there are none)
|
||||
def all(path, type)
|
||||
defaults = {}
|
||||
old_scope = nil
|
||||
matching_sets(path, type).each do |set|
|
||||
if has_precedence?(old_scope, set['scope'])
|
||||
defaults.merge! set['values']
|
||||
old_scope = set['scope']
|
||||
else
|
||||
defaults = set['values'].merge(defaults)
|
||||
end
|
||||
end
|
||||
defaults
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Checks if a given default setting scope matches the given path and type
|
||||
#
|
||||
# scope - the hash indicating the scope, as defined in _config.yml
|
||||
# path - the path to check for
|
||||
# type - the type (:post, :page or :draft) to check for
|
||||
#
|
||||
# Returns true if the scope applies to the given path and type
|
||||
def applies?(scope, path, type)
|
||||
applies_path?(scope, path) && applies_type?(scope, type)
|
||||
end
|
||||
|
||||
def applies_path?(scope, path)
|
||||
return true if scope['path'].empty?
|
||||
|
||||
scope_path = Pathname.new(scope['path'])
|
||||
Pathname.new(sanitize_path(path)).ascend do |path|
|
||||
if path == scope_path
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def applies_type?(scope, type)
|
||||
!scope.has_key?('type') || scope['type'] == type.to_s
|
||||
end
|
||||
|
||||
# Checks if a given set of default values is valid
|
||||
#
|
||||
# set - the default value hash, as defined in _config.yml
|
||||
#
|
||||
# Returns true if the set is valid and can be used in this class
|
||||
def valid?(set)
|
||||
set.is_a?(Hash) && set['scope'].is_a?(Hash) && set['scope']['path'].is_a?(String) && set['values'].is_a?(Hash)
|
||||
end
|
||||
|
||||
# Determines if a new scope has precedence over an old one
|
||||
#
|
||||
# old_scope - the old scope hash, or nil if there's none
|
||||
# new_scope - the new scope hash
|
||||
#
|
||||
# Returns true if the new scope has precedence over the older
|
||||
def has_precedence?(old_scope, new_scope)
|
||||
return true if old_scope.nil?
|
||||
|
||||
new_path = sanitize_path(new_scope['path'])
|
||||
old_path = sanitize_path(old_scope['path'])
|
||||
|
||||
if new_path.length != old_path.length
|
||||
new_path.length >= old_path.length
|
||||
elsif new_scope.has_key? 'type'
|
||||
true
|
||||
else
|
||||
!old_scope.has_key? 'type'
|
||||
end
|
||||
end
|
||||
|
||||
# Collects a list of sets that match the given path and type
|
||||
#
|
||||
# Returns an array of hashes
|
||||
def matching_sets(path, type)
|
||||
valid_sets.select do |set|
|
||||
applies?(set['scope'], path, type)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a list of valid sets
|
||||
#
|
||||
# This is not cached to allow plugins to modify the configuration
|
||||
# and have their changes take effect
|
||||
#
|
||||
# Returns an array of hashes
|
||||
def valid_sets
|
||||
sets = @site.config['defaults']
|
||||
return [] unless sets.is_a?(Array)
|
||||
|
||||
sets.select do |set|
|
||||
unless valid?(set)
|
||||
Jekyll.logger.warn "Default:", "An invalid default set was found"
|
||||
end
|
||||
valid?(set)
|
||||
end
|
||||
end
|
||||
|
||||
# Sanitizes the given path by removing a leading and addding a trailing slash
|
||||
def sanitize_path(path)
|
||||
if path.nil? || path.empty?
|
||||
""
|
||||
else
|
||||
path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1/')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,7 @@
|
||||
module Jekyll
|
||||
|
||||
class Generator < Plugin
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,185 +1,60 @@
|
||||
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
|
||||
class Pagination < Generator
|
||||
safe true
|
||||
|
||||
# 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" => <Number>,
|
||||
# "per_page" => <Number>,
|
||||
# "posts" => [<Post>],
|
||||
# "total_posts" => <Number>,
|
||||
# "total_pages" => <Number>,
|
||||
# "previous_page" => <Number>,
|
||||
# "next_page" => <Number> }}
|
||||
def paginate(site, page)
|
||||
all_posts = site.site_payload['site']['posts']
|
||||
pages = Pager.calculate_pages(all_posts, site.config['paginate'].to_i)
|
||||
(1..pages).each do |num_page|
|
||||
pager = Pager.new(site, 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
|
||||
def generate(site)
|
||||
site.pages.dup.each do |page|
|
||||
paginate(site, page) if Pager.pagination_enabled?(site.config, page.name)
|
||||
end
|
||||
end
|
||||
|
||||
# Paginates the blog's posts. Renders the index.html file into paginated
|
||||
# directories, ie: page2/index.html, page3/index.html, etc and adds more
|
||||
# site-wide data.
|
||||
# +page+ is the index.html Page that requires pagination
|
||||
#
|
||||
# {"paginator" => { "page" => <Number>,
|
||||
# "per_page" => <Number>,
|
||||
# "posts" => [<Post>],
|
||||
# "total_posts" => <Number>,
|
||||
# "total_pages" => <Number>,
|
||||
# "previous_page" => <Number>,
|
||||
# "next_page" => <Number> }}
|
||||
def paginate(site, page)
|
||||
all_posts = site.site_payload['site']['posts']
|
||||
pages = Pager.calculate_pages(all_posts, site.config['paginate'].to_i)
|
||||
(1..pages).each do |num_page|
|
||||
pager = Pager.new(site.config, num_page, all_posts, pages)
|
||||
if num_page > 1
|
||||
newpage = Page.new(site, site.source, page.dir, page.name)
|
||||
newpage.pager = pager
|
||||
newpage.dir = File.join(page.dir, "page#{num_page}")
|
||||
site.pages << newpage
|
||||
else
|
||||
page.pager = pager
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Pager
|
||||
attr_reader :page, :per_page, :posts, :total_posts, :total_pages,
|
||||
:previous_page, :previous_page_path, :next_page, :next_page_path
|
||||
attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
# 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
|
||||
def self.pagination_enabled?(config, file)
|
||||
file == 'index.html' && !config['paginate'].nil?
|
||||
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)
|
||||
def initialize(config, page, all_posts, num_pages = nil)
|
||||
@page = page
|
||||
@per_page = site.config['paginate'].to_i
|
||||
@per_page = config['paginate'].to_i
|
||||
@total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
|
||||
|
||||
if @page > @total_pages
|
||||
@@ -192,14 +67,9 @@ module Jekyll
|
||||
@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,
|
||||
@@ -208,10 +78,10 @@ module Jekyll
|
||||
'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
|
||||
'next_page' => next_page
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
module Jekyll
|
||||
|
||||
class Layout
|
||||
include Convertible
|
||||
|
||||
# Gets the Site object.
|
||||
attr_reader :site
|
||||
|
||||
# Gets the name of this layout.
|
||||
attr_reader :name
|
||||
|
||||
# Gets/Sets the extension of this layout.
|
||||
attr_accessor :site
|
||||
attr_accessor :ext
|
||||
|
||||
# Gets/Sets the Hash that holds the metadata for this layout.
|
||||
attr_accessor :data
|
||||
|
||||
# Gets/Sets the content of this layout.
|
||||
attr_accessor :content
|
||||
attr_accessor :data, :content
|
||||
|
||||
# Initialize a new Layout.
|
||||
# +site+ is the Site
|
||||
# +base+ is the String path to the <source>
|
||||
# +name+ is the String filename of the post file
|
||||
#
|
||||
# site - The Site.
|
||||
# base - The String path to the source.
|
||||
# name - The String filename of the post file.
|
||||
# Returns <Page>
|
||||
def initialize(site, base, name)
|
||||
@site = site
|
||||
@base = base
|
||||
@@ -29,17 +20,17 @@ module Jekyll
|
||||
|
||||
self.data = {}
|
||||
|
||||
process(name)
|
||||
read_yaml(base, name)
|
||||
self.process(name)
|
||||
self.read_yaml(base, name)
|
||||
end
|
||||
|
||||
# Extract information from the layout filename.
|
||||
# Extract information from the layout filename
|
||||
# +name+ is the String filename of the layout file
|
||||
#
|
||||
# name - The String filename of the layout file.
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def process(name)
|
||||
self.ext = File.extname(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,53 +0,0 @@
|
||||
module Jekyll
|
||||
class LayoutReader
|
||||
attr_reader :site
|
||||
def initialize(site)
|
||||
@site = site
|
||||
@layouts = {}
|
||||
end
|
||||
|
||||
def read
|
||||
layout_entries.each do |f|
|
||||
@layouts[layout_name(f)] = Layout.new(site, layout_directory, f)
|
||||
end
|
||||
|
||||
@layouts
|
||||
end
|
||||
|
||||
def layout_directory
|
||||
@layout_directory ||= (layout_directory_in_cwd || layout_directory_inside_source)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def layout_entries
|
||||
entries = []
|
||||
within(layout_directory) do
|
||||
entries = EntryFilter.new(site).filter(Dir['**/*.*'])
|
||||
end
|
||||
entries
|
||||
end
|
||||
|
||||
def layout_name(file)
|
||||
file.split(".")[0..-2].join(".")
|
||||
end
|
||||
|
||||
def within(directory)
|
||||
return unless File.exist?(directory)
|
||||
Dir.chdir(directory) { yield }
|
||||
end
|
||||
|
||||
def layout_directory_inside_source
|
||||
Jekyll.sanitized_path(site.source, site.config['layouts'])
|
||||
end
|
||||
|
||||
def layout_directory_in_cwd
|
||||
dir = Jekyll.sanitized_path(Dir.pwd, site.config['layouts'])
|
||||
if File.directory?(dir)
|
||||
dir
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,22 +0,0 @@
|
||||
module Jekyll
|
||||
module LiquidExtensions
|
||||
|
||||
# Lookup a Liquid variable in the given context.
|
||||
#
|
||||
# context - the Liquid context in question.
|
||||
# variable - the variable name, as a string.
|
||||
#
|
||||
# Returns the value of the variable in the context
|
||||
# or the variable name if not found.
|
||||
def lookup_variable(context, variable)
|
||||
lookup = context
|
||||
|
||||
variable.split(".").each do |value|
|
||||
lookup = lookup[value]
|
||||
end
|
||||
|
||||
lookup || variable
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
26
lib/jekyll/migrators/csv.rb
Normal file
26
lib/jekyll/migrators/csv.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module Jekyll
|
||||
module CSV
|
||||
#Reads a csv with title, permalink, body, published_at, and filter.
|
||||
#It creates a post file for each row in the csv
|
||||
def self.process(file = "posts.csv")
|
||||
FileUtils.mkdir_p "_posts"
|
||||
posts = 0
|
||||
FasterCSV.foreach(file) do |row|
|
||||
next if row[0] == "title"
|
||||
posts += 1
|
||||
name = row[3].split(" ")[0]+"-"+row[1]+(row[4] =~ /markdown/ ? ".markdown" : ".textile")
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts <<-HEADER
|
||||
---
|
||||
layout: post
|
||||
title: #{row[0]}
|
||||
---
|
||||
|
||||
HEADER
|
||||
f.puts row[2]
|
||||
end
|
||||
end
|
||||
"Created #{posts} posts!"
|
||||
end
|
||||
end
|
||||
end
|
||||
86
lib/jekyll/migrators/drupal.rb
Normal file
86
lib/jekyll/migrators/drupal.rb
Normal file
@@ -0,0 +1,86 @@
|
||||
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
|
||||
# installed, running the following commands should work:
|
||||
# $ sudo gem install sequel
|
||||
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
||||
|
||||
module Jekyll
|
||||
module Drupal
|
||||
|
||||
# Reads a MySQL database via Sequel and creates a post file for each
|
||||
# post in wp_posts that has post_status = 'publish'.
|
||||
# This restriction is made because 'draft' posts are not guaranteed to
|
||||
# have valid dates.
|
||||
QUERY = "SELECT node.nid, node.title, node_revisions.body, node.created, node.status FROM node, node_revisions WHERE (node.type = 'blog' OR node.type = 'story') AND node.vid = node_revisions.vid"
|
||||
|
||||
def self.process(dbname, user, pass, host = 'localhost')
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
|
||||
FileUtils.mkdir_p "_posts"
|
||||
FileUtils.mkdir_p "_drafts"
|
||||
|
||||
# Create the refresh layout
|
||||
# Change the refresh url if you customized your permalink config
|
||||
File.open("_layouts/refresh.html", "w") do |f|
|
||||
f.puts <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="refresh" content="0;url={{ page.refresh_to_post_id }}.html" />
|
||||
</head>
|
||||
</html>
|
||||
EOF
|
||||
end
|
||||
|
||||
db[QUERY].each do |post|
|
||||
# Get required fields and construct Jekyll compatible name
|
||||
node_id = post[:nid]
|
||||
title = post[:title]
|
||||
content = post[:body]
|
||||
created = post[:created]
|
||||
time = Time.at(created)
|
||||
is_published = post[:status] == 1
|
||||
dir = is_published ? "_posts" : "_drafts"
|
||||
slug = title.strip.downcase.gsub(/(&|&)/, ' and ').gsub(/[\s\.\/\\]/, '-').gsub(/[^\w-]/, '').gsub(/[-_]{2,}/, '-').gsub(/^[-_]/, '').gsub(/[-_]$/, '')
|
||||
name = time.strftime("%Y-%m-%d-") + slug + '.md'
|
||||
|
||||
# Get the relevant fields as a hash, delete empty fields and convert
|
||||
# to YAML for the header
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
'created' => created,
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
# Write out the data and content to file
|
||||
File.open("#{dir}/#{name}", "w") do |f|
|
||||
f.puts data
|
||||
f.puts "---"
|
||||
f.puts content
|
||||
end
|
||||
|
||||
# Make a file to redirect from the old Drupal URL
|
||||
if is_published
|
||||
FileUtils.mkdir_p "node/#{node_id}"
|
||||
File.open("node/#{node_id}/index.md", "w") do |f|
|
||||
f.puts "---"
|
||||
f.puts "layout: refresh"
|
||||
f.puts "refresh_to_post_id: /#{time.strftime("%Y/%m/%d/") + slug}"
|
||||
f.puts "---"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Make dirs & files for nodes of type 'page'
|
||||
# Make refresh pages for these as well
|
||||
|
||||
# TODO: Make refresh dirs & files according to entries in url_alias table
|
||||
end
|
||||
end
|
||||
end
|
||||
53
lib/jekyll/migrators/marley.rb
Normal file
53
lib/jekyll/migrators/marley.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
require 'yaml'
|
||||
require 'fileutils'
|
||||
|
||||
module Jekyll
|
||||
module Marley
|
||||
|
||||
def self.regexp
|
||||
{ :id => /^\d{0,4}-{0,1}(.*)$/,
|
||||
:title => /^#\s*(.*)\s+$/,
|
||||
:title_with_date => /^#\s*(.*)\s+\(([0-9\/]+)\)$/,
|
||||
:published_on => /.*\s+\(([0-9\/]+)\)$/,
|
||||
:perex => /^([^\#\n]+\n)$/,
|
||||
:meta => /^\{\{\n(.*)\}\}\n$/mi # Multiline Regexp
|
||||
}
|
||||
end
|
||||
|
||||
def self.process(marley_data_dir)
|
||||
raise ArgumentError, "marley dir #{marley_data_dir} not found" unless File.directory?(marley_data_dir)
|
||||
|
||||
FileUtils.mkdir_p "_posts"
|
||||
|
||||
posts = 0
|
||||
Dir["#{marley_data_dir}/**/*.txt"].each do |f|
|
||||
next unless File.exists?(f)
|
||||
|
||||
#copied over from marley's app/lib/post.rb
|
||||
file_content = File.read(f)
|
||||
meta_content = file_content.slice!( self.regexp[:meta] )
|
||||
body = file_content.sub( self.regexp[:title], '').sub( self.regexp[:perex], '').strip
|
||||
|
||||
title = file_content.scan( self.regexp[:title] ).first.to_s.strip
|
||||
prerex = file_content.scan( self.regexp[:perex] ).first.to_s.strip
|
||||
published_on = DateTime.parse( post[:published_on] ) rescue File.mtime( File.dirname(f) )
|
||||
meta = ( meta_content ) ? YAML::load( meta_content.scan( self.regexp[:meta]).to_s ) : {}
|
||||
meta['title'] = title
|
||||
meta['layout'] = 'post'
|
||||
|
||||
formatted_date = published_on.strftime('%Y-%m-%d')
|
||||
post_name = File.dirname(f).split(%r{/}).last.gsub(/\A\d+-/, '')
|
||||
|
||||
name = "#{formatted_date}-#{post_name}"
|
||||
File.open("_posts/#{name}.markdown", "w") do |f|
|
||||
f.puts meta.to_yaml
|
||||
f.puts "---\n"
|
||||
f.puts "\n#{prerex}\n\n" if prerex
|
||||
f.puts body
|
||||
end
|
||||
posts += 1
|
||||
end
|
||||
"Created #{posts} posts!"
|
||||
end
|
||||
end
|
||||
end
|
||||
79
lib/jekyll/migrators/mephisto.rb
Normal file
79
lib/jekyll/migrators/mephisto.rb
Normal file
@@ -0,0 +1,79 @@
|
||||
# Quickly hacked together my Michael Ivey
|
||||
# Based on mt.rb by Nick Gerakines, open source and publically
|
||||
# available under the MIT license. Use this module at your own risk.
|
||||
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
require 'fastercsv'
|
||||
require 'fileutils'
|
||||
require File.join(File.dirname(__FILE__),"csv.rb")
|
||||
|
||||
# NOTE: This converter requires Sequel and the MySQL gems.
|
||||
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
||||
# installed, running the following commands should work:
|
||||
# $ sudo gem install sequel
|
||||
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
||||
|
||||
module Jekyll
|
||||
module Mephisto
|
||||
#Accepts a hash with database config variables, exports mephisto posts into a csv
|
||||
#export PGPASSWORD if you must
|
||||
def self.postgres(c)
|
||||
sql = <<-SQL
|
||||
BEGIN;
|
||||
CREATE TEMP TABLE jekyll AS
|
||||
SELECT title, permalink, body, published_at, filter FROM contents
|
||||
WHERE user_id = 1 AND type = 'Article' ORDER BY published_at;
|
||||
COPY jekyll TO STDOUT WITH CSV HEADER;
|
||||
ROLLBACK;
|
||||
SQL
|
||||
command = %Q(psql -h #{c[:host] || "localhost"} -c "#{sql.strip}" #{c[:database]} #{c[:username]} -o #{c[:filename] || "posts.csv"})
|
||||
puts command
|
||||
`#{command}`
|
||||
CSV.process
|
||||
end
|
||||
|
||||
# This query will pull blog posts from all entries across all blogs. If
|
||||
# you've got unpublished, deleted or otherwise hidden posts please sift
|
||||
# through the created posts to make sure nothing is accidently published.
|
||||
|
||||
QUERY = "SELECT id, permalink, body, published_at, title FROM contents WHERE user_id = 1 AND type = 'Article' AND published_at IS NOT NULL ORDER BY published_at"
|
||||
|
||||
def self.process(dbname, user, pass, host = 'localhost')
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
|
||||
FileUtils.mkdir_p "_posts"
|
||||
|
||||
db[QUERY].each do |post|
|
||||
title = post[:title]
|
||||
slug = post[:permalink]
|
||||
date = post[:published_at]
|
||||
content = post[:body]
|
||||
# more_content = ''
|
||||
|
||||
# Be sure to include the body and extended body.
|
||||
# if more_content != nil
|
||||
# content = content + " \n" + more_content
|
||||
# end
|
||||
|
||||
# Ideally, this script would determine the post format (markdown, html
|
||||
# , etc) and create files with proper extensions. At this point it
|
||||
# just assumes that markdown will be acceptable.
|
||||
name = [date.year, date.month, date.day, slug].join('-') + ".markdown"
|
||||
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
'mt_id' => post[:entry_id],
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts data
|
||||
f.puts "---"
|
||||
f.puts content
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
77
lib/jekyll/migrators/mt.rb
Normal file
77
lib/jekyll/migrators/mt.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
# Created by Nick Gerakines, open source and publically available under the
|
||||
# MIT license. Use this module at your own risk.
|
||||
# I'm an Erlang/Perl/C++ guy so please forgive my dirty ruby.
|
||||
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
require 'fileutils'
|
||||
require 'yaml'
|
||||
|
||||
# NOTE: This converter requires Sequel and the MySQL gems.
|
||||
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
||||
# installed, running the following commands should work:
|
||||
# $ sudo gem install sequel
|
||||
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
||||
|
||||
module Jekyll
|
||||
module MT
|
||||
# This query will pull blog posts from all entries across all blogs. If
|
||||
# you've got unpublished, deleted or otherwise hidden posts please sift
|
||||
# through the created posts to make sure nothing is accidently published.
|
||||
QUERY = "SELECT entry_id, entry_basename, entry_text, entry_text_more, entry_authored_on, entry_title, entry_convert_breaks FROM mt_entry"
|
||||
|
||||
def self.process(dbname, user, pass, host = 'localhost')
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
|
||||
FileUtils.mkdir_p "_posts"
|
||||
|
||||
db[QUERY].each do |post|
|
||||
title = post[:entry_title]
|
||||
slug = post[:entry_basename].gsub(/_/, '-')
|
||||
date = post[:entry_authored_on]
|
||||
content = post[:entry_text]
|
||||
more_content = post[:entry_text_more]
|
||||
entry_convert_breaks = post[:entry_convert_breaks]
|
||||
|
||||
# Be sure to include the body and extended body.
|
||||
if more_content != nil
|
||||
content = content + " \n" + more_content
|
||||
end
|
||||
|
||||
# Ideally, this script would determine the post format (markdown, html
|
||||
# , etc) and create files with proper extensions. At this point it
|
||||
# just assumes that markdown will be acceptable.
|
||||
name = [date.year, date.month, date.day, slug].join('-') + '.' + self.suffix(entry_convert_breaks)
|
||||
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
'mt_id' => post[:entry_id],
|
||||
'date' => date
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts data
|
||||
f.puts "---"
|
||||
f.puts content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.suffix(entry_type)
|
||||
if entry_type.nil? || entry_type.include?("markdown")
|
||||
# The markdown plugin I have saves this as "markdown_with_smarty_pants", so I just look for "markdown".
|
||||
"markdown"
|
||||
elsif entry_type.include?("textile")
|
||||
# This is saved as "textile_2" on my installation of MT 5.1.
|
||||
"textile"
|
||||
elsif entry_type == "0" || entry_type.include?("richtext")
|
||||
# richtext looks to me like it's saved as HTML, so I include it here.
|
||||
"html"
|
||||
else
|
||||
# Other values might need custom work.
|
||||
entry_type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
73
lib/jekyll/migrators/posterous.rb
Normal file
73
lib/jekyll/migrators/posterous.rb
Normal file
@@ -0,0 +1,73 @@
|
||||
require 'rubygems'
|
||||
require 'jekyll'
|
||||
require 'fileutils'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require "json"
|
||||
|
||||
# ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, blog)'
|
||||
|
||||
module Jekyll
|
||||
module Posterous
|
||||
|
||||
def self.fetch(uri_str, limit = 10)
|
||||
# You should choose better exception.
|
||||
raise ArgumentError, 'Stuck in a redirect loop. Please double check your email and password' if limit == 0
|
||||
|
||||
response = nil
|
||||
Net::HTTP.start('posterous.com') {|http|
|
||||
req = Net::HTTP::Get.new(uri_str)
|
||||
req.basic_auth @email, @pass
|
||||
response = http.request(req)
|
||||
}
|
||||
|
||||
case response
|
||||
when Net::HTTPSuccess then response
|
||||
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
||||
else response.error!
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.process(email, pass, blog = 'primary')
|
||||
@email, @pass = email, pass
|
||||
@api_token = JSON.parse(self.fetch("/api/2/auth/token").body)['api_token']
|
||||
FileUtils.mkdir_p "_posts"
|
||||
|
||||
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}").body)
|
||||
page = 1
|
||||
|
||||
while posts.any?
|
||||
|
||||
posts.each do |post|
|
||||
title = post["title"]
|
||||
slug = title.gsub(/[^[:alnum:]]+/, '-').downcase
|
||||
date = Date.parse(post["display_date"])
|
||||
content = post["body_html"]
|
||||
published = !post["is_private"]
|
||||
name = "%02d-%02d-%02d-%s.html" % [date.year, date.month, date.day, slug]
|
||||
|
||||
# Get the relevant fields as a hash, delete empty fields and convert
|
||||
# to YAML for the header
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
'published' => published
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
# Write out the data and content to file
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts data
|
||||
f.puts "---"
|
||||
f.puts content
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
page += 1
|
||||
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}&page=#{page}").body)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
50
lib/jekyll/migrators/textpattern.rb
Normal file
50
lib/jekyll/migrators/textpattern.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
require 'fileutils'
|
||||
|
||||
# NOTE: This converter requires Sequel and the MySQL gems.
|
||||
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
||||
# installed, running the following commands should work:
|
||||
# $ sudo gem install sequel
|
||||
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
||||
|
||||
module Jekyll
|
||||
module TextPattern
|
||||
# Reads a MySQL database via Sequel and creates a post file for each post.
|
||||
# The only posts selected are those with a status of 4 or 5, which means "live"
|
||||
# and "sticky" respectively.
|
||||
# Other statuses is 1 => draft, 2 => hidden and 3 => pending
|
||||
QUERY = "select Title, url_title, Posted, Body, Keywords from textpattern where Status = '4' or Status = '5'"
|
||||
|
||||
def self.process(dbname, user, pass, host = 'localhost')
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
|
||||
FileUtils.mkdir_p "_posts"
|
||||
|
||||
db[QUERY].each do |post|
|
||||
# Get required fields and construct Jekyll compatible name
|
||||
title = post[:Title]
|
||||
slug = post[:url_title]
|
||||
date = post[:Posted]
|
||||
content = post[:Body]
|
||||
|
||||
name = [date.strftime("%Y-%m-%d"), slug].join('-') + ".textile"
|
||||
|
||||
# Get the relevant fields as a hash, delete empty fields and convert
|
||||
# to YAML for the header
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
'tags' => post[:Keywords].split(',')
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
# Write out the data and content to file
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts data
|
||||
f.puts "---"
|
||||
f.puts content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
49
lib/jekyll/migrators/typo.rb
Normal file
49
lib/jekyll/migrators/typo.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
# Author: Toby DiPasquale <toby@cbcg.net>
|
||||
require 'fileutils'
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
|
||||
module Jekyll
|
||||
module Typo
|
||||
# this SQL *should* work for both MySQL and PostgreSQL, but I haven't
|
||||
# tested PostgreSQL yet (as of 2008-12-16)
|
||||
SQL = <<-EOS
|
||||
SELECT c.id id,
|
||||
c.title title,
|
||||
c.permalink slug,
|
||||
c.body body,
|
||||
c.published_at date,
|
||||
c.state state,
|
||||
COALESCE(tf.name, 'html') filter
|
||||
FROM contents c
|
||||
LEFT OUTER JOIN text_filters tf
|
||||
ON c.text_filter_id = tf.id
|
||||
EOS
|
||||
|
||||
def self.process dbname, user, pass, host='localhost'
|
||||
FileUtils.mkdir_p '_posts'
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
db[SQL].each do |post|
|
||||
next unless post[:state] =~ /Published/
|
||||
|
||||
name = [ sprintf("%.04d", post[:date].year),
|
||||
sprintf("%.02d", post[:date].month),
|
||||
sprintf("%.02d", post[:date].day),
|
||||
post[:slug].strip ].join('-')
|
||||
# Can have more than one text filter in this field, but we just want
|
||||
# the first one for this
|
||||
name += '.' + post[:filter].split(' ')[0]
|
||||
|
||||
File.open("_posts/#{name}", 'w') do |f|
|
||||
f.puts({ 'layout' => 'post',
|
||||
'title' => post[:title].to_s,
|
||||
'typo_id' => post[:id]
|
||||
}.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
|
||||
f.puts '---'
|
||||
f.puts post[:body].delete("\r")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end # module Typo
|
||||
end # module Jekyll
|
||||
56
lib/jekyll/migrators/wordpress.rb
Normal file
56
lib/jekyll/migrators/wordpress.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
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
|
||||
# installed, running the following commands should work:
|
||||
# $ sudo gem install sequel
|
||||
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
||||
|
||||
module Jekyll
|
||||
module WordPress
|
||||
|
||||
def self.process(dbname, user, pass, host = 'localhost', table_prefix = 'wp_')
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
|
||||
FileUtils.mkdir_p "_posts"
|
||||
|
||||
# Reads a MySQL database via Sequel and creates a post file for each
|
||||
# post in wp_posts that has post_status = 'publish'.
|
||||
# This restriction is made because 'draft' posts are not guaranteed to
|
||||
# have valid dates.
|
||||
query = "select post_title, post_name, post_date, post_content, post_excerpt, ID, guid from #{table_prefix}posts where post_status = 'publish' and post_type = 'post'"
|
||||
|
||||
db[query].each do |post|
|
||||
# Get required fields and construct Jekyll compatible name
|
||||
title = post[:post_title]
|
||||
slug = post[:post_name]
|
||||
date = post[:post_date]
|
||||
content = post[:post_content]
|
||||
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
|
||||
slug]
|
||||
|
||||
# Get the relevant fields as a hash, delete empty fields and convert
|
||||
# to YAML for the header
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
'excerpt' => post[:post_excerpt].to_s,
|
||||
'wordpress_id' => post[:ID],
|
||||
'wordpress_url' => post[:guid],
|
||||
'date' => date
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
# Write out the data and content to file
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts data
|
||||
f.puts "---"
|
||||
f.puts content
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
45
lib/jekyll/migrators/wordpressdotcom.rb
Normal file
45
lib/jekyll/migrators/wordpressdotcom.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
# coding: utf-8
|
||||
|
||||
require 'rubygems'
|
||||
require 'hpricot'
|
||||
require 'fileutils'
|
||||
require 'yaml'
|
||||
|
||||
module Jekyll
|
||||
|
||||
# This importer takes a wordpress.xml file,
|
||||
# which can be exported from your
|
||||
# wordpress.com blog (/wp-admin/export.php)
|
||||
module WordpressDotCom
|
||||
def self.process(filename = "wordpress.xml")
|
||||
FileUtils.mkdir_p "_posts"
|
||||
posts = 0
|
||||
|
||||
doc = Hpricot::XML(File.read(filename))
|
||||
|
||||
(doc/:channel/:item).each do |item|
|
||||
title = item.at(:title).inner_text.strip
|
||||
permalink_title = item.at('wp:post_name').inner_text
|
||||
date = Time.parse(item.at(:pubDate).inner_text)
|
||||
tags = (item/:category).map{|c| c.inner_text}.reject{|c| c == 'Uncategorized'}.uniq
|
||||
name = "#{date.strftime('%Y-%m-%d')}-#{permalink_title}.html"
|
||||
header = {
|
||||
'layout' => 'post',
|
||||
'title' => title,
|
||||
'tags' => tags
|
||||
}
|
||||
|
||||
File.open("_posts/#{name}", "w") do |f|
|
||||
f.puts header.to_yaml
|
||||
f.puts '---'
|
||||
f.puts item.at('content:encoded').inner_text
|
||||
end
|
||||
|
||||
posts += 1
|
||||
end
|
||||
|
||||
puts "Imported #{posts} posts"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,95 +0,0 @@
|
||||
-# These are the same MIME types that GitHub Pages uses as of 26 January 2014
|
||||
|
||||
text/html html htm shtml
|
||||
text/css css
|
||||
text/xml xml rss xsl xsd
|
||||
image/gif gif
|
||||
image/jpeg jpeg jpg
|
||||
application/x-javascript js
|
||||
application/atom+xml atom
|
||||
application/json json geojson topojson
|
||||
|
||||
text/mathml mml
|
||||
text/plain txt
|
||||
text/vnd.sun.j2me.app-descriptor jad
|
||||
text/vnd.wap.wml wml
|
||||
text/x-component htc
|
||||
text/cache-manifest manifest appcache
|
||||
text/coffeescript coffee
|
||||
text/plain pde
|
||||
text/plain md markdown
|
||||
text/vcard vcf vcard
|
||||
|
||||
image/png png
|
||||
image/svg+xml svg
|
||||
image/svg+xml svgz
|
||||
image/tiff tif tiff
|
||||
image/vnd.wap.wbmp wbmp
|
||||
image/x-icon ico
|
||||
image/x-jng jng
|
||||
image/x-ms-bmp bmp
|
||||
|
||||
application/vnd.ms-fontobject eot
|
||||
application/x-font-ttf ttf
|
||||
application/x-font-woff woff
|
||||
font/opentype otf
|
||||
|
||||
application/java-archive jar ear
|
||||
application/mac-binhex40 hqx
|
||||
application/msword doc
|
||||
application/pdf pdf
|
||||
application/postscript ps eps ai
|
||||
application/rdf+xml rdf
|
||||
application/rtf rtf
|
||||
application/vnd.apple.pkpass pkpass
|
||||
application/vnd.ms-excel xls
|
||||
application/vnd.ms-powerpoint ppt
|
||||
application/vnd.wap.wmlc wmlc
|
||||
application/xhtml+xml xhtml
|
||||
application/x-cocoa cco
|
||||
application/x-chrome-extension crx
|
||||
application/x-java-archive-diff jardiff
|
||||
application/x-java-jnlp-file jnlp
|
||||
application/x-makeself run
|
||||
application/x-ms-application application
|
||||
application/x-ms-manifest manifest
|
||||
application/x-ms-vsto vsto
|
||||
application/x-ns-proxy-autoconfig pac
|
||||
application/x-perl pl pm
|
||||
application/x-pilot prc pdb
|
||||
application/x-rar-compressed rar
|
||||
application/x-redhat-package-manager rpm
|
||||
application/x-sea sea
|
||||
application/x-shockwave-flash swf
|
||||
application/x-stuffit sit
|
||||
application/x-tcl tcl tk
|
||||
application/x-web-app-manifest+json webapp
|
||||
application/x-x509-ca-cert der pem crt
|
||||
application/x-xpinstall xpi
|
||||
application/x-zip war
|
||||
application/zip zip
|
||||
|
||||
application/octet-stream bin exe dll
|
||||
application/octet-stream deb
|
||||
application/octet-stream deploy
|
||||
application/octet-stream dmg
|
||||
application/octet-stream iso img
|
||||
application/octet-stream msi msp msm
|
||||
|
||||
audio/midi mid midi kar
|
||||
audio/mpeg mp3
|
||||
audio/x-realaudio ra
|
||||
audio/ogg ogg
|
||||
|
||||
video/3gpp 3gpp 3gp
|
||||
video/m4v m4v
|
||||
video/mp4 mp4
|
||||
video/mpeg mpeg mpg
|
||||
video/ogg ogg ogv
|
||||
video/quicktime mov
|
||||
video/webm webm
|
||||
video/x-flv flv
|
||||
video/x-mng mng
|
||||
video/x-ms-asf asx asf
|
||||
video/x-ms-wmv wmv
|
||||
video/x-msvideo avi
|
||||
@@ -1,167 +1,143 @@
|
||||
module Jekyll
|
||||
|
||||
class Page
|
||||
include Convertible
|
||||
|
||||
attr_writer :dir
|
||||
attr_accessor :site, :pager
|
||||
attr_accessor :name, :ext, :basename
|
||||
attr_accessor :name, :ext, :basename, :dir
|
||||
attr_accessor :data, :content, :output
|
||||
|
||||
# Attributes for Liquid templates
|
||||
ATTRIBUTES_FOR_LIQUID = %w[
|
||||
content
|
||||
dir
|
||||
name
|
||||
path
|
||||
url
|
||||
]
|
||||
|
||||
# Initialize a new Page.
|
||||
# +site+ is the Site
|
||||
# +base+ is the String path to the <source>
|
||||
# +dir+ is the String path between <source> and the file
|
||||
# +name+ is the String filename of the file
|
||||
#
|
||||
# site - The Site object.
|
||||
# base - The String path to the source.
|
||||
# dir - The String path between the source and the file.
|
||||
# name - The String filename of the file.
|
||||
# Returns <Page>
|
||||
def initialize(site, base, dir, name)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@name = name
|
||||
|
||||
|
||||
process(name)
|
||||
read_yaml(File.join(base, dir), name)
|
||||
|
||||
data.default_proc = proc do |hash, key|
|
||||
site.frontmatter_defaults.find(File.join(dir, name), type, key)
|
||||
end
|
||||
self.process(name)
|
||||
self.read_yaml(File.join(base, dir), name)
|
||||
end
|
||||
|
||||
# The generated directory into which the page will be placed
|
||||
# upon generation. This is derived from the permalink or, if
|
||||
# permalink is absent, we be '/'
|
||||
# permalink is absent, set to '/'
|
||||
#
|
||||
# Returns the String destination directory.
|
||||
# Returns <String>
|
||||
def dir
|
||||
url[-1, 1] == '/' ? url : File.dirname(url)
|
||||
end
|
||||
|
||||
# The full path and filename of the post. Defined in the YAML of the post
|
||||
# body.
|
||||
# The full path and filename of the post.
|
||||
# Defined in the YAML of the post body
|
||||
# (Optional)
|
||||
#
|
||||
# Returns the String permalink or nil if none has been set.
|
||||
# Returns <String>
|
||||
def permalink
|
||||
return nil if data.nil? || data['permalink'].nil?
|
||||
if site.config['relative_permalinks']
|
||||
File.join(@dir, data['permalink'])
|
||||
else
|
||||
data['permalink']
|
||||
end
|
||||
self.data && self.data['permalink']
|
||||
end
|
||||
|
||||
# The template of the permalink.
|
||||
#
|
||||
# Returns the template String.
|
||||
def template
|
||||
if site.permalink_style == :pretty
|
||||
if index? && html?
|
||||
"/:path/"
|
||||
elsif html?
|
||||
"/:path/:basename/"
|
||||
else
|
||||
"/:path/:basename:output_ext"
|
||||
end
|
||||
if self.site.permalink_style == :pretty && !index? && html?
|
||||
"/:basename/"
|
||||
else
|
||||
"/:path/:basename:output_ext"
|
||||
"/:basename:output_ext"
|
||||
end
|
||||
end
|
||||
|
||||
# The generated relative url of this page. e.g. /about.html.
|
||||
# The generated relative url of this page
|
||||
# e.g. /about.html
|
||||
#
|
||||
# Returns the String url.
|
||||
# Returns <String>
|
||||
def url
|
||||
@url ||= URL.new({
|
||||
:template => template,
|
||||
:placeholders => url_placeholders,
|
||||
:permalink => permalink
|
||||
}).to_s
|
||||
return @url if @url
|
||||
|
||||
url = if permalink
|
||||
permalink
|
||||
else
|
||||
{
|
||||
"basename" => self.basename,
|
||||
"output_ext" => self.output_ext,
|
||||
}.inject(template) { |result, token|
|
||||
result.gsub(/:#{token.first}/, token.last)
|
||||
}.gsub(/\/\//, "/")
|
||||
end
|
||||
|
||||
# sanitize url
|
||||
@url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
||||
@url += "/" if url =~ /\/$/
|
||||
@url
|
||||
end
|
||||
|
||||
# Returns a hash of URL placeholder names (as symbols) mapping to the
|
||||
# desired placeholder replacements. For details see "url.rb"
|
||||
def url_placeholders
|
||||
{
|
||||
:path => @dir,
|
||||
:basename => basename,
|
||||
:output_ext => output_ext
|
||||
}
|
||||
end
|
||||
|
||||
# Extract information from the page filename.
|
||||
# Extract information from the page filename
|
||||
# +name+ is the String filename of the page file
|
||||
#
|
||||
# name - The String filename of the page file.
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def process(name)
|
||||
self.ext = File.extname(name)
|
||||
self.basename = name[0 .. -ext.length - 1]
|
||||
self.basename = name[0 .. -self.ext.length-1]
|
||||
end
|
||||
|
||||
# Add any necessary layouts to this post
|
||||
# +layouts+ is a Hash of {"name" => "layout"}
|
||||
# +site_payload+ is the site payload hash
|
||||
#
|
||||
# layouts - The Hash of {"name" => "layout"}.
|
||||
# site_payload - The site payload Hash.
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def render(layouts, site_payload)
|
||||
payload = Utils.deep_merge_hashes({
|
||||
"page" => to_liquid,
|
||||
payload = {
|
||||
"page" => self.to_liquid,
|
||||
'paginator' => pager.to_liquid
|
||||
}, site_payload)
|
||||
}.deep_merge(site_payload)
|
||||
|
||||
do_layout(payload, layouts)
|
||||
end
|
||||
|
||||
# The path to the source file
|
||||
#
|
||||
# Returns the path to the source file
|
||||
def path
|
||||
data.fetch('path', relative_path.sub(/\A\//, ''))
|
||||
def to_liquid
|
||||
self.data.deep_merge({
|
||||
"url" => File.join(@dir, self.url),
|
||||
"content" => self.content })
|
||||
end
|
||||
|
||||
# The path to the page source file, relative to the site source
|
||||
def relative_path
|
||||
File.join(*[@dir, @name].map(&:to_s).reject(&:empty?))
|
||||
end
|
||||
|
||||
|
||||
# Obtain destination path.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# dest - The String path to the destination dir.
|
||||
#
|
||||
# Returns the destination file path String.
|
||||
# Returns destination file path.
|
||||
def destination(dest)
|
||||
path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
|
||||
path = File.join(path, "index.html") if url =~ /\/$/
|
||||
# The url needs to be unescaped in order to preserve the correct filename
|
||||
path = File.join(dest, @dir, CGI.unescape(self.url))
|
||||
path = File.join(path, "index.html") if self.url =~ /\/$/
|
||||
path
|
||||
end
|
||||
|
||||
# Returns the object as a debug String.
|
||||
def inspect
|
||||
"#<Jekyll:Page @name=#{name.inspect}>"
|
||||
# Write the generated page file to the destination directory.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns nothing
|
||||
def write(dest)
|
||||
path = destination(dest)
|
||||
FileUtils.mkdir_p(File.dirname(path))
|
||||
File.open(path, 'w') do |f|
|
||||
f.write(self.output)
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Jekyll:Page @name=#{self.name.inspect}>"
|
||||
end
|
||||
|
||||
# Returns the Boolean of whether this Page is HTML or not.
|
||||
def html?
|
||||
output_ext == '.html'
|
||||
end
|
||||
|
||||
# Returns the Boolean of whether this Page is an index file or not.
|
||||
def index?
|
||||
basename == 'index'
|
||||
end
|
||||
|
||||
def uses_relative_permalinks
|
||||
permalink && !@dir.empty? && site.config['relative_permalinks']
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module Jekyll
|
||||
|
||||
class Plugin
|
||||
PRIORITIES = { :lowest => -100,
|
||||
:low => -10,
|
||||
@@ -6,15 +7,22 @@ module Jekyll
|
||||
:high => 10,
|
||||
:highest => 100 }
|
||||
|
||||
# Fetch all the subclasses of this class and its subclasses' subclasses.
|
||||
# Install a hook so that subclasses are recorded. This method is only
|
||||
# ever called by Ruby itself.
|
||||
#
|
||||
# Returns an array of descendant classes.
|
||||
def self.descendants
|
||||
descendants = []
|
||||
ObjectSpace.each_object(singleton_class) do |k|
|
||||
descendants.unshift k unless k == self
|
||||
end
|
||||
descendants
|
||||
# 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
|
||||
@@ -26,7 +34,6 @@ module Jekyll
|
||||
#
|
||||
# Returns the Symbol priority.
|
||||
def self.priority(priority = nil)
|
||||
@priority ||= nil
|
||||
if priority && PRIORITIES.has_key?(priority)
|
||||
@priority = priority
|
||||
end
|
||||
@@ -65,4 +72,5 @@ module Jekyll
|
||||
# no-op for default
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
module Jekyll
|
||||
class PluginManager
|
||||
attr_reader :site
|
||||
|
||||
# Create an instance of this class.
|
||||
#
|
||||
# site - the instance of Jekyll::Site we're concerned with
|
||||
#
|
||||
# Returns nothing
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
# Require all the plugins which are allowed.
|
||||
#
|
||||
# Returns nothing
|
||||
def conscientious_require
|
||||
require_plugin_files
|
||||
require_gems
|
||||
end
|
||||
|
||||
# Require each of the gem plugins specified.
|
||||
#
|
||||
# Returns nothing.
|
||||
def require_gems
|
||||
site.gems.each do |gem|
|
||||
if plugin_allowed?(gem)
|
||||
require gem
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check whether a gem plugin is allowed to be used during this build.
|
||||
#
|
||||
# gem_name - the name of the gem
|
||||
#
|
||||
# Returns true if the gem name is in the whitelist or if the site is not
|
||||
# in safe mode.
|
||||
def plugin_allowed?(gem_name)
|
||||
!site.safe || whitelist.include?(gem_name)
|
||||
end
|
||||
|
||||
# Build an array of allowed plugin gem names.
|
||||
#
|
||||
# Returns an array of strings, each string being the name of a gem name
|
||||
# that is allowed to be used.
|
||||
def whitelist
|
||||
@whitelist ||= Array[site.config['whitelist']].flatten
|
||||
end
|
||||
|
||||
# Require all .rb files if safe mode is off
|
||||
#
|
||||
# Returns nothing.
|
||||
def require_plugin_files
|
||||
unless site.safe
|
||||
plugins_path.each do |plugins|
|
||||
Dir[File.join(plugins, "**", "*.rb")].sort.each do |f|
|
||||
require f
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Setup the plugin search path
|
||||
#
|
||||
# Returns an Array of plugin search paths
|
||||
def plugins_path
|
||||
if (site.config['plugins'] == Jekyll::Configuration::DEFAULTS['plugins'])
|
||||
[Jekyll.sanitized_path(site.source, site.config['plugins'])]
|
||||
else
|
||||
Array(site.config['plugins']).map { |d| File.expand_path(d) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,147 +1,64 @@
|
||||
module Jekyll
|
||||
|
||||
class Post
|
||||
include Comparable
|
||||
include Convertible
|
||||
|
||||
# Valid post name regex.
|
||||
class << self
|
||||
attr_accessor :lsi
|
||||
end
|
||||
|
||||
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
||||
|
||||
EXCERPT_ATTRIBUTES_FOR_LIQUID = %w[
|
||||
title
|
||||
url
|
||||
dir
|
||||
date
|
||||
id
|
||||
categories
|
||||
next
|
||||
previous
|
||||
tags
|
||||
path
|
||||
]
|
||||
|
||||
# Attributes for Liquid templates
|
||||
ATTRIBUTES_FOR_LIQUID = EXCERPT_ATTRIBUTES_FOR_LIQUID + %w[
|
||||
content
|
||||
excerpt
|
||||
]
|
||||
|
||||
# Post name validator. Post filenames must be like:
|
||||
# 2008-11-05-my-awesome-post.textile
|
||||
# 2008-11-05-my-awesome-post.textile
|
||||
#
|
||||
# Returns true if valid, false if not.
|
||||
# Returns <Bool>
|
||||
def self.valid?(name)
|
||||
name =~ MATCHER
|
||||
end
|
||||
|
||||
attr_accessor :site
|
||||
attr_accessor :data, :extracted_excerpt, :content, :output, :ext
|
||||
attr_accessor :date, :slug, :tags, :categories
|
||||
|
||||
attr_reader :name
|
||||
attr_accessor :data, :content, :output, :ext
|
||||
attr_accessor :date, :slug, :published, :tags, :categories
|
||||
|
||||
# Initialize this Post instance.
|
||||
# +site+ is the Site
|
||||
# +base+ is the String path to the dir containing the post file
|
||||
# +name+ is the String filename of the post file
|
||||
# +categories+ is an Array of Strings for the categories for this post
|
||||
#
|
||||
# site - The Site.
|
||||
# base - The String path to the dir containing the post file.
|
||||
# name - The String filename of the post file.
|
||||
#
|
||||
# Returns the new Post.
|
||||
# Returns <Post>
|
||||
def initialize(site, source, dir, name)
|
||||
@site = site
|
||||
@dir = dir
|
||||
@base = containing_dir(source, dir)
|
||||
@base = File.join(source, dir, '_posts')
|
||||
@name = name
|
||||
|
||||
self.categories = dir.downcase.split('/').reject { |x| x.empty? }
|
||||
process(name)
|
||||
read_yaml(@base, name)
|
||||
self.categories = dir.split('/').reject { |x| x.empty? }
|
||||
self.process(name)
|
||||
self.read_yaml(@base, name)
|
||||
|
||||
data.default_proc = proc do |hash, key|
|
||||
site.frontmatter_defaults.find(File.join(dir, name), type, key)
|
||||
#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 data.has_key?('date')
|
||||
self.date = Time.parse(data["date"].to_s)
|
||||
end
|
||||
|
||||
populate_categories
|
||||
populate_tags
|
||||
end
|
||||
|
||||
def published?
|
||||
if data.has_key?('published') && data['published'] == false
|
||||
false
|
||||
if self.data.has_key?('published') && self.data['published'] == false
|
||||
self.published = false
|
||||
else
|
||||
true
|
||||
self.published = true
|
||||
end
|
||||
|
||||
self.tags = self.data.pluralized_array("tag", "tags")
|
||||
|
||||
if self.categories.empty?
|
||||
self.categories = self.data.pluralized_array('category', 'categories')
|
||||
end
|
||||
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!
|
||||
end
|
||||
|
||||
def populate_tags
|
||||
self.tags = Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
|
||||
end
|
||||
|
||||
# Get the full path to the directory containing the post files
|
||||
def containing_dir(source, dir)
|
||||
return File.join(source, dir, '_posts')
|
||||
end
|
||||
|
||||
# Read the YAML frontmatter.
|
||||
#
|
||||
# base - The String path to the dir containing the file.
|
||||
# name - The String filename of the file.
|
||||
#
|
||||
# Returns nothing.
|
||||
def read_yaml(base, name)
|
||||
super(base, name)
|
||||
self.extracted_excerpt = extract_excerpt
|
||||
end
|
||||
|
||||
# The post excerpt. This is either a custom excerpt
|
||||
# set in YAML front matter or the result of extract_excerpt.
|
||||
#
|
||||
# Returns excerpt string.
|
||||
def excerpt
|
||||
data.fetch('excerpt', extracted_excerpt.to_s)
|
||||
end
|
||||
|
||||
# Public: the Post title, from the YAML Front-Matter or from the slug
|
||||
#
|
||||
# Returns the post title
|
||||
def title
|
||||
data.fetch("title", titleized_slug)
|
||||
end
|
||||
|
||||
# Turns the post slug into a suitable title
|
||||
def titleized_slug
|
||||
slug.split('-').select {|w| w.capitalize! || w }.join(' ')
|
||||
end
|
||||
|
||||
# Public: the path to the post relative to the site source,
|
||||
# from the YAML Front-Matter or from a combination of
|
||||
# the directory it's in, "_posts", and the name of the
|
||||
# post file
|
||||
#
|
||||
# Returns the path to the file relative to the site source
|
||||
def path
|
||||
data.fetch('path', relative_path.sub(/\A\//, ''))
|
||||
end
|
||||
|
||||
# The path to the post source file, relative to the site source
|
||||
def relative_path
|
||||
File.join(*[@dir, "_posts", @name].map(&:to_s).reject(&:empty?))
|
||||
end
|
||||
|
||||
# Compares Post objects. First compares the Post date. If the dates are
|
||||
# equal, it compares the Post slugs.
|
||||
#
|
||||
# other - The other Post we are comparing to.
|
||||
# Spaceship is based on Post#date, slug
|
||||
#
|
||||
# Returns -1, 0, 1
|
||||
def <=>(other)
|
||||
@@ -152,167 +69,187 @@ module Jekyll
|
||||
return cmp
|
||||
end
|
||||
|
||||
# Extract information from the post filename.
|
||||
# Extract information from the post filename
|
||||
# +name+ is the String filename of the post file
|
||||
#
|
||||
# name - The String filename of the post file.
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def process(name)
|
||||
m, cats, date, slug, ext = *name.match(MATCHER)
|
||||
self.date = Time.parse(date)
|
||||
self.slug = slug
|
||||
self.ext = ext
|
||||
rescue ArgumentError
|
||||
path = File.join(@dir || "", name)
|
||||
msg = "Post '#{path}' does not have a valid date.\n"
|
||||
msg << "Fix the date, or exclude the file or directory from being processed"
|
||||
raise FatalException.new(msg)
|
||||
raise FatalException.new("Post #{name} does not have a valid date.")
|
||||
end
|
||||
|
||||
# The generated directory into which the post will be placed
|
||||
# upon generation. This is derived from the permalink or, if
|
||||
# permalink is absent, set to the default date
|
||||
# e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing.
|
||||
# e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing
|
||||
#
|
||||
# Returns the String directory.
|
||||
# Returns <String>
|
||||
def dir
|
||||
File.dirname(url)
|
||||
end
|
||||
|
||||
# The full path and filename of the post. Defined in the YAML of the post
|
||||
# body (optional).
|
||||
# The full path and filename of the post.
|
||||
# Defined in the YAML of the post body
|
||||
# (Optional)
|
||||
#
|
||||
# Returns the String permalink.
|
||||
# Returns <String>
|
||||
def permalink
|
||||
data && data['permalink']
|
||||
self.data && self.data['permalink']
|
||||
end
|
||||
|
||||
def template
|
||||
case site.permalink_style
|
||||
case self.site.permalink_style
|
||||
when :pretty
|
||||
"/:categories/:year/:month/:day/:title/"
|
||||
when :none
|
||||
"/:categories/:title.html"
|
||||
when :date
|
||||
"/:categories/:year/:month/:day/:title.html"
|
||||
when :ordinal
|
||||
"/:categories/:year/:y_day/:title.html"
|
||||
else
|
||||
site.permalink_style.to_s
|
||||
self.site.permalink_style.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# The generated relative url of this post.
|
||||
# The generated relative url of this post
|
||||
# e.g. /2008/11/05/my-awesome-post.html
|
||||
#
|
||||
# Returns the String url.
|
||||
# Returns <String>
|
||||
def url
|
||||
@url ||= URL.new({
|
||||
:template => template,
|
||||
:placeholders => url_placeholders,
|
||||
:permalink => permalink
|
||||
}).to_s
|
||||
return @url if @url
|
||||
|
||||
url = if permalink
|
||||
permalink
|
||||
else
|
||||
{
|
||||
"year" => date.strftime("%Y"),
|
||||
"month" => date.strftime("%m"),
|
||||
"day" => date.strftime("%d"),
|
||||
"title" => CGI.escape(slug),
|
||||
"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(/:#{Regexp.escape token.first}/, token.last)
|
||||
}.gsub(/\/\//, "/")
|
||||
end
|
||||
|
||||
# sanitize url
|
||||
@url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
||||
@url += "/" if url =~ /\/$/
|
||||
@url
|
||||
end
|
||||
|
||||
# Returns a hash of URL placeholder names (as symbols) mapping to the
|
||||
# desired placeholder replacements. For details see "url.rb"
|
||||
def url_placeholders
|
||||
{
|
||||
:year => date.strftime("%Y"),
|
||||
:month => date.strftime("%m"),
|
||||
:day => date.strftime("%d"),
|
||||
:title => slug,
|
||||
:i_day => date.strftime("%d").to_i.to_s,
|
||||
:i_month => date.strftime("%m").to_i.to_s,
|
||||
:categories => (categories || []).map { |c| c.to_s }.join('/'),
|
||||
:short_month => date.strftime("%b"),
|
||||
:short_year => date.strftime("%y"),
|
||||
:y_day => date.strftime("%j"),
|
||||
:output_ext => output_ext
|
||||
}
|
||||
end
|
||||
|
||||
# The UID for this post (useful in feeds).
|
||||
# The UID for this post (useful in feeds)
|
||||
# e.g. /2008/11/05/my-awesome-post
|
||||
#
|
||||
# Returns the String UID.
|
||||
# Returns <String>
|
||||
def id
|
||||
File.join(dir, slug)
|
||||
File.join(self.dir, self.slug)
|
||||
end
|
||||
|
||||
# Calculate related posts.
|
||||
#
|
||||
# Returns an Array of related Posts.
|
||||
# Returns [<Post>]
|
||||
def related_posts(posts)
|
||||
Jekyll::RelatedPosts.new(self).build
|
||||
return [] unless posts.size > 1
|
||||
|
||||
if self.site.lsi
|
||||
self.class.lsi ||= begin
|
||||
puts "Running the classifier... this could take a while."
|
||||
lsi = Classifier::LSI.new
|
||||
posts.each { |x| $stdout.print(".");$stdout.flush;lsi.add_item(x) }
|
||||
puts ""
|
||||
lsi
|
||||
end
|
||||
|
||||
related = self.class.lsi.find_related(self.content, 11)
|
||||
related - [self]
|
||||
else
|
||||
(posts - [self])[0..9]
|
||||
end
|
||||
end
|
||||
|
||||
# Add any necessary layouts to this post.
|
||||
# Add any necessary layouts to this post
|
||||
# +layouts+ is a Hash of {"name" => "layout"}
|
||||
# +site_payload+ is the site payload hash
|
||||
#
|
||||
# layouts - A Hash of {"name" => "layout"}.
|
||||
# site_payload - The site payload hash.
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def render(layouts, site_payload)
|
||||
# construct payload
|
||||
payload = Utils.deep_merge_hashes({
|
||||
payload = {
|
||||
"site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
|
||||
"page" => to_liquid(EXCERPT_ATTRIBUTES_FOR_LIQUID)
|
||||
}, site_payload)
|
||||
"page" => self.to_liquid
|
||||
}.deep_merge(site_payload)
|
||||
|
||||
if generate_excerpt?
|
||||
extracted_excerpt.do_layout(payload, {})
|
||||
end
|
||||
|
||||
do_layout(payload.merge({"page" => to_liquid}), layouts)
|
||||
do_layout(payload, layouts)
|
||||
end
|
||||
|
||||
|
||||
# Obtain destination path.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# dest - The String path to the destination dir.
|
||||
#
|
||||
# Returns destination file path String.
|
||||
# Returns destination file path.
|
||||
def destination(dest)
|
||||
# The url needs to be unescaped in order to preserve the correct filename
|
||||
path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
|
||||
path = File.join(path, "index.html") if path[/\.html$/].nil?
|
||||
path = File.join(dest, CGI.unescape(self.url))
|
||||
path = File.join(path, "index.html") if template[/\.html$/].nil?
|
||||
path
|
||||
end
|
||||
|
||||
# Returns the shorthand String identifier of this Post.
|
||||
# Write the generated post file to the destination directory.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns nothing
|
||||
def write(dest)
|
||||
path = destination(dest)
|
||||
FileUtils.mkdir_p(File.dirname(path))
|
||||
File.open(path, 'w') do |f|
|
||||
f.write(self.output)
|
||||
end
|
||||
end
|
||||
|
||||
# Convert this post into a Hash for use in Liquid templates.
|
||||
#
|
||||
# Returns <Hash>
|
||||
def to_liquid
|
||||
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,
|
||||
"categories" => self.categories,
|
||||
"next" => self.next,
|
||||
"previous" => self.previous,
|
||||
"tags" => self.tags,
|
||||
"content" => self.content })
|
||||
end
|
||||
|
||||
def inspect
|
||||
"<Post: #{id}>"
|
||||
"<Post: #{self.id}>"
|
||||
end
|
||||
|
||||
def next
|
||||
pos = site.posts.index {|post| post.equal?(self) }
|
||||
if pos && pos < site.posts.length - 1
|
||||
site.posts[pos + 1]
|
||||
pos = self.site.posts.index(self)
|
||||
|
||||
if pos && pos < self.site.posts.length-1
|
||||
self.site.posts[pos+1]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def previous
|
||||
pos = site.posts.index {|post| post.equal?(self) }
|
||||
pos = self.site.posts.index(self)
|
||||
if pos && pos > 0
|
||||
site.posts[pos - 1]
|
||||
self.site.posts[pos-1]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def extract_excerpt
|
||||
if generate_excerpt?
|
||||
Jekyll::Excerpt.new(self)
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def generate_excerpt?
|
||||
!(site.config['excerpt_separator'].to_s.empty?)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
module Jekyll
|
||||
class Publisher
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def publish?(thing)
|
||||
can_be_published?(thing) && !hidden_in_the_future?(thing)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_be_published?(thing)
|
||||
thing.data.fetch('published', true) || @site.unpublished
|
||||
end
|
||||
|
||||
def hidden_in_the_future?(thing)
|
||||
thing.is_a?(Post) && !@site.future && thing.date > @site.time
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,59 +0,0 @@
|
||||
module Jekyll
|
||||
class RelatedPosts
|
||||
|
||||
class << self
|
||||
attr_accessor :lsi
|
||||
end
|
||||
|
||||
attr_reader :post, :site
|
||||
|
||||
def initialize(post)
|
||||
@post = post
|
||||
@site = post.site
|
||||
require 'classifier' if site.lsi
|
||||
end
|
||||
|
||||
def build
|
||||
return [] unless site.posts.size > 1
|
||||
|
||||
if site.lsi
|
||||
build_index
|
||||
lsi_related_posts
|
||||
else
|
||||
most_recent_posts
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def build_index
|
||||
self.class.lsi ||= begin
|
||||
lsi = Classifier::LSI.new(:auto_rebuild => false)
|
||||
display("Populating LSI...")
|
||||
|
||||
site.posts.each do |x|
|
||||
lsi.add_item(x)
|
||||
end
|
||||
|
||||
display("Rebuilding index...")
|
||||
lsi.build_index
|
||||
display("")
|
||||
lsi
|
||||
end
|
||||
end
|
||||
|
||||
def lsi_related_posts
|
||||
self.class.lsi.find_related(post.content, 11) - [post]
|
||||
end
|
||||
|
||||
def most_recent_posts
|
||||
recent_posts = site.posts.reverse - [post]
|
||||
recent_posts.first(10)
|
||||
end
|
||||
|
||||
def display(output)
|
||||
$stdout.print("\n")
|
||||
$stdout.print(Jekyll.logger.formatted_topic(output))
|
||||
$stdout.flush
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,132 +0,0 @@
|
||||
module Jekyll
|
||||
class Renderer
|
||||
|
||||
attr_reader :document, :site
|
||||
|
||||
def initialize(site, document)
|
||||
@site = site
|
||||
@document = document
|
||||
end
|
||||
|
||||
# Determine which converters to use based on this document's
|
||||
# extension.
|
||||
#
|
||||
# Returns an array of Converter instances.
|
||||
def converters
|
||||
@converters ||= site.converters.select { |c| c.matches(document.extname) }
|
||||
end
|
||||
|
||||
# Determine the extname the outputted file should have
|
||||
#
|
||||
# Returns the output extname including the leading period.
|
||||
def output_ext
|
||||
converters.first.output_ext(document.extname)
|
||||
end
|
||||
|
||||
######################
|
||||
## DAT RENDER THO
|
||||
######################
|
||||
|
||||
def run
|
||||
payload = Utils.deep_merge_hashes({
|
||||
"page" => document.to_liquid
|
||||
}, site.site_payload)
|
||||
|
||||
info = {
|
||||
filters: [Jekyll::Filters],
|
||||
registers: { :site => site, :page => payload['page'] }
|
||||
}
|
||||
|
||||
# render and transform content (this becomes the final content of the object)
|
||||
payload["highlighter_prefix"] = converters.first.highlighter_prefix
|
||||
payload["highlighter_suffix"] = converters.first.highlighter_suffix
|
||||
|
||||
output = document.content
|
||||
|
||||
if document.render_with_liquid?
|
||||
output = render_liquid(output, payload, info)
|
||||
end
|
||||
|
||||
place_in_layouts(
|
||||
convert(output),
|
||||
payload,
|
||||
info
|
||||
)
|
||||
end
|
||||
|
||||
# Convert the given content using the converters which match this renderer's document.
|
||||
#
|
||||
# content - the raw, unconverted content
|
||||
#
|
||||
# Returns the converted content.
|
||||
def convert(content)
|
||||
converters.reduce(content) do |output, converter|
|
||||
begin
|
||||
converter.convert output
|
||||
rescue => e
|
||||
Jekyll.logger.error "Conversion error:", "#{converter.class} encountered an error converting '#{document.relative_path}'."
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Render the given content with the payload and info
|
||||
#
|
||||
# content -
|
||||
# payload -
|
||||
# info -
|
||||
# path - (optional) the path to the file, for use in ex
|
||||
#
|
||||
# Returns the content, rendered by Liquid.
|
||||
def render_liquid(content, payload, info, path = nil)
|
||||
Liquid::Template.parse(content).render!(payload, info)
|
||||
rescue Tags::IncludeTagError => e
|
||||
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || document.relative_path}"
|
||||
raise e
|
||||
rescue Exception => e
|
||||
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || document.relative_path}"
|
||||
raise e
|
||||
end
|
||||
|
||||
# Render layouts and place given content inside.
|
||||
#
|
||||
# content - the content to be placed in the layout
|
||||
#
|
||||
#
|
||||
# Returns the content placed in the Liquid-rendered layouts
|
||||
def place_in_layouts(content, payload, info)
|
||||
output = content.dup
|
||||
layout = site.layouts[document.data["layout"]]
|
||||
used = Set.new([layout])
|
||||
|
||||
while layout
|
||||
payload = Utils.deep_merge_hashes(
|
||||
payload,
|
||||
{
|
||||
"content" => output,
|
||||
"page" => document.to_liquid,
|
||||
"layout" => layout.data
|
||||
}
|
||||
)
|
||||
|
||||
output = render_liquid(
|
||||
layout.content,
|
||||
payload,
|
||||
info,
|
||||
File.join(site.config['layouts'], layout.name)
|
||||
)
|
||||
|
||||
if layout = site.layouts[layout.data["layout"]]
|
||||
if used.include?(layout)
|
||||
layout = nil # avoid recursive chain
|
||||
else
|
||||
used << layout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,458 +1,280 @@
|
||||
require 'set'
|
||||
|
||||
module Jekyll
|
||||
|
||||
class Site
|
||||
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
||||
:exclude, :include, :source, :dest, :lsi, :highlighter,
|
||||
:permalink_style, :time, :future, :unpublished, :safe, :plugins, :limit_posts,
|
||||
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :gems,
|
||||
:plugin_manager
|
||||
:categories, :exclude, :source, :dest, :lsi, :pygments,
|
||||
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts
|
||||
|
||||
attr_accessor :converters, :generators
|
||||
|
||||
# Public: Initialize a new Site.
|
||||
# Initialize the site
|
||||
# +config+ is a Hash containing site configurations details
|
||||
#
|
||||
# config - A Hash containing site configuration details.
|
||||
# Returns <Site>
|
||||
def initialize(config)
|
||||
self.config = config.clone
|
||||
|
||||
%w[safe lsi highlighter baseurl exclude include future unpublished
|
||||
show_drafts limit_posts keep_files gems].each do |opt|
|
||||
self.send("#{opt}=", config[opt])
|
||||
end
|
||||
self.config = config.clone
|
||||
|
||||
self.safe = config['safe']
|
||||
self.source = File.expand_path(config['source'])
|
||||
self.dest = File.expand_path(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.limit_posts = config['limit_posts'] || nil
|
||||
|
||||
self.plugin_manager = Jekyll::PluginManager.new(self)
|
||||
self.plugins = plugin_manager.plugins_path
|
||||
|
||||
self.file_read_opts = {}
|
||||
self.file_read_opts[:encoding] = config['encoding'] if config['encoding']
|
||||
|
||||
reset
|
||||
setup
|
||||
self.reset
|
||||
self.setup
|
||||
end
|
||||
|
||||
# Public: Read, process, and write this Site to output.
|
||||
#
|
||||
# Returns nothing.
|
||||
def process
|
||||
reset
|
||||
read
|
||||
generate
|
||||
render
|
||||
cleanup
|
||||
write
|
||||
def reset
|
||||
self.time = if self.config['time']
|
||||
Time.parse(self.config['time'].to_s)
|
||||
else
|
||||
Time.now
|
||||
end
|
||||
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] = [] }
|
||||
|
||||
raise ArgumentError, "Limit posts must be nil or >= 1" if !self.limit_posts.nil? && self.limit_posts < 1
|
||||
end
|
||||
|
||||
# Reset Site details.
|
||||
def setup
|
||||
require 'classifier' if self.lsi
|
||||
|
||||
# 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
|
||||
|
||||
self.converters = Jekyll::Converter.subclasses.select do |c|
|
||||
!self.safe || c.safe
|
||||
end.map do |c|
|
||||
c.new(self.config)
|
||||
end
|
||||
|
||||
self.generators = Jekyll::Generator.subclasses.select do |c|
|
||||
!self.safe || c.safe
|
||||
end.map do |c|
|
||||
c.new(self.config)
|
||||
end
|
||||
end
|
||||
|
||||
# Do the actual work of processing the site and generating the
|
||||
# real deal. 5 phases; reset, read, generate, render, write. This allows
|
||||
# rendering to have full site payload available.
|
||||
#
|
||||
# Returns nothing
|
||||
def reset
|
||||
self.time = (config['time'] ? Time.parse(config['time'].to_s) : Time.now)
|
||||
self.layouts = {}
|
||||
self.posts = []
|
||||
self.pages = []
|
||||
self.static_files = []
|
||||
self.data = {}
|
||||
@collections = nil
|
||||
|
||||
if limit_posts < 0
|
||||
raise ArgumentError, "limit_posts must be a non-negative number"
|
||||
end
|
||||
def process
|
||||
self.reset
|
||||
self.read
|
||||
self.generate
|
||||
self.render
|
||||
self.cleanup
|
||||
self.write
|
||||
end
|
||||
|
||||
# Load necessary libraries, plugins, converters, and generators.
|
||||
#
|
||||
# Returns nothing.
|
||||
def setup
|
||||
ensure_not_in_dest
|
||||
|
||||
plugin_manager.conscientious_require
|
||||
|
||||
self.converters = instantiate_subclasses(Jekyll::Converter)
|
||||
self.generators = instantiate_subclasses(Jekyll::Generator)
|
||||
end
|
||||
|
||||
# Check that the destination dir isn't the source dir or a directory
|
||||
# parent to the source dir.
|
||||
def ensure_not_in_dest
|
||||
dest_pathname = Pathname.new(dest)
|
||||
Pathname.new(source).ascend do |path|
|
||||
if path == dest_pathname
|
||||
raise FatalException.new "Destination directory cannot be or contain the Source directory."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The list of collections and their corresponding Jekyll::Collection instances.
|
||||
# If config['collections'] is set, a new instance is created for each item in the collection.
|
||||
# If config['collections'] is not set, a new hash is returned.
|
||||
#
|
||||
# Returns a Hash containing collection name-to-instance pairs.
|
||||
def collections
|
||||
@collections ||= Hash[collection_names.map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
|
||||
end
|
||||
|
||||
# The list of collection names.
|
||||
#
|
||||
# Returns an array of collection names from the configuration,
|
||||
# or an empty array if the `collections` key is not set.
|
||||
def collection_names
|
||||
case config['collections']
|
||||
when Hash
|
||||
config['collections'].keys
|
||||
when Array
|
||||
config['collections']
|
||||
when nil
|
||||
[]
|
||||
else
|
||||
raise ArgumentError, "Your `collections` key must be a hash or an array."
|
||||
end
|
||||
end
|
||||
|
||||
# Read Site data from disk and load it into internal data structures.
|
||||
#
|
||||
# Returns nothing.
|
||||
def read
|
||||
self.layouts = LayoutReader.new(self).read
|
||||
read_directories
|
||||
read_data(config['data_source'])
|
||||
read_collections
|
||||
self.read_layouts # existing implementation did this at top level only so preserved that
|
||||
self.read_directories
|
||||
end
|
||||
|
||||
# Recursively traverse directories to find posts, pages and static files
|
||||
# that will become part of the site according to the rules in
|
||||
# filter_entries.
|
||||
# Read all the files in <source>/<dir>/_layouts and create a new Layout
|
||||
# object with each one.
|
||||
#
|
||||
# dir - The String relative path of the directory to read. Default: ''.
|
||||
#
|
||||
# Returns nothing.
|
||||
def read_directories(dir = '')
|
||||
base = File.join(source, dir)
|
||||
entries = Dir.chdir(base) { filter_entries(Dir.entries('.'), base) }
|
||||
|
||||
read_posts(dir)
|
||||
read_drafts(dir) if show_drafts
|
||||
posts.sort!
|
||||
limit_posts! if limit_posts > 0 # limit the posts if :limit_posts option is set
|
||||
# Returns nothing
|
||||
def read_layouts(dir = '')
|
||||
base = File.join(self.source, dir, "_layouts")
|
||||
return unless File.exists?(base)
|
||||
entries = []
|
||||
Dir.chdir(base) { entries = filter_entries(Dir['*.*']) }
|
||||
|
||||
entries.each do |f|
|
||||
f_abs = File.join(base, f)
|
||||
if File.directory?(f_abs)
|
||||
f_rel = File.join(dir, f)
|
||||
read_directories(f_rel) unless dest.sub(/\/$/, '') == f_abs
|
||||
elsif has_yaml_header?(f_abs)
|
||||
page = Page.new(self, source, dir, f)
|
||||
pages << page if publisher.publish?(page)
|
||||
else
|
||||
static_files << StaticFile.new(self, source, dir, f)
|
||||
end
|
||||
name = f.split(".")[0..-2].join(".")
|
||||
self.layouts[name] = Layout.new(self, base, f)
|
||||
end
|
||||
|
||||
pages.sort_by!(&:name)
|
||||
end
|
||||
|
||||
# Read all the files in <source>/<dir>/_posts and create a new Post
|
||||
# object with each one.
|
||||
#
|
||||
# dir - The String relative path of the directory to read.
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def read_posts(dir)
|
||||
posts = read_content(dir, '_posts', Post)
|
||||
base = File.join(self.source, dir, '_posts')
|
||||
return unless File.exists?(base)
|
||||
entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
|
||||
|
||||
posts.each do |post|
|
||||
aggregate_post_info(post) if publisher.publish?(post)
|
||||
end
|
||||
end
|
||||
# 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)
|
||||
|
||||
# Read all the files in <source>/<dir>/_drafts and create a new Post
|
||||
# object with each one.
|
||||
#
|
||||
# dir - The String relative path of the directory to read.
|
||||
#
|
||||
# Returns nothing.
|
||||
def read_drafts(dir)
|
||||
drafts = read_content(dir, '_drafts', Draft)
|
||||
|
||||
drafts.each do |draft|
|
||||
if draft.published?
|
||||
aggregate_post_info(draft)
|
||||
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 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.posts.sort!
|
||||
|
||||
# limit the posts if :limit_posts option is set
|
||||
self.posts = self.posts[-limit_posts, limit_posts] if limit_posts
|
||||
end
|
||||
|
||||
def read_content(dir, magic_dir, klass)
|
||||
get_entries(dir, magic_dir).map do |entry|
|
||||
klass.new(self, source, dir, entry) if klass.valid?(entry)
|
||||
end.reject do |entry|
|
||||
entry.nil?
|
||||
end
|
||||
end
|
||||
|
||||
# Read and parse all yaml files under <source>/<dir>
|
||||
#
|
||||
# Returns nothing
|
||||
def read_data(dir)
|
||||
base = File.join(source, dir)
|
||||
return unless File.directory?(base) && (!safe || !File.symlink?(base))
|
||||
|
||||
entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
|
||||
entries.delete_if { |e| File.directory?(File.join(base, e)) }
|
||||
|
||||
entries.each do |entry|
|
||||
path = File.join(source, dir, entry)
|
||||
next if File.symlink?(path) && safe
|
||||
|
||||
key = sanitize_filename(File.basename(entry, '.*'))
|
||||
self.data[key] = SafeYAML.load_file(path)
|
||||
end
|
||||
end
|
||||
|
||||
# Read in all collections specified in the configuration
|
||||
#
|
||||
# Returns nothing.
|
||||
def read_collections
|
||||
collections.each do |_, collection|
|
||||
collection.read unless collection.label.eql?("data")
|
||||
end
|
||||
end
|
||||
|
||||
# Run each of the Generators.
|
||||
#
|
||||
# Returns nothing.
|
||||
def generate
|
||||
generators.each do |generator|
|
||||
self.generators.each do |generator|
|
||||
generator.generate(self)
|
||||
end
|
||||
end
|
||||
|
||||
# Render the site to the destination.
|
||||
#
|
||||
# Returns nothing.
|
||||
def render
|
||||
relative_permalinks_deprecation_method
|
||||
|
||||
collections.each do |label, collection|
|
||||
collection.docs.each do |document|
|
||||
document.output = Jekyll::Renderer.new(self, document).run
|
||||
end
|
||||
self.posts.each do |post|
|
||||
post.render(self.layouts, site_payload)
|
||||
end
|
||||
|
||||
payload = site_payload
|
||||
[posts, pages].flatten.each do |page_or_post|
|
||||
page_or_post.render(layouts, payload)
|
||||
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
|
||||
|
||||
# Remove orphaned files and empty directories in destination.
|
||||
|
||||
# Remove orphaned files and empty directories in destination
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def cleanup
|
||||
site_cleaner.cleanup!
|
||||
# all files and directories in destination, including hidden ones
|
||||
dest_files = Set.new
|
||||
Dir.glob(File.join(self.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
|
||||
dest_files << file unless file =~ /\/\.{1,2}$/
|
||||
end
|
||||
|
||||
# files to be written
|
||||
files = Set.new
|
||||
self.posts.each do |post|
|
||||
files << post.destination(self.dest)
|
||||
end
|
||||
self.pages.each do |page|
|
||||
files << page.destination(self.dest)
|
||||
end
|
||||
self.static_files.each do |sf|
|
||||
files << sf.destination(self.dest)
|
||||
end
|
||||
|
||||
# adding files' parent directories
|
||||
dirs = Set.new
|
||||
files.each { |file| dirs << File.dirname(file) }
|
||||
files.merge(dirs)
|
||||
|
||||
obsolete_files = dest_files - files
|
||||
|
||||
FileUtils.rm_rf(obsolete_files.to_a)
|
||||
end
|
||||
|
||||
# Write static files, pages, and posts.
|
||||
# Write static files, pages and posts
|
||||
#
|
||||
# Returns nothing.
|
||||
# Returns nothing
|
||||
def write
|
||||
each_site_file { |item| item.write(dest) }
|
||||
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
|
||||
|
||||
# Construct a Hash of Posts indexed by the specified Post attribute.
|
||||
# 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
|
||||
#
|
||||
# post_attr - The String name of the Post attribute.
|
||||
# Returns nothing
|
||||
def read_directories(dir = '')
|
||||
base = File.join(self.source, dir)
|
||||
entries = Dir.chdir(base){ filter_entries(Dir['*']) }
|
||||
|
||||
self.read_posts(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
|
||||
# otherwise treat it as a static file
|
||||
static_files << StaticFile.new(self, self.source, dir, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Constructs a hash map of Posts indexed by the specified Post attribute
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# post_attr_hash('categories')
|
||||
# # => { 'tech' => [<Post A>, <Post B>],
|
||||
# # 'ruby' => [<Post B>] }
|
||||
#
|
||||
# Returns the Hash: { attr => posts } where
|
||||
# attr - One of the values for the requested attribute.
|
||||
# posts - The Array of Posts with the given attr value.
|
||||
# Returns {post_attr => [<Post>]}
|
||||
def post_attr_hash(post_attr)
|
||||
# Build a hash map based on the specified post attribute ( post attr =>
|
||||
# array of posts ) then sort each array in reverse order.
|
||||
hash = Hash.new { |h, key| h[key] = [] }
|
||||
posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
|
||||
hash.values.each { |posts| posts.sort!.reverse! }
|
||||
hash
|
||||
# Build a hash map based on the specified post attribute ( post attr => array of posts )
|
||||
# then sort each array in reverse order
|
||||
hash = Hash.new { |hash, key| hash[key] = Array.new }
|
||||
self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
|
||||
hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a} }
|
||||
return hash
|
||||
end
|
||||
|
||||
def tags
|
||||
post_attr_hash('tags')
|
||||
end
|
||||
|
||||
def categories
|
||||
post_attr_hash('categories')
|
||||
end
|
||||
|
||||
# Prepare site data for site payload. The method maintains backward compatibility
|
||||
# if the key 'data' is already used in _config.yml.
|
||||
# The Hash payload containing site-wide data
|
||||
#
|
||||
# Returns the Hash to be hooked to site.data.
|
||||
def site_data
|
||||
config['data'] || data
|
||||
end
|
||||
|
||||
# The Hash payload containing site-wide data.
|
||||
#
|
||||
# Returns the Hash: { "site" => data } where data is a Hash with keys:
|
||||
# "time" - The Time as specified in the configuration or the
|
||||
# current time if none was specified.
|
||||
# "posts" - The Array of Posts, sorted chronologically by post date
|
||||
# and then title.
|
||||
# "pages" - The Array of all Pages.
|
||||
# "html_pages" - The Array of HTML Pages.
|
||||
# "categories" - The Hash of category values and Posts.
|
||||
# See Site#post_attr_hash for type info.
|
||||
# "tags" - The Hash of tag values and Posts.
|
||||
# See Site#post_attr_hash for type info.
|
||||
# Returns {"site" => {"time" => <Time>,
|
||||
# "posts" => [<Post>],
|
||||
# "pages" => [<Page>],
|
||||
# "categories" => [<Post>]}
|
||||
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
|
||||
}))
|
||||
}
|
||||
{"site" => self.config.merge({
|
||||
"time" => self.time,
|
||||
"posts" => self.posts.sort { |a,b| b <=> a },
|
||||
"pages" => self.pages,
|
||||
"html_pages" => self.pages.reject { |page| !page.html? },
|
||||
"categories" => post_attr_hash('categories'),
|
||||
"tags" => post_attr_hash('tags')})}
|
||||
end
|
||||
|
||||
# Filter out any files/directories that are hidden or backup files (start
|
||||
# with "." or "#" or end with "~"), or contain site content (start with "_"),
|
||||
# or are excluded in the site configuration, unless they are web server
|
||||
# files such as '.htaccess'.
|
||||
#
|
||||
# entries - The Array of String file/directory entries to filter.
|
||||
#
|
||||
# Returns the Array of filtered entries.
|
||||
def filter_entries(entries, base_directory = nil)
|
||||
EntryFilter.new(self, base_directory).filter(entries)
|
||||
end
|
||||
|
||||
# Get the implementation class for the given Converter.
|
||||
#
|
||||
# klass - The Class of the Converter to fetch.
|
||||
#
|
||||
# Returns the Converter instance implementing the given Converter.
|
||||
def getConverterImpl(klass)
|
||||
matches = converters.select { |c| c.class == klass }
|
||||
if impl = matches.first
|
||||
impl
|
||||
else
|
||||
raise "Converter implementation not found for #{klass}"
|
||||
end
|
||||
end
|
||||
|
||||
# Create array of instances of the subclasses of the class or module
|
||||
# passed in as argument.
|
||||
#
|
||||
# klass - class or module containing the subclasses which should be
|
||||
# instantiated
|
||||
#
|
||||
# Returns array of instances of subclasses of parameter
|
||||
def instantiate_subclasses(klass)
|
||||
klass.descendants.select do |c|
|
||||
!safe || c.safe
|
||||
end.sort.map do |c|
|
||||
c.new(config)
|
||||
end
|
||||
end
|
||||
|
||||
# Read the entries from a particular directory for processing
|
||||
#
|
||||
# dir - The String relative path of the directory to read
|
||||
# subfolder - The String directory to read
|
||||
#
|
||||
# Returns the list of entries to process
|
||||
def get_entries(dir, subfolder)
|
||||
base = File.join(source, dir, subfolder)
|
||||
return [] unless File.exist?(base)
|
||||
entries = Dir.chdir(base) { filter_entries(Dir['**/*'], base) }
|
||||
entries.delete_if { |e| File.directory?(File.join(base, e)) }
|
||||
end
|
||||
|
||||
# Aggregate post information
|
||||
#
|
||||
# post - The Post object to aggregate information for
|
||||
#
|
||||
# Returns nothing
|
||||
def aggregate_post_info(post)
|
||||
posts << post
|
||||
end
|
||||
|
||||
def relative_permalinks_deprecation_method
|
||||
if config['relative_permalinks'] && has_relative_page?
|
||||
Jekyll.logger.warn "Deprecation:", "Starting in 2.0, permalinks for pages" +
|
||||
" in subfolders must be relative to the" +
|
||||
" site source directory, not the parent" +
|
||||
" directory. Check http://jekyllrb.com/docs/upgrading/"+
|
||||
" for more info."
|
||||
end
|
||||
end
|
||||
|
||||
def docs_to_write
|
||||
documents.select(&:write?)
|
||||
end
|
||||
|
||||
def documents
|
||||
collections.reduce(Set.new) do |docs, (_, collection)|
|
||||
docs.merge(collection.docs)
|
||||
end.to_a
|
||||
end
|
||||
|
||||
def each_site_file
|
||||
%w(posts pages static_files docs_to_write).each do |type|
|
||||
send(type).each do |item|
|
||||
yield item
|
||||
# files such as '.htaccess'
|
||||
def filter_entries(entries)
|
||||
entries = entries.reject do |e|
|
||||
unless ['.htaccess'].include?(e)
|
||||
['.', '_', '#'].include?(e[0..0]) ||
|
||||
e[-1..-1] == '~' ||
|
||||
self.exclude.include?(e) ||
|
||||
File.symlink?(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def frontmatter_defaults
|
||||
@frontmatter_defaults ||= Configuration::FrontmatterDefaults.new(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def has_relative_page?
|
||||
pages.any? { |page| page.uses_relative_permalinks }
|
||||
end
|
||||
|
||||
def has_yaml_header?(file)
|
||||
!!(File.open(file, 'rb') { |f| f.read(5) } =~ /\A---\r?\n/)
|
||||
end
|
||||
|
||||
def limit_posts!
|
||||
limit = posts.length < limit_posts ? posts.length : limit_posts
|
||||
self.posts = posts[-limit, limit]
|
||||
end
|
||||
|
||||
def site_cleaner
|
||||
@site_cleaner ||= Cleaner.new(self)
|
||||
end
|
||||
|
||||
def sanitize_filename(name)
|
||||
name.gsub!(/[^\w\s_-]+/, '')
|
||||
name.gsub!(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
|
||||
name.gsub(/\s+/, '_')
|
||||
end
|
||||
|
||||
def publisher
|
||||
@publisher ||= Publisher.new(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
module Jekyll
|
||||
|
||||
class StaticFile
|
||||
# The cache of last modification times [path] -> mtime.
|
||||
@@mtimes = Hash.new
|
||||
@@mtimes = Hash.new # the cache of last modification times [path] -> mtime
|
||||
|
||||
# Initialize a new StaticFile.
|
||||
# +site+ is the Site
|
||||
# +base+ is the String path to the <source>
|
||||
# +dir+ is the String path between <source> and the file
|
||||
# +name+ is the String filename of the file
|
||||
#
|
||||
# site - The Site.
|
||||
# base - The String path to the <source>.
|
||||
# dir - The String path between <source> and the file.
|
||||
# name - The String filename of the file.
|
||||
# Returns <StaticFile>
|
||||
def initialize(site, base, dir, name)
|
||||
@site = site
|
||||
@base = base
|
||||
@@ -16,26 +17,24 @@ module Jekyll
|
||||
@name = name
|
||||
end
|
||||
|
||||
# Obtains source file path.
|
||||
#
|
||||
# Returns source file path.
|
||||
def path
|
||||
File.join(*[@base, @dir, @name].compact)
|
||||
end
|
||||
|
||||
# Returns the source file path relative to the site source
|
||||
def relative_path
|
||||
@relative_path ||= path.sub(/\A#{@site.source}/, '')
|
||||
File.join(@base, @dir, @name)
|
||||
end
|
||||
|
||||
# Obtain destination path.
|
||||
#
|
||||
# dest - The String path to the destination dir.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns destination file path.
|
||||
def destination(dest)
|
||||
File.join(*[dest, @dir, @name].compact)
|
||||
File.join(dest, @dir, @name)
|
||||
end
|
||||
|
||||
# Returns last modification time for this file.
|
||||
# Obtain mtime of the source path.
|
||||
#
|
||||
# Returns last modifiaction time for this file.
|
||||
def mtime
|
||||
File.stat(path).mtime.to_i
|
||||
end
|
||||
@@ -48,14 +47,13 @@ module Jekyll
|
||||
end
|
||||
|
||||
# Write the static file to the destination directory (if modified).
|
||||
#
|
||||
# dest - The String path to the destination dir.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns false if the file was not modified since last time (no-op).
|
||||
def write(dest)
|
||||
dest_path = destination(dest)
|
||||
|
||||
return false if File.exist?(dest_path) and !modified?
|
||||
return false if File.exist? dest_path and !modified?
|
||||
@@mtimes[path] = mtime
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(dest_path))
|
||||
@@ -69,15 +67,9 @@ module Jekyll
|
||||
# Returns nothing.
|
||||
def self.reset_cache
|
||||
@@mtimes = Hash.new
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{
|
||||
"path" => relative_path,
|
||||
"modified_time" => mtime.to_s,
|
||||
"extname" => File.extname(relative_path)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
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
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
# 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)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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?
|
||||
"<script src=\"https://gist.github.com/#{gist_id}.js\"> </script>"
|
||||
else
|
||||
"<script src=\"https://gist.github.com/#{gist_id}.js?file=#{filename}\"> </script>"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('gist', Jekyll::GistTag)
|
||||
@@ -1,95 +1,73 @@
|
||||
module Jekyll
|
||||
module Tags
|
||||
class HighlightBlock < Liquid::Block
|
||||
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+)?)*)$/
|
||||
class HighlightBlock < Liquid::Block
|
||||
include Liquid::StandardFilters
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
super
|
||||
if markup.strip =~ SYNTAX
|
||||
@lang = $1.downcase
|
||||
@options = {}
|
||||
if defined?($2) && $2 != ''
|
||||
$2.split.each do |opt|
|
||||
key, value = opt.split('=')
|
||||
@options[key.to_sym] = value || true
|
||||
# we need a language, but the linenos argument is optional.
|
||||
SYNTAX = /(\w+)\s?([\w\s=]+)*/
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
super
|
||||
if markup =~ SYNTAX
|
||||
@lang = $1
|
||||
if defined? $2
|
||||
tmp_options = {}
|
||||
$2.split.each do |opt|
|
||||
key, value = opt.split('=')
|
||||
if value.nil?
|
||||
if key == 'linenos'
|
||||
value = 'inline'
|
||||
else
|
||||
value = true
|
||||
end
|
||||
end
|
||||
tmp_options[key] = value
|
||||
end
|
||||
@options[:linenos] = "inline" if @options.key?(:linenos) and @options[:linenos] == true
|
||||
tmp_options = tmp_options.to_a.collect { |opt| opt.join('=') }
|
||||
# additional options to pass to Albino.
|
||||
@options = { 'O' => tmp_options.join(',') }
|
||||
else
|
||||
raise SyntaxError.new <<-eos
|
||||
Syntax Error in tag 'highlight' while parsing the following markup:
|
||||
|
||||
#{markup}
|
||||
|
||||
Valid syntax: highlight <lang> [linenos]
|
||||
eos
|
||||
@options = {}
|
||||
end
|
||||
else
|
||||
raise SyntaxError.new("Syntax Error in 'highlight' - Valid syntax: highlight <lang> [linenos]")
|
||||
end
|
||||
|
||||
def render(context)
|
||||
prefix = context["highlighter_prefix"] || ""
|
||||
suffix = context["highlighter_suffix"] || ""
|
||||
code = super.to_s.strip
|
||||
|
||||
output = case context.registers[:site].highlighter
|
||||
when 'pygments'
|
||||
render_pygments(code)
|
||||
when 'rouge'
|
||||
render_rouge(code)
|
||||
else
|
||||
render_codehighlighter(code)
|
||||
end
|
||||
|
||||
rendered_output = add_code_tag(output)
|
||||
prefix + rendered_output + suffix
|
||||
end
|
||||
|
||||
def render_pygments(code)
|
||||
require 'pygments'
|
||||
@options[:encoding] = 'utf-8'
|
||||
|
||||
highlighted_code = Pygments.highlight(code, :lexer => @lang, :options => @options)
|
||||
|
||||
if highlighted_code.nil?
|
||||
Jekyll.logger.error "There was an error highlighting your code:"
|
||||
puts
|
||||
Jekyll.logger.error code
|
||||
puts
|
||||
Jekyll.logger.error "While attempting to convert the above code, Pygments.rb" +
|
||||
" returned an unacceptable value."
|
||||
Jekyll.logger.error "This is usually a timeout problem solved by running `jekyll build` again."
|
||||
raise ArgumentError.new("Pygments.rb returned an unacceptable value when attempting to highlight some code.")
|
||||
end
|
||||
|
||||
highlighted_code
|
||||
end
|
||||
|
||||
def render_rouge(code)
|
||||
require 'rouge'
|
||||
formatter = Rouge::Formatters::HTML.new(line_numbers: @options[:linenos], wrap: false)
|
||||
lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
|
||||
code = formatter.format(lexer.lex(code))
|
||||
"<div class=\"highlight\"><pre>#{code}</pre></div>"
|
||||
end
|
||||
|
||||
def render_codehighlighter(code)
|
||||
"<div class=\"highlight\"><pre>#{h(code).strip}</pre></div>"
|
||||
end
|
||||
|
||||
def add_code_tag(code)
|
||||
# Add nested <code> tags to code blocks
|
||||
code = code.sub(/<pre>\n*/,'<pre><code class="' + @lang.to_s.gsub("+", "-") + '">')
|
||||
code = code.sub(/\n*<\/pre>/,"</code></pre>")
|
||||
code.strip
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def render(context)
|
||||
if context.registers[:site].pygments
|
||||
render_pygments(context, super.join)
|
||||
else
|
||||
render_codehighlighter(context, super.join)
|
||||
end
|
||||
end
|
||||
|
||||
def render_pygments(context, code)
|
||||
output = add_code_tags(Albino.new(code, @lang).to_s(@options), @lang)
|
||||
output = context["pygments_prefix"] + output if context["pygments_prefix"]
|
||||
output = output + context["pygments_suffix"] if context["pygments_suffix"]
|
||||
output
|
||||
end
|
||||
|
||||
def render_codehighlighter(context, code)
|
||||
#The div is required because RDiscount blows ass
|
||||
<<-HTML
|
||||
<div>
|
||||
<pre>
|
||||
<code class='#{@lang}'>#{h(code).strip}</code>
|
||||
</pre>
|
||||
</div>
|
||||
HTML
|
||||
end
|
||||
|
||||
def add_code_tags(code, lang)
|
||||
# Add nested <code> tags to code blocks
|
||||
code = code.sub(/<pre>/,'<pre><code class="' + lang + '">')
|
||||
code = code.sub(/<\/pre>/,"</code></pre>")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('highlight', Jekyll::Tags::HighlightBlock)
|
||||
Liquid::Template.register_tag('highlight', Jekyll::HighlightBlock)
|
||||
|
||||
@@ -1,146 +1,37 @@
|
||||
module Jekyll
|
||||
module Tags
|
||||
class IncludeTagError < StandardError
|
||||
attr_accessor :path
|
||||
|
||||
def initialize(msg, path)
|
||||
super(msg)
|
||||
@path = path
|
||||
end
|
||||
class IncludeTag < Liquid::Tag
|
||||
def initialize(tag_name, file, tokens)
|
||||
super
|
||||
@file = file.strip
|
||||
end
|
||||
|
||||
class IncludeTag < Liquid::Tag
|
||||
def render(context)
|
||||
includes_dir = File.join(context.registers[:site].source, '_includes')
|
||||
|
||||
SYNTAX_EXAMPLE = "{% include file.ext param='value' param2='value' %}"
|
||||
|
||||
VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
|
||||
VARIABLE_SYNTAX = /(?<variable>\{\{\s*(?<name>[\w\-\.]+)\s*(\|.*)?\}\})(?<params>.*)/
|
||||
|
||||
INCLUDES_DIR = '_includes'
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
super
|
||||
matched = markup.strip.match(VARIABLE_SYNTAX)
|
||||
if matched
|
||||
@file = matched['variable'].strip
|
||||
@params = matched['params'].strip
|
||||
else
|
||||
@file, @params = markup.strip.split(' ', 2);
|
||||
end
|
||||
validate_params if @params
|
||||
if File.symlink?(includes_dir)
|
||||
return "Includes directory '#{includes_dir}' cannot be a symlink"
|
||||
end
|
||||
|
||||
def parse_params(context)
|
||||
params = {}
|
||||
markup = @params
|
||||
|
||||
while match = VALID_SYNTAX.match(markup) do
|
||||
markup = markup[match.end(0)..-1]
|
||||
|
||||
value = if match[2]
|
||||
match[2].gsub(/\\"/, '"')
|
||||
elsif match[3]
|
||||
match[3].gsub(/\\'/, "'")
|
||||
elsif match[4]
|
||||
context[match[4]]
|
||||
end
|
||||
|
||||
params[match[1]] = value
|
||||
end
|
||||
params
|
||||
if @file !~ /^[a-zA-Z0-9_\/\.-]+$/ || @file =~ /\.\// || @file =~ /\/\./
|
||||
return "Include file '#{@file}' contains invalid characters or sequences"
|
||||
end
|
||||
|
||||
def validate_file_name(file)
|
||||
if file !~ /^[a-zA-Z0-9_\/\.-]+$/ || file =~ /\.\// || file =~ /\/\./
|
||||
raise ArgumentError.new <<-eos
|
||||
Invalid syntax for include tag. File contains invalid characters or sequences:
|
||||
|
||||
#{file}
|
||||
|
||||
Valid syntax:
|
||||
|
||||
#{SYNTAX_EXAMPLE}
|
||||
|
||||
eos
|
||||
end
|
||||
end
|
||||
|
||||
def validate_params
|
||||
full_valid_syntax = Regexp.compile('\A\s*(?:' + VALID_SYNTAX.to_s + '(?=\s|\z)\s*)*\z')
|
||||
unless @params =~ full_valid_syntax
|
||||
raise ArgumentError.new <<-eos
|
||||
Invalid syntax for include tag:
|
||||
|
||||
#{@params}
|
||||
|
||||
Valid syntax:
|
||||
|
||||
#{SYNTAX_EXAMPLE}
|
||||
|
||||
eos
|
||||
end
|
||||
end
|
||||
|
||||
# Grab file read opts in the context
|
||||
def file_read_opts(context)
|
||||
context.registers[:site].file_read_opts
|
||||
end
|
||||
|
||||
# Render the variable if required
|
||||
def render_variable(context)
|
||||
if @file.match(VARIABLE_SYNTAX)
|
||||
partial = Liquid::Template.parse(@file)
|
||||
partial.render!(context)
|
||||
end
|
||||
end
|
||||
|
||||
def render(context)
|
||||
dir = File.join(File.realpath(context.registers[:site].source), INCLUDES_DIR)
|
||||
|
||||
file = render_variable(context) || @file
|
||||
validate_file_name(file)
|
||||
|
||||
path = File.join(dir, file)
|
||||
validate_path(path, dir, context.registers[:site].safe)
|
||||
|
||||
begin
|
||||
partial = Liquid::Template.parse(source(path, context))
|
||||
|
||||
Dir.chdir(includes_dir) do
|
||||
choices = Dir['**/*'].reject { |x| File.symlink?(x) }
|
||||
if choices.include?(@file)
|
||||
source = File.read(@file)
|
||||
partial = Liquid::Template.parse(source)
|
||||
context.stack do
|
||||
context['include'] = parse_params(context) if @params
|
||||
partial.render!(context)
|
||||
partial.render(context)
|
||||
end
|
||||
rescue => e
|
||||
raise IncludeTagError.new e.message, File.join(INCLUDES_DIR, @file)
|
||||
else
|
||||
"Included file '#{@file}' not found in _includes directory"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_path(path, dir, safe)
|
||||
if safe && !realpath_prefixed_with?(path, dir)
|
||||
raise IOError.new "The included file '#{path}' should exist and should not be a symlink"
|
||||
elsif !File.exist?(path)
|
||||
raise IOError.new "Included file '#{path_relative_to_source(dir, path)}' not found"
|
||||
end
|
||||
end
|
||||
|
||||
def path_relative_to_source(dir, path)
|
||||
File.join(INCLUDES_DIR, path.sub(Regexp.new("^#{dir}"), ""))
|
||||
end
|
||||
|
||||
def realpath_prefixed_with?(path, dir)
|
||||
File.exist?(path) && File.realpath(path).start_with?(dir)
|
||||
end
|
||||
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
|
||||
# This method allows to modify the file content by inheriting from the class.
|
||||
def source(file, context)
|
||||
File.read(file, file_read_opts(context))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('include', Jekyll::Tags::IncludeTag)
|
||||
Liquid::Template.register_tag('include', Jekyll::IncludeTag)
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
module Jekyll
|
||||
module Tags
|
||||
class PostComparer
|
||||
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)$/
|
||||
|
||||
attr_accessor :date, :slug
|
||||
|
||||
def initialize(name)
|
||||
all, path, date, slug = *name.sub(/^\//, "").match(MATCHER)
|
||||
raise ArgumentError.new("'#{name}' does not contain valid date and/or title") unless all
|
||||
@slug = path ? path + slug : slug
|
||||
@date = Time.parse(date)
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
slug == post_slug(other) &&
|
||||
date.year == other.date.year &&
|
||||
date.month == other.date.month &&
|
||||
date.day == other.date.day
|
||||
end
|
||||
|
||||
private
|
||||
# Construct the directory-aware post slug for a Jekyll::Post
|
||||
#
|
||||
# other - the Jekyll::Post
|
||||
#
|
||||
# Returns the post slug with the subdirectory (relative to _posts)
|
||||
def post_slug(other)
|
||||
path = other.name.split("/")[0...-1].join("/")
|
||||
if path.nil? || path == ""
|
||||
other.slug
|
||||
else
|
||||
path + '/' + other.slug
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PostUrl < Liquid::Tag
|
||||
def initialize(tag_name, post, tokens)
|
||||
super
|
||||
@orig_post = post.strip
|
||||
begin
|
||||
@post = PostComparer.new(@orig_post)
|
||||
rescue
|
||||
raise ArgumentError.new <<-eos
|
||||
Could not parse name of post "#{@orig_post}" in tag 'post_url'.
|
||||
|
||||
Make sure the post exists and the name is correct.
|
||||
eos
|
||||
end
|
||||
end
|
||||
|
||||
def render(context)
|
||||
site = context.registers[:site]
|
||||
|
||||
site.posts.each do |p|
|
||||
if @post == p
|
||||
return p.url
|
||||
end
|
||||
end
|
||||
|
||||
raise ArgumentError.new <<-eos
|
||||
Could not find post "#{@orig_post}" in tag 'post_url'.
|
||||
|
||||
Make sure the post exists and the name is correct.
|
||||
eos
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('post_url', Jekyll::Tags::PostUrl)
|
||||
@@ -1,109 +0,0 @@
|
||||
require 'uri'
|
||||
|
||||
# Public: Methods that generate a URL for a resource such as a Post or a Page.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# URL.new({
|
||||
# :template => /:categories/:title.html",
|
||||
# :placeholders => {:categories => "ruby", :title => "something"}
|
||||
# }).to_s
|
||||
#
|
||||
module Jekyll
|
||||
class URL
|
||||
|
||||
# options - One of :permalink or :template must be supplied.
|
||||
# :template - The String used as template for URL generation,
|
||||
# for example "/:path/:basename:output_ext", where
|
||||
# a placeholder is prefixed with a colon.
|
||||
# :placeholders - A hash containing the placeholders which will be
|
||||
# replaced when used inside the template. E.g.
|
||||
# { "year" => Time.now.strftime("%Y") } would replace
|
||||
# the placeholder ":year" with the current year.
|
||||
# :permalink - If supplied, no URL will be generated from the
|
||||
# template. Instead, the given permalink will be
|
||||
# used as URL.
|
||||
def initialize(options)
|
||||
@template = options[:template]
|
||||
@placeholders = options[:placeholders] || {}
|
||||
@permalink = options[:permalink]
|
||||
|
||||
if (@template || @permalink).nil?
|
||||
raise ArgumentError, "One of :template or :permalink must be supplied."
|
||||
end
|
||||
end
|
||||
|
||||
# The generated relative URL of the resource
|
||||
#
|
||||
# Returns the String URL
|
||||
def to_s
|
||||
sanitize_url(@permalink || generate_url)
|
||||
end
|
||||
|
||||
# Internal: Generate the URL by replacing all placeholders with their
|
||||
# respective values
|
||||
#
|
||||
# Returns the _unsanitizied_ String URL
|
||||
def generate_url
|
||||
@placeholders.inject(@template) do |result, token|
|
||||
result.gsub(/:#{token.first}/, self.class.escape_path(token.last))
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a sanitized String URL
|
||||
def sanitize_url(in_url)
|
||||
|
||||
# Remove all double slashes
|
||||
url = in_url.gsub(/\/\//, "/")
|
||||
|
||||
# Remove every URL segment that consists solely of dots
|
||||
url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
||||
|
||||
# Append a trailing slash to the URL if the unsanitized URL had one
|
||||
url += "/" if in_url =~ /\/$/
|
||||
|
||||
# Always add a leading slash
|
||||
url.gsub!(/\A([^\/])/, '/\1')
|
||||
|
||||
url
|
||||
end
|
||||
|
||||
# Escapes a path to be a valid URL path segment
|
||||
#
|
||||
# path - The path to be escaped.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# URL.escape_path("/a b")
|
||||
# # => "/a%20b"
|
||||
#
|
||||
# Returns the escaped path.
|
||||
def self.escape_path(path)
|
||||
# Because URI.escape doesn't escape '?', '[' and ']' by defaut,
|
||||
# specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
|
||||
#
|
||||
# URI path segment is defined in RFC 3986 as follows:
|
||||
# segment = *pchar
|
||||
# pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
# pct-encoded = "%" HEXDIG HEXDIG
|
||||
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
# / "*" / "+" / "," / ";" / "="
|
||||
URI.escape(path, /[^a-zA-Z\d\-._~!$&\'()*+,;=:@\/]/)
|
||||
end
|
||||
|
||||
# Unescapes a URL path segment
|
||||
#
|
||||
# path - The path to be unescaped.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# URL.unescape_path("/a%20b")
|
||||
# # => "/a b"
|
||||
#
|
||||
# Returns the unescaped path.
|
||||
def self.unescape_path(path)
|
||||
URI.unescape(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,79 +0,0 @@
|
||||
module Jekyll
|
||||
module Utils
|
||||
class << self
|
||||
|
||||
# Merges a master hash with another hash, recursively.
|
||||
#
|
||||
# master_hash - the "parent" hash whose values will be overridden
|
||||
# other_hash - the other hash whose values will be persisted after the merge
|
||||
#
|
||||
# This code was lovingly stolen from some random gem:
|
||||
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
||||
#
|
||||
# Thanks to whoever made it.
|
||||
def deep_merge_hashes(master_hash, other_hash)
|
||||
target = master_hash.dup
|
||||
|
||||
other_hash.keys.each do |key|
|
||||
if other_hash[key].is_a? Hash and target[key].is_a? Hash
|
||||
target[key] = Utils.deep_merge_hashes(target[key], other_hash[key])
|
||||
next
|
||||
end
|
||||
|
||||
target[key] = other_hash[key]
|
||||
end
|
||||
|
||||
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 plural key
|
||||
#
|
||||
# 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]
|
||||
when String
|
||||
array = hash[plural_key].split
|
||||
when Array
|
||||
array = hash[plural_key].compact
|
||||
end
|
||||
end
|
||||
array || []
|
||||
end
|
||||
|
||||
def transform_keys(hash)
|
||||
result = {}
|
||||
hash.each_key do |key|
|
||||
result[yield(key)] = hash[key]
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# Apply #to_sym to all keys in the hash
|
||||
#
|
||||
# hash - the hash to which to apply this transformation
|
||||
#
|
||||
# Returns a new hash with symbolized keys
|
||||
def symbolize_hash_keys(hash)
|
||||
transform_keys(hash) { |key| key.to_sym rescue key }
|
||||
end
|
||||
|
||||
# Apply #to_s to all keys in the Hash
|
||||
#
|
||||
# hash - the hash to which to apply this transformation
|
||||
#
|
||||
# Returns a new hash with stringified keys
|
||||
def stringify_hash_keys(hash)
|
||||
transform_keys(hash) { |key| key.to_s rescue key }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +0,0 @@
|
||||
module Jekyll
|
||||
VERSION = '2.0.3'
|
||||
end
|
||||
1
lib/site_template/.gitignore
vendored
1
lib/site_template/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
_site
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user