mirror of
https://github.com/jekyll/jekyll.git
synced 2026-04-28 03:01:03 -04:00
Compare commits
377 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf71631c34 | ||
|
|
6c0e4b37b9 | ||
|
|
0f71bdb540 | ||
|
|
dce4ccc5a4 | ||
|
|
6c94db1486 | ||
|
|
68eaadd13a | ||
|
|
01a90904e2 | ||
|
|
d2814cf750 | ||
|
|
f58a821e20 | ||
|
|
38844cd3bc | ||
|
|
a31780a1ec | ||
|
|
5f4dfe388f | ||
|
|
f82c51df7a | ||
|
|
13cc44fb12 | ||
|
|
be8b7715d3 | ||
|
|
edef0251d3 | ||
|
|
9da714dbe1 | ||
|
|
8cc7f06b36 | ||
|
|
4b5a4e8713 | ||
|
|
08725eb234 | ||
|
|
16ea3262da | ||
|
|
a04c270f1b | ||
|
|
bd01e647a7 | ||
|
|
034b06431e | ||
|
|
c70dac3cee | ||
|
|
ca48ea91e6 | ||
|
|
f68bbcbe8d | ||
|
|
d61c1e930a | ||
|
|
bc3771aa22 | ||
|
|
e902bb9c30 | ||
|
|
42f63f919f | ||
|
|
033333f9bc | ||
|
|
b3634b522a | ||
|
|
84c1a72443 | ||
|
|
c1f0e070c9 | ||
|
|
13df722073 | ||
|
|
86397cbf00 | ||
|
|
a8a837cc8e | ||
|
|
9e0eb75170 | ||
|
|
36b1f8f9b1 | ||
|
|
c1ed790534 | ||
|
|
9bd48752e6 | ||
|
|
5db040802f | ||
|
|
65622cb1e0 | ||
|
|
226c7cc121 | ||
|
|
0a58d78338 | ||
|
|
5b680f8dd8 | ||
|
|
e8fd7ebbc3 | ||
|
|
31901ee15b | ||
|
|
3ab016870d | ||
|
|
61acd47ed2 | ||
|
|
dca30c3ad1 | ||
|
|
f85e229a9e | ||
|
|
ac7a0cc95f | ||
|
|
53b999418c | ||
|
|
00146909f9 | ||
|
|
a556e4f29e | ||
|
|
e2d26ff8ed | ||
|
|
9396c9d04c | ||
|
|
38ed81e0ce | ||
|
|
4a8fc1fa6e | ||
|
|
d53ea4a0dd | ||
|
|
f7ab019a39 | ||
|
|
4afee1bda1 | ||
|
|
8cc537026d | ||
|
|
054b796b9f | ||
|
|
c1a7662311 | ||
|
|
accdb2d39f | ||
|
|
4c08643c50 | ||
|
|
d97bcadd03 | ||
|
|
f688c9df81 | ||
|
|
8ecb70d3e3 | ||
|
|
e9cf7b4636 | ||
|
|
16c19ecd19 | ||
|
|
b1049c84cd | ||
|
|
b6678d4e43 | ||
|
|
3fa9af17fa | ||
|
|
9b423a96fd | ||
|
|
391d1a9677 | ||
|
|
f4fb833d34 | ||
|
|
d9bc00c804 | ||
|
|
b3cec39843 | ||
|
|
f6acbb869e | ||
|
|
adf9ca5a05 | ||
|
|
5e997cae21 | ||
|
|
f35d287c66 | ||
|
|
1c3fedbb31 | ||
|
|
a56eeb8289 | ||
|
|
908526455c | ||
|
|
ce5a2a04e3 | ||
|
|
44015665b9 | ||
|
|
6a605753c1 | ||
|
|
99ee0c4803 | ||
|
|
de3ee99d30 | ||
|
|
36411dd10f | ||
|
|
f8484570b7 | ||
|
|
9bb2066cf1 | ||
|
|
0ce13c943d | ||
|
|
de8bd48154 | ||
|
|
68c69fb3ef | ||
|
|
46a95bc036 | ||
|
|
23d26c5525 | ||
|
|
6932a40d17 | ||
|
|
aa9993a6c1 | ||
|
|
8a417e86c0 | ||
|
|
f91954be76 | ||
|
|
ace9911001 | ||
|
|
cb77a5287b | ||
|
|
31c65c56f4 | ||
|
|
5335debfb2 | ||
|
|
1fba2ef516 | ||
|
|
f6ef6f21ae | ||
|
|
e8d119ef0a | ||
|
|
2c542a652d | ||
|
|
fa90573176 | ||
|
|
a8efc3a0a2 | ||
|
|
0ba1f6c83e | ||
|
|
03cb12aeb3 | ||
|
|
3efe008544 | ||
|
|
08bc63289d | ||
|
|
81971c3342 | ||
|
|
8405d30764 | ||
|
|
18545ddf71 | ||
|
|
fc86c9dd85 | ||
|
|
84b26a31da | ||
|
|
315f4c9222 | ||
|
|
cedda3afa3 | ||
|
|
ff3ca307b9 | ||
|
|
1261840769 | ||
|
|
bae52e0aba | ||
|
|
ad4a80a653 | ||
|
|
80b13a81fa | ||
|
|
ef2fabb189 | ||
|
|
e8971b2999 | ||
|
|
bfdf7fb13a | ||
|
|
f73dac1582 | ||
|
|
5a807aa12e | ||
|
|
cd946a587b | ||
|
|
4fd2b54a7d | ||
|
|
4dde9d9668 | ||
|
|
355b20265e | ||
|
|
cfd6ebc747 | ||
|
|
e4a2319bf3 | ||
|
|
657b2e4be3 | ||
|
|
98fa570c86 | ||
|
|
ef6aa6b5c4 | ||
|
|
58354e269e | ||
|
|
a4f3f5c583 | ||
|
|
5fec20dfa8 | ||
|
|
a2e0d76d4e | ||
|
|
ba77a024d6 | ||
|
|
0756d1f765 | ||
|
|
d88354e2df | ||
|
|
dd5fb69c2f | ||
|
|
2b2e027b34 | ||
|
|
fff313a62d | ||
|
|
6dcfe1283a | ||
|
|
b68149c7bc | ||
|
|
a076ce0702 | ||
|
|
58b4ffd935 | ||
|
|
a0aa3fa6dc | ||
|
|
a1550b3378 | ||
|
|
2aac9f3716 | ||
|
|
db03bcac8f | ||
|
|
21c92d3eb2 | ||
|
|
ce228ac1c4 | ||
|
|
93c029d62e | ||
|
|
2292e4268c | ||
|
|
e0e4a47af1 | ||
|
|
45bf0e8a33 | ||
|
|
6b74454a07 | ||
|
|
2a7b1cbd98 | ||
|
|
c92eb564d2 | ||
|
|
98f3767af3 | ||
|
|
602a252364 | ||
|
|
add546f61e | ||
|
|
8d4b96084a | ||
|
|
60fed241cd | ||
|
|
d020d4f2bf | ||
|
|
473f3ffc11 | ||
|
|
4c1021d597 | ||
|
|
c89d8dd0f3 | ||
|
|
52b82af6e2 | ||
|
|
1adf9a7c64 | ||
|
|
59ee8a6869 | ||
|
|
c569f5e641 | ||
|
|
5a37e0d96e | ||
|
|
7806a0d6bb | ||
|
|
5c17d6266d | ||
|
|
d25f419159 | ||
|
|
b8c04dfb6d | ||
|
|
d80c643681 | ||
|
|
0d69e9346b | ||
|
|
45e13cf46f | ||
|
|
0cb1ebcda1 | ||
|
|
b5916caf4b | ||
|
|
5ea06f3ad9 | ||
|
|
39a487cdf0 | ||
|
|
b1a055f10d | ||
|
|
fab5a715c5 | ||
|
|
597c7a7904 | ||
|
|
b299a7bad3 | ||
|
|
52342fe6e1 | ||
|
|
e3bd1c88e9 | ||
|
|
4cc0873172 | ||
|
|
2f2e45bedf | ||
|
|
dc5520484b | ||
|
|
86ac1eee9c | ||
|
|
0dfb486c37 | ||
|
|
e8e8f6449a | ||
|
|
176c047ff1 | ||
|
|
a493267191 | ||
|
|
f1c8e388e4 | ||
|
|
8b678a8f01 | ||
|
|
ec637a13e9 | ||
|
|
05e8deae8d | ||
|
|
3bc4f590ea | ||
|
|
237df13778 | ||
|
|
74b17ff777 | ||
|
|
ca98cd0152 | ||
|
|
b616a4ceca | ||
|
|
09946386b1 | ||
|
|
150ff1e5e4 | ||
|
|
e39810c984 | ||
|
|
5468548948 | ||
|
|
e0ceee2e89 | ||
|
|
52fbd940c3 | ||
|
|
750b5f5cb8 | ||
|
|
606269b33c | ||
|
|
414dfbe26e | ||
|
|
0a1e3cd250 | ||
|
|
535d78de7d | ||
|
|
8379958d95 | ||
|
|
d8d7ae3f0e | ||
|
|
5bbdf2239d | ||
|
|
937efcf8b8 | ||
|
|
a116eaf0ee | ||
|
|
d79d94277b | ||
|
|
486ae25fc1 | ||
|
|
86b1450234 | ||
|
|
99098dd8c7 | ||
|
|
102f6be6a2 | ||
|
|
921aee23d3 | ||
|
|
285f0aa327 | ||
|
|
49c39f43a1 | ||
|
|
ee0167d706 | ||
|
|
605adf88d5 | ||
|
|
e1dbda47ed | ||
|
|
8a11c0e92a | ||
|
|
6f90939221 | ||
|
|
8a926d4f29 | ||
|
|
a42690dd38 | ||
|
|
d948e0da91 | ||
|
|
35a467117e | ||
|
|
6968346703 | ||
|
|
445347e259 | ||
|
|
53368cc3e1 | ||
|
|
252ca94b81 | ||
|
|
80a229aef2 | ||
|
|
5a1d736242 | ||
|
|
26f9ad3d98 | ||
|
|
4bcece18ae | ||
|
|
a5d236eb53 | ||
|
|
db75c8f588 | ||
|
|
3c39612d84 | ||
|
|
e0477e32cc | ||
|
|
bad2d172ec | ||
|
|
6342a3842c | ||
|
|
ccbd2c39a1 | ||
|
|
13245354b2 | ||
|
|
080108b0f0 | ||
|
|
9da140fcb2 | ||
|
|
288d5045d2 | ||
|
|
73fa7dcad4 | ||
|
|
f3fd105b40 | ||
|
|
6eb29a4808 | ||
|
|
1f6940feb2 | ||
|
|
9a0485e812 | ||
|
|
563933f114 | ||
|
|
5b540cf744 | ||
|
|
dd268fae95 | ||
|
|
45b46722bf | ||
|
|
2e187864cf | ||
|
|
3c9ed56129 | ||
|
|
56176a3799 | ||
|
|
8443676eeb | ||
|
|
5247f4ff92 | ||
|
|
93e7240731 | ||
|
|
2e00c5957b | ||
|
|
f099d00cb0 | ||
|
|
808d6c6a62 | ||
|
|
07c2451c06 | ||
|
|
556131793f | ||
|
|
8d0e3dd0e2 | ||
|
|
3d77c20578 | ||
|
|
b85eb4a373 | ||
|
|
f2ea61ba2a | ||
|
|
d590f2ac06 | ||
|
|
c52484a257 | ||
|
|
4e302c0445 | ||
|
|
3c0bc3b2de | ||
|
|
03f511be61 | ||
|
|
829530be36 | ||
|
|
aff6c9c23a | ||
|
|
0d78cb7063 | ||
|
|
c22cd84153 | ||
|
|
6c41f93493 | ||
|
|
778894dc6b | ||
|
|
937dad66a7 | ||
|
|
3ebe81bf06 | ||
|
|
ea2175b0ed | ||
|
|
9d4e1697ef | ||
|
|
e8a25964c5 | ||
|
|
8b5eb0395f | ||
|
|
15eaebe981 | ||
|
|
cce2b8f32c | ||
|
|
ae4e7725bf | ||
|
|
a4082378f7 | ||
|
|
479d8c7572 | ||
|
|
c60be9c571 | ||
|
|
ab262a2ab5 | ||
|
|
1423ea91a0 | ||
|
|
a5155c8e00 | ||
|
|
d602600394 | ||
|
|
18b512a531 | ||
|
|
4c81c4a6b6 | ||
|
|
73d42b24ad | ||
|
|
cb13ea3000 | ||
|
|
4b39c44664 | ||
|
|
9f3244e884 | ||
|
|
6edfae8c26 | ||
|
|
6fec047631 | ||
|
|
e72cde12fa | ||
|
|
6a3680c60b | ||
|
|
3e42982201 | ||
|
|
872a221862 | ||
|
|
be56a0f685 | ||
|
|
fa53f5f7fe | ||
|
|
06cc318b6f | ||
|
|
a67cea2637 | ||
|
|
54d713b26a | ||
|
|
5e725eb4c4 | ||
|
|
86e72a8b25 | ||
|
|
fa1043ca69 | ||
|
|
2b925baf01 | ||
|
|
0e132bf2cb | ||
|
|
0d05f27fe4 | ||
|
|
8807c7660b | ||
|
|
992c4a8e6f | ||
|
|
7655c745c4 | ||
|
|
fb49027997 | ||
|
|
fab8442432 | ||
|
|
c180bc47bf | ||
|
|
25d4951f0b | ||
|
|
0ec9a1330a | ||
|
|
2569e9fb5e | ||
|
|
2135a53897 | ||
|
|
82d96448b5 | ||
|
|
e9ccc5d600 | ||
|
|
2960687ab9 | ||
|
|
ee65dadc9a | ||
|
|
0168581a82 | ||
|
|
9a81255729 | ||
|
|
023b1d50bd | ||
|
|
8147c317d7 | ||
|
|
60709da706 | ||
|
|
45b33f7b96 | ||
|
|
efdd5ef395 | ||
|
|
ad617da4e0 | ||
|
|
1211f23b53 | ||
|
|
4bcfaeae69 | ||
|
|
2303115235 | ||
|
|
d0f46c2120 | ||
|
|
a3c18fb095 | ||
|
|
36183cbcfb | ||
|
|
bb4d1ee119 | ||
|
|
c489c1c2b2 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,3 +1,8 @@
|
||||
test/dest/
|
||||
Gemfile.lock
|
||||
test/dest
|
||||
*.gem
|
||||
pkg/
|
||||
*.swp
|
||||
*~
|
||||
_site/
|
||||
.bundle/
|
||||
|
||||
221
History.txt
221
History.txt
@@ -1,3 +1,197 @@
|
||||
== 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
|
||||
@@ -7,28 +201,35 @@
|
||||
* Add array_to_sentence_string filter [github.com/mchung]
|
||||
* Add a converter for textpattern [github.com/PerfectlyNormal]
|
||||
* Add a working Mephisto / MySQL converter [github.com/ivey]
|
||||
* Allowing .htaccess files to be copied over into the generated site [github.com/briandoll]
|
||||
* Allowing .htaccess files to be copied over into the generated site
|
||||
[github.com/briandoll]
|
||||
* Add option to not put file date in permalink URL [github.com/mreid]
|
||||
* Add line number capabilities to highlight blocks [github.com/jcon]
|
||||
* Bug Fixes
|
||||
* Fix permalink behavior [github.com/cavalle]
|
||||
* Fixed an issue with pygments, markdown, and newlines [github.com/zpinter]
|
||||
* Fixed an issue with pygments, markdown, and newlines
|
||||
[github.com/zpinter]
|
||||
* Ampersands need to be escaped [github.com/pufuwozu, github.com/ap]
|
||||
* Test and fix the site.categories hash [github.com/zzot]
|
||||
* Fix site payload available to files [github.com/matrix9180]
|
||||
|
||||
== 0.3.0 / 2008-12-24
|
||||
* Major Enhancements
|
||||
* Added --server option to start a simple WEBrick server on destination directory [github.com/johnreilly and github.com/mchung]
|
||||
* Added --server option to start a simple WEBrick server on destination
|
||||
directory [github.com/johnreilly and github.com/mchung]
|
||||
* Minor Enhancements
|
||||
* Added post categories based on directories containing _posts [github.com/mreid]
|
||||
* Added post categories based on directories containing _posts
|
||||
[github.com/mreid]
|
||||
* Added post topics based on directories underneath _posts
|
||||
* Added new date filter that shows the full month name [github.com/mreid]
|
||||
* Merge Post's YAML front matter into its to_liquid payload [github.com/remi]
|
||||
* Merge Post's YAML front matter into its to_liquid payload
|
||||
[github.com/remi]
|
||||
* Restrict includes to regular files underneath _includes
|
||||
* Bug Fixes
|
||||
* Change YAML delimiter matcher so as to not chew up 2nd level markdown headers [github.com/mreid]
|
||||
* Fix bug that meant page data (such as the date) was not available in templates [github.com/mreid]
|
||||
* Change YAML delimiter matcher so as to not chew up 2nd level markdown
|
||||
headers [github.com/mreid]
|
||||
* Fix bug that meant page data (such as the date) was not available in
|
||||
templates [github.com/mreid]
|
||||
* Properly reject directories in _layouts
|
||||
|
||||
== 0.2.1 / 2008-12-15
|
||||
@@ -51,9 +252,11 @@
|
||||
* Code highlighting with Pygments if --pygments is specified
|
||||
* Disable true LSI by default, enable with --lsi
|
||||
* Minor Enhancements
|
||||
* Output informative message if RDiscount is not available [github.com/JackDanger]
|
||||
* Output informative message if RDiscount is not available
|
||||
[github.com/JackDanger]
|
||||
* Bug Fixes
|
||||
* Prevent Jekyll from picking up the output directory as a source [github.com/JackDanger]
|
||||
* Prevent Jekyll from picking up the output directory as a source
|
||||
[github.com/JackDanger]
|
||||
* Skip related_posts when there is only one post [github.com/JackDanger]
|
||||
|
||||
== 0.1.4 / 2008-12-08
|
||||
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2008 Tom Preston-Werner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the 'Software'), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
487
README.textile
487
README.textile
@@ -1,475 +1,40 @@
|
||||
h1. Jekyll
|
||||
|
||||
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. Visit
|
||||
"http://tom.preston-werner.com":http://tom.preston-werner.com to see an
|
||||
example of a Jekyll generated blog.
|
||||
By Tom Preston-Werner, Nick Quaranto, and many awesome contributors!
|
||||
|
||||
To understand how this all works, open up my
|
||||
"TPW":http://github.com/mojombo/tpw repo in a new browser window. I'll be
|
||||
referencing the code there.
|
||||
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.
|
||||
|
||||
Take a look at
|
||||
"index.html":http://github.com/mojombo/tpw/tree/master/index.html. This file
|
||||
represents the homepage of the site. At the top of the file is a chunk of YAML
|
||||
that contains metadata about the file. This data tells Jekyll what layout to
|
||||
give the file, what the page's title should be, etc. In this case, I specify
|
||||
that the "default" template should be used. You can find the layout files in
|
||||
the "_layouts":http://github.com/mojombo/tpw/tree/master/_layouts directory.
|
||||
If you open
|
||||
"default.html":http://github.com/mojombo/tpw/tree/master/_layouts/default.html
|
||||
you can see that the homepage is constructed by wrapping index.html with this
|
||||
layout.
|
||||
h2. Getting Started
|
||||
|
||||
You'll also notice Liquid templating code in these files.
|
||||
"Liquid":http://www.liquidmarkup.org/ is a simple, extensible templating
|
||||
language that makes it easy to embed data in your templates. For my homepage I
|
||||
wanted to have a list of all my blog posts. Jekyll hands me a Hash containing
|
||||
various data about my site. A reverse chronological list of all my blog posts
|
||||
can be found in <code>site.posts</code>. Each post, in turn, contains various
|
||||
fields such as <code>title</code> and <code>date</code>.
|
||||
* "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
|
||||
|
||||
Jekyll gets the list of blog posts by parsing the files in any
|
||||
"_posts":http://github.com/mojombo/tpw/tree/master/_posts directory found in
|
||||
subdirectories below the root.
|
||||
Each post's filename contains (by default) the publishing date and slug (what shows up in the
|
||||
URL) that the final HTML file should have. Open up the file corresponding to a
|
||||
blog post:
|
||||
"2008-11-17-blogging-like-a-hacker.textile":http://github.com/mojombo/tpw/tree/master/_posts/2008-11-17-blogging-like-a-hacker.textile.
|
||||
GitHub renders textile files by default, so to better understand the file,
|
||||
click on the
|
||||
"raw":http://github.com/mojombo/tpw/tree/master/_posts/2008-11-17-blogging-like-a-hacker.textile?raw=true
|
||||
view to see the original file. Here I've specified the <code>post</code>
|
||||
layout. If you look at that file you'll see an example of a nested layout.
|
||||
Layouts can contain other layouts allowing you a great deal of flexibility in
|
||||
how pages are assembled. In my case I use a nested layout in order to show
|
||||
related posts for each blog entry. The YAML also specifies the post's title
|
||||
which is then embedded in the post's body via Liquid.
|
||||
h2. Diving In
|
||||
|
||||
Posts are handled in a special way by Jekyll. The date you specify in the
|
||||
filename is used to construct the URL in the generated site. The example post,
|
||||
for instance, ends up at
|
||||
<code>http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html</code>.
|
||||
* "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
|
||||
|
||||
Categories for posts are derived from the directory structure the posts were
|
||||
found within. A post that appears in the directory foo/bar/_posts is placed in
|
||||
the categories 'foo' and 'bar'. By selecting posts from particular categories
|
||||
in your Liquid templates, you will be able to host multiple blogs within a
|
||||
site.
|
||||
h2. Runtime Dependencies
|
||||
|
||||
Files that do not reside in directories prefixed with an underscore are
|
||||
mirrored into a corresponding directory structure in the generated site. If a
|
||||
file does not have a YAML preface, it is not run through the Liquid
|
||||
interpreter. Binary files are copied over unmodified.
|
||||
* 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)
|
||||
|
||||
Jekyll is still a very young project. I've only developed the exact
|
||||
functionality that I've needed. As time goes on I'd like to see the project
|
||||
mature and support additional features. If you end up using Jekyll for your
|
||||
own blog, drop me a line and let me know what you'd like to see in future
|
||||
versions. Better yet, fork the project over at GitHub and hack in the features
|
||||
yourself!
|
||||
h2. Developer Dependencies
|
||||
|
||||
h2. Example Proto-Site
|
||||
|
||||
My own personal site/blog is generated with Jekyll.
|
||||
|
||||
The proto-site repo
|
||||
("http://github.com/mojombo/tpw":http://github.com/mojombo/tpw) is converted
|
||||
into the actual site
|
||||
("http://tom.preston-werner.com/":http://tom.preston-werner.com)
|
||||
|
||||
h2. Install
|
||||
|
||||
The best way to install Jekyll is via RubyGems:
|
||||
|
||||
$ sudo gem install mojombo-jekyll -s http://gems.github.com/
|
||||
|
||||
Jekyll requires the gems `directory_watcher`, `liquid`, `open4`,
|
||||
and `maruku` (for markdown support). These are automatically
|
||||
installed by the gem install command.
|
||||
|
||||
Maruku comes with optional support for LaTeX to PNG rendering via
|
||||
"blahtex":http://gva.noekeon.org/blahtexml/ (Version 0.6) which must be in
|
||||
your $PATH along with `dvips`.
|
||||
|
||||
(NOTE: the version of maruku I am using is `remi-maruku` on GitHub as it
|
||||
does not assume a fixed location for `dvips`.)
|
||||
|
||||
h2. Run
|
||||
|
||||
$ cd /path/to/proto/site
|
||||
$ jekyll
|
||||
|
||||
This will generate the site and place it in /path/to/proto/site/_site. If
|
||||
you'd like the generated site placed somewhere else:
|
||||
|
||||
$ jekyll /path/to/place/generated/site
|
||||
|
||||
And if you don't want to be in the proto site root to run Jekyll:
|
||||
|
||||
$ jekyll /path/to/proto/site /path/to/place/generated/site
|
||||
|
||||
h2. Run Options
|
||||
|
||||
There is an autobuild feature that will regenerate your site if any of the
|
||||
files change. The autobuild feature can be used on any of the invocations:
|
||||
|
||||
$ jekyll --auto
|
||||
|
||||
By default, the "related posts" functionality will produce crappy results.
|
||||
In order to get high quality results with a true LSI algorithm, you must
|
||||
enable it (it may take some time to run if you have many posts):
|
||||
|
||||
$ jekyll --lsi
|
||||
|
||||
For static code highlighting, you can install Pygments (see below) and then
|
||||
use that to make your code blocks look pretty. To activate Pygments support
|
||||
during the conversion:
|
||||
|
||||
$ jekyll --pygments
|
||||
|
||||
By default, Jekyll uses "Maruku":http://maruku.rubyforge.org (pure Ruby) for
|
||||
Markdown support. If you'd like to use RDiscount (faster, but requires
|
||||
compilation), you must install it (gem install rdiscount) and then you can
|
||||
have it used instead:
|
||||
|
||||
$ jekyll --rdiscount
|
||||
|
||||
When previewing complex sites locally, simply opening the site in a web
|
||||
browser (using file://) can cause problems with links that are relative to
|
||||
the site root (e.g., "/stylesheets/style.css"). To get around this, Jekyll
|
||||
can launch a simple WEBrick server (works well in conjunction with --auto).
|
||||
Default port is 4000:
|
||||
|
||||
$ jekyll --server [PORT]
|
||||
|
||||
By default, the permalink for each post begins with its date in 'YYYY/MM/DD'
|
||||
format. If you do not wish to have the date appear in the URL of each post,
|
||||
you can change the permalink style to 'none' so that only the 'slug' part of
|
||||
the filename is used. For example, with the permalink style set to 'none' the
|
||||
file '2009-01-01-happy-new-year.markdown' will have a permalink like
|
||||
'http://yoursite.com/happy-new-year.html'. The date of the post will still be
|
||||
read from the filename (and is required!) to be used elsewhere in Jekyll.
|
||||
Example usage:
|
||||
|
||||
$ jekyll --permalink none
|
||||
|
||||
h2. Data
|
||||
|
||||
Jekyll traverses your site looking for files to process. Any files with YAML
|
||||
front matter (see below) are subject to processing. For each of these files,
|
||||
Jekyll makes a variety of data available to the pages via the Liquid
|
||||
templating system. The following is a reference of the available data.
|
||||
|
||||
h3. Global
|
||||
|
||||
site
|
||||
Sitewide information.
|
||||
|
||||
page
|
||||
For Posts, this is the union of the data in the YAML front matter and the
|
||||
computed data (such as URL and date). For regular pages, this is just the
|
||||
YAML front matter.
|
||||
|
||||
content
|
||||
In layout files, this contains the content of the subview(s). In Posts or
|
||||
Pages, this is undefined.
|
||||
|
||||
h3. Site
|
||||
|
||||
site.time
|
||||
The current Time (when you run the jekyll command).
|
||||
|
||||
site.posts
|
||||
A reverse chronological list of all Posts.
|
||||
|
||||
site.related_posts
|
||||
If the page being processed is a Post, this contains a list of up to ten
|
||||
related Posts. By default, these are low quality but fast to compute. For
|
||||
high quality but slow to compute results, run the jekyll command with the
|
||||
--lsi (latent semantic indexing) option.
|
||||
|
||||
site.categories.CATEGORY
|
||||
The list of all Posts in category CATEGORY.
|
||||
|
||||
h3. Post
|
||||
|
||||
post.title
|
||||
The title of the Post.
|
||||
|
||||
post.url
|
||||
The URL of the Post without the domain.
|
||||
e.g. /2008/12/14/my-post.html
|
||||
|
||||
post.date
|
||||
The Date assigned to the Post.
|
||||
|
||||
post.id
|
||||
An identifier unique to the Post (useful in RSS feeds).
|
||||
e.g. /2008/12/14/my-post
|
||||
|
||||
post.categories
|
||||
The list of categories to which this post belongs. Categories are
|
||||
derived from the directory structure above the _posts directory. For
|
||||
example, a post at /work/code/_posts/2008-12-24-closures.textile
|
||||
would have this field set to ['work', 'code'].
|
||||
|
||||
post.topics
|
||||
The list of topics for this Post. Topics are derived from the directory
|
||||
structure beneath the _posts directory. For example, a post at
|
||||
/_posts/music/metal/2008-12-24-metalocalypse.textile would have this field
|
||||
set to ['music', 'metal'].
|
||||
|
||||
post.content
|
||||
The content of the Post.
|
||||
|
||||
h2. YAML Front Matter
|
||||
|
||||
Any files that contain a YAML front matter block will be processed by Jekyll
|
||||
as special files. The front matter must be the first thing in the file and
|
||||
takes the form of:
|
||||
|
||||
<pre>
|
||||
---
|
||||
layout: post
|
||||
title: Blogging Like a Hacker
|
||||
---
|
||||
</pre>
|
||||
|
||||
Between the triple-dashed lines, you can set predefined variables (see below
|
||||
for a reference) or custom data of your own.
|
||||
|
||||
h3. Predefined Global Variables
|
||||
|
||||
layout
|
||||
If set, this specifies the layout file to use. Use the layout file
|
||||
name without file extension. Layout files must be placed in the
|
||||
<code>_layouts</code> directory.
|
||||
|
||||
h3. Predefined Post Variables
|
||||
|
||||
permalink
|
||||
If you need your processed URLs to be something other than the default
|
||||
/year/month/day/title.html then you can set this variable and it will
|
||||
be used as the final URL.
|
||||
|
||||
h3. Custom Variables
|
||||
|
||||
Any variables in the front matter that are not predefined are mixed into the
|
||||
data that is sent to the Liquid templating engine during the conversion. For
|
||||
instance, if you set a <code>title</code>, you can use that in your layout to
|
||||
set the page title:
|
||||
|
||||
<pre>
|
||||
<title>{{ page.title }}</title>
|
||||
</pre>
|
||||
|
||||
h2. Filters, Tags, and Blocks
|
||||
|
||||
In addition to the built-in Liquid filters, tags, and blocks, Jekyll provides
|
||||
some additional items that you can use in your site.
|
||||
|
||||
h3. Date to XML Schema (Filter)
|
||||
|
||||
Convert a Time into XML Schema format.
|
||||
|
||||
{{ site.time | date_to_xmlschema }}
|
||||
|
||||
becomes
|
||||
|
||||
2008-11-17T13:07:54-08:00
|
||||
|
||||
h3. XML Escape (Filter)
|
||||
|
||||
Escape some text for use in XML.
|
||||
|
||||
{{ post.content | xml_escape }}
|
||||
|
||||
h3. Number of Words (Filter)
|
||||
|
||||
Count the number of words in some text.
|
||||
|
||||
{{ post.content | number_of_words }}
|
||||
|
||||
becomes
|
||||
|
||||
1337
|
||||
|
||||
h3. Array to Sentence String
|
||||
|
||||
Convert an array into a sentence.
|
||||
|
||||
{{ page.tags | array_to_sentence_string }}
|
||||
|
||||
becomes
|
||||
|
||||
foo, bar, and baz
|
||||
|
||||
h3. Include (Tag)
|
||||
|
||||
If you have small page fragments that you wish to include in multiple places
|
||||
on your site, you can use the <code>include</code> tag.
|
||||
|
||||
<pre>{% include sig.textile %}</pre>
|
||||
|
||||
Jekyll expects all include files to be placed in an <code>_includes</code>
|
||||
directory at the root of your source dir. So this will embed the contents of
|
||||
<code>/path/to/proto/site/_includes/sig.textile</code> into the calling file.
|
||||
|
||||
h3. Code Highlighting (Block)
|
||||
|
||||
Jekyll has built in support for syntax highlighting of over "100
|
||||
languages":http://pygments.org/languages/ via "Pygments":http://pygments.org/.
|
||||
In order to take advantage of this you'll need to have Pygments installed, and
|
||||
the pygmentize binary must be in your path. When you run Jekyll, make sure you
|
||||
run it with Pygments support:
|
||||
|
||||
$ jekyll --pygments
|
||||
|
||||
To denote a code block that should be highlighted:
|
||||
|
||||
<pre>
|
||||
{% highlight ruby %}
|
||||
def foo
|
||||
puts 'foo'
|
||||
end
|
||||
{% endhighlight %}
|
||||
</pre>
|
||||
|
||||
The argument to <code>highlight</code> is the language identifier. To find the
|
||||
appropriate identifier to use for your favorite language, look for the "short
|
||||
name" on the "Lexers":http://pygments.org/docs/lexers/ page.
|
||||
|
||||
There is a second argument to <code>highlight</code> called
|
||||
<code>linenos</code> that is optional. Including the <code>linenos</code>
|
||||
argument will force the highlighted code to include line numbers. For
|
||||
instance, the following code block would include line numbers next to each
|
||||
line:
|
||||
|
||||
<pre>
|
||||
{% highlight ruby linenos %}
|
||||
def foo
|
||||
puts 'foo'
|
||||
end
|
||||
{% endhighlight %}
|
||||
</pre>
|
||||
|
||||
In order for the highlighting to show up, you'll need to include a
|
||||
highlighting stylesheet. For an example stylesheet you can look at
|
||||
"syntax.css":http://github.com/mojombo/tpw/tree/master/css/syntax.css. These
|
||||
are the same styles as used by GitHub and you are free to use them for your
|
||||
own site. If you use linenos, you might want to include an additional CSS
|
||||
class definition for <code>lineno</code> in syntax.css to distinguish the line
|
||||
numbers from the highlighted code.
|
||||
|
||||
h2. Categories
|
||||
|
||||
Posts are placed into categories based on the directory structure they are
|
||||
found within (see above for an example). The categories can be accessed from
|
||||
within a Liquid template as follows:
|
||||
|
||||
<pre>
|
||||
{% for post in site.categories.foo %}
|
||||
<li><span>{{ post.date | date_to_string }}</span> - {{ post.title }}</li>
|
||||
{% endfor %}
|
||||
</pre>
|
||||
|
||||
This would list all the posts in the category 'foo' by date and title.
|
||||
|
||||
The posts within each category are sorted in reverse chronological order.
|
||||
|
||||
h2. Blog migrations
|
||||
|
||||
h3. Movable Type
|
||||
|
||||
To migrate your MT blog into Jekyll, you'll need read access to the database.
|
||||
The lib/jekyll/converters/mt.rb module provides a simple convert to create
|
||||
.markdown files in a _posts directory based on the entries contained therein.
|
||||
|
||||
$ export DB=my_mtdb
|
||||
$ export USER=dbuser
|
||||
$ export PASS=dbpass
|
||||
$ ruby -r './lib/jekyll/converters/mt' -e 'Jekyll::MT.process( \
|
||||
"#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")'
|
||||
|
||||
You may need to adjust the SQL query used to retrieve MT entries. Left alone,
|
||||
it will attempt to pull all entries across all blogs regardless of status.
|
||||
Please check the results and verify the posts before publishing.
|
||||
|
||||
h3. Typo 4+
|
||||
|
||||
To migrate your Typo blog into Jekyll, you'll need read access to the MySQL
|
||||
database. The lib/jekyll/converters/typo.rb module provides a simple convert
|
||||
to create .html, .textile, or .markdown files in a _posts directory based on
|
||||
the entries contained therein.
|
||||
|
||||
$ export DB=my_typo_db
|
||||
$ export USER=dbuser
|
||||
$ export PASS=dbpass
|
||||
$ ruby -r './lib/jekyll/converters/typo' -e 'Jekyll::Typo.process( \
|
||||
"#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")'
|
||||
|
||||
You may need to adjust the code used to filter Typo entries. Left alone,
|
||||
it will attempt to pull all entries across all blogs that were published.
|
||||
This code also has only been tested with Typo version 4+. Previous versions
|
||||
of Typo may not convert correctly. Please check the results and verify the
|
||||
posts before publishing.
|
||||
|
||||
h3. TextPattern 4
|
||||
|
||||
To migrate your TextPattern blog into Jekyll, you'll need read access to the MySQL
|
||||
database. The lib/jekyll/converters/textpattern.rb module provides a simple convert to create .textile files in a _posts directory based on
|
||||
the entries contained therein.
|
||||
|
||||
$ ruby -r './lib/jekyll/converters/textpattern' -e 'Jekyll::TextPattern.process( \
|
||||
"database_name", "username", "password", "hostname")'
|
||||
|
||||
The hostname defaults to _localhost_, all other variables are needed
|
||||
You may need to adjust the code used to filter entries. Left alone,
|
||||
it will attempt to pull all entries that are live or sticky.
|
||||
|
||||
h2. Contribute
|
||||
|
||||
If you'd like to hack on Jekyll, start by forking my repo on GitHub:
|
||||
|
||||
http://github.com/mojombo/jekyll
|
||||
|
||||
To get all of the dependencies, install the gem first. The best way to get
|
||||
your changes merged back into core is as follows:
|
||||
|
||||
# Clone down your fork
|
||||
# Create a topic branch to contain your change
|
||||
# Hack away
|
||||
# Add tests and make sure everything still passes by running `rake`
|
||||
# If you are adding new functionality, document it in README.textile
|
||||
# Do not change the version number, I will do that on my end
|
||||
# If necessary, rebase your commits into logical chunks, without errors
|
||||
# Push the branch up to GitHub
|
||||
# Send me (mojombo) a pull request for your branch
|
||||
* Shoulda: Test framework (Ruby)
|
||||
* RR: Mocking (Ruby)
|
||||
* RedGreen: Nicer test output (Ruby)
|
||||
|
||||
h2. License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2008 Tom Preston-Werner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
See LICENSE.
|
||||
|
||||
185
Rakefile
185
Rakefile
@@ -1,74 +1,159 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'date'
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Helper functions
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
def name
|
||||
@name ||= Dir['*.gemspec'].first.split('.').first
|
||||
end
|
||||
|
||||
def version
|
||||
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
||||
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
||||
end
|
||||
|
||||
def date
|
||||
Date.today.to_s
|
||||
end
|
||||
|
||||
def rubyforge_project
|
||||
name
|
||||
end
|
||||
|
||||
def gemspec_file
|
||||
"#{name}.gemspec"
|
||||
end
|
||||
|
||||
def gem_file
|
||||
"#{name}-#{version}.gem"
|
||||
end
|
||||
|
||||
def replace_header(head, header_name)
|
||||
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
||||
end
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Standard tasks
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
task :default => [:test, :features]
|
||||
|
||||
require 'rake/testtask'
|
||||
Rake::TestTask.new(:test) do |test|
|
||||
test.libs << 'lib' << 'test'
|
||||
test.pattern = 'test/**/test_*.rb'
|
||||
test.verbose = true
|
||||
end
|
||||
|
||||
desc "Generate RCov test coverage and open in your browser"
|
||||
task :coverage do
|
||||
require 'rcov'
|
||||
sh "rm -fr coverage"
|
||||
sh "rcov test/test_*.rb"
|
||||
sh "open coverage/index.html"
|
||||
end
|
||||
|
||||
require 'rake/rdoctask'
|
||||
|
||||
begin
|
||||
require 'jeweler'
|
||||
Jeweler::Tasks.new do |s|
|
||||
s.name = "jekyll"
|
||||
s.summary = %Q{Jekyll is a simple, blog aware, static site generator.}
|
||||
s.email = "tom@mojombo.com"
|
||||
s.homepage = "http://github.com/mojombo/jekyll"
|
||||
s.description = "Jekyll is a simple, blog aware, static site generator."
|
||||
s.authors = ["Tom Preston-Werner"]
|
||||
s.rubyforge_project = "jekyll"
|
||||
s.add_dependency('RedCloth', '>= 4.0.4')
|
||||
s.add_dependency('liquid', '>= 1.9.0')
|
||||
s.add_dependency('classifier', '>= 1.3.1')
|
||||
s.add_dependency('maruku', '>= 0.5.9')
|
||||
s.add_dependency('directory_watcher', '>= 1.1.1')
|
||||
s.add_dependency('open4', '>= 0.9.6')
|
||||
end
|
||||
rescue LoadError
|
||||
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
||||
end
|
||||
|
||||
Rake::TestTask.new do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/test_*.rb'
|
||||
t.verbose = false
|
||||
end
|
||||
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'jekyll'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.title = "#{name} #{version}"
|
||||
rdoc.rdoc_files.include('README*')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rcov/rcovtask'
|
||||
Rcov::RcovTask.new do |t|
|
||||
t.libs << 'test'
|
||||
t.test_files = FileList['test/**/test_*.rb']
|
||||
t.verbose = true
|
||||
end
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
task :default => :test
|
||||
|
||||
# console
|
||||
|
||||
desc "Open an irb session preloaded with this library"
|
||||
task :console do
|
||||
sh "irb -rubygems -r ./lib/jekyll.rb"
|
||||
sh "irb -rubygems -r ./lib/#{name}.rb"
|
||||
end
|
||||
|
||||
# converters
|
||||
#############################################################################
|
||||
#
|
||||
# Custom tasks (add your own tasks here)
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
namespace :convert do
|
||||
namespace :migrate do
|
||||
desc "Migrate from mephisto in the current directory"
|
||||
task :mephisto do
|
||||
sh %q(ruby -r './lib/jekyll/converters/mephisto' -e 'Jekyll::Mephisto.postgres(:database => "#{ENV["DB"]}")')
|
||||
sh %q(ruby -r './lib/jekyll/migrators/mephisto' -e 'Jekyll::Mephisto.postgres(:database => "#{ENV["DB"]}")')
|
||||
end
|
||||
desc "Migrate from Movable Type in the current directory"
|
||||
task :mt do
|
||||
sh %q(ruby -r './lib/jekyll/converters/mt' -e 'Jekyll::MT.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
|
||||
sh %q(ruby -r './lib/jekyll/migrators/mt' -e 'Jekyll::MT.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
|
||||
end
|
||||
desc "Migrate from Typo in the current directory"
|
||||
task :typo do
|
||||
sh %q(ruby -r './lib/jekyll/converters/typo' -e 'Jekyll::Typo.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
|
||||
sh %q(ruby -r './lib/jekyll/migrators/typo' -e 'Jekyll::Typo.process("#{ENV["DB"]}", "#{ENV["USER"]}", "#{ENV["PASS"]}")')
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
Cucumber::Rake::Task.new(:features) do |t|
|
||||
t.cucumber_opts = "--format progress"
|
||||
end
|
||||
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
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Packaging tasks
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
task :release => :build do
|
||||
unless `git branch` =~ /^\* master$/
|
||||
puts "You must be on the master branch to release!"
|
||||
exit!
|
||||
end
|
||||
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
||||
sh "git tag v#{version}"
|
||||
sh "git push origin master"
|
||||
sh "git push origin v#{version}"
|
||||
sh "gem push pkg/#{name}-#{version}.gem"
|
||||
end
|
||||
|
||||
task :build => :gemspec do
|
||||
sh "mkdir -p pkg"
|
||||
sh "gem build #{gemspec_file}"
|
||||
sh "mv #{gem_file} pkg"
|
||||
end
|
||||
|
||||
task :gemspec do
|
||||
# read spec file and split out manifest section
|
||||
spec = File.read(gemspec_file)
|
||||
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
||||
|
||||
# replace name version and date
|
||||
replace_header(head, :name)
|
||||
replace_header(head, :version)
|
||||
replace_header(head, :date)
|
||||
#comment this out if your rubyforge_project has a different name
|
||||
replace_header(head, :rubyforge_project)
|
||||
|
||||
# determine file list from git ls-files
|
||||
files = `git ls-files`.
|
||||
split("\n").
|
||||
sort.
|
||||
reject { |file| file =~ /^\./ }.
|
||||
reject { |file| file =~ /^(rdoc|pkg|coverage)/ }.
|
||||
map { |file| " #{file}" }.
|
||||
join("\n")
|
||||
|
||||
# piece file back together and write
|
||||
manifest = " s.files = %w[\n#{files}\n ]\n"
|
||||
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
||||
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
||||
puts "Updated #{gemspec_file}"
|
||||
end
|
||||
3
TODO
3
TODO
@@ -1,3 +0,0 @@
|
||||
[ ] Easier configuration of Maruka and blahtex directories [mdreid]
|
||||
[ ] Accurate "related posts" calculator
|
||||
[ ] Autobuild
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
:minor: 4
|
||||
:patch: 0
|
||||
:major: 0
|
||||
258
bin/jekyll
258
bin/jekyll
@@ -9,58 +9,202 @@ Basic Command Line Usage:
|
||||
jekyll # . -> ./_site
|
||||
jekyll <path to write generated site> # . -> <path>
|
||||
jekyll <path to source> <path to write generated site> # <path> -> <path>
|
||||
|
||||
Options:
|
||||
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'
|
||||
|
||||
options = {}
|
||||
|
||||
exec = {}
|
||||
options = {}
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = help
|
||||
|
||||
opts.on("--auto", "Auto-regenerate") do
|
||||
options[:auto] = true
|
||||
opts.on("--file [PATH]", "File to import from") do |import_file|
|
||||
options['file'] = import_file
|
||||
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
|
||||
|
||||
|
||||
opts.on("--[no-]safe", "Safe mode (default unsafe)") do |safe|
|
||||
options['safe'] = safe
|
||||
end
|
||||
|
||||
opts.on("--[no-]auto", "Auto-regenerate") do |auto|
|
||||
options['auto'] = auto
|
||||
end
|
||||
|
||||
opts.on("--server [PORT]", "Start web server (default port 4000)") do |port|
|
||||
options[:server] = true
|
||||
options[:server_port] = port || 4000
|
||||
options['server'] = true
|
||||
options['server_port'] = port unless port.nil?
|
||||
end
|
||||
|
||||
opts.on("--lsi", "Use LSI for better related posts") do
|
||||
Jekyll.lsi = true
|
||||
|
||||
opts.on("--no-server", "Do not start a web server") do |part|
|
||||
options['server'] = false
|
||||
end
|
||||
|
||||
opts.on("--pygments", "Use pygments to highlight code") do
|
||||
Jekyll.pygments = true
|
||||
|
||||
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
|
||||
require 'rdiscount'
|
||||
Jekyll.markdown_proc = Proc.new { |x| RDiscount.new(x).to_html }
|
||||
puts 'Using rdiscount for Markdown'
|
||||
rescue LoadError
|
||||
puts 'You must have the rdiscount gem installed first'
|
||||
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("--permalink [TYPE]", "Use 'date' (default) for YYYY/MM/DD") do |style|
|
||||
Jekyll.permalink_style = (style || 'date').to_sym
|
||||
|
||||
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!
|
||||
|
||||
def clean(dest)
|
||||
FileUtils.rm_rf(dest)
|
||||
FileUtils.mkdir_p(dest)
|
||||
|
||||
# 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) }
|
||||
@@ -70,63 +214,63 @@ def globs(source)
|
||||
end
|
||||
end
|
||||
|
||||
source = nil
|
||||
destination = nil
|
||||
# Create the Site
|
||||
site = Jekyll::Site.new(options)
|
||||
|
||||
case ARGV.size
|
||||
when 0
|
||||
source = '.'
|
||||
destination = File.join('.', '_site')
|
||||
when 1
|
||||
source = '.'
|
||||
destination = ARGV[0]
|
||||
when 2
|
||||
source = ARGV[0]
|
||||
destination = ARGV[1]
|
||||
else
|
||||
puts "Invalid options. Run `jekyll --help` for assistance."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
if options[:auto]
|
||||
# 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"
|
||||
Jekyll.process(source, destination)
|
||||
site.process
|
||||
end
|
||||
|
||||
|
||||
dw.start
|
||||
|
||||
unless options[:server]
|
||||
|
||||
unless options['server']
|
||||
loop { sleep 1000 }
|
||||
end
|
||||
else
|
||||
Jekyll.process(source, destination)
|
||||
puts "Successfully generated site in #{destination}"
|
||||
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
|
||||
|
||||
if options[:server]
|
||||
# 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],
|
||||
:DocumentRoot => destination
|
||||
: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
|
||||
end
|
||||
|
||||
1
cucumber.yml
Normal file
1
cucumber.yml
Normal file
@@ -0,0 +1 @@
|
||||
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.
|
||||
94
features/create_sites.feature
Normal file
94
features/create_sites.feature
Normal file
@@ -0,0 +1,94 @@
|
||||
Feature: Create sites
|
||||
As a hacker who likes to blog
|
||||
I want to be able to make a static site
|
||||
In order to share my awesome ideas with the interwebs
|
||||
|
||||
Scenario: Basic site
|
||||
Given I have an "index.html" file that contains "Basic Site"
|
||||
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 | 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"
|
||||
|
||||
Scenario: Basic site with layout and a page
|
||||
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
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: Basic Site with Layout" in "_site/index.html"
|
||||
|
||||
Scenario: Basic site with layout and a post
|
||||
Given I have a _layouts directory
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| Wargames | 3/27/2009 | default | The only winning move is not to play. |
|
||||
And I have a default layout that contains "Post Layout: {{ content }}"
|
||||
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"
|
||||
|
||||
Scenario: Basic site with layouts, pages, posts and files
|
||||
Given I have a _layouts directory
|
||||
And I have a page layout that contains "Page {{ page.title }}: {{ content }}"
|
||||
And I have a post layout that contains "Post {{ page.title }}: {{ content }}"
|
||||
And I have an "index.html" page with layout "page" that contains "Site contains {{ site.pages.size }} pages and {{ site.posts.size }} posts"
|
||||
And I have a blog directory
|
||||
And I have a "blog/index.html" page with layout "page" that contains "blog category index page"
|
||||
And I have an "about.html" file that contains "No replacement {{ site.posts.size }}"
|
||||
And I have an "another_file" file that contains ""
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 3/27/2009 | post | content for entry1. |
|
||||
| entry2 | 4/27/2009 | post | content for entry2. |
|
||||
And I have a category/_posts directory
|
||||
And I have the following posts in "category":
|
||||
| title | date | layout | content |
|
||||
| entry3 | 5/27/2009 | post | content for entry3. |
|
||||
| entry4 | 6/27/2009 | post | content for entry4. |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Page : Site contains 2 pages and 4 posts" in "_site/index.html"
|
||||
And I should see "No replacement \{\{ site.posts.size \}\}" in "_site/about.html"
|
||||
And I should see "" in "_site/another_file"
|
||||
And I should see "Page : blog category index page" in "_site/blog/index.html"
|
||||
And I should see "Post entry1: <p>content for entry1.</p>" in "_site/2009/03/27/entry1.html"
|
||||
And I should see "Post entry2: <p>content for entry2.</p>" in "_site/2009/04/27/entry2.html"
|
||||
And I should see "Post entry3: <p>content for entry3.</p>" in "_site/category/2009/05/27/entry3.html"
|
||||
And I should see "Post entry4: <p>content for entry4.</p>" in "_site/category/2009/06/27/entry4.html"
|
||||
|
||||
Scenario: Basic site with include tag
|
||||
Given I have a _includes directory
|
||||
And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
|
||||
And I have an "_includes/about.textile" file that contains "Generated by Jekyll"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"
|
||||
|
||||
Scenario: Basic site with subdir include tag
|
||||
Given I have a _includes directory
|
||||
And I have an "_includes/about.textile" file that contains "Generated by Jekyll"
|
||||
And I have an info directory
|
||||
And I have an "info/index.html" page that contains "Basic Site with subdir include tag: {% include about.textile %}"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Basic Site with subdir include tag: Generated by Jekyll" in "_site/info/index.html"
|
||||
|
||||
Scenario: Basic site with nested include tag
|
||||
Given I have a _includes directory
|
||||
And I have an "_includes/about.textile" file that contains "Generated by {% include jekyll.textile %}"
|
||||
And I have an "_includes/jekyll.textile" file that contains "Jekyll"
|
||||
And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}"
|
||||
When I debug jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"
|
||||
60
features/embed_filters.feature
Normal file
60
features/embed_filters.feature
Normal file
@@ -0,0 +1,60 @@
|
||||
Feature: Embed filters
|
||||
As a hacker who likes to blog
|
||||
I want to be able to transform text inside a post or page
|
||||
In order to perform cool stuff in my posts
|
||||
|
||||
Scenario: Convert date to XML schema
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 3/27/2009 | default | These aren't the droids you're looking for. |
|
||||
And I have a default layout that contains "{{ site.time | date_to_xmlschema }}"
|
||||
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"
|
||||
|
||||
Scenario: Escape text for XML
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star & Wars | 3/27/2009 | default | These aren't the droids you're looking for. |
|
||||
And I have a default layout that contains "{{ page.title | xml_escape }}"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Star & Wars" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Calculate number of words
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 3/27/2009 | default | These aren't the droids you're looking for. |
|
||||
And I have a default layout that contains "{{ content | xml_escape }}"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "7" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Convert an array into a sentence
|
||||
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 | 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
|
||||
Then the _site directory should exist
|
||||
And I should see "scifi, movies, and force" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Textilize a given string
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| title | date | layout | content |
|
||||
| Star Wars | 3/27/2009 | default | These aren't the droids you're looking for. |
|
||||
And I have a default layout that contains "By {{ '_Obi-wan_' | textilize }}"
|
||||
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"
|
||||
|
||||
30
features/markdown.feature
Normal file
30
features/markdown.feature
Normal file
@@ -0,0 +1,30 @@
|
||||
Feature: Markdown
|
||||
As a hacker who likes to blog
|
||||
I want to be able to make a static site
|
||||
In order to share my awesome ideas with the interwebs
|
||||
|
||||
Scenario: Markdown in list on index
|
||||
Given I have a configuration file with "paginate" set to "5"
|
||||
And I have an "index.html" page that contains "Index - {% for post in site.posts %} {{ post.content }} {% endfor %}"
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content | type |
|
||||
| Hackers | 3/27/2009 | # My Title | markdown |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Index" in "_site/index.html"
|
||||
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/2009/03/27/hackers.html"
|
||||
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/index.html"
|
||||
|
||||
Scenario: Markdown in pagination on index
|
||||
Given I have a configuration file with "paginate" set to "5"
|
||||
And I have an "index.html" page that contains "Index - {% for post in paginator.posts %} {{ post.content }} {% endfor %}"
|
||||
And I have a _posts directory
|
||||
And I have the following post:
|
||||
| title | date | content | type |
|
||||
| Hackers | 3/27/2009 | # My Title | markdown |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Index" in "_site/index.html"
|
||||
And I should see "<h1 id='my_title'>My Title</h1>" in "_site/index.html"
|
||||
|
||||
27
features/pagination.feature
Normal file
27
features/pagination.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
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 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 |
|
||||
65
features/permalinks.feature
Normal file
65
features/permalinks.feature
Normal file
@@ -0,0 +1,65 @@
|
||||
Feature: Fancy permalinks
|
||||
As a hacker who likes to blog
|
||||
I want to be able to set permalinks
|
||||
In order to make my blog URLs awesome
|
||||
|
||||
Scenario: Use none permalink schema
|
||||
Given I have a _posts directory
|
||||
And I have the following post:
|
||||
| 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
|
||||
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 | 3/27/2009 | Totally wordpress. |
|
||||
And I have a configuration file with "permalink" set to "pretty"
|
||||
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"
|
||||
|
||||
Scenario: Use pretty permalink schema for pages
|
||||
Given I have an "index.html" page that contains "Totally index"
|
||||
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
|
||||
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"
|
||||
And I should see "Totally uhm, sitemap" in "_site/sitemap.xml"
|
||||
|
||||
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 | 3/27/2009 | Totally custom. |
|
||||
And I have a configuration file with "permalink" set to "/blog/:year/:month/:day/:title"
|
||||
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 | 3/27/2009 | Totally custom. |
|
||||
And I have a configuration file with "permalink" set to "/:categories/:title.html"
|
||||
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 | 3/27/2009 | Totally custom. |
|
||||
And I have a configuration file with "permalink" set to "/:month-:day-:year/:title.html"
|
||||
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"
|
||||
153
features/post_data.feature
Normal file
153
features/post_data.feature
Normal file
@@ -0,0 +1,153 @@
|
||||
Feature: Post data
|
||||
As a hacker who likes to blog
|
||||
I want to be able to embed data into my posts
|
||||
In order to make the posts slightly dynamic
|
||||
|
||||
Scenario: Use post.title 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 | 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
|
||||
Then the _site directory should exist
|
||||
And I should see "Post title: Star Wars" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Use post.url 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 | 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
|
||||
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"
|
||||
|
||||
Scenario: Use post.date 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 | 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: 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 | 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
|
||||
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"
|
||||
|
||||
Scenario: Use post.content 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 | 3/27/2009 | simple | Luke, I am your father. |
|
||||
And I have a simple layout that contains "Post content: {{ content }}"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Post content: <p>Luke, I am your father.</p>" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Use post.categories variable when category is in a folder
|
||||
Given I have a movies directory
|
||||
And I have a movies/_posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post in "movies":
|
||||
| title | date | layout | 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
|
||||
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.tags variable
|
||||
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 | 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
|
||||
Then the _site directory should exist
|
||||
And I should see "Post tags: twist" in "_site/2009/05/18/star-wars.html"
|
||||
|
||||
Scenario: Use post.categories variable when categories are in folders
|
||||
Given I have a 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 | 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
|
||||
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 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 | 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
|
||||
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
|
||||
Given I have a _posts directory
|
||||
And I have a _layouts directory
|
||||
And I have the following post:
|
||||
| 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
|
||||
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: 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 | 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"
|
||||
|
||||
Scenario: Use a custom variable
|
||||
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 | 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
|
||||
Then the _site directory should exist
|
||||
And I should see "Post author: Darth Vader" in "_site/2009/03/27/star-wars.html"
|
||||
|
||||
Scenario: Previous and next posts title
|
||||
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 | 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
|
||||
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"
|
||||
126
features/site_configuration.feature
Normal file
126
features/site_configuration.feature
Normal file
@@ -0,0 +1,126 @@
|
||||
Feature: Site configuration
|
||||
As a hacker who likes to blog
|
||||
I want to be able to configure jekyll
|
||||
In order to make setting up a site easier
|
||||
|
||||
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
|
||||
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
|
||||
Then the _mysite directory should exist
|
||||
And I should see "Changing destination directory" in "_mysite/index.html"
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
Scenario: Exclude files with YAML array
|
||||
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:
|
||||
| value |
|
||||
| README |
|
||||
| Rakefile |
|
||||
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
|
||||
|
||||
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
|
||||
Then the _site directory should exist
|
||||
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
|
||||
Then the _site directory should exist
|
||||
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
|
||||
Then the _site directory should exist
|
||||
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" 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 "puts 'Hello world!'" in "_site/index.html"
|
||||
|
||||
Scenario: Set time and no future dated posts
|
||||
Given I have a _layouts directory
|
||||
And I have a page layout that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}"
|
||||
And I have a post layout that contains "Post Layout: {{ content }}"
|
||||
And I have an "index.html" page with layout "page" that contains "site index page"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| time | 2010-01-01 |
|
||||
| future | false |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 12/31/2007 | post | content for entry1. |
|
||||
| entry2 | 01/31/2020 | post | content for entry2. |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 1 on 2010-01-01" in "_site/index.html"
|
||||
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
|
||||
And the "_site/2020/01/31/entry2.html" file should not exist
|
||||
|
||||
Scenario: Set time and future dated posts allowed
|
||||
Given I have a _layouts directory
|
||||
And I have a page layout that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}"
|
||||
And I have a post layout that contains "Post Layout: {{ content }}"
|
||||
And I have an "index.html" page with layout "page" that contains "site index page"
|
||||
And I have a configuration file with:
|
||||
| key | value |
|
||||
| time | 2010-01-01 |
|
||||
| future | true |
|
||||
And I have a _posts directory
|
||||
And I have the following posts:
|
||||
| title | date | layout | content |
|
||||
| entry1 | 12/31/2007 | post | content for entry1. |
|
||||
| entry2 | 01/31/2020 | post | content for entry2. |
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html"
|
||||
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
|
||||
And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html"
|
||||
|
||||
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 | 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
|
||||
82
features/site_data.feature
Normal file
82
features/site_data.feature
Normal file
@@ -0,0 +1,82 @@
|
||||
Feature: Site data
|
||||
As a hacker who likes to blog
|
||||
I want to be able to embed data into my site
|
||||
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@me.com"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
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
|
||||
Then the _site directory should exist
|
||||
And I should see today's time in "_site/index.html"
|
||||
|
||||
Scenario: Use site.posts variable for latest post
|
||||
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 | 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"
|
||||
|
||||
Scenario: Use site.posts variable in a loop
|
||||
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 | 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"
|
||||
|
||||
Scenario: Use site.categories.code variable
|
||||
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 | 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"
|
||||
|
||||
Scenario: Use site.tags variable
|
||||
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 | 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"
|
||||
|
||||
Scenario: Order Posts by name when on the same date
|
||||
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 | 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://mysite.com"
|
||||
When I run jekyll
|
||||
Then the _site directory should exist
|
||||
And I should see "http://mysite.com" in "_site/index.html"
|
||||
145
features/step_definitions/jekyll_steps.rb
Normal file
145
features/step_definitions/jekyll_steps.rb
Normal file
@@ -0,0 +1,145 @@
|
||||
Before do
|
||||
FileUtils.mkdir(TEST_DIR)
|
||||
Dir.chdir(TEST_DIR)
|
||||
end
|
||||
|
||||
After do
|
||||
Dir.chdir(TEST_DIR)
|
||||
FileUtils.rm_rf(TEST_DIR)
|
||||
end
|
||||
|
||||
Given /^I have a blank site in "(.*)"$/ do |path|
|
||||
FileUtils.mkdir(path)
|
||||
end
|
||||
|
||||
# Like "I have a foo file" but gives a yaml front matter so jekyll actually processes it
|
||||
Given /^I have an? "(.*)" page(?: with (.*) "(.*)")? that contains "(.*)"$/ do |file, key, value, text|
|
||||
File.open(file, 'w') do |f|
|
||||
f.write <<EOF
|
||||
---
|
||||
#{key || 'layout'}: #{value || 'nil'}
|
||||
---
|
||||
#{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 a (.*) layout that contains "(.*)"$/ do |layout, text|
|
||||
File.open(File.join('_layouts', layout + '.html'), 'w') do |f|
|
||||
f.write(text)
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have an? (.*) directory$/ do |dir|
|
||||
FileUtils.mkdir_p(dir)
|
||||
end
|
||||
|
||||
Given /^I have the following posts?(?: (.*) "(.*)")?:$/ do |direction, folder, table|
|
||||
table.hashes.each do |post|
|
||||
date = Date.strptime(post['date'], '%m/%d/%Y').strftime('%Y-%m-%d')
|
||||
title = post['title'].downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-')
|
||||
|
||||
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
|
||||
|
||||
File.open(path, 'w') do |f|
|
||||
f.write <<EOF
|
||||
---
|
||||
#{matter}
|
||||
---
|
||||
#{content}
|
||||
EOF
|
||||
f.close
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
Given /^I have a configuration file with:$/ do |table|
|
||||
File.open('_config.yml', 'w') do |f|
|
||||
table.hashes.each do |row|
|
||||
f.write("#{row["key"]}: #{row["value"]}\n")
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have a configuration file with "([^\"]*)" set to:$/ do |key, table|
|
||||
File.open('_config.yml', 'w') do |f|
|
||||
f.write("#{key}:\n")
|
||||
table.hashes.each do |row|
|
||||
f.write("- #{row["value"]}\n")
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
When /^I run jekyll$/ do
|
||||
run_jekyll
|
||||
end
|
||||
|
||||
When /^I debug jekyll$/ do
|
||||
run_jekyll(:debug => true)
|
||||
end
|
||||
|
||||
When /^I change "(.*)" to contain "(.*)"$/ do |file, text|
|
||||
File.open(file, 'a') do |f|
|
||||
f.write(text)
|
||||
end
|
||||
end
|
||||
|
||||
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), File.open(file).readlines.join
|
||||
end
|
||||
|
||||
Then /^the "(.*)" file should exist$/ do |file|
|
||||
assert File.file?(file)
|
||||
end
|
||||
|
||||
Then /^the "(.*)" file should not exist$/ do |file|
|
||||
assert !File.exists?(file)
|
||||
end
|
||||
|
||||
Then /^I should see today's time in "(.*)"$/ do |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.open(file).readlines.join
|
||||
end
|
||||
19
features/support/env.rb
Normal file
19
features/support/env.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
require 'fileutils'
|
||||
require 'rr'
|
||||
require 'test/unit'
|
||||
|
||||
World do
|
||||
include Test::Unit::Assertions
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
# work around "invalid option: --format" cucumber bug (see #296)
|
||||
Test::Unit.run = true
|
||||
180
jekyll.gemspec
180
jekyll.gemspec
@@ -1,48 +1,142 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{jekyll}
|
||||
s.version = "0.4.0"
|
||||
|
||||
s.specification_version = 2 if s.respond_to? :specification_version=
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["Tom Preston-Werner"]
|
||||
s.date = %q{2009-02-03}
|
||||
s.description = %q{Jekyll is a simple, blog aware, static site generator.}
|
||||
s.email = %q{tom@mojombo.com}
|
||||
s.files = ["History.txt", "README.textile", "VERSION.yml", "bin/jekyll", "lib/jekyll", "lib/jekyll/albino.rb", "lib/jekyll/converters", "lib/jekyll/converters/csv.rb", "lib/jekyll/converters/mephisto.rb", "lib/jekyll/converters/mt.rb", "lib/jekyll/converters/textpattern.rb", "lib/jekyll/converters/typo.rb", "lib/jekyll/converters/wordpress.rb", "lib/jekyll/convertible.rb", "lib/jekyll/core_ext.rb", "lib/jekyll/filters.rb", "lib/jekyll/layout.rb", "lib/jekyll/page.rb", "lib/jekyll/post.rb", "lib/jekyll/site.rb", "lib/jekyll/tags", "lib/jekyll/tags/highlight.rb", "lib/jekyll/tags/include.rb", "lib/jekyll.rb", "test/dest", "test/dest/2008", "test/dest/2008/10", "test/dest/2008/10/18", "test/dest/2008/10/18/foo-bar.html", "test/dest/2008/11", "test/dest/2008/11/21", "test/dest/2008/11/21/complex.html", "test/dest/2008/12", "test/dest/2008/12/13", "test/dest/2008/12/13/include.html", "test/dest/_posts", "test/dest/_posts/2008-10-18-foo-bar.html", "test/dest/_posts/2008-11-21-complex.html", "test/dest/_posts/2008-12-03-permalinked-post.html", "test/dest/_posts/2008-12-13-include.html", "test/dest/category", "test/dest/category/2008", "test/dest/category/2008/09", "test/dest/category/2008/09/23", "test/dest/category/2008/09/23/categories.html", "test/dest/category/_posts", "test/dest/category/_posts/2008-9-23-categories.html", "test/dest/css", "test/dest/css/screen.css", "test/dest/foo", "test/dest/foo/2008", "test/dest/foo/2008/12", "test/dest/foo/2008/12/12", "test/dest/foo/2008/12/12/topical-post.html", "test/dest/foo/_posts", "test/dest/foo/_posts/bar", "test/dest/foo/_posts/bar/2008-12-12-topical-post.html", "test/dest/index.html", "test/dest/my_category", "test/dest/my_category/permalinked-post", "test/dest/z_category", "test/dest/z_category/2008", "test/dest/z_category/2008/09", "test/dest/z_category/2008/09/23", "test/dest/z_category/2008/09/23/categories.html", "test/dest/z_category/_posts", "test/dest/z_category/_posts/2008-9-23-categories.html", "test/helper.rb", "test/source", "test/source/_includes", "test/source/_includes/sig.markdown", "test/source/_layouts", "test/source/_layouts/default.html", "test/source/_layouts/simple.html", "test/source/_posts", "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/category", "test/source/category/_posts", "test/source/category/_posts/2008-9-23-categories.textile", "test/source/css", "test/source/css/screen.css", "test/source/foo", "test/source/foo/_posts", "test/source/foo/_posts/bar", "test/source/foo/_posts/bar/2008-12-12-topical-post.textile", "test/source/index.html", "test/source/z_category", "test/source/z_category/_posts", "test/source/z_category/_posts/2008-9-23-categories.textile", "test/suite.rb", "test/test_filters.rb", "test/test_generated_site.rb", "test/test_jekyll.rb", "test/test_post.rb", "test/test_site.rb", "test/test_tags.rb"]
|
||||
s.has_rdoc = true
|
||||
s.homepage = %q{http://github.com/mojombo/jekyll}
|
||||
s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
|
||||
s.require_paths = ["lib"]
|
||||
s.rubyforge_project = %q{jekyll}
|
||||
s.rubygems_version = %q{1.3.0}
|
||||
s.summary = %q{Jekyll is a simple, blog aware, static site generator.}
|
||||
s.rubygems_version = '1.3.5'
|
||||
|
||||
if s.respond_to? :specification_version then
|
||||
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||
s.specification_version = 2
|
||||
s.name = 'jekyll'
|
||||
s.version = '0.10.0'
|
||||
s.date = '2010-12-16'
|
||||
s.rubyforge_project = 'jekyll'
|
||||
|
||||
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
||||
s.add_runtime_dependency(%q<RedCloth>, [">= 4.0.4"])
|
||||
s.add_runtime_dependency(%q<liquid>, [">= 1.9.0"])
|
||||
s.add_runtime_dependency(%q<classifier>, [">= 1.3.1"])
|
||||
s.add_runtime_dependency(%q<maruku>, [">= 0.5.9"])
|
||||
s.add_runtime_dependency(%q<directory_watcher>, [">= 1.1.1"])
|
||||
s.add_runtime_dependency(%q<open4>, [">= 0.9.6"])
|
||||
else
|
||||
s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
|
||||
s.add_dependency(%q<liquid>, [">= 1.9.0"])
|
||||
s.add_dependency(%q<classifier>, [">= 1.3.1"])
|
||||
s.add_dependency(%q<maruku>, [">= 0.5.9"])
|
||||
s.add_dependency(%q<directory_watcher>, [">= 1.1.1"])
|
||||
s.add_dependency(%q<open4>, [">= 0.9.6"])
|
||||
end
|
||||
else
|
||||
s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
|
||||
s.add_dependency(%q<liquid>, [">= 1.9.0"])
|
||||
s.add_dependency(%q<classifier>, [">= 1.3.1"])
|
||||
s.add_dependency(%q<maruku>, [">= 0.5.9"])
|
||||
s.add_dependency(%q<directory_watcher>, [">= 1.1.1"])
|
||||
s.add_dependency(%q<open4>, [">= 0.9.6"])
|
||||
end
|
||||
s.summary = "A simple, blog aware, static site generator."
|
||||
s.description = "Jekyll is a simple, blog aware, static site generator."
|
||||
|
||||
s.authors = ["Tom Preston-Werner"]
|
||||
s.email = 'tom@mojombo.com'
|
||||
s.homepage = 'http://github.com/mojombo/jekyll'
|
||||
|
||||
s.require_paths = %w[lib]
|
||||
|
||||
s.executables = ["jekyll"]
|
||||
s.default_executable = 'jekyll'
|
||||
|
||||
s.rdoc_options = ["--charset=UTF-8"]
|
||||
s.extra_rdoc_files = %w[README.textile LICENSE]
|
||||
|
||||
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('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
|
||||
|
||||
144
lib/jekyll.rb
144
lib/jekyll.rb
@@ -1,34 +1,30 @@
|
||||
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
||||
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
||||
|
||||
# Require all of the Ruby files in the given directory.
|
||||
#
|
||||
# path - The String relative path from here to the directory.
|
||||
#
|
||||
# Returns nothing.
|
||||
def require_all(path)
|
||||
glob = File.join(File.dirname(__FILE__), path, '*.rb')
|
||||
Dir[glob].each do |f|
|
||||
require f
|
||||
end
|
||||
end
|
||||
|
||||
# rubygems
|
||||
require 'rubygems'
|
||||
|
||||
# core
|
||||
# stdlib
|
||||
require 'fileutils'
|
||||
require 'time'
|
||||
require 'yaml'
|
||||
|
||||
# stdlib
|
||||
require 'English'
|
||||
|
||||
# 3rd party
|
||||
require 'liquid'
|
||||
require 'redcloth'
|
||||
begin
|
||||
require 'maruku'
|
||||
require 'maruku/ext/math'
|
||||
# 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] = 'blahtex'
|
||||
MaRuKu::Globals[:html_png_dir] = 'images/latex'
|
||||
MaRuKu::Globals[:html_png_url] = '/images/latex/'
|
||||
rescue LoadError
|
||||
puts "The maruku gem is required for markdown support!"
|
||||
end
|
||||
require 'maruku'
|
||||
require 'albino'
|
||||
|
||||
# internal requires
|
||||
require 'jekyll/core_ext'
|
||||
@@ -38,27 +34,93 @@ require 'jekyll/layout'
|
||||
require 'jekyll/page'
|
||||
require 'jekyll/post'
|
||||
require 'jekyll/filters'
|
||||
require 'jekyll/tags/highlight'
|
||||
require 'jekyll/tags/include'
|
||||
require 'jekyll/albino'
|
||||
require 'jekyll/static_file'
|
||||
require 'jekyll/errors'
|
||||
|
||||
# extensions
|
||||
require 'jekyll/plugin'
|
||||
require 'jekyll/converter'
|
||||
require 'jekyll/generator'
|
||||
require_all 'jekyll/converters'
|
||||
require_all 'jekyll/generators'
|
||||
require_all 'jekyll/tags'
|
||||
|
||||
module Jekyll
|
||||
VERSION = '0.3.0'
|
||||
|
||||
class << self
|
||||
attr_accessor :source, :dest, :lsi, :pygments, :markdown_proc, :content_type, :permalink_style
|
||||
end
|
||||
|
||||
Jekyll.lsi = false
|
||||
Jekyll.pygments = false
|
||||
Jekyll.markdown_proc = Proc.new { |x| Maruku.new(x).to_html }
|
||||
Jekyll.permalink_style = :date
|
||||
|
||||
def self.process(source, dest)
|
||||
require 'classifier' if Jekyll.lsi
|
||||
|
||||
Jekyll.source = source
|
||||
Jekyll.dest = dest
|
||||
Jekyll::Site.new(source, dest).process
|
||||
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::DEFAULTS for a
|
||||
# list of option names and their defaults.
|
||||
#
|
||||
# Returns the final configuration Hash.
|
||||
def self.configuration(override)
|
||||
# _config.yml may override default source location, but until
|
||||
# then, we need to know where to look for _config.yml
|
||||
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
|
||||
Jekyll::DEFAULTS.deep_merge(config).deep_merge(override)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
##
|
||||
# Wrapper for the Pygments command line tool, pygmentize.
|
||||
#
|
||||
# Pygments: http://pygments.org/
|
||||
#
|
||||
# Assumes pygmentize is in the path. If not, set its location
|
||||
# with Albino.bin = '/path/to/pygmentize'
|
||||
#
|
||||
# Use like so:
|
||||
#
|
||||
# @syntaxer = Albino.new('/some/file.rb', :ruby)
|
||||
# puts @syntaxer.colorize
|
||||
#
|
||||
# This'll print out an HTMLized, Ruby-highlighted version
|
||||
# of '/some/file.rb'.
|
||||
#
|
||||
# To use another formatter, pass it as the third argument:
|
||||
#
|
||||
# @syntaxer = Albino.new('/some/file.rb', :ruby, :bbcode)
|
||||
# puts @syntaxer.colorize
|
||||
#
|
||||
# You can also use the #colorize class method:
|
||||
#
|
||||
# puts Albino.colorize('/some/file.rb', :ruby)
|
||||
#
|
||||
# Another also: you get a #to_s, for somewhat nicer use in Rails views.
|
||||
#
|
||||
# ... helper file ...
|
||||
# def highlight(text)
|
||||
# Albino.new(text, :ruby)
|
||||
# end
|
||||
#
|
||||
# ... view file ...
|
||||
# <%= highlight text %>
|
||||
#
|
||||
# The default lexer is 'text'. You need to specify a lexer yourself;
|
||||
# because we are using STDIN there is no auto-detect.
|
||||
#
|
||||
# To see all lexers and formatters available, run `pygmentize -L`.
|
||||
#
|
||||
# Chris Wanstrath // chris@ozmm.org
|
||||
# GitHub // http://github.com
|
||||
#
|
||||
require 'open4'
|
||||
|
||||
class Albino
|
||||
@@bin = Rails.development? ? 'pygmentize' : '/usr/bin/pygmentize' rescue 'pygmentize'
|
||||
|
||||
def self.bin=(path)
|
||||
@@bin = path
|
||||
end
|
||||
|
||||
def self.colorize(*args)
|
||||
new(*args).colorize
|
||||
end
|
||||
|
||||
def initialize(target, lexer = :text, format = :html)
|
||||
@target = File.exists?(target) ? File.read(target) : target rescue target
|
||||
@options = { :l => lexer, :f => format }
|
||||
end
|
||||
|
||||
def execute(command)
|
||||
pid, stdin, stdout, stderr = Open4.popen4(command)
|
||||
stdin.puts @target
|
||||
stdin.close
|
||||
stdout.read.strip
|
||||
end
|
||||
|
||||
def colorize(options = {})
|
||||
execute @@bin + convert_options(options)
|
||||
end
|
||||
alias_method :to_s, :colorize
|
||||
|
||||
def convert_options(options = {})
|
||||
@options.merge(options).inject('') do |string, (flag, value)|
|
||||
string + " -#{flag} #{value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
require 'rubygems'
|
||||
require 'test/spec'
|
||||
require 'mocha'
|
||||
begin require 'redgreen'; rescue LoadError; end
|
||||
|
||||
context "Albino" do
|
||||
setup do
|
||||
@syntaxer = Albino.new(__FILE__, :ruby)
|
||||
end
|
||||
|
||||
specify "defaults to text" do
|
||||
syntaxer = Albino.new(__FILE__)
|
||||
syntaxer.expects(:execute).with('pygmentize -f html -l text').returns(true)
|
||||
syntaxer.colorize
|
||||
end
|
||||
|
||||
specify "accepts options" do
|
||||
@syntaxer.expects(:execute).with('pygmentize -f html -l ruby').returns(true)
|
||||
@syntaxer.colorize
|
||||
end
|
||||
|
||||
specify "works with strings" do
|
||||
syntaxer = Albino.new('class New; end', :ruby)
|
||||
assert_match %r(highlight), syntaxer.colorize
|
||||
end
|
||||
|
||||
specify "aliases to_s" do
|
||||
assert_equal @syntaxer.colorize, @syntaxer.to_s
|
||||
end
|
||||
|
||||
specify "class method colorize" do
|
||||
assert_equal @syntaxer.colorize, Albino.colorize(__FILE__, :ruby)
|
||||
end
|
||||
end
|
||||
end
|
||||
50
lib/jekyll/converter.rb
Normal file
50
lib/jekyll/converter.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
module Jekyll
|
||||
|
||||
class Converter < Plugin
|
||||
# Public: Get or set the pygments prefix. When an argument is specified,
|
||||
# the prefix will be set. If no argument is specified, the current prefix
|
||||
# will be returned.
|
||||
#
|
||||
# pygments_prefix - The String prefix (default: nil).
|
||||
#
|
||||
# Returns the String prefix.
|
||||
def self.pygments_prefix(pygments_prefix = nil)
|
||||
@pygments_prefix = pygments_prefix if pygments_prefix
|
||||
@pygments_prefix
|
||||
end
|
||||
|
||||
# Public: Get or set the pygments suffix. When an argument is specified,
|
||||
# the suffix will be set. If no argument is specified, the current suffix
|
||||
# will be returned.
|
||||
#
|
||||
# pygments_suffix - The String suffix (default: nil).
|
||||
#
|
||||
# Returns the String suffix.
|
||||
def self.pygments_suffix(pygments_suffix = nil)
|
||||
@pygments_suffix = pygments_suffix if pygments_suffix
|
||||
@pygments_suffix
|
||||
end
|
||||
|
||||
# Initialize the converter.
|
||||
#
|
||||
# Returns an initialized Converter.
|
||||
def initialize(config = {})
|
||||
@config = config
|
||||
end
|
||||
|
||||
# Get the pygments prefix.
|
||||
#
|
||||
# Returns the String prefix.
|
||||
def pygments_prefix
|
||||
self.class.pygments_prefix
|
||||
end
|
||||
|
||||
# Get the pygments suffix.
|
||||
#
|
||||
# Returns the String suffix.
|
||||
def pygments_suffix
|
||||
self.class.pygments_suffix
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
22
lib/jekyll/converters/identity.rb
Normal file
22
lib/jekyll/converters/identity.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
module Jekyll
|
||||
|
||||
class IdentityConverter < Converter
|
||||
safe true
|
||||
|
||||
priority :lowest
|
||||
|
||||
def matches(ext)
|
||||
true
|
||||
end
|
||||
|
||||
def output_ext(ext)
|
||||
ext
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
content
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
113
lib/jekyll/converters/markdown.rb
Normal file
113
lib/jekyll/converters/markdown.rb
Normal file
@@ -0,0 +1,113 @@
|
||||
module Jekyll
|
||||
|
||||
class MarkdownConverter < Converter
|
||||
safe true
|
||||
|
||||
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
|
||||
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)
|
||||
ext =~ /(markdown|mkdn?|md)/i
|
||||
end
|
||||
|
||||
def output_ext(ext)
|
||||
".html"
|
||||
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'],
|
||||
|
||||
: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
|
||||
33
lib/jekyll/converters/textile.rb
Normal file
33
lib/jekyll/converters/textile.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
module Jekyll
|
||||
|
||||
class TextileConverter < Converter
|
||||
safe true
|
||||
|
||||
pygments_prefix '<notextile>'
|
||||
pygments_suffix '</notextile>'
|
||||
|
||||
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 matches(ext)
|
||||
ext =~ /textile/i
|
||||
end
|
||||
|
||||
def output_ext(ext)
|
||||
".html"
|
||||
end
|
||||
|
||||
def convert(content)
|
||||
setup
|
||||
RedCloth.new(content).to_html
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,69 +1,100 @@
|
||||
# Convertible provides methods for converting a pagelike item
|
||||
# from a certain type of markup into actual content
|
||||
#
|
||||
# Requires
|
||||
# self.site -> Jekyll::Site
|
||||
# self.content
|
||||
# self.content=
|
||||
# self.data=
|
||||
# self.ext=
|
||||
# self.output=
|
||||
module Jekyll
|
||||
module Convertible
|
||||
# Return the contents as a string
|
||||
# Returns the contents as a String.
|
||||
def to_s
|
||||
self.content || ''
|
||||
end
|
||||
|
||||
# Read the YAML frontmatter
|
||||
# +base+ is the String path to the dir containing the file
|
||||
# +name+ is the String filename of the file
|
||||
|
||||
# Read the YAML frontmatter.
|
||||
#
|
||||
# Returns nothing
|
||||
# base - The String path to the dir containing the file.
|
||||
# name - The String filename of the file.
|
||||
#
|
||||
# Returns nothing.
|
||||
def read_yaml(base, name)
|
||||
self.content = File.read(File.join(base, name))
|
||||
|
||||
if self.content =~ /^(---\s*\n.*?)\n---\s*\n/m
|
||||
self.content = self.content[($1.size + 5)..-1]
|
||||
|
||||
self.data = YAML.load($1)
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
self.data ||= {}
|
||||
end
|
||||
|
||||
# Transform the contents based on the file extension.
|
||||
|
||||
# Transform the contents based on the content type.
|
||||
#
|
||||
# Returns nothing
|
||||
# Returns nothing.
|
||||
def transform
|
||||
case Jekyll.content_type
|
||||
when :textile
|
||||
self.ext = ".html"
|
||||
self.content = RedCloth.new(self.content).to_html
|
||||
when :markdown
|
||||
self.ext = ".html"
|
||||
self.content = Jekyll.markdown_proc.call(self.content)
|
||||
end
|
||||
self.content = converter.convert(self.content)
|
||||
end
|
||||
|
||||
def determine_content_type
|
||||
case self.ext[1..-1]
|
||||
when /textile/i
|
||||
return :textile
|
||||
when /markdown/i, /mkdn/i, /md/i
|
||||
return :markdown
|
||||
end
|
||||
return :unknown
|
||||
end
|
||||
|
||||
# Add any necessary layouts to this convertible document
|
||||
# +layouts+ is a Hash of {"name" => "layout"}
|
||||
# +site_payload+ is the site payload hash
|
||||
|
||||
# Determine the extension depending on content_type.
|
||||
#
|
||||
# Returns nothing
|
||||
# Returns the String extension for the output file.
|
||||
# e.g. ".html" for an HTML output file.
|
||||
def output_ext
|
||||
converter.output_ext(self.ext)
|
||||
end
|
||||
|
||||
# Determine which converter to use based on this convertible's
|
||||
# extension.
|
||||
#
|
||||
# Returns the Converter instance.
|
||||
def converter
|
||||
@converter ||= self.site.converters.find { |c| c.matches(self.ext) }
|
||||
end
|
||||
|
||||
# Add any necessary layouts to this convertible document.
|
||||
#
|
||||
# payload - The site payload Hash.
|
||||
# layouts - A Hash of {"name" => "layout"}.
|
||||
#
|
||||
# Returns nothing.
|
||||
def do_layout(payload, layouts)
|
||||
info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } }
|
||||
|
||||
# render and transform content (this becomes the final content of the object)
|
||||
Jekyll.content_type = self.determine_content_type
|
||||
self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters])
|
||||
payload["pygments_prefix"] = converter.pygments_prefix
|
||||
payload["pygments_suffix"] = converter.pygments_suffix
|
||||
|
||||
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 = self.content
|
||||
|
||||
|
||||
# recursively render layouts
|
||||
layout = layouts[self.data["layout"]]
|
||||
while layout
|
||||
payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
|
||||
self.output = Liquid::Template.parse(layout.content).render(payload, [Jekyll::Filters])
|
||||
|
||||
|
||||
begin
|
||||
self.output = Liquid::Template.parse(layout.content).render(payload, info)
|
||||
rescue => e
|
||||
puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
|
||||
end
|
||||
|
||||
layout = layouts[layout.data["layout"]]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,22 +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
|
||||
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
|
||||
|
||||
6
lib/jekyll/errors.rb
Normal file
6
lib/jekyll/errors.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Jekyll
|
||||
|
||||
class FatalException < StandardError
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,6 +1,12 @@
|
||||
require 'uri'
|
||||
|
||||
module Jekyll
|
||||
|
||||
|
||||
module Filters
|
||||
def textilize(input)
|
||||
TextileConverter.new.convert(input)
|
||||
end
|
||||
|
||||
def date_to_string(date)
|
||||
date.strftime("%d %b %Y")
|
||||
end
|
||||
@@ -8,19 +14,27 @@ module Jekyll
|
||||
def date_to_long_string(date)
|
||||
date.strftime("%d %B %Y")
|
||||
end
|
||||
|
||||
|
||||
def date_to_xmlschema(date)
|
||||
date.xmlschema
|
||||
end
|
||||
|
||||
|
||||
def xml_escape(input)
|
||||
input.gsub("&", "&").gsub("<", "<").gsub(">", ">")
|
||||
CGI.escapeHTML(input)
|
||||
end
|
||||
|
||||
|
||||
def cgi_escape(input)
|
||||
CGI::escape(input)
|
||||
end
|
||||
|
||||
def uri_escape(input)
|
||||
URI.escape(input)
|
||||
end
|
||||
|
||||
def number_of_words(input)
|
||||
input.split.length
|
||||
end
|
||||
|
||||
|
||||
def array_to_sentence_string(array)
|
||||
connector = "and"
|
||||
case array.length
|
||||
@@ -35,5 +49,5 @@ module Jekyll
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
7
lib/jekyll/generator.rb
Normal file
7
lib/jekyll/generator.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Jekyll
|
||||
|
||||
class Generator < Plugin
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
87
lib/jekyll/generators/pagination.rb
Normal file
87
lib/jekyll/generators/pagination.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
module Jekyll
|
||||
|
||||
class Pagination < Generator
|
||||
safe true
|
||||
|
||||
def generate(site)
|
||||
site.pages.dup.each do |page|
|
||||
paginate(site, page) if Pager.pagination_enabled?(site.config, page.name)
|
||||
end
|
||||
end
|
||||
|
||||
# Paginates the blog's posts. Renders the index.html file into paginated
|
||||
# directories, ie: page2/index.html, page3/index.html, etc and adds more
|
||||
# site-wide data.
|
||||
# +page+ is the index.html Page that requires pagination
|
||||
#
|
||||
# {"paginator" => { "page" => <Number>,
|
||||
# "per_page" => <Number>,
|
||||
# "posts" => [<Post>],
|
||||
# "total_posts" => <Number>,
|
||||
# "total_pages" => <Number>,
|
||||
# "previous_page" => <Number>,
|
||||
# "next_page" => <Number> }}
|
||||
def paginate(site, page)
|
||||
all_posts = site.site_payload['site']['posts']
|
||||
pages = Pager.calculate_pages(all_posts, site.config['paginate'].to_i)
|
||||
(1..pages).each do |num_page|
|
||||
pager = Pager.new(site.config, num_page, all_posts, pages)
|
||||
if num_page > 1
|
||||
newpage = Page.new(site, site.source, page.dir, page.name)
|
||||
newpage.pager = pager
|
||||
newpage.dir = File.join(page.dir, "page#{num_page}")
|
||||
site.pages << newpage
|
||||
else
|
||||
page.pager = pager
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Pager
|
||||
attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page
|
||||
|
||||
def self.calculate_pages(all_posts, per_page)
|
||||
num_pages = all_posts.size / per_page.to_i
|
||||
num_pages = num_pages + 1 if all_posts.size % per_page.to_i != 0
|
||||
num_pages
|
||||
end
|
||||
|
||||
def self.pagination_enabled?(config, file)
|
||||
file == 'index.html' && !config['paginate'].nil?
|
||||
end
|
||||
|
||||
def initialize(config, page, all_posts, num_pages = nil)
|
||||
@page = page
|
||||
@per_page = config['paginate'].to_i
|
||||
@total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
|
||||
|
||||
if @page > @total_pages
|
||||
raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
|
||||
end
|
||||
|
||||
init = (@page - 1) * @per_page
|
||||
offset = (init + @per_page - 1) >= all_posts.size ? all_posts.size : (init + @per_page - 1)
|
||||
|
||||
@total_posts = all_posts.size
|
||||
@posts = all_posts[init..offset]
|
||||
@previous_page = @page != 1 ? @page - 1 : nil
|
||||
@next_page = @page != @total_pages ? @page + 1 : nil
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{
|
||||
'page' => page,
|
||||
'per_page' => per_page,
|
||||
'posts' => posts,
|
||||
'total_posts' => total_posts,
|
||||
'total_pages' => total_pages,
|
||||
'previous_page' => previous_page,
|
||||
'next_page' => next_page
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -2,25 +2,28 @@ module Jekyll
|
||||
|
||||
class Layout
|
||||
include Convertible
|
||||
|
||||
|
||||
attr_accessor :site
|
||||
attr_accessor :ext
|
||||
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
|
||||
#
|
||||
# Returns <Page>
|
||||
def initialize(base, name)
|
||||
def initialize(site, base, name)
|
||||
@site = site
|
||||
@base = base
|
||||
@name = name
|
||||
|
||||
|
||||
self.data = {}
|
||||
|
||||
|
||||
self.process(name)
|
||||
self.read_yaml(base, name)
|
||||
end
|
||||
|
||||
|
||||
# Extract information from the layout filename
|
||||
# +name+ is the String filename of the layout file
|
||||
#
|
||||
|
||||
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
|
||||
@@ -13,7 +13,7 @@ require File.join(File.dirname(__FILE__),"csv.rb")
|
||||
# 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
|
||||
@@ -38,24 +38,24 @@ module Jekyll
|
||||
# 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)
|
||||
|
||||
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.
|
||||
@@ -66,14 +66,14 @@ module Jekyll
|
||||
'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
|
||||
@@ -5,55 +5,73 @@
|
||||
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_created_on, entry_title FROM mt_entry"
|
||||
|
||||
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)
|
||||
|
||||
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]
|
||||
date = post[:entry_created_on]
|
||||
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('-') + ".markdown"
|
||||
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
|
||||
@@ -17,21 +17,21 @@ module Jekyll
|
||||
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)
|
||||
|
||||
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
|
||||
# to YAML for the header
|
||||
data = {
|
||||
'layout' => 'post',
|
||||
'title' => title.to_s,
|
||||
@@ -2,27 +2,27 @@
|
||||
require 'fileutils'
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
|
||||
|
||||
module Jekyll
|
||||
module Typo
|
||||
# this SQL *should* work for both MySQL and PostgreSQL, but I haven't
|
||||
# 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.title title,
|
||||
c.permalink slug,
|
||||
c.body body,
|
||||
c.published_at date,
|
||||
c.published_at date,
|
||||
c.state state,
|
||||
COALESCE(tf.name, 'html') filter
|
||||
FROM contents c
|
||||
FROM contents c
|
||||
LEFT OUTER JOIN text_filters tf
|
||||
ON c.text_filter_id = tf.id
|
||||
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
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
||||
db[SQL].each do |post|
|
||||
next unless post[:state] =~ /Published/
|
||||
|
||||
@@ -30,7 +30,7 @@ module Jekyll
|
||||
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
|
||||
# Can have more than one text filter in this field, but we just want
|
||||
# the first one for this
|
||||
name += '.' + post[:filter].split(' ')[0]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
require 'fileutils'
|
||||
require 'yaml'
|
||||
|
||||
# NOTE: This converter requires Sequel and the MySQL gems.
|
||||
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
||||
@@ -10,35 +11,36 @@ require 'fileutils'
|
||||
|
||||
module Jekyll
|
||||
module WordPress
|
||||
|
||||
# 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 * from wp_posts where post_status = 'publish' and post_type = 'post'"
|
||||
|
||||
def self.process(dbname, user, pass, host = 'localhost')
|
||||
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
|
||||
|
||||
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"
|
||||
|
||||
db[QUERY].each do |post|
|
||||
|
||||
# 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 = [date.year, date.month, date.day, slug].join('-') + ".markdown"
|
||||
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
|
||||
# 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]
|
||||
'wordpress_url' => post[:guid],
|
||||
'date' => date
|
||||
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
||||
|
||||
# Write out the data and content to file
|
||||
@@ -51,4 +53,4 @@ module Jekyll
|
||||
|
||||
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
|
||||
@@ -2,63 +2,142 @@ module Jekyll
|
||||
|
||||
class Page
|
||||
include Convertible
|
||||
|
||||
attr_accessor :ext
|
||||
|
||||
attr_accessor :site, :pager
|
||||
attr_accessor :name, :ext, :basename, :dir
|
||||
attr_accessor :data, :content, :output
|
||||
|
||||
|
||||
# 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
|
||||
#
|
||||
# Returns <Page>
|
||||
def initialize(base, dir, name)
|
||||
def initialize(site, base, dir, name)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@dir = dir
|
||||
@name = name
|
||||
|
||||
self.data = {}
|
||||
|
||||
|
||||
self.process(name)
|
||||
self.read_yaml(File.join(base, dir), name)
|
||||
#self.transform
|
||||
end
|
||||
|
||||
|
||||
# The generated directory into which the page will be placed
|
||||
# upon generation. This is derived from the permalink or, if
|
||||
# permalink is absent, set to '/'
|
||||
#
|
||||
# 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
|
||||
# (Optional)
|
||||
#
|
||||
# Returns <String>
|
||||
def permalink
|
||||
self.data && self.data['permalink']
|
||||
end
|
||||
|
||||
def template
|
||||
if self.site.permalink_style == :pretty && !index? && html?
|
||||
"/:basename/"
|
||||
else
|
||||
"/:basename:output_ext"
|
||||
end
|
||||
end
|
||||
|
||||
# The generated relative url of this page
|
||||
# e.g. /about.html
|
||||
#
|
||||
# Returns <String>
|
||||
def url
|
||||
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
|
||||
|
||||
# Extract information from the page filename
|
||||
# +name+ is the String filename of the page file
|
||||
#
|
||||
# Returns nothing
|
||||
def process(name)
|
||||
self.ext = File.extname(name)
|
||||
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
|
||||
#
|
||||
# Returns nothing
|
||||
def render(layouts, site_payload)
|
||||
payload = {"page" => self.data}.deep_merge(site_payload)
|
||||
payload = {
|
||||
"page" => self.to_liquid,
|
||||
'paginator' => pager.to_liquid
|
||||
}.deep_merge(site_payload)
|
||||
|
||||
do_layout(payload, layouts)
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
self.data.deep_merge({
|
||||
"url" => File.join(@dir, self.url),
|
||||
"content" => self.content })
|
||||
end
|
||||
|
||||
# Obtain destination path.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns destination file path.
|
||||
def destination(dest)
|
||||
# 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
|
||||
|
||||
# Write the generated page file to the destination directory.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns nothing
|
||||
def write(dest)
|
||||
FileUtils.mkdir_p(File.join(dest, @dir))
|
||||
|
||||
name = @name
|
||||
if self.ext != ""
|
||||
name = @name.split(".")[0..-2].join('.') + self.ext
|
||||
end
|
||||
|
||||
path = File.join(dest, @dir, name)
|
||||
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
|
||||
|
||||
def html?
|
||||
output_ext == '.html'
|
||||
end
|
||||
|
||||
def index?
|
||||
basename == 'index'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
76
lib/jekyll/plugin.rb
Normal file
76
lib/jekyll/plugin.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
module Jekyll
|
||||
|
||||
class Plugin
|
||||
PRIORITIES = { :lowest => -100,
|
||||
:low => -10,
|
||||
:normal => 0,
|
||||
:high => 10,
|
||||
:highest => 100 }
|
||||
|
||||
# Install a hook so that subclasses are recorded. This method is only
|
||||
# ever called by Ruby itself.
|
||||
#
|
||||
# base - The Class subclass.
|
||||
#
|
||||
# Returns nothing.
|
||||
def self.inherited(base)
|
||||
subclasses << base
|
||||
subclasses.sort!
|
||||
end
|
||||
|
||||
# The list of Classes that have been subclassed.
|
||||
#
|
||||
# Returns an Array of Class objects.
|
||||
def self.subclasses
|
||||
@subclasses ||= []
|
||||
end
|
||||
|
||||
# Get or set the priority of this plugin. When called without an
|
||||
# argument it returns the priority. When an argument is given, it will
|
||||
# set the priority.
|
||||
#
|
||||
# priority - The Symbol priority (default: nil). Valid options are:
|
||||
# :lowest, :low, :normal, :high, :highest
|
||||
#
|
||||
# Returns the Symbol priority.
|
||||
def self.priority(priority = nil)
|
||||
if priority && PRIORITIES.has_key?(priority)
|
||||
@priority = priority
|
||||
end
|
||||
@priority || :normal
|
||||
end
|
||||
|
||||
# Get or set the safety of this plugin. When called without an argument
|
||||
# it returns the safety. When an argument is given, it will set the
|
||||
# safety.
|
||||
#
|
||||
# safe - The Boolean safety (default: nil).
|
||||
#
|
||||
# Returns the safety Boolean.
|
||||
def self.safe(safe = nil)
|
||||
if safe
|
||||
@safe = safe
|
||||
end
|
||||
@safe || false
|
||||
end
|
||||
|
||||
# Spaceship is priority [higher -> lower]
|
||||
#
|
||||
# other - The class to be compared.
|
||||
#
|
||||
# Returns -1, 0, 1.
|
||||
def self.<=>(other)
|
||||
PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
|
||||
end
|
||||
|
||||
# Initialize a new plugin. This should be overridden by the subclass.
|
||||
#
|
||||
# config - The Hash of configuration options.
|
||||
#
|
||||
# Returns a new instance.
|
||||
def initialize(config = {})
|
||||
# no-op for default
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3,13 +3,13 @@ module Jekyll
|
||||
class Post
|
||||
include Comparable
|
||||
include Convertible
|
||||
|
||||
|
||||
class << self
|
||||
attr_accessor :lsi
|
||||
end
|
||||
|
||||
|
||||
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
||||
|
||||
|
||||
# Post name validator. Post filenames must be like:
|
||||
# 2008-11-05-my-awesome-post.textile
|
||||
#
|
||||
@@ -17,36 +17,58 @@ module Jekyll
|
||||
def self.valid?(name)
|
||||
name =~ MATCHER
|
||||
end
|
||||
|
||||
attr_accessor :date, :slug, :ext, :categories, :topics
|
||||
attr_accessor :data, :content, :output
|
||||
|
||||
|
||||
attr_accessor :site
|
||||
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
|
||||
#
|
||||
# Returns <Post>
|
||||
def initialize(source, dir, name)
|
||||
def initialize(site, source, dir, name)
|
||||
@site = site
|
||||
@base = File.join(source, dir, '_posts')
|
||||
@name = name
|
||||
|
||||
|
||||
self.categories = dir.split('/').reject { |x| x.empty? }
|
||||
|
||||
parts = name.split('/')
|
||||
self.topics = parts.size > 1 ? parts[0..-2] : []
|
||||
|
||||
self.process(name)
|
||||
self.read_yaml(@base, name)
|
||||
|
||||
#If we've added a date and time to the yaml, use that instead of the filename date
|
||||
#Means we'll sort correctly.
|
||||
if self.data.has_key?('date')
|
||||
# ensure Time via to_s and reparse
|
||||
self.date = Time.parse(self.data["date"].to_s)
|
||||
end
|
||||
|
||||
if self.data.has_key?('published') && self.data['published'] == false
|
||||
self.published = false
|
||||
else
|
||||
self.published = true
|
||||
end
|
||||
|
||||
self.tags = self.data.pluralized_array("tag", "tags")
|
||||
|
||||
if self.categories.empty?
|
||||
self.categories = self.data.pluralized_array('category', 'categories')
|
||||
end
|
||||
end
|
||||
|
||||
# Spaceship is based on Post#date
|
||||
|
||||
# Spaceship is based on Post#date, slug
|
||||
#
|
||||
# Returns -1, 0, 1
|
||||
def <=>(other)
|
||||
self.date <=> other.date
|
||||
cmp = self.date <=> other.date
|
||||
if 0 == cmp
|
||||
cmp = self.slug <=> other.slug
|
||||
end
|
||||
return cmp
|
||||
end
|
||||
|
||||
|
||||
# Extract information from the post filename
|
||||
# +name+ is the String filename of the post file
|
||||
#
|
||||
@@ -56,8 +78,10 @@ module Jekyll
|
||||
self.date = Time.parse(date)
|
||||
self.slug = slug
|
||||
self.ext = ext
|
||||
rescue ArgumentError
|
||||
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
|
||||
@@ -65,18 +89,9 @@ module Jekyll
|
||||
#
|
||||
# Returns <String>
|
||||
def dir
|
||||
if permalink
|
||||
permalink.to_s.split("/")[0..-2].join("/") + '/'
|
||||
else
|
||||
prefix = self.categories.empty? ? '' : '/' + self.categories.join('/')
|
||||
if Jekyll.permalink_style == :date
|
||||
prefix + date.strftime("/%Y/%m/%d/")
|
||||
else
|
||||
prefix + '/'
|
||||
end
|
||||
end
|
||||
File.dirname(url)
|
||||
end
|
||||
|
||||
|
||||
# The full path and filename of the post.
|
||||
# Defined in the YAML of the post body
|
||||
# (Optional)
|
||||
@@ -85,30 +100,65 @@ module Jekyll
|
||||
def permalink
|
||||
self.data && self.data['permalink']
|
||||
end
|
||||
|
||||
|
||||
def template
|
||||
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"
|
||||
else
|
||||
self.site.permalink_style.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# The generated relative url of this post
|
||||
# e.g. /2008/11/05/my-awesome-post.html
|
||||
#
|
||||
# Returns <String>
|
||||
def url
|
||||
permalink || self.dir + self.slug + ".html"
|
||||
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
|
||||
|
||||
|
||||
# The UID for this post (useful in feeds)
|
||||
# e.g. /2008/11/05/my-awesome-post
|
||||
#
|
||||
# Returns <String>
|
||||
def id
|
||||
self.dir + self.slug
|
||||
File.join(self.dir, self.slug)
|
||||
end
|
||||
|
||||
|
||||
# Calculate related posts.
|
||||
#
|
||||
# Returns [<Post>]
|
||||
def related_posts(posts)
|
||||
return [] unless posts.size > 1
|
||||
|
||||
if Jekyll.lsi
|
||||
|
||||
if self.site.lsi
|
||||
self.class.lsi ||= begin
|
||||
puts "Running the classifier... this could take a while."
|
||||
lsi = Classifier::LSI.new
|
||||
@@ -123,7 +173,7 @@ module Jekyll
|
||||
(posts - [self])[0..9]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Add any necessary layouts to this post
|
||||
# +layouts+ is a Hash of {"name" => "layout"}
|
||||
# +site_payload+ is the site payload hash
|
||||
@@ -131,44 +181,75 @@ module Jekyll
|
||||
# Returns nothing
|
||||
def render(layouts, site_payload)
|
||||
# construct payload
|
||||
payload =
|
||||
{
|
||||
payload = {
|
||||
"site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
|
||||
"page" => self.to_liquid
|
||||
}
|
||||
payload = payload.deep_merge(site_payload)
|
||||
|
||||
}.deep_merge(site_payload)
|
||||
|
||||
do_layout(payload, layouts)
|
||||
end
|
||||
|
||||
# Obtain destination path.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns destination file path.
|
||||
def destination(dest)
|
||||
# The url needs to be unescaped in order to preserve the correct filename
|
||||
path = File.join(dest, CGI.unescape(self.url))
|
||||
path = File.join(path, "index.html") if template[/\.html$/].nil?
|
||||
path
|
||||
end
|
||||
|
||||
# Write the generated post file to the destination directory.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns nothing
|
||||
def write(dest)
|
||||
FileUtils.mkdir_p(File.join(dest, dir))
|
||||
|
||||
path = File.join(dest, self.url)
|
||||
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
|
||||
{ "title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
|
||||
"url" => self.url,
|
||||
"date" => self.date,
|
||||
"id" => self.id,
|
||||
"topics" => self.topics,
|
||||
"content" => self.content }.deep_merge(self.data)
|
||||
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: #{self.id}>"
|
||||
end
|
||||
|
||||
def next
|
||||
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 = self.site.posts.index(self)
|
||||
if pos && pos > 0
|
||||
self.site.posts[pos-1]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,136 +1,233 @@
|
||||
require 'set'
|
||||
|
||||
module Jekyll
|
||||
|
||||
|
||||
class Site
|
||||
attr_accessor :source, :dest
|
||||
attr_accessor :layouts, :posts, :categories
|
||||
|
||||
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
||||
:categories, :exclude, :source, :dest, :lsi, :pygments,
|
||||
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts
|
||||
|
||||
attr_accessor :converters, :generators
|
||||
|
||||
# Initialize the site
|
||||
# +source+ is String path to the source directory containing
|
||||
# the proto-site
|
||||
# +dest+ is the String path to the directory where the generated
|
||||
# site should be written
|
||||
# +config+ is a Hash containing site configurations details
|
||||
#
|
||||
# Returns <Site>
|
||||
def initialize(source, dest)
|
||||
self.source = source
|
||||
self.dest = dest
|
||||
self.layouts = {}
|
||||
self.posts = []
|
||||
self.categories = Hash.new { |hash, key| hash[key] = Array.new }
|
||||
def initialize(config)
|
||||
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.reset
|
||||
self.setup
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
# real deal. 5 phases; reset, read, generate, render, write. This allows
|
||||
# rendering to have full site payload available.
|
||||
#
|
||||
# Returns nothing
|
||||
def process
|
||||
self.read_layouts
|
||||
self.transform_pages
|
||||
self.write_posts
|
||||
self.reset
|
||||
self.read
|
||||
self.generate
|
||||
self.render
|
||||
self.cleanup
|
||||
self.write
|
||||
end
|
||||
|
||||
# Read all the files in <source>/_layouts except backup files
|
||||
# (end with "~") into memory for later use.
|
||||
|
||||
def read
|
||||
self.read_layouts # existing implementation did this at top level only so preserved that
|
||||
self.read_directories
|
||||
end
|
||||
|
||||
# Read all the files in <source>/<dir>/_layouts and create a new Layout
|
||||
# object with each one.
|
||||
#
|
||||
# Returns nothing
|
||||
def read_layouts
|
||||
base = File.join(self.source, "_layouts")
|
||||
entries = Dir.entries(base)
|
||||
entries = entries.reject { |e| e[-1..-1] == '~' }
|
||||
entries = entries.reject { |e| File.directory?(File.join(base, e)) }
|
||||
|
||||
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|
|
||||
name = f.split(".")[0..-2].join(".")
|
||||
self.layouts[name] = Layout.new(base, f)
|
||||
self.layouts[name] = Layout.new(self, base, f)
|
||||
end
|
||||
rescue Errno::ENOENT => e
|
||||
# ignore missing layout dir
|
||||
end
|
||||
|
||||
# Read all the files in <base>/_posts except backup files (end with "~")
|
||||
# and create a new Post object with each one.
|
||||
|
||||
# Read all the files in <source>/<dir>/_posts and create a new Post
|
||||
# object with each one.
|
||||
#
|
||||
# Returns nothing
|
||||
def read_posts(dir)
|
||||
base = File.join(self.source, dir, '_posts')
|
||||
|
||||
entries = []
|
||||
Dir.chdir(base) { entries = Dir['**/*'] }
|
||||
entries = entries.reject { |e| e[-1..-1] == '~' }
|
||||
entries = entries.reject { |e| File.directory?(File.join(base, e)) }
|
||||
return unless File.exists?(base)
|
||||
entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
|
||||
|
||||
# first pass processes, but does not yet render post content
|
||||
entries.each do |f|
|
||||
if Post.valid?(f)
|
||||
post = Post.new(self.source, dir, f)
|
||||
self.posts << post
|
||||
post.categories.each { |c| self.categories[c] << post }
|
||||
post = Post.new(self, self.source, dir, f)
|
||||
|
||||
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
|
||||
|
||||
# second pass renders each post now that full site payload is available
|
||||
|
||||
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 generate
|
||||
self.generators.each do |generator|
|
||||
generator.generate(self)
|
||||
end
|
||||
end
|
||||
|
||||
def render
|
||||
self.posts.each do |post|
|
||||
post.render(self.layouts, site_payload)
|
||||
end
|
||||
|
||||
self.posts.sort!
|
||||
self.categories.values.map { |cats| cats.sort! { |a, b| b <=> a} }
|
||||
|
||||
self.pages.each do |page|
|
||||
page.render(self.layouts, site_payload)
|
||||
end
|
||||
|
||||
self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} }
|
||||
self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a} }
|
||||
rescue Errno::ENOENT => e
|
||||
# ignore missing layout dir
|
||||
end
|
||||
|
||||
# Write each post to <dest>/<year>/<month>/<day>/<slug>
|
||||
# Remove orphaned files and empty directories in destination
|
||||
#
|
||||
# Returns nothing
|
||||
def write_posts
|
||||
def 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
|
||||
#
|
||||
# Returns nothing
|
||||
def write
|
||||
self.posts.each do |post|
|
||||
post.write(self.dest)
|
||||
end
|
||||
self.pages.each do |page|
|
||||
page.write(self.dest)
|
||||
end
|
||||
self.static_files.each do |sf|
|
||||
sf.write(self.dest)
|
||||
end
|
||||
end
|
||||
|
||||
# Copy all regular files from <source> to <dest>/ ignoring
|
||||
# any files/directories that are hidden or backup files (start
|
||||
# with "." or end with "~") or contain site content (start with "_")
|
||||
# unless they are "_posts" directories or web server files such as
|
||||
# '.htaccess'
|
||||
|
||||
# Reads the directories and finds posts, pages and static files that will
|
||||
# become part of the valid site according to the rules in +filter_entries+.
|
||||
# The +dir+ String is a relative path used to call this method
|
||||
# recursively as it descends through directories
|
||||
#
|
||||
# Returns nothing
|
||||
def transform_pages(dir = '')
|
||||
def read_directories(dir = '')
|
||||
base = File.join(self.source, dir)
|
||||
entries = Dir.entries(base)
|
||||
entries = entries.reject { |e| e[-1..-1] == '~' }
|
||||
entries = entries.reject do |e|
|
||||
(e != '_posts') and ['.', '_'].include?(e[0..0]) unless ['.htaccess'].include?(e)
|
||||
end
|
||||
directories = entries.select { |e| File.directory?(File.join(base, e)) }
|
||||
files = entries.reject { |e| File.directory?(File.join(base, e)) }
|
||||
entries = Dir.chdir(base){ filter_entries(Dir['*']) }
|
||||
|
||||
# we need to make sure to process _posts *first* otherwise they
|
||||
# might not be available yet to other templates as {{ site.posts }}
|
||||
if entries.include?('_posts')
|
||||
entries.delete('_posts')
|
||||
read_posts(dir)
|
||||
end
|
||||
[directories, files].each do |entries|
|
||||
entries.each do |f|
|
||||
if File.directory?(File.join(base, f))
|
||||
next if self.dest.sub(/\/$/, '') == File.join(base, f)
|
||||
transform_pages(File.join(dir, f))
|
||||
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
|
||||
first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
|
||||
|
||||
if first3 == "---"
|
||||
# file appears to have a YAML header so process it as a page
|
||||
page = Page.new(self.source, dir, f)
|
||||
page.render(self.layouts, site_payload)
|
||||
page.write(self.dest)
|
||||
else
|
||||
# otherwise copy the file without transforming it
|
||||
FileUtils.mkdir_p(File.join(self.dest, dir))
|
||||
FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
|
||||
end
|
||||
# otherwise treat it as a static file
|
||||
static_files << StaticFile.new(self, self.source, dir, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -152,16 +249,32 @@ module Jekyll
|
||||
#
|
||||
# Returns {"site" => {"time" => <Time>,
|
||||
# "posts" => [<Post>],
|
||||
# "categories" => [<Post>],
|
||||
# "topics" => [<Post>] }}
|
||||
# "pages" => [<Page>],
|
||||
# "categories" => [<Post>]}
|
||||
def site_payload
|
||||
{"site" => {
|
||||
"time" => Time.now,
|
||||
"posts" => self.posts.sort { |a,b| b <=> a },
|
||||
"categories" => post_attr_hash('categories'),
|
||||
"topics" => post_attr_hash('topics')
|
||||
}}
|
||||
{"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
|
||||
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'
|
||||
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
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
75
lib/jekyll/static_file.rb
Normal file
75
lib/jekyll/static_file.rb
Normal file
@@ -0,0 +1,75 @@
|
||||
module Jekyll
|
||||
|
||||
class StaticFile
|
||||
@@mtimes = Hash.new # the cache of last modification times [path] -> mtime
|
||||
|
||||
# Initialize a new StaticFile.
|
||||
# +site+ is the Site
|
||||
# +base+ is the String path to the <source>
|
||||
# +dir+ is the String path between <source> and the file
|
||||
# +name+ is the String filename of the file
|
||||
#
|
||||
# Returns <StaticFile>
|
||||
def initialize(site, base, dir, name)
|
||||
@site = site
|
||||
@base = base
|
||||
@dir = dir
|
||||
@name = name
|
||||
end
|
||||
|
||||
# Obtains source file path.
|
||||
#
|
||||
# Returns source file path.
|
||||
def path
|
||||
File.join(@base, @dir, @name)
|
||||
end
|
||||
|
||||
# Obtain destination path.
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns destination file path.
|
||||
def destination(dest)
|
||||
File.join(dest, @dir, @name)
|
||||
end
|
||||
|
||||
# Obtain mtime of the source path.
|
||||
#
|
||||
# Returns last modifiaction time for this file.
|
||||
def mtime
|
||||
File.stat(path).mtime.to_i
|
||||
end
|
||||
|
||||
# Is source path modified?
|
||||
#
|
||||
# Returns true if modified since last write.
|
||||
def modified?
|
||||
@@mtimes[path] != mtime
|
||||
end
|
||||
|
||||
# Write the static file to the destination directory (if modified).
|
||||
# +dest+ is the String path to the destination dir
|
||||
#
|
||||
# Returns false if the file was not modified since last time (no-op).
|
||||
def write(dest)
|
||||
dest_path = destination(dest)
|
||||
|
||||
return false if File.exist? dest_path and !modified?
|
||||
@@mtimes[path] = mtime
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(dest_path))
|
||||
FileUtils.cp(path, dest_path)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Reset the mtimes cache (for testing purposes).
|
||||
#
|
||||
# Returns nothing.
|
||||
def self.reset_cache
|
||||
@@mtimes = Hash.new
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,17 +1,31 @@
|
||||
module Jekyll
|
||||
|
||||
|
||||
class HighlightBlock < Liquid::Block
|
||||
include Liquid::StandardFilters
|
||||
|
||||
# we need a language, but the linenos argument is optional.
|
||||
SYNTAX = /(\w+)\s?(:?linenos)?\s?/
|
||||
|
||||
SYNTAX = /(\w+)\s?([\w\s=]+)*/
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
super
|
||||
if markup =~ SYNTAX
|
||||
@lang = $1
|
||||
if defined? $2
|
||||
tmp_options = {}
|
||||
$2.split.each do |opt|
|
||||
key, value = opt.split('=')
|
||||
if value.nil?
|
||||
if key == 'linenos'
|
||||
value = 'inline'
|
||||
else
|
||||
value = true
|
||||
end
|
||||
end
|
||||
tmp_options[key] = value
|
||||
end
|
||||
tmp_options = tmp_options.to_a.collect { |opt| opt.join('=') }
|
||||
# additional options to pass to Albino.
|
||||
@options = { 'O' => 'linenos=inline' }
|
||||
@options = { 'O' => tmp_options.join(',') }
|
||||
else
|
||||
@options = {}
|
||||
end
|
||||
@@ -19,23 +33,22 @@ module Jekyll
|
||||
raise SyntaxError.new("Syntax Error in 'highlight' - Valid syntax: highlight <lang> [linenos]")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def render(context)
|
||||
if Jekyll.pygments
|
||||
render_pygments(context, super.to_s)
|
||||
if context.registers[:site].pygments
|
||||
render_pygments(context, super.join)
|
||||
else
|
||||
render_codehighlighter(context, super.to_s)
|
||||
render_codehighlighter(context, super.join)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def render_pygments(context, code)
|
||||
if Jekyll.content_type == :markdown
|
||||
return "\n" + Albino.new(code, @lang).to_s(@options) + "\n"
|
||||
else
|
||||
"<notextile>" + Albino.new(code, @lang).to_s(@options) + "</notextile>"
|
||||
end
|
||||
output = add_code_tags(Albino.new(code, @lang).to_s(@options), @lang)
|
||||
output = context["pygments_prefix"] + output if context["pygments_prefix"]
|
||||
output = output + context["pygments_suffix"] if context["pygments_suffix"]
|
||||
output
|
||||
end
|
||||
|
||||
|
||||
def render_codehighlighter(context, code)
|
||||
#The div is required because RDiscount blows ass
|
||||
<<-HTML
|
||||
@@ -46,8 +59,15 @@ module Jekyll
|
||||
</div>
|
||||
HTML
|
||||
end
|
||||
|
||||
def add_code_tags(code, lang)
|
||||
# Add nested <code> tags to code blocks
|
||||
code = code.sub(/<pre>/,'<pre><code class="' + lang + '">')
|
||||
code = code.sub(/<\/pre>/,"</code></pre>")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('highlight', Jekyll::HighlightBlock)
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
module Jekyll
|
||||
|
||||
|
||||
class IncludeTag < Liquid::Tag
|
||||
def initialize(tag_name, file, tokens)
|
||||
super
|
||||
@file = file.strip
|
||||
end
|
||||
|
||||
|
||||
def render(context)
|
||||
includes_dir = File.join(context.registers[:site].source, '_includes')
|
||||
|
||||
if File.symlink?(includes_dir)
|
||||
return "Includes directory '#{includes_dir}' cannot be a symlink"
|
||||
end
|
||||
|
||||
if @file !~ /^[a-zA-Z0-9_\/\.-]+$/ || @file =~ /\.\// || @file =~ /\/\./
|
||||
return "Include file '#{@file}' contains invalid characters or sequences"
|
||||
end
|
||||
|
||||
Dir.chdir(File.join(Jekyll.source, '_includes')) do
|
||||
|
||||
Dir.chdir(includes_dir) do
|
||||
choices = Dir['**/*'].reject { |x| File.symlink?(x) }
|
||||
if choices.include?(@file)
|
||||
source = File.read(@file)
|
||||
@@ -25,7 +31,7 @@ module Jekyll
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('include', Jekyll::IncludeTag)
|
||||
|
||||
@@ -1,13 +1,34 @@
|
||||
require 'rubygems'
|
||||
gem 'RedCloth', '>= 4.2.1'
|
||||
|
||||
require File.join(File.dirname(__FILE__), *%w[.. lib jekyll])
|
||||
|
||||
require 'RedCloth'
|
||||
require 'rdiscount'
|
||||
require 'kramdown'
|
||||
|
||||
require 'test/unit'
|
||||
require 'redgreen'
|
||||
require 'shoulda'
|
||||
require 'rr'
|
||||
|
||||
include Jekyll
|
||||
|
||||
def dest_dir
|
||||
File.join(File.dirname(__FILE__), *%w[dest])
|
||||
end
|
||||
# Send STDERR into the void to suppress program output messages
|
||||
STDERR.reopen(test(?e, '/dev/null') ? '/dev/null' : 'NUL:')
|
||||
|
||||
def clear_dest
|
||||
FileUtils.rm_rf(dest_dir)
|
||||
end
|
||||
class Test::Unit::TestCase
|
||||
include RR::Adapters::TestUnit
|
||||
|
||||
def dest_dir(*subdirs)
|
||||
File.join(File.dirname(__FILE__), 'dest', *subdirs)
|
||||
end
|
||||
|
||||
def source_dir(*subdirs)
|
||||
File.join(File.dirname(__FILE__), 'source', *subdirs)
|
||||
end
|
||||
|
||||
def clear_dest
|
||||
FileUtils.rm_rf(dest_dir)
|
||||
end
|
||||
end
|
||||
|
||||
8
test/source/.htaccess
Normal file
8
test/source/.htaccess
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
layout: nil
|
||||
---
|
||||
ErrorDocument 404 /404.html
|
||||
ErrorDocument 500 /500.html
|
||||
{% for post in site.posts %}
|
||||
# {{ post.url }}
|
||||
{% endfor %}
|
||||
8
test/source/_posts/2008-02-02-not-published.textile
Normal file
8
test/source/_posts/2008-02-02-not-published.textile
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
layout: default
|
||||
title: Not published!
|
||||
published: false
|
||||
category: publish_test
|
||||
---
|
||||
|
||||
This should *not* be published!
|
||||
8
test/source/_posts/2008-02-02-published.textile
Normal file
8
test/source/_posts/2008-02-02-published.textile
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
layout: default
|
||||
title: Publish
|
||||
category: publish_test
|
||||
---
|
||||
|
||||
This should be published.
|
||||
|
||||
10
test/source/_posts/2009-01-27-array-categories.textile
Normal file
10
test/source/_posts/2009-01-27-array-categories.textile
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
layout: default
|
||||
title: Array categories in YAML
|
||||
categories:
|
||||
- foo
|
||||
- bar
|
||||
- baz
|
||||
---
|
||||
|
||||
Best *post* ever
|
||||
7
test/source/_posts/2009-01-27-categories.textile
Normal file
7
test/source/_posts/2009-01-27-categories.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
title: Categories in YAML
|
||||
categories: foo bar baz
|
||||
---
|
||||
|
||||
Best *post* ever
|
||||
7
test/source/_posts/2009-01-27-category.textile
Normal file
7
test/source/_posts/2009-01-27-category.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
title: Category in YAML
|
||||
category: foo
|
||||
---
|
||||
|
||||
Best *post* ever
|
||||
7
test/source/_posts/2009-01-27-empty-categories.textile
Normal file
7
test/source/_posts/2009-01-27-empty-categories.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
title: Category in YAML
|
||||
categories:
|
||||
---
|
||||
|
||||
Best *post* ever
|
||||
7
test/source/_posts/2009-01-27-empty-category.textile
Normal file
7
test/source/_posts/2009-01-27-empty-category.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
title: Category in YAML
|
||||
category:
|
||||
---
|
||||
|
||||
Best *post* ever
|
||||
6
test/source/_posts/2009-03-12-hash-#1.markdown
Normal file
6
test/source/_posts/2009-03-12-hash-#1.markdown
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
layout: default
|
||||
title: Hash #1
|
||||
---
|
||||
|
||||
Hashes are nice
|
||||
6
test/source/_posts/2009-05-18-empty-tag.textile
Normal file
6
test/source/_posts/2009-05-18-empty-tag.textile
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: A Tag
|
||||
tag:
|
||||
---
|
||||
|
||||
Whoa.
|
||||
6
test/source/_posts/2009-05-18-empty-tags.textile
Normal file
6
test/source/_posts/2009-05-18-empty-tags.textile
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Some Tags
|
||||
tags:
|
||||
---
|
||||
|
||||
Awesome!
|
||||
6
test/source/_posts/2009-05-18-tag.textile
Normal file
6
test/source/_posts/2009-05-18-tag.textile
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: A Tag
|
||||
tag: code
|
||||
---
|
||||
|
||||
Whoa.
|
||||
9
test/source/_posts/2009-05-18-tags.textile
Normal file
9
test/source/_posts/2009-05-18-tags.textile
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Some Tags
|
||||
tags:
|
||||
- food
|
||||
- cooking
|
||||
- pizza
|
||||
---
|
||||
|
||||
Awesome!
|
||||
3
test/source/_posts/2009-06-22-empty-yaml.textile
Normal file
3
test/source/_posts/2009-06-22-empty-yaml.textile
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
---
|
||||
Empty YAML.
|
||||
1
test/source/_posts/2009-06-22-no-yaml.textile
Normal file
1
test/source/_posts/2009-06-22-no-yaml.textile
Normal file
@@ -0,0 +1 @@
|
||||
No YAML.
|
||||
5
test/source/_posts/2010-01-08-triple-dash.markdown
Normal file
5
test/source/_posts/2010-01-08-triple-dash.markdown
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Foo --- Bar
|
||||
---
|
||||
|
||||
Triple the fun!
|
||||
7
test/source/_posts/2010-01-09-date-override.textile
Normal file
7
test/source/_posts/2010-01-09-date-override.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
date: 2010-01-10
|
||||
---
|
||||
|
||||
Post with a front matter date
|
||||
|
||||
{{ page.date | date_to_string }}
|
||||
7
test/source/_posts/2010-01-09-time-override.textile
Normal file
7
test/source/_posts/2010-01-09-time-override.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
date: 2010-01-10 13:07:09
|
||||
---
|
||||
|
||||
Post with a front matter time
|
||||
|
||||
{{ page.date | date_to_string }}
|
||||
7
test/source/_posts/2010-01-09-timezone-override.textile
Normal file
7
test/source/_posts/2010-01-09-timezone-override.textile
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
date: 2010-01-10 13:07:09 +00:00
|
||||
---
|
||||
|
||||
Post with a front matter time with timezone
|
||||
|
||||
{{ page.date | date_to_string }}
|
||||
4
test/source/_posts/2010-01-16-override-data.textile
Normal file
4
test/source/_posts/2010-01-16-override-data.textile
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
date: 2010-01-10 13:07:09
|
||||
tags: A string
|
||||
---
|
||||
6
test/source/about.html
Normal file
6
test/source/about.html
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: About
|
||||
permalink: /about/
|
||||
---
|
||||
|
||||
About the site
|
||||
5
test/source/contacts.html
Normal file
5
test/source/contacts.html
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Contact Information
|
||||
---
|
||||
|
||||
Contact Information
|
||||
7
test/source/deal.with.dots.html
Normal file
7
test/source/deal.with.dots.html
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Deal with dots
|
||||
permalink: /deal.with.dots/
|
||||
---
|
||||
|
||||
Let's test if jekyll deals properly with dots.
|
||||
|
||||
32
test/source/sitemap.xml
Normal file
32
test/source/sitemap.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: nil
|
||||
---
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
|
||||
<url>
|
||||
<loc>http://example.com</loc>
|
||||
<lastmod>{{ site.time | date: "%Y-%m-%d" }}</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
|
||||
{% for post in site.posts %}
|
||||
<url>
|
||||
<loc>http://example.com{{ post.url }}/</loc>
|
||||
<lastmod>{{ post.date | date: "%Y-%m-%d" }}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.2</priority>
|
||||
</url>
|
||||
{% endfor %}
|
||||
|
||||
{% for page in site.html_pages %}
|
||||
<url>
|
||||
<loc>http://example.com{{ page.url }}</loc>
|
||||
<lastmod>{{ site.time | date: "%Y-%m-%d" }}</lastmod>
|
||||
{% if page.changefreq %}<changefreq>{{ page.changefreq }}</changefreq>{% endif %}
|
||||
{% if page.priority %}<priority>{{ page.priority }}</priority>{% endif %}
|
||||
</url>
|
||||
{% endfor %}
|
||||
</urlset>
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Test title"
|
||||
tag: "Ruby"
|
||||
---
|
||||
|
||||
This is the content
|
||||
29
test/test_configuration.rb
Normal file
29
test/test_configuration.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestConfiguration < Test::Unit::TestCase
|
||||
context "loading configuration" do
|
||||
setup do
|
||||
@path = File.join(Dir.pwd, '_config.yml')
|
||||
end
|
||||
|
||||
should "fire warning with no _config.yml" do
|
||||
mock(YAML).load_file(@path) { raise "No such file or directory - #{@path}" }
|
||||
mock($stderr).puts("WARNING: Could not read configuration. Using defaults (and options).")
|
||||
mock($stderr).puts("\tNo such file or directory - #{@path}")
|
||||
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
|
||||
end
|
||||
|
||||
should "load configuration as hash" do
|
||||
mock(YAML).load_file(@path) { Hash.new }
|
||||
mock($stdout).puts("Configuration from #{@path}")
|
||||
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
|
||||
end
|
||||
|
||||
should "fire warning with bad config" do
|
||||
mock(YAML).load_file(@path) { Array.new }
|
||||
mock($stderr).puts("WARNING: Could not read configuration. Using defaults (and options).")
|
||||
mock($stderr).puts("\tInvalid configuration - #{@path}")
|
||||
assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
|
||||
end
|
||||
end
|
||||
end
|
||||
66
test/test_core_ext.rb
Normal file
66
test/test_core_ext.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestCoreExt < Test::Unit::TestCase
|
||||
context "hash" do
|
||||
|
||||
context "pluralized_array" do
|
||||
|
||||
should "return empty array with no values" do
|
||||
data = {}
|
||||
assert_equal [], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return empty array with no matching values" do
|
||||
data = { 'foo' => 'bar' }
|
||||
assert_equal [], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return empty array with matching nil singular" do
|
||||
data = { 'foo' => 'bar', 'tag' => nil, 'tags' => ['dog', 'cat'] }
|
||||
assert_equal [], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return single value array with matching singular" do
|
||||
data = { 'foo' => 'bar', 'tag' => 'dog', 'tags' => ['dog', 'cat'] }
|
||||
assert_equal ['dog'], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return single value array with matching singular with spaces" do
|
||||
data = { 'foo' => 'bar', 'tag' => 'dog cat', 'tags' => ['dog', 'cat'] }
|
||||
assert_equal ['dog cat'], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return empty array with matching nil plural" do
|
||||
data = { 'foo' => 'bar', 'tags' => nil }
|
||||
assert_equal [], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return empty array with matching empty array" do
|
||||
data = { 'foo' => 'bar', 'tags' => [] }
|
||||
assert_equal [], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return single value array with matching plural with single string value" do
|
||||
data = { 'foo' => 'bar', 'tags' => 'dog' }
|
||||
assert_equal ['dog'], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return multiple value array with matching plural with single string value with spaces" do
|
||||
data = { 'foo' => 'bar', 'tags' => 'dog cat' }
|
||||
assert_equal ['dog', 'cat'], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return single value array with matching plural with single value array" do
|
||||
data = { 'foo' => 'bar', 'tags' => ['dog'] }
|
||||
assert_equal ['dog'], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
should "return multiple value array with matching plural with multiple value array" do
|
||||
data = { 'foo' => 'bar', 'tags' => ['dog', 'cat'] }
|
||||
assert_equal ['dog', 'cat'], data.pluralized_array('tag', 'tags')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,37 +1,53 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestFilters < Test::Unit::TestCase
|
||||
|
||||
class JekyllFilter
|
||||
include Jekyll::Filters
|
||||
end
|
||||
|
||||
def setup
|
||||
@filter = JekyllFilter.new
|
||||
end
|
||||
|
||||
def test_array_to_sentence_string_with_no_args
|
||||
assert_equal "", @filter.array_to_sentence_string([])
|
||||
end
|
||||
context "filters" do
|
||||
setup do
|
||||
@filter = JekyllFilter.new
|
||||
end
|
||||
|
||||
def test_array_to_sentence_string_with_one_arg
|
||||
assert_equal "1", @filter.array_to_sentence_string([1])
|
||||
assert_equal "chunky", @filter.array_to_sentence_string(["chunky"])
|
||||
should "textilize with simple string" do
|
||||
assert_equal "<p>something <strong>really</strong> simple</p>", @filter.textilize("something *really* simple")
|
||||
end
|
||||
|
||||
should "convert array to sentence string with no args" do
|
||||
assert_equal "", @filter.array_to_sentence_string([])
|
||||
end
|
||||
|
||||
should "convert array to sentence string with one arg" do
|
||||
assert_equal "1", @filter.array_to_sentence_string([1])
|
||||
assert_equal "chunky", @filter.array_to_sentence_string(["chunky"])
|
||||
end
|
||||
|
||||
should "convert array to sentence string with two args" do
|
||||
assert_equal "1 and 2", @filter.array_to_sentence_string([1, 2])
|
||||
assert_equal "chunky and bacon", @filter.array_to_sentence_string(["chunky", "bacon"])
|
||||
end
|
||||
|
||||
should "convert array to sentence string with multiple args" do
|
||||
assert_equal "1, 2, 3, and 4", @filter.array_to_sentence_string([1, 2, 3, 4])
|
||||
assert_equal "chunky, bacon, bits, and pieces", @filter.array_to_sentence_string(["chunky", "bacon", "bits", "pieces"])
|
||||
end
|
||||
|
||||
should "escape xml with ampersands" do
|
||||
assert_equal "AT&T", @filter.xml_escape("AT&T")
|
||||
assert_equal "<code>command &lt;filename&gt;</code>", @filter.xml_escape("<code>command <filename></code>")
|
||||
end
|
||||
|
||||
should "escape space as plus" do
|
||||
assert_equal "my+things", @filter.cgi_escape("my things")
|
||||
end
|
||||
|
||||
should "escape special characters" do
|
||||
assert_equal "hey%21", @filter.cgi_escape("hey!")
|
||||
end
|
||||
|
||||
should "escape space as %20" do
|
||||
assert_equal "my%20things", @filter.uri_escape("my things")
|
||||
end
|
||||
end
|
||||
|
||||
def test_array_to_sentence_string_with_two_args
|
||||
assert_equal "1 and 2", @filter.array_to_sentence_string([1, 2])
|
||||
assert_equal "chunky and bacon", @filter.array_to_sentence_string(["chunky", "bacon"])
|
||||
end
|
||||
|
||||
def test_array_to_sentence_string_with_multiple_args
|
||||
assert_equal "1, 2, 3, and 4", @filter.array_to_sentence_string([1, 2, 3, 4])
|
||||
assert_equal "chunky, bacon, bits, and pieces", @filter.array_to_sentence_string(["chunky", "bacon", "bits", "pieces"])
|
||||
end
|
||||
|
||||
def test_xml_escape_with_ampersands
|
||||
assert_equal "AT&T", @filter.xml_escape("AT&T")
|
||||
assert_equal "<code>command &lt;filename&gt;</code>", @filter.xml_escape("<code>command <filename></code>")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,22 +1,72 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestGeneratedSite < Test::Unit::TestCase
|
||||
def setup
|
||||
clear_dest
|
||||
source = File.join(File.dirname(__FILE__), *%w[source])
|
||||
@s = Site.new(source, dest_dir)
|
||||
@s.process
|
||||
@index = File.read(File.join(dest_dir, 'index.html'))
|
||||
end
|
||||
|
||||
def test_site_posts_in_index
|
||||
# confirm that {{ site.posts }} is working
|
||||
puts @s.posts.size
|
||||
assert @index.include?("#{@s.posts.size} Posts")
|
||||
context "generated sites" do
|
||||
setup do
|
||||
clear_dest
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir})
|
||||
end
|
||||
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
@site.process
|
||||
@index = File.read(dest_dir('index.html'))
|
||||
end
|
||||
|
||||
should "ensure post count is as expected" do
|
||||
assert_equal 26, @site.posts.size
|
||||
end
|
||||
|
||||
should "insert site.posts into the index" do
|
||||
assert @index.include?("#{@site.posts.size} Posts")
|
||||
end
|
||||
|
||||
should "render latest post's content" do
|
||||
assert @index.include?(@site.posts.last.content)
|
||||
end
|
||||
|
||||
should "hide unpublished posts" do
|
||||
published = Dir[dest_dir('publish_test/2008/02/02/*.html')].map {|f| File.basename(f)}
|
||||
|
||||
assert_equal 1, published.size
|
||||
assert_equal "published.html", published.first
|
||||
end
|
||||
|
||||
should "not copy _posts directory" do
|
||||
assert !File.exist?(dest_dir('_posts'))
|
||||
end
|
||||
|
||||
should "process other static files and generate correct permalinks" do
|
||||
assert File.exists?(dest_dir('/about/index.html'))
|
||||
assert File.exists?(dest_dir('/contacts.html'))
|
||||
end
|
||||
end
|
||||
|
||||
def test_post_content_in_index
|
||||
# confirm that the {{ post.content }} is rendered OK
|
||||
assert @index.include?('<p>This <em>is</em> cool</p>')
|
||||
context "generating limited posts" do
|
||||
setup do
|
||||
clear_dest
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'limit_posts' => 5})
|
||||
end
|
||||
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
@site.process
|
||||
@index = File.read(dest_dir('index.html'))
|
||||
end
|
||||
|
||||
should "generate only the specified number of posts" do
|
||||
assert_equal 5, @site.posts.size
|
||||
end
|
||||
|
||||
should "ensure limit posts is 1 or more" do
|
||||
assert_raise ArgumentError do
|
||||
clear_dest
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'limit_posts' => 0})
|
||||
end
|
||||
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
23
test/test_kramdown.rb
Normal file
23
test/test_kramdown.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestKramdown < Test::Unit::TestCase
|
||||
context "kramdown" do
|
||||
setup do
|
||||
config = {
|
||||
'markdown' => 'kramdown',
|
||||
'kramdown' => {
|
||||
'auto_ids' => false,
|
||||
'footnote_nr' => 1,
|
||||
'entity_output' => 'as_char',
|
||||
'toc_levels' => '1..6'
|
||||
}
|
||||
}
|
||||
@markdown = MarkdownConverter.new config
|
||||
end
|
||||
|
||||
# http://kramdown.rubyforge.org/converter/html.html#options
|
||||
should "pass kramdown options" do
|
||||
assert_equal "<h1>Some Header</h1>", @markdown.convert('# Some Header #').strip
|
||||
end
|
||||
end
|
||||
end
|
||||
117
test/test_page.rb
Normal file
117
test/test_page.rb
Normal file
@@ -0,0 +1,117 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestPage < Test::Unit::TestCase
|
||||
def setup_page(file)
|
||||
@page = Page.new(@site, source_dir, '', file)
|
||||
end
|
||||
|
||||
def do_render(page)
|
||||
layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")}
|
||||
page.render(layouts, {"site" => {"posts" => []}})
|
||||
end
|
||||
|
||||
context "A Page" do
|
||||
setup do
|
||||
clear_dest
|
||||
stub(Jekyll).configuration { Jekyll::DEFAULTS }
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
end
|
||||
|
||||
context "processing pages" do
|
||||
should "create url based on filename" do
|
||||
@page = setup_page('contacts.html')
|
||||
assert_equal "/contacts.html", @page.url
|
||||
end
|
||||
|
||||
should "deal properly with extensions" do
|
||||
@page = setup_page('deal.with.dots.html')
|
||||
assert_equal ".html", @page.ext
|
||||
end
|
||||
|
||||
should "deal properly with dots" do
|
||||
@page = setup_page('deal.with.dots.html')
|
||||
assert_equal "deal.with.dots", @page.basename
|
||||
end
|
||||
|
||||
context "with pretty url style" do
|
||||
setup do
|
||||
@site.permalink_style = :pretty
|
||||
end
|
||||
|
||||
should "return dir correctly" do
|
||||
@page = setup_page('contacts.html')
|
||||
assert_equal '/contacts/', @page.dir
|
||||
end
|
||||
|
||||
should "return dir correctly for index page" do
|
||||
@page = setup_page('index.html')
|
||||
assert_equal '/', @page.dir
|
||||
end
|
||||
end
|
||||
|
||||
context "with any other url style" do
|
||||
should "return dir correctly" do
|
||||
@site.permalink_style = nil
|
||||
@page = setup_page('contacts.html')
|
||||
assert_equal '/', @page.dir
|
||||
end
|
||||
end
|
||||
|
||||
should "respect permalink in yaml front matter" do
|
||||
file = "about.html"
|
||||
@page = setup_page(file)
|
||||
|
||||
assert_equal "/about/", @page.permalink
|
||||
assert_equal @page.permalink, @page.url
|
||||
assert_equal "/about/", @page.dir
|
||||
end
|
||||
end
|
||||
|
||||
context "rendering" do
|
||||
setup do
|
||||
clear_dest
|
||||
end
|
||||
|
||||
should "write properly" do
|
||||
page = setup_page('contacts.html')
|
||||
do_render(page)
|
||||
page.write(dest_dir)
|
||||
|
||||
assert File.directory?(dest_dir)
|
||||
assert File.exists?(File.join(dest_dir, 'contacts.html'))
|
||||
end
|
||||
|
||||
should "write properly without html extension" do
|
||||
page = setup_page('contacts.html')
|
||||
page.site.permalink_style = :pretty
|
||||
do_render(page)
|
||||
page.write(dest_dir)
|
||||
|
||||
assert File.directory?(dest_dir)
|
||||
assert File.exists?(File.join(dest_dir, 'contacts', 'index.html'))
|
||||
end
|
||||
|
||||
should "write properly with extension different from html" do
|
||||
page = setup_page("sitemap.xml")
|
||||
page.site.permalink_style = :pretty
|
||||
do_render(page)
|
||||
page.write(dest_dir)
|
||||
|
||||
assert_equal("/sitemap.xml", page.url)
|
||||
assert_nil(page.url[/\.html$/])
|
||||
assert File.directory?(dest_dir)
|
||||
assert File.exists?(File.join(dest_dir,'sitemap.xml'))
|
||||
end
|
||||
|
||||
should "write dotfiles properly" do
|
||||
page = setup_page('.htaccess')
|
||||
do_render(page)
|
||||
page.write(dest_dir)
|
||||
|
||||
assert File.directory?(dest_dir)
|
||||
assert File.exists?(File.join(dest_dir, '.htaccess'))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
113
test/test_pager.rb
Normal file
113
test/test_pager.rb
Normal file
@@ -0,0 +1,113 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestPager < Test::Unit::TestCase
|
||||
|
||||
should "calculate number of pages" do
|
||||
assert_equal(0, Pager.calculate_pages([], '2'))
|
||||
assert_equal(1, Pager.calculate_pages([1], '2'))
|
||||
assert_equal(1, Pager.calculate_pages([1,2], '2'))
|
||||
assert_equal(2, Pager.calculate_pages([1,2,3], '2'))
|
||||
assert_equal(2, Pager.calculate_pages([1,2,3,4], '2'))
|
||||
assert_equal(3, Pager.calculate_pages([1,2,3,4,5], '2'))
|
||||
end
|
||||
|
||||
context "pagination disabled" do
|
||||
setup do
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({
|
||||
'source' => source_dir,
|
||||
'destination' => dest_dir
|
||||
})
|
||||
end
|
||||
@config = Jekyll.configuration
|
||||
end
|
||||
|
||||
should "report that pagination is disabled" do
|
||||
assert !Pager.pagination_enabled?(@config, 'index.html')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "pagination enabled for 2" do
|
||||
setup do
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({
|
||||
'source' => source_dir,
|
||||
'destination' => dest_dir,
|
||||
'paginate' => 2
|
||||
})
|
||||
end
|
||||
|
||||
@config = Jekyll.configuration
|
||||
@site = Site.new(@config)
|
||||
@site.process
|
||||
@posts = @site.posts
|
||||
end
|
||||
|
||||
should "report that pagination is enabled" do
|
||||
assert Pager.pagination_enabled?(@config, 'index.html')
|
||||
end
|
||||
|
||||
context "with 4 posts" do
|
||||
setup do
|
||||
@posts = @site.posts[1..4] # limit to 4
|
||||
end
|
||||
|
||||
should "create first pager" do
|
||||
pager = Pager.new(@config, 1, @posts)
|
||||
assert_equal(2, pager.posts.size)
|
||||
assert_equal(2, pager.total_pages)
|
||||
assert_nil(pager.previous_page)
|
||||
assert_equal(2, pager.next_page)
|
||||
end
|
||||
|
||||
should "create second pager" do
|
||||
pager = Pager.new(@config, 2, @posts)
|
||||
assert_equal(2, pager.posts.size)
|
||||
assert_equal(2, pager.total_pages)
|
||||
assert_equal(1, pager.previous_page)
|
||||
assert_nil(pager.next_page)
|
||||
end
|
||||
|
||||
should "not create third pager" do
|
||||
assert_raise(RuntimeError) { Pager.new(@config, 3, @posts) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "with 5 posts" do
|
||||
setup do
|
||||
@posts = @site.posts[1..5] # limit to 5
|
||||
end
|
||||
|
||||
should "create first pager" do
|
||||
pager = Pager.new(@config, 1, @posts)
|
||||
assert_equal(2, pager.posts.size)
|
||||
assert_equal(3, pager.total_pages)
|
||||
assert_nil(pager.previous_page)
|
||||
assert_equal(2, pager.next_page)
|
||||
end
|
||||
|
||||
should "create second pager" do
|
||||
pager = Pager.new(@config, 2, @posts)
|
||||
assert_equal(2, pager.posts.size)
|
||||
assert_equal(3, pager.total_pages)
|
||||
assert_equal(1, pager.previous_page)
|
||||
assert_equal(3, pager.next_page)
|
||||
end
|
||||
|
||||
should "create third pager" do
|
||||
pager = Pager.new(@config, 3, @posts)
|
||||
assert_equal(1, pager.posts.size)
|
||||
assert_equal(3, pager.total_pages)
|
||||
assert_equal(2, pager.previous_page)
|
||||
assert_nil(pager.next_page)
|
||||
end
|
||||
|
||||
should "not create fourth pager" do
|
||||
assert_raise(RuntimeError) { Pager.new(@config, 4, @posts) }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,113 +1,402 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestPost < Test::Unit::TestCase
|
||||
def setup
|
||||
|
||||
end
|
||||
|
||||
def test_valid
|
||||
assert Post.valid?("2008-10-19-foo-bar.textile")
|
||||
assert Post.valid?("foo/bar/2008-10-19-foo-bar.textile")
|
||||
|
||||
assert !Post.valid?("lol2008-10-19-foo-bar.textile")
|
||||
assert !Post.valid?("blah")
|
||||
end
|
||||
|
||||
def test_process
|
||||
p = Post.allocate
|
||||
p.process("2008-10-19-foo-bar.textile")
|
||||
|
||||
assert_equal Time.parse("2008-10-19"), p.date
|
||||
assert_equal "foo-bar", p.slug
|
||||
assert_equal ".textile", p.ext
|
||||
end
|
||||
|
||||
def test_url
|
||||
p = Post.allocate
|
||||
p.categories = []
|
||||
p.process("2008-10-19-foo-bar.textile")
|
||||
|
||||
assert_equal "/2008/10/19/foo-bar.html", p.url
|
||||
end
|
||||
|
||||
def test_permalink
|
||||
p = Post.allocate
|
||||
p.process("2008-12-03-permalinked-post.textile")
|
||||
p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-12-03-permalinked-post.textile")
|
||||
|
||||
assert_equal "my_category/permalinked-post", p.permalink
|
||||
def setup_post(file)
|
||||
Post.new(@site, source_dir, '', file)
|
||||
end
|
||||
|
||||
def test_dir_respects_permalink
|
||||
p = Post.allocate
|
||||
p.process("2008-12-03-permalinked-post.textile")
|
||||
p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-12-03-permalinked-post.textile")
|
||||
|
||||
assert_equal "my_category/", p.dir
|
||||
end
|
||||
|
||||
def test_url_respects_permalink
|
||||
p = Post.allocate
|
||||
p.process("2008-12-03-permalinked-post.textile")
|
||||
p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-12-03-permalinked-post.textile")
|
||||
|
||||
assert_equal "my_category/permalinked-post", p.url
|
||||
def do_render(post)
|
||||
layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")}
|
||||
post.render(layouts, {"site" => {"posts" => []}})
|
||||
end
|
||||
|
||||
def test_read_yaml
|
||||
p = Post.allocate
|
||||
p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile")
|
||||
|
||||
assert_equal({"title" => "Foo Bar", "layout" => "default"}, p.data)
|
||||
assert_equal "\nh1. {{ page.title }}\n\nBest *post* ever", p.content
|
||||
context "A Post" do
|
||||
setup do
|
||||
clear_dest
|
||||
stub(Jekyll).configuration { Jekyll::DEFAULTS }
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
end
|
||||
|
||||
should "ensure valid posts are valid" do
|
||||
assert Post.valid?("2008-09-09-foo-bar.textile")
|
||||
assert Post.valid?("foo/bar/2008-09-09-foo-bar.textile")
|
||||
|
||||
assert !Post.valid?("lol2008-09-09-foo-bar.textile")
|
||||
assert !Post.valid?("blah")
|
||||
end
|
||||
|
||||
context "processing posts" do
|
||||
setup do
|
||||
@post = Post.allocate
|
||||
@post.site = @site
|
||||
|
||||
@real_file = "2008-10-18-foo-bar.textile"
|
||||
@fake_file = "2008-09-09-foo-bar.textile"
|
||||
@source = source_dir('_posts')
|
||||
end
|
||||
|
||||
should "keep date, title, and markup type" do
|
||||
@post.categories = []
|
||||
@post.process(@fake_file)
|
||||
|
||||
assert_equal Time.parse("2008-09-09"), @post.date
|
||||
assert_equal "foo-bar", @post.slug
|
||||
assert_equal ".textile", @post.ext
|
||||
assert_equal "/2008/09/09", @post.dir
|
||||
assert_equal "/2008/09/09/foo-bar", @post.id
|
||||
end
|
||||
|
||||
should "create url based on date and title" do
|
||||
@post.categories = []
|
||||
@post.process(@fake_file)
|
||||
assert_equal "/2008/09/09/foo-bar.html", @post.url
|
||||
end
|
||||
|
||||
should "raise a good error on invalid post date" do
|
||||
assert_raise Jekyll::FatalException do
|
||||
@post.process("2009-27-03-foo-bar.textile")
|
||||
end
|
||||
end
|
||||
|
||||
should "CGI escape urls" do
|
||||
@post.categories = []
|
||||
@post.process("2009-03-12-hash-#1.markdown")
|
||||
assert_equal "/2009/03/12/hash-%231.html", @post.url
|
||||
assert_equal "/2009/03/12/hash-#1", @post.id
|
||||
end
|
||||
|
||||
should "respect permalink in yaml front matter" do
|
||||
file = "2008-12-03-permalinked-post.textile"
|
||||
@post.process(file)
|
||||
@post.read_yaml(@source, file)
|
||||
|
||||
assert_equal "my_category/permalinked-post", @post.permalink
|
||||
assert_equal "my_category", @post.dir
|
||||
assert_equal "my_category/permalinked-post", @post.url
|
||||
end
|
||||
|
||||
context "with CRLF linebreaks" do
|
||||
setup do
|
||||
@real_file = "2009-05-24-yaml-linebreak.markdown"
|
||||
@source = source_dir('win/_posts')
|
||||
end
|
||||
should "read yaml front-matter" do
|
||||
@post.read_yaml(@source, @real_file)
|
||||
|
||||
assert_equal({"title" => "Test title", "layout" => "post", "tag" => "Ruby"}, @post.data)
|
||||
assert_equal "This is the content", @post.content
|
||||
end
|
||||
end
|
||||
|
||||
context "with embedded triple dash" do
|
||||
setup do
|
||||
@real_file = "2010-01-08-triple-dash.markdown"
|
||||
end
|
||||
should "consume the embedded dashes" do
|
||||
@post.read_yaml(@source, @real_file)
|
||||
|
||||
assert_equal({"title" => "Foo --- Bar"}, @post.data)
|
||||
assert_equal "Triple the fun!", @post.content
|
||||
end
|
||||
end
|
||||
|
||||
context "with site wide permalink" do
|
||||
setup do
|
||||
@post.categories = []
|
||||
end
|
||||
|
||||
context "with unspecified (date) style" do
|
||||
setup do
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
|
||||
assert_equal "/2008/09/09/foo-bar.html", @post.url
|
||||
end
|
||||
end
|
||||
|
||||
context "with unspecified (date) style and a category" do
|
||||
setup do
|
||||
@post.categories << "beer"
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
|
||||
assert_equal "/beer/2008/09/09/foo-bar.html", @post.url
|
||||
end
|
||||
end
|
||||
|
||||
context "with unspecified (date) style and categories" do
|
||||
setup do
|
||||
@post.categories << "food"
|
||||
@post.categories << "beer"
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
|
||||
assert_equal "/food/beer/2008/09/09/foo-bar.html", @post.url
|
||||
end
|
||||
end
|
||||
|
||||
context "with none style" do
|
||||
setup do
|
||||
@post.site.permalink_style = :none
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/:categories/:title.html", @post.template
|
||||
assert_equal "/foo-bar.html", @post.url
|
||||
end
|
||||
end
|
||||
|
||||
context "with pretty style" do
|
||||
setup do
|
||||
@post.site.permalink_style = :pretty
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/:categories/:year/:month/:day/:title/", @post.template
|
||||
assert_equal "/2008/09/09/foo-bar/", @post.url
|
||||
end
|
||||
end
|
||||
|
||||
context "with custom date permalink" do
|
||||
setup do
|
||||
@post.site.permalink_style = '/:categories/:year/:i_month/:i_day/:title/'
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/2008/9/9/foo-bar/", @post.url
|
||||
end
|
||||
end
|
||||
|
||||
context "with prefix style and no extension" do
|
||||
setup do
|
||||
@post.site.permalink_style = "/prefix/:title"
|
||||
@post.process(@fake_file)
|
||||
end
|
||||
|
||||
should "process the url correctly" do
|
||||
assert_equal "/prefix/:title", @post.template
|
||||
assert_equal "/prefix/foo-bar", @post.url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "read yaml front-matter" do
|
||||
@post.read_yaml(@source, @real_file)
|
||||
|
||||
assert_equal({"title" => "Foo Bar", "layout" => "default"}, @post.data)
|
||||
assert_equal "h1. {{ page.title }}\n\nBest *post* ever", @post.content
|
||||
end
|
||||
|
||||
should "transform textile" do
|
||||
@post.process(@real_file)
|
||||
@post.read_yaml(@source, @real_file)
|
||||
@post.transform
|
||||
|
||||
assert_equal "<h1>{{ page.title }}</h1>\n<p>Best <strong>post</strong> ever</p>", @post.content
|
||||
end
|
||||
end
|
||||
|
||||
context "when in a site" do
|
||||
setup do
|
||||
clear_dest
|
||||
stub(Jekyll).configuration { Jekyll::DEFAULTS }
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
@site.posts = [setup_post('2008-02-02-published.textile'),
|
||||
setup_post('2009-01-27-categories.textile')]
|
||||
end
|
||||
|
||||
should "have next post" do
|
||||
assert_equal(@site.posts.last, @site.posts.first.next)
|
||||
end
|
||||
|
||||
should "have previous post" do
|
||||
assert_equal(@site.posts.first, @site.posts.last.previous)
|
||||
end
|
||||
|
||||
should "not have previous post if first" do
|
||||
assert_equal(nil, @site.posts.first.previous)
|
||||
end
|
||||
|
||||
should "not have next post if last" do
|
||||
assert_equal(nil, @site.posts.last.next)
|
||||
end
|
||||
end
|
||||
|
||||
context "initializing posts" do
|
||||
should "publish when published yaml is no specified" do
|
||||
post = setup_post("2008-02-02-published.textile")
|
||||
assert_equal true, post.published
|
||||
end
|
||||
|
||||
should "not published when published yaml is false" do
|
||||
post = setup_post("2008-02-02-not-published.textile")
|
||||
assert_equal false, post.published
|
||||
end
|
||||
|
||||
should "recognize date in yaml" do
|
||||
post = setup_post("2010-01-09-date-override.textile")
|
||||
do_render(post)
|
||||
assert_equal Time, post.date.class
|
||||
assert_equal Time, post.to_liquid["date"].class
|
||||
assert_equal "/2010/01/10/date-override.html", post.url
|
||||
assert_equal "<p>Post with a front matter date</p>\n<p>10 Jan 2010</p>", post.output
|
||||
end
|
||||
|
||||
should "recognize time in yaml" do
|
||||
post = setup_post("2010-01-09-time-override.textile")
|
||||
do_render(post)
|
||||
assert_equal Time, post.date.class
|
||||
assert_equal Time, post.to_liquid["date"].class
|
||||
assert_equal "/2010/01/10/time-override.html", post.url
|
||||
assert_equal "<p>Post with a front matter time</p>\n<p>10 Jan 2010</p>", post.output
|
||||
end
|
||||
|
||||
should "recognize time with timezone in yaml" do
|
||||
post = setup_post("2010-01-09-timezone-override.textile")
|
||||
do_render(post)
|
||||
assert_equal Time, post.date.class
|
||||
assert_equal Time, post.to_liquid["date"].class
|
||||
assert_equal "/2010/01/10/timezone-override.html", post.url
|
||||
assert_equal "<p>Post with a front matter time with timezone</p>\n<p>10 Jan 2010</p>", post.output
|
||||
end
|
||||
|
||||
should "to_liquid prioritizes post attributes over data" do
|
||||
post = setup_post("2010-01-16-override-data.textile")
|
||||
assert_equal Array, post.tags.class
|
||||
assert_equal Array, post.to_liquid["tags"].class
|
||||
assert_equal Time, post.date.class
|
||||
assert_equal Time, post.to_liquid["date"].class
|
||||
end
|
||||
|
||||
should "recognize category in yaml" do
|
||||
post = setup_post("2009-01-27-category.textile")
|
||||
assert post.categories.include?('foo')
|
||||
end
|
||||
|
||||
should "recognize several categories in yaml" do
|
||||
post = setup_post("2009-01-27-categories.textile")
|
||||
assert post.categories.include?('foo')
|
||||
assert post.categories.include?('bar')
|
||||
assert post.categories.include?('baz')
|
||||
end
|
||||
|
||||
should "recognize empty category in yaml" do
|
||||
post = setup_post("2009-01-27-empty-category.textile")
|
||||
assert_equal [], post.categories
|
||||
end
|
||||
|
||||
should "recognize empty categories in yaml" do
|
||||
post = setup_post("2009-01-27-empty-categories.textile")
|
||||
assert_equal [], post.categories
|
||||
end
|
||||
|
||||
should "recognize tag in yaml" do
|
||||
post = setup_post("2009-05-18-tag.textile")
|
||||
assert post.tags.include?('code')
|
||||
end
|
||||
|
||||
should "recognize tags in yaml" do
|
||||
post = setup_post("2009-05-18-tags.textile")
|
||||
assert post.tags.include?('food')
|
||||
assert post.tags.include?('cooking')
|
||||
assert post.tags.include?('pizza')
|
||||
end
|
||||
|
||||
should "recognize empty tag in yaml" do
|
||||
post = setup_post("2009-05-18-empty-tag.textile")
|
||||
assert_equal [], post.tags
|
||||
end
|
||||
|
||||
should "recognize empty tags in yaml" do
|
||||
post = setup_post("2009-05-18-empty-tags.textile")
|
||||
assert_equal [], post.tags
|
||||
end
|
||||
|
||||
should "allow no yaml" do
|
||||
post = setup_post("2009-06-22-no-yaml.textile")
|
||||
assert_equal "No YAML.", post.content
|
||||
end
|
||||
|
||||
should "allow empty yaml" do
|
||||
post = setup_post("2009-06-22-empty-yaml.textile")
|
||||
assert_equal "Empty YAML.", post.content
|
||||
end
|
||||
|
||||
context "rendering" do
|
||||
setup do
|
||||
clear_dest
|
||||
end
|
||||
|
||||
should "render properly" do
|
||||
post = setup_post("2008-10-18-foo-bar.textile")
|
||||
do_render(post)
|
||||
assert_equal "<<< <h1>Foo Bar</h1>\n<p>Best <strong>post</strong> ever</p> >>>", post.output
|
||||
end
|
||||
|
||||
should "write properly" do
|
||||
post = setup_post("2008-10-18-foo-bar.textile")
|
||||
do_render(post)
|
||||
post.write(dest_dir)
|
||||
|
||||
assert File.directory?(dest_dir)
|
||||
assert File.exists?(File.join(dest_dir, '2008', '10', '18', 'foo-bar.html'))
|
||||
end
|
||||
|
||||
should "write properly without html extension" do
|
||||
post = setup_post("2008-10-18-foo-bar.textile")
|
||||
post.site.permalink_style = ":title"
|
||||
do_render(post)
|
||||
post.write(dest_dir)
|
||||
|
||||
assert File.directory?(dest_dir)
|
||||
assert File.exists?(File.join(dest_dir, 'foo-bar', 'index.html'))
|
||||
end
|
||||
|
||||
should "insert data" do
|
||||
post = setup_post("2008-11-21-complex.textile")
|
||||
do_render(post)
|
||||
|
||||
assert_equal "<<< <p>url: /2008/11/21/complex.html<br />\ndate: #{Time.parse("2008-11-21")}<br />\nid: /2008/11/21/complex</p> >>>", post.output
|
||||
end
|
||||
|
||||
should "include templates" do
|
||||
post = setup_post("2008-12-13-include.markdown")
|
||||
post.site.source = File.join(File.dirname(__FILE__), 'source')
|
||||
do_render(post)
|
||||
|
||||
assert_equal "<<< <hr />\n<p>Tom Preston-Werner github.com/mojombo</p>\n\n<p>This <em>is</em> cool</p> >>>", post.output
|
||||
end
|
||||
|
||||
should "render date specified in front matter properly" do
|
||||
post = setup_post("2010-01-09-date-override.textile")
|
||||
do_render(post)
|
||||
|
||||
assert_equal "<p>Post with a front matter date</p>\n<p>10 Jan 2010</p>", post.output
|
||||
end
|
||||
|
||||
should "render time specified in front matter properly" do
|
||||
post = setup_post("2010-01-09-time-override.textile")
|
||||
do_render(post)
|
||||
|
||||
assert_equal "<p>Post with a front matter time</p>\n<p>10 Jan 2010</p>", post.output
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
should "generate categories and topics" do
|
||||
post = Post.new(@site, File.join(File.dirname(__FILE__), *%w[source]), 'foo', 'bar/2008-12-12-topical-post.textile')
|
||||
assert_equal ['foo'], post.categories
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def test_transform
|
||||
p = Post.allocate
|
||||
p.process("2008-10-18-foo-bar.textile")
|
||||
p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile")
|
||||
p.transform
|
||||
|
||||
assert_equal "<h1>{{ page.title }}</h1>\n<p>Best <strong>post</strong> ever</p>", p.content
|
||||
end
|
||||
|
||||
def test_render
|
||||
p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-10-18-foo-bar.textile")
|
||||
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
|
||||
p.render(layouts, {"site" => {"posts" => []}})
|
||||
|
||||
assert_equal "<<< <h1>Foo Bar</h1>\n<p>Best <strong>post</strong> ever</p> >>>", p.output
|
||||
end
|
||||
|
||||
def test_write
|
||||
clear_dest
|
||||
|
||||
p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-10-18-foo-bar.textile")
|
||||
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
|
||||
p.render(layouts, {"site" => {"posts" => []}})
|
||||
p.write(dest_dir)
|
||||
end
|
||||
|
||||
def test_data
|
||||
p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-11-21-complex.textile")
|
||||
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
|
||||
p.render(layouts, {"site" => {"posts" => []}})
|
||||
|
||||
assert_equal "<<< <p>url: /2008/11/21/complex.html<br />\ndate: #{Time.parse("2008-11-21")}<br />\nid: /2008/11/21/complex</p> >>>", p.output
|
||||
end
|
||||
|
||||
def test_categories_and_topics
|
||||
p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), 'foo', 'bar/2008-12-12-topical-post.textile')
|
||||
assert_equal ['foo'], p.categories
|
||||
assert_equal ['bar'], p.topics
|
||||
end
|
||||
|
||||
def test_include
|
||||
Jekyll.source = File.join(File.dirname(__FILE__), *%w[source])
|
||||
p = Post.new(File.join(File.dirname(__FILE__), *%w[source]), '', "2008-12-13-include.markdown")
|
||||
layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
|
||||
p.render(layouts, {"site" => {"posts" => []}})
|
||||
|
||||
assert_equal "<<< <hr />\n<p>Tom Preston-Werner github.com/mojombo</p>\n\n<p>This <em>is</em> cool</p> >>>", p.output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
18
test/test_rdiscount.rb
Normal file
18
test/test_rdiscount.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestRdiscount < Test::Unit::TestCase
|
||||
|
||||
context "rdiscount" do
|
||||
setup do
|
||||
config = {
|
||||
'rdiscount' => { 'extensions' => ['smart'] },
|
||||
'markdown' => 'rdiscount'
|
||||
}
|
||||
@markdown = MarkdownConverter.new config
|
||||
end
|
||||
|
||||
should "pass rdiscount extensions" do
|
||||
assert_equal "<p>“smart”</p>", @markdown.convert('"smart"').strip
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,33 +1,186 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestSite < Test::Unit::TestCase
|
||||
def setup
|
||||
source = File.join(File.dirname(__FILE__), *%w[source])
|
||||
@s = Site.new(source, dest_dir)
|
||||
end
|
||||
|
||||
def test_site_init
|
||||
context "creating sites" do
|
||||
setup do
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir})
|
||||
end
|
||||
@site = Site.new(Jekyll.configuration)
|
||||
end
|
||||
|
||||
should "have an empty tag hash by default" do
|
||||
assert_equal Hash.new, @site.tags
|
||||
end
|
||||
|
||||
should "reset data before processing" do
|
||||
clear_dest
|
||||
@site.process
|
||||
before_posts = @site.posts.length
|
||||
before_layouts = @site.layouts.length
|
||||
before_categories = @site.categories.length
|
||||
before_tags = @site.tags.length
|
||||
before_pages = @site.pages.length
|
||||
before_static_files = @site.static_files.length
|
||||
before_time = @site.time
|
||||
|
||||
@site.process
|
||||
assert_equal before_posts, @site.posts.length
|
||||
assert_equal before_layouts, @site.layouts.length
|
||||
assert_equal before_categories, @site.categories.length
|
||||
assert_equal before_tags, @site.tags.length
|
||||
assert_equal before_pages, @site.pages.length
|
||||
assert_equal before_static_files, @site.static_files.length
|
||||
assert before_time <= @site.time
|
||||
end
|
||||
|
||||
should "write only modified static files" do
|
||||
clear_dest
|
||||
StaticFile.reset_cache
|
||||
|
||||
@site.process
|
||||
some_static_file = @site.static_files[0].path
|
||||
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
|
||||
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
|
||||
|
||||
# need to sleep because filesystem timestamps have best resolution in seconds
|
||||
sleep 1
|
||||
@site.process
|
||||
mtime2 = File.stat(dest).mtime.to_i
|
||||
assert_equal mtime1, mtime2
|
||||
|
||||
# simulate file modification by user
|
||||
FileUtils.touch some_static_file
|
||||
|
||||
sleep 1
|
||||
@site.process
|
||||
mtime3 = File.stat(dest).mtime.to_i
|
||||
assert_not_equal mtime2, mtime3 # must be regenerated!
|
||||
|
||||
sleep 1
|
||||
@site.process
|
||||
mtime4 = File.stat(dest).mtime.to_i
|
||||
assert_equal mtime3, mtime4 # no modifications, so must be the same
|
||||
end
|
||||
|
||||
should "write static files if not modified but missing in destination" do
|
||||
clear_dest
|
||||
StaticFile.reset_cache
|
||||
|
||||
@site.process
|
||||
some_static_file = @site.static_files[0].path
|
||||
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
|
||||
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
|
||||
|
||||
# need to sleep because filesystem timestamps have best resolution in seconds
|
||||
sleep 1
|
||||
@site.process
|
||||
mtime2 = File.stat(dest).mtime.to_i
|
||||
assert_equal mtime1, mtime2
|
||||
|
||||
# simulate destination file deletion
|
||||
File.unlink dest
|
||||
|
||||
sleep 1
|
||||
@site.process
|
||||
mtime3 = File.stat(dest).mtime.to_i
|
||||
assert_not_equal mtime2, mtime3 # must be regenerated and differ!
|
||||
|
||||
sleep 1
|
||||
@site.process
|
||||
mtime4 = File.stat(dest).mtime.to_i
|
||||
assert_equal mtime3, mtime4 # no modifications, so must be the same
|
||||
end
|
||||
|
||||
should "read layouts" do
|
||||
@site.read_layouts
|
||||
assert_equal ["default", "simple"].sort, @site.layouts.keys.sort
|
||||
end
|
||||
|
||||
should "read posts" do
|
||||
@site.read_posts('')
|
||||
posts = Dir[source_dir('_posts', '*')]
|
||||
assert_equal posts.size - 1, @site.posts.size
|
||||
end
|
||||
|
||||
should "deploy payload" do
|
||||
clear_dest
|
||||
@site.process
|
||||
|
||||
posts = Dir[source_dir("**", "_posts", "*")]
|
||||
categories = %w(bar baz category foo z_category publish_test win).sort
|
||||
|
||||
assert_equal posts.size - 1, @site.posts.size
|
||||
assert_equal categories, @site.categories.keys.sort
|
||||
assert_equal 4, @site.categories['foo'].size
|
||||
end
|
||||
|
||||
should "filter entries" do
|
||||
ent1 = %w[foo.markdown bar.markdown baz.markdown #baz.markdown#
|
||||
.baz.markdow foo.markdown~]
|
||||
ent2 = %w[.htaccess _posts _pages bla.bla]
|
||||
|
||||
assert_equal %w[foo.markdown bar.markdown baz.markdown], @site.filter_entries(ent1)
|
||||
assert_equal %w[.htaccess bla.bla], @site.filter_entries(ent2)
|
||||
end
|
||||
|
||||
should "filter entries with exclude" do
|
||||
excludes = %w[README TODO]
|
||||
includes = %w[index.html site.css]
|
||||
|
||||
@site.exclude = excludes
|
||||
assert_equal includes, @site.filter_entries(excludes + includes)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def test_read_layouts
|
||||
@s.read_layouts
|
||||
context 'with orphaned files in destination' do
|
||||
setup do
|
||||
clear_dest
|
||||
@site.process
|
||||
# generate some orphaned files:
|
||||
# hidden file
|
||||
File.open(dest_dir('.htpasswd'), 'w')
|
||||
# single file
|
||||
File.open(dest_dir('obsolete.html'), 'w')
|
||||
# single file in sub directory
|
||||
FileUtils.mkdir(dest_dir('qux'))
|
||||
File.open(dest_dir('qux/obsolete.html'), 'w')
|
||||
# empty directory
|
||||
FileUtils.mkdir(dest_dir('quux'))
|
||||
end
|
||||
|
||||
teardown do
|
||||
FileUtils.rm_f(dest_dir('.htpasswd'))
|
||||
FileUtils.rm_f(dest_dir('obsolete.html'))
|
||||
FileUtils.rm_rf(dest_dir('qux'))
|
||||
FileUtils.rm_f(dest_dir('quux'))
|
||||
end
|
||||
|
||||
should 'remove orphaned files in destination' do
|
||||
@site.process
|
||||
assert !File.exist?(dest_dir('.htpasswd'))
|
||||
assert !File.exist?(dest_dir('obsolete.html'))
|
||||
assert !File.exist?(dest_dir('qux'))
|
||||
assert !File.exist?(dest_dir('quux'))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
assert_equal ["default", "simple"].sort, @s.layouts.keys.sort
|
||||
end
|
||||
|
||||
def test_read_posts
|
||||
@s.read_posts('')
|
||||
context 'with an invalid markdown processor in the configuration' do
|
||||
should 'not throw an error at initialization time' do
|
||||
bad_processor = 'not a processor name'
|
||||
assert_nothing_raised do
|
||||
Site.new(Jekyll.configuration.merge({ 'markdown' => bad_processor }))
|
||||
end
|
||||
end
|
||||
|
||||
should 'throw FatalException at process time' do
|
||||
bad_processor = 'not a processor name'
|
||||
s = Site.new(Jekyll.configuration.merge({ 'markdown' => bad_processor }))
|
||||
assert_raise Jekyll::FatalException do
|
||||
s.process
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 4, @s.posts.size
|
||||
end
|
||||
|
||||
def test_site_payload
|
||||
clear_dest
|
||||
@s.process
|
||||
|
||||
assert_equal 7, @s.posts.length
|
||||
assert_equal ["category", "foo", "z_category"].sort, @s.categories.keys.sort
|
||||
assert_equal 1, @s.categories['foo'].length
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,31 +1,127 @@
|
||||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
class TestTags < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@content = <<CONTENT
|
||||
|
||||
def create_post(content, override = {}, converter_class = Jekyll::MarkdownConverter)
|
||||
stub(Jekyll).configuration do
|
||||
Jekyll::DEFAULTS.merge({'pygments' => true}).merge(override)
|
||||
end
|
||||
site = Site.new(Jekyll.configuration)
|
||||
info = { :filters => [Jekyll::Filters], :registers => { :site => site } }
|
||||
@converter = site.converters.find { |c| c.class == converter_class }
|
||||
payload = { "pygments_prefix" => @converter.pygments_prefix,
|
||||
"pygments_suffix" => @converter.pygments_suffix }
|
||||
|
||||
@result = Liquid::Template.parse(content).render(payload, info)
|
||||
@result = @converter.convert(@result)
|
||||
end
|
||||
|
||||
def fill_post(code, override = {})
|
||||
content = <<CONTENT
|
||||
---
|
||||
layout: post
|
||||
title: This is a test
|
||||
|
||||
---
|
||||
This document results in a markdown error with maruku
|
||||
{% highlight ruby %}
|
||||
puts "hi"
|
||||
|
||||
puts "bye"
|
||||
This document results in a markdown error with maruku
|
||||
|
||||
{% highlight text %}#{code}{% endhighlight %}
|
||||
CONTENT
|
||||
create_post(content, override)
|
||||
end
|
||||
|
||||
context "post content has highlight tag" do
|
||||
setup do
|
||||
fill_post("test")
|
||||
end
|
||||
|
||||
should "not cause a markdown error" do
|
||||
assert_no_match /markdown\-html\-error/, @result
|
||||
end
|
||||
|
||||
should "render markdown with pygments line handling" do
|
||||
assert_match %{<pre><code class='text'>test\n</code></pre>}, @result
|
||||
end
|
||||
end
|
||||
|
||||
context "post content has highlight with file reference" do
|
||||
setup do
|
||||
fill_post("./jekyll.gemspec")
|
||||
end
|
||||
|
||||
should "not embed the file" do
|
||||
assert_match %{<pre><code class='text'>./jekyll.gemspec\n</code></pre>}, @result
|
||||
end
|
||||
end
|
||||
|
||||
context "post content has highlight tag with UTF character" do
|
||||
setup do
|
||||
fill_post("Æ")
|
||||
end
|
||||
|
||||
should "render markdown with pygments line handling" do
|
||||
assert_match %{<pre><code class='text'>Æ\n</code></pre>}, @result
|
||||
end
|
||||
end
|
||||
|
||||
context "simple post with markdown and pre tags" do
|
||||
setup do
|
||||
@content = <<CONTENT
|
||||
---
|
||||
title: Maruku vs. RDiscount
|
||||
---
|
||||
|
||||
_FIGHT!_
|
||||
|
||||
{% highlight ruby %}
|
||||
puts "3..2..1.."
|
||||
{% endhighlight %}
|
||||
|
||||
*FINISH HIM*
|
||||
CONTENT
|
||||
end
|
||||
|
||||
def test_markdown_with_pygments_line_handling
|
||||
Jekyll.pygments = true
|
||||
Jekyll.content_type = :markdown
|
||||
|
||||
result = Liquid::Template.parse(@content).render({}, [Jekyll::Filters])
|
||||
result = Jekyll.markdown_proc.call(result)
|
||||
assert_no_match(/markdown\-html\-error/,result)
|
||||
end
|
||||
|
||||
context "using Textile" do
|
||||
setup do
|
||||
create_post(@content, {}, Jekyll::TextileConverter)
|
||||
end
|
||||
|
||||
# Broken in RedCloth 4.1.9
|
||||
should "not textilize highlight block" do
|
||||
assert_no_match %r{3\.\.2\.\.1\.\."</span><br />}, @result
|
||||
end
|
||||
end
|
||||
|
||||
context "using Maruku" do
|
||||
setup do
|
||||
create_post(@content)
|
||||
end
|
||||
|
||||
should "parse correctly" do
|
||||
assert_match %r{<em>FIGHT!</em>}, @result
|
||||
assert_match %r{<em>FINISH HIM</em>}, @result
|
||||
end
|
||||
end
|
||||
|
||||
context "using RDiscount" do
|
||||
setup do
|
||||
create_post(@content, 'markdown' => 'rdiscount')
|
||||
end
|
||||
|
||||
should "parse correctly" do
|
||||
assert_match %r{<em>FIGHT!</em>}, @result
|
||||
assert_match %r{<em>FINISH HIM</em>}, @result
|
||||
end
|
||||
end
|
||||
|
||||
context "using Kramdown" do
|
||||
setup do
|
||||
create_post(@content, 'markdown' => 'kramdown')
|
||||
end
|
||||
|
||||
should "parse correctly" do
|
||||
assert_match %r{<em>FIGHT!</em>}, @result
|
||||
assert_match %r{<em>FINISH HIM</em>}, @result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user