Merge branch 'alagu-posterous-importer-rebased' of git://github.com/simensen/jekyll

This commit is contained in:
Alagu 2012-10-03 19:33:01 -07:00
commit 67f403a9d6
35 changed files with 912 additions and 1705 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ pkg/
*~
_site/
.bundle/
.DS_Store
bbin/

View File

@ -1,3 +1,23 @@
== HEAD
* Minor Enhancements
* Add ability to explicitly specify included files (#261)
* Add --default-mimetype option (#279)
* Allow setting of RedCloth options (#284)
* Add post_url Liquid tag for internal post linking (#369)
* Bug Fixes
* Allow some special characters in highlight names
* URL escape category names in URL generation (#360)
* Fix error with limit_posts (#442)
== 0.11.2 / 2011-12-27
* Bug Fixes
* Fix gemspec
== 0.11.1 / 2011-12-27
* Bug Fixes
* Fix extra blank line in highlight blocks (#409)
* Update dependencies
== 0.11.0 / 2011-07-10
* Major Enhancements
* Add command line importer functionality (#253)

View File

@ -1,5 +1,6 @@
require 'rubygems'
require 'rake'
require 'rdoc'
require 'date'
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[lib]))
@ -49,6 +50,10 @@ task :default => [:test, :features]
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
if `which pygmentize` == ''
puts "You must have Pygments installed to run the tests."
exit 1
end
test.libs << 'lib' << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
@ -62,7 +67,7 @@ task :coverage do
sh "open coverage/index.html"
end
require 'rake/rdoctask'
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "#{name} #{version}"
@ -119,7 +124,7 @@ task :release => :build do
puts "You must be on the master branch to release!"
exit!
end
sh "git commit --allow-empty -a -m 'Release #{version}'"
sh "git commit --allow-empty -m 'Release #{version}'"
sh "git tag v#{version}"
sh "git push origin master"
sh "git push origin v#{version}"

View File

@ -71,6 +71,10 @@ opts = OptionParser.new do |opts|
options['baseurl'] = baseurl
end
opts.on("--default-mimetype [MT]", "Mimetype to use when no file extension (if --server)") do |mt|
options['default-mimetype'] = mt
end
opts.on("--[no-]lsi", "Use LSI for better related posts") do |lsi|
options['lsi'] = lsi
end
@ -162,7 +166,7 @@ if ARGV.size > 0
migrators = {
:posterous => 'Posterous',
:wordpressdotcom => 'WordpressDotCom',
:wordpress => 'Wordpress',
:wordpress => 'WordPress',
:csv => 'CSV',
:drupal => 'Drupal',
:enki => 'Enki',
@ -191,7 +195,7 @@ end
# Get source and destintation from command line
# Get source and destination from command line
case ARGV.size
when 0
when 1
@ -267,6 +271,9 @@ if options['server']
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
mime_types.store 'js', 'application/javascript'
if options['default-mimetype']
mime_types.store(nil, options['default-mimetype'])
end
s = HTTPServer.new(
:Port => options['server_port'],

View File

@ -1,574 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.5.3" />
<title>Jekyll</title>
<style type="text/css">
/* ---------------------------------------------------------------------------
Bare AsciiDoc styles
Ryan Tomayko <r@tomayko.com>
--------------------------------------------------------------------------- */
body {
font-family:verdana,helvetica,arial,sans-serif;
font-size:81.25%; /* 13px */
line-height:1.538; /* 20px */
margin:40px auto 50px auto;
max-width:53.8461538462em; /* 790px */
color:#333;
}
em {
font-style:italic;
}
strong {
font-weight:bold;
color:#000;
}
tt {
font-family:consolas, 'lucida console', 'bitstream vera sans mono',
'courier new', monospace;
color:#000;
}
p, ul, ol, dl {
margin:10px 0
}
dl {
margin-left:40px
}
dt {
font-weight:normal;
color:#000;
}
h1, h2, h3, h4, h5 {
font-family:'lucida grande',georgia,verdana,helvetica,arial,sans-serif;
font-weight:normal;
color:#000;
}
h1 {
font-size:40px;
line-height:1.428;
margin:20px 0;
}
h2 {
font-size:30px;
line-height:1.36363636; /* repeating, of course */
margin:60px 0 20px 0;
}
h2 + .sectionbody {}
h3 {
font-size:24px;
line-height:1.1;
margin:30px 0 10px 0;
}
h4 {
font-size:18px;
line-height:1.1;
margin:20px 0 5px 0;
}
h5 {
font-size:13px;
font-style:italic;
line-height:1.1;
}
#header {
text-align:center;
margin-bottom:30px;
}
#header h1 { margin-bottom:0 }
.title, .sidebar-title {
font-weight:normal;
color:#000;
margin-bottom:0;
}
.admonitionblock .title {
font-weight:bold;
}
.admonitionblock {
margin:30px 0px;
color:#555;
}
.admonitionblock td.icon {
width:30px;
padding-right:20px;
padding-left:20px;
text-transform:uppercase;
font-weight:bold;
color:#888;
}
.listingblock {
margin: 13px 0;
}
.listingblock .content {
border:1px solid silver;
background:#eee;
padding:5px;
}
.listingblock .content pre {
margin:0;
}
.literalblock .content {
margin-left:40px;
}
.verseblock .content {
white-space:pre
}
.sidebarblock .sidebar-content {
border:1px solid silver;
background:#FFFFEE;
padding:0 10px;
color:#222;
font-size:smaller;
line-height:1.5;
}
.sidebar-title {
margin:10px 0;
font-weight:bold;
color:#442;
}
.quoteblock-content {
font-style:italic;
color:#444;
margin-left:40px;
}
.quoteblock-content .attribution {
font-style:normal;
text-align:right;
color:#000;
}
.exampleblock-content *:first-child { margin-top:0 }
.exampleblock-content {
border-left:2px solid silver;
padding-left:8px;
}
#footer {
font-size:11px;
margin-top:40px;
border-top:1px solid silver;
color:#555;
}
#author {
color:#000;
text-transform:uppercase
}
</style>
<script type="text/javascript">
/*<![CDATA[*/
window.onload = function(){asciidoc.footnotes(); asciidoc.toc(2);}
var asciidoc = { // Namespace.
/////////////////////////////////////////////////////////////////////
// Table Of Contents generator
/////////////////////////////////////////////////////////////////////
/* Author: Mihai Bazon, September 2002
* http://students.infoiasi.ro/~mishoo
*
* Table Of Content generator
* Version: 0.4
*
* Feel free to use this script under the terms of the GNU General Public
* License, as long as you do not remove or alter this notice.
*/
/* modified by Troy D. Hanson, September 2006. License: GPL */
/* modified by Stuart Rackham, 2006, 2009. License: GPL */
// toclevels = 1..4.
toc: function (toclevels) {
function getText(el) {
var text = "";
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
text += i.data;
else if (i.firstChild != null)
text += getText(i);
}
return text;
}
function TocEntry(el, text, toclevel) {
this.element = el;
this.text = text;
this.toclevel = toclevel;
}
function tocEntries(el, toclevels) {
var result = new Array;
var re = new RegExp('[hH]([2-'+(toclevels+1)+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
var cont = document.getElementById("content");
var noteholder = document.getElementById("footnotes");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>";
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
}
}
/*]]>*/
</script>
</head>
<body>
<div id="header">
<h1>Jekyll</h1>
<span id="author">Tom Preston-Werner</span><br />
<span id="email"><tt>&lt;<a href="mailto:&lt;tom@mojombo.com&gt;">&lt;tom@mojombo.com&gt;</a>&gt;</tt></span><br />
<div id="toc">
<div id="toctitle">Table of Contents</div>
<noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
</div>
</div>
<div id="content">
<h2 id="_preface">1. Preface</h2>
<div class="sectionbody">
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>If you&#8217;re looking for a simple, yet powerful solution to your blogging or
static site needs, Jekyll may be just what you&#8217;ve been looking for.</p></div>
<h3 id="_what_this_book_covers">1.1. What this book covers</h3><div style="clear:left"></div>
<div class="paragraph"><p><em>Chapter 1, Quick Start</em> 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.</p></div>
<div class="paragraph"><p><em>Chapter 2, Directory Layout</em> covers the various files and directories that
comprise a Jekyll site.</p></div>
<div class="paragraph"><p><em>Chapter 3, Tags and Filters</em></p></div>
<div class="paragraph"><p><em>Chapter X, Deploying your Jekyll Site</em></p></div>
<div class="paragraph"><p><em>Chapter X, Customizing Jekyll with Plugins</em></p></div>
<div class="paragraph"><p><em>Chapter X, Migrating to Jekyll from your Existing Blog</em></p></div>
<div class="paragraph"><p><em>Chapter X, Configuration Reference</em></p></div>
</div>
<h2 id="_chapter_1_quick_start">2. Chapter 1: Quick Start</h2>
<div class="sectionbody">
<div class="paragraph"><p>This chapter is designed to get you up and running with Jekyll as quickly as
possible.</p></div>
<h3 id="_installation">2.1. Installation</h3><div style="clear:left"></div>
<div class="paragraph"><p>The best way to install Jekyll is via RubyGems:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>gem install jekyll</tt></pre>
</div></div>
<div class="paragraph"><p>This is all you need in order to get started with a basic Jekyll site. Some
options require additional packages to be installed.</p></div>
<div class="paragraph"><p>If you encounter errors during gem installation, you may need to install the
header files for compiling extension modules for ruby 1.8:</p></div>
<div class="listingblock">
<div class="title">Debian</div>
<div class="content">
<pre><tt>sudo apt-get install ruby1.8-dev</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">Red Hat / CentOS / Fedora systems</div>
<div class="content">
<pre><tt>sudo yum install ruby-devel</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">NearlyFreeSpeech</div>
<div class="content">
<pre><tt>RB_USER_INSTALL=true gem install jekyll</tt></pre>
</div></div>
<div class="paragraph"><p>If you encounter errors like <tt>Failed to build gem native extension</tt> on Windows
you may need to install <a href="http://wiki.github.com/oneclick/rubyinstaller/development-kit">RubyInstaller
DevKit</a>.</p></div>
<h4 id="_latex_to_png">2.1.1. LaTeX to PNG</h4>
<div class="paragraph"><p>Maruku comes with optional support for LaTeX to PNG rendering via blahtex
(Version 0.6) which must be in your $PATH along with @dvips@.</p></div>
<div class="paragraph"><p>(NOTE: "remi&#8217;s fork of Maruku":http://github.com/remi/maruku/tree/master does
not assume a fixed location for @dvips@ if you need that fixed)</p></div>
<h4 id="_rdiscount">2.1.2. RDiscount</h4>
<div class="paragraph"><p>If you prefer to use
<a href="http://github.com/rtomayko/rdiscount/tree/master">RDiscount</a> instead of
<a href="http://maruku.rubyforge.org/">Maruku</a> for markdown, just make sure it&#8217;s
installed:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>sudo gem install rdiscount</tt></pre>
</div></div>
<div class="paragraph"><p>And run Jekyll with the following option:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>jekyll --rdiscount</tt></pre>
</div></div>
<div class="paragraph"><p>Or, in your @_config.yml@ file put the following so you don&#8217;t have to specify the flag:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>markdown: rdiscount</tt></pre>
</div></div>
<h4 id="_pygments">2.1.3. Pygments</h4>
<div class="paragraph"><p>If you want syntax highlighting via the @{% highlight %}@ tag in your posts,
you&#8217;ll need to install <a href="http://pygments.org/">Pygments</a>.</p></div>
<div class="listingblock">
<div class="title">On OSX with Homebrew</div>
<div class="content">
<pre><tt>brew install pip &amp;&amp; pip install pygments</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">On OSX with MacPorts</div>
<div class="content">
<pre><tt>sudo port install python25 py25-pygments</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">Bare OS X Leopard</div>
<div class="content">
<pre><tt>sudo easy_install Pygments</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">Archlinux</div>
<div class="content">
<pre><tt>sudo pacman -S python-pygments</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">Archlinux python2 for Pygments</div>
<div class="content">
<pre><tt>$ sudo pacman -S python2-pygments</tt></pre>
</div></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">python2 pygments version creates a <tt>pygmentize2</tt> executable, while
Jekyll tries to find <tt>pygmentize</tt>. Either create a symlink <tt># ln -s
/usr/bin/pygmentize2 /usr/bin/pygmentize</tt> or use the python3 version.</td>
</tr></table>
</div>
<div class="listingblock">
<div class="title">Ubuntu and Debian</div>
<div class="content">
<pre><tt>sudo apt-get install python-pygments</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">Gentoo</div>
<div class="content">
<pre><tt>$ sudo emerge -av dev-python/pygments</tt></pre>
</div></div>
<h3 id="_creating_your_first_site">2.2. Creating your First Site</h3><div style="clear:left"></div>
<div class="paragraph"><p>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:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>$ mkdir mysite
$ cd mysite
$ jekyll gen</tt></pre>
</div></div>
<div class="paragraph"><p>Make sure the directory is empty or Jekyll will refuse to run. If everything
was successful, you&#8217;ll be left with a complete, valid Jekyll site that&#8217;s ready
to be converted into a static site.</p></div>
<div class="paragraph"><p>To perform the conversion, make sure you&#8217;re in the root of your Jekyll site
directory and run:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>$ jekyll --server</tt></pre>
</div></div>
<div class="paragraph"><p>If all goes well, you should get a few lines with information about config
file detection, source and destination paths, and a success message.</p></div>
<div class="paragraph"><p>The <tt>--server</tt> 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.</p></div>
<div class="paragraph"><p>Open up your favorite web browser and navigate to:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>http://localhost:4000</tt></pre>
</div></div>
<div class="paragraph"><p>Congratulations! You have now successfully created and converted your first
Jekyll site!</p></div>
</div>
<h2 id="_chapter_2_directory_layout">3. Chapter 2: Directory Layout</h2>
<div class="sectionbody">
<div class="paragraph"><p>If you followed the Quick Start in the last chapter, you have a Jekyll site on
your local machine. Let&#8217;s take a closer look at it and see what makes it tick.
The file layout should look something like this:</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>.
|-- _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</tt></pre>
</div></div>
<div class="paragraph"><p>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&#8217;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.</p></div>
<h3 id="_config_yml">3.1. _config.yml</h3><div style="clear:left"></div>
<div class="paragraph"><p>This file stores configuration data. A majority of these options can be
specified from the command line executable but it&#8217;s easier to throw them in
here so you don&#8217;t have to type them out every time. Detailed explanations of
configuration directives can be found in Chapter X.</p></div>
<h3 id="_layouts">3.2. _layouts</h3><div style="clear:left"></div>
<div class="paragraph"><p>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 <tt>{{ content }}</tt> specifies where the content will be placed
during the conversion process.</p></div>
<h3 id="_posts">3.3. _posts</h3><div style="clear:left"></div>
<div class="paragraph"><p>If you&#8217;re using Jekyll as a blog engine, this is where you&#8217;ll place your blog
posts. A post&#8217;s filename contains several pieces of data, so you must be very
careful about how these files are named. The filename format is:
<tt>YEAR-MONTH-DATE-SLUG.MARKUP</tt>. 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.</p></div>
<h3 id="_site">3.4. _site</h3><div style="clear:left"></div>
<div class="paragraph"><p>This is where the generated site will be placed (by default) once Jekyll is
done transforming it. If you&#8217;re using version control, you&#8217;ll want to add this
directory to the list of files to be ignored.</p></div>
<h3 id="_normal_files_with_yaml_front_matter">3.5. Normal Files with YAML Front Matter</h3><div style="clear:left"></div>
<div class="paragraph"><p>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.</p></div>
<h3 id="_everything_else">3.6. Everything Else</h3><div style="clear:left"></div>
<div class="paragraph"><p>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,
<tt>images/logo.png</tt> will be copied to the same location in the generated site.</p></div>
<div class="paragraph"><p>h2. Running Jekyll</p></div>
<div class="paragraph"><p>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:</p></div>
<div class="paragraph"><p>@jekyll --server@</p></div>
<div class="paragraph"><p>and then browse to <a href="http://0.0.0.0:4000">http://0.0.0.0:4000</a>. There&#8217;s plenty of <a id="configuration options|Configuration"></a> available to you as well.</p></div>
<div class="paragraph"><p>On Debian or Ubuntu, you may need to add @/var/lib/gems/1.8/bin/@ to your path.</p></div>
<div class="paragraph"><p>h2. Deployment</p></div>
<div class="paragraph"><p>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
<a id="Deployment"></a> page for more information regarding specific scenarios.</p></div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated 2011-03-18 21:05:02 PDT
</div>
</div>
</body>
</html>

View File

@ -1,41 +0,0 @@
== 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_

View File

@ -1,153 +0,0 @@
== 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!

View File

@ -1,90 +0,0 @@
== 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.

View File

@ -1,231 +0,0 @@
/* BEG */
/* ---------------------------------------------------------------------------
Bare AsciiDoc styles
Ryan Tomayko <r@tomayko.com>
--------------------------------------------------------------------------- */
body {
font-family:verdana,helvetica,arial,sans-serif;
font-size:81.25%; /* 13px */
line-height:1.538; /* 20px */
margin:40px 50px;
max-width:53.8461538462em; /* 790px */
color:#333;
}
em {
font-style:italic;
}
strong {
font-weight:bold;
color:#000;
}
tt {
font-family:consolas, 'lucida console', 'bitstream vera sans mono',
'courier new', monospace;
color:#000;
}
p, ul, ol, dl {
margin:10px 0
}
dl { }
dt {
font-weight:normal;
color:#000;
}
h1, h2, h3, h4, h5 {
font-family:'lucida grande',georgia,verdana,helvetica,arial,sans-serif;
font-weight:normal;
color:#000;
}
h1 {
font-size:30px;
line-height:1.428;
margin:20px 0;
}
h2 {
font-size:23px;
line-height:1.36363636; /* repeating, of course */
margin:20px 0;
}
h2 + .sectionbody {}
h3 {
font-size:18px;
line-height:1.1;
margin:30px 0 10px 0;
}
h4 {
font-size:13px;
font-weight:bold;
line-height:1.538;
}
h5 {
font-size:13px;
font-style:italic;
line-height:1.538;
}
pre {
font-size:larger;
}
#header {
text-align:center;
margin-bottom:30px;
}
#header h1 { margin-bottom:0 }
.title, .sidebar-title {
font-weight:normal;
margin-bottom:0;
}
.admonitionblock .title {
font-weight:bold;
}
.admonitionblock {
margin:30px 0px;
color:#555;
}
.admonitionblock td.icon {
width:30px;
padding-right:20px;
padding-left:20px;
text-transform:uppercase;
font-weight:bold;
color:#888;
}
.listingblock .content {
border:1px solid silver;
background:#eee;
padding:5px;
}
.listingblock .content pre {
margin:0;
}
.literalblock .content {
margin-left:40px;
}
.verseblock .content {
white-space:pre
}
.sidebarblock .sidebar-content {
border:1px solid silver;
background:#FFFFEE;
padding:0 10px;
color:#222;
font-size:smaller;
line-height:1.5;
}
.sidebar-title {
margin:10px 0;
font-weight:bold;
color:#442;
}
.quoteblock-content {
font-style:italic;
color:#444;
margin-left:40px;
}
.quoteblock-content .attribution {
font-style:normal;
text-align:right;
color:#000;
}
.exampleblock-content *:first-child { margin-top:0 }
.exampleblock-content {
border-left:2px solid silver;
padding-left:8px;
}
#footer {
font-size:11px;
margin-top:40px;
border-top:1px solid silver;
color:#555;
}
#author {
color:#000;
text-transform:uppercase
}
/* vim: set ft=css ts=4 sw=4 noexpandtab: */
/* END @import url(bare.css); */
/* ---------------------------------------------------------------------------
FreeBSD AsciiDoc Theme
Ryan Tomayko <r@tomayko.com>
Based on The FreeBSD Handbook and various other FreeBSD documenration.
--------------------------------------------------------------------------- */
body {
font-family:verdana,helvetica,arial,sans-serif;
font-size:100%;
color:#000;
}
tt { color:#007A00 }
pre tt { color:#000 }
dt { color:#000 }
h1, h2, h3, h4, h5 {
font-family:'lucida grande',helvetica,verdana,sans-serif;
color:#900;
font-weight:bold;
}
#header {
text-align:left;
}
#header h1 { margin-bottom:40px }
h1 {
font-size:36px;
line-height:1;
margin:40px 0;
}
h2 {
font-size:28px;
line-height:1;
margin:30px 0 20px 0;
}
.sectionbody {
margin-left:30px;
}
pre {
background:#EEE;
}
/* vim: set ft=css ts=4 sw=4 noexpandtab: */

View File

@ -1,177 +0,0 @@
/* ---------------------------------------------------------------------------
Bare AsciiDoc styles
Ryan Tomayko <r@tomayko.com>
--------------------------------------------------------------------------- */
body {
font-family:verdana,helvetica,arial,sans-serif;
font-size:81.25%; /* 13px */
line-height:1.538; /* 20px */
margin:40px auto 50px auto;
max-width:53.8461538462em; /* 790px */
color:#333;
}
em {
font-style:italic;
}
strong {
font-weight:bold;
color:#000;
}
tt {
font-family:consolas, 'lucida console', 'bitstream vera sans mono',
'courier new', monospace;
color:#000;
}
p, ul, ol, dl {
margin:10px 0
}
dl {
margin-left:40px
}
dt {
font-weight:normal;
color:#000;
}
h1, h2, h3, h4, h5 {
font-family:'lucida grande',georgia,verdana,helvetica,arial,sans-serif;
font-weight:normal;
color:#000;
}
h1 {
font-size:40px;
line-height:1.428;
margin:20px 0;
}
h2 {
font-size:30px;
line-height:1.36363636; /* repeating, of course */
margin:60px 0 20px 0;
}
h2 + .sectionbody {}
h3 {
font-size:24px;
line-height:1.1;
margin:30px 0 10px 0;
}
h4 {
font-size:18px;
line-height:1.1;
margin:20px 0 5px 0;
}
h5 {
font-size:13px;
font-style:italic;
line-height:1.1;
}
#header {
text-align:center;
margin-bottom:30px;
}
#header h1 { margin-bottom:0 }
.title, .sidebar-title {
font-weight:normal;
color:#000;
margin-bottom:0;
}
.admonitionblock .title {
font-weight:bold;
}
.admonitionblock {
margin:30px 0px;
color:#555;
}
.admonitionblock td.icon {
width:30px;
padding-right:20px;
padding-left:20px;
text-transform:uppercase;
font-weight:bold;
color:#888;
}
.listingblock {
margin: 13px 0;
}
.listingblock .content {
border:1px solid silver;
background:#eee;
padding:5px;
}
.listingblock .content pre {
margin:0;
}
.literalblock .content {
margin-left:40px;
}
.verseblock .content {
white-space:pre
}
.sidebarblock .sidebar-content {
border:1px solid silver;
background:#FFFFEE;
padding:0 10px;
color:#222;
font-size:smaller;
line-height:1.5;
}
.sidebar-title {
margin:10px 0;
font-weight:bold;
color:#442;
}
.quoteblock-content {
font-style:italic;
color:#444;
margin-left:40px;
}
.quoteblock-content .attribution {
font-style:normal;
text-align:right;
color:#000;
}
.exampleblock-content *:first-child { margin-top:0 }
.exampleblock-content {
border-left:2px solid silver;
padding-left:8px;
}
#footer {
font-size:11px;
margin-top:40px;
border-top:1px solid silver;
color:#555;
}
#author {
color:#000;
text-transform:uppercase
}

View File

@ -92,3 +92,15 @@ Feature: Create sites
When I debug jekyll
Then the _site directory should exist
And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html"
Scenario: Basic site with internal post linking
Given I have an "index.html" page that contains "URL: {% post_url 2020-01-31-entry2 %}"
And I have a configuration file with "permalink" set to "pretty"
And I have a _posts directory
And I have the following posts:
| title | date | layout | content |
| entry1 | 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 "URL: /2020/01/31/entry2/" in "_site/index.html"

48
g.pl
View File

@ -1,48 +0,0 @@
#!/usr/bin/perl
#fetch Gravatars
use strict;
use warnings;
use LWP::Simple;
use Digest::MD5 qw(md5_hex);
my $size = 90;
my $output_dir = '.git/avatar';
die("no .git/ directory found in current path\n") unless -d '.git';
mkdir($output_dir) unless -d $output_dir;
open(GITLOG, q/git log --pretty=format:"%ae|%an" |/) or die("failed to read git-log: $!\n");
my %processed_authors;
while(<GITLOG>) {
chomp;
my($email, $author) = split(/\|/, $_);
next if $processed_authors{$author}++;
my $author_image_file = $output_dir . '/' . $author . '.png';
#skip images we have
next if -e $author_image_file;
#try and fetch image
my $grav_url = "http://www.gravatar.com/avatar/".md5_hex(lc $email)."?d=404&size=".$size;
warn "fetching image for '$author' $email ($grav_url)...\n";
my $rc = getstore($grav_url, $author_image_file);
sleep(1);
if($rc != 200) {
unlink($author_image_file);
next;
}
}
close GITLOG;

View File

@ -4,8 +4,8 @@ Gem::Specification.new do |s|
s.rubygems_version = '1.3.5'
s.name = 'jekyll'
s.version = '0.11.0'
s.date = '2011-07-10'
s.version = '0.11.2'
s.date = '2011-12-27'
s.rubyforge_project = 'jekyll'
s.summary = "A simple, blog aware, static site generator."
@ -22,20 +22,22 @@ Gem::Specification.new do |s|
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_runtime_dependency('liquid', "~> 2.3")
s.add_runtime_dependency('classifier', "~> 1.3")
s.add_runtime_dependency('directory_watcher', "~> 1.1")
s.add_runtime_dependency('maruku', "~> 0.5")
s.add_runtime_dependency('kramdown', "~> 0.13")
s.add_runtime_dependency('albino', "~> 1.3")
s.add_development_dependency('redgreen', ">= 1.2.2")
s.add_development_dependency('shoulda', ">= 2.11.3")
s.add_development_dependency('rr', ">= 1.0.2")
s.add_development_dependency('cucumber', ">= 0.10.3")
s.add_development_dependency('RedCloth', ">= 4.2.1")
s.add_development_dependency('rdiscount', ">= 1.6.5")
s.add_development_dependency('redcarpet', ">= 1.9.0")
s.add_development_dependency('rake', "~> 0.9")
s.add_development_dependency('rdoc', "~> 3.11")
s.add_development_dependency('redgreen', "~> 1.2")
s.add_development_dependency('shoulda', "~> 2.11")
s.add_development_dependency('rr', "~> 1.0")
s.add_development_dependency('cucumber', "1.1")
s.add_development_dependency('RedCloth', "~> 4.2")
s.add_development_dependency('rdiscount', "~> 1.6")
s.add_development_dependency('redcarpet', "~> 1.9")
# = MANIFEST =
s.files = %w[
@ -46,14 +48,6 @@ Gem::Specification.new do |s|
Rakefile
bin/jekyll
cucumber.yml
doc/output/book.html
doc/output/ch00-preface.asc
doc/output/ch01-quick-start.asc
doc/output/ch02-directory-layout.asc
doc/output/stylesheets/handbookish-quirks.css
doc/output/stylesheets/handbookish.css
doc/output/stylesheets/scribe-quirks.css
doc/output/stylesheets/scribe.css
features/create_sites.feature
features/embed_filters.feature
features/markdown.feature
@ -64,7 +58,6 @@ Gem::Specification.new do |s|
features/site_data.feature
features/step_definitions/jekyll_steps.rb
features/support/env.rb
g.pl
jekyll.gemspec
lib/jekyll.rb
lib/jekyll/converter.rb
@ -97,8 +90,6 @@ Gem::Specification.new do |s|
lib/jekyll/static_file.rb
lib/jekyll/tags/highlight.rb
lib/jekyll/tags/include.rb
output/stylesheets/scribe-quirks.css
output/stylesheets/scribe.css
test/helper.rb
test/source/.htaccess
test/source/_includes/sig.markdown
@ -152,6 +143,7 @@ Gem::Specification.new do |s|
test/test_redcarpet.rb
test/test_site.rb
test/test_tags.rb
test/test_redcloth.rb
]
# = MANIFEST =

View File

@ -46,7 +46,7 @@ require_all 'jekyll/generators'
require_all 'jekyll/tags'
module Jekyll
VERSION = '0.11.0'
VERSION = '0.11.2'
# Default options. Overriden by values in _config.yml or command-line opts.
# (Strings rather symbols used for compatability with YAML).
@ -65,7 +65,8 @@ module Jekyll
'pygments' => false,
'markdown' => 'maruku',
'permalink' => 'date',
'include' => ['.htaccess'],
'markdown_ext' => 'markdown,mkd,mkdn,md',
'textile_ext' => 'textile',
@ -97,6 +98,9 @@ module Jekyll
'coderay_bold_every' => 10,
'coderay_css' => 'style'
}
},
'redcloth' => {
'hard_breaks' => true
}
}

View File

@ -27,7 +27,23 @@ module Jekyll
def convert(content)
setup
RedCloth.new(content).to_html
# Shortcut if config doesn't contain RedCloth section
return RedCloth.new(content).to_html if @config['redcloth'].nil?
# List of attributes defined on RedCloth
# (from http://redcloth.rubyforge.org/classes/RedCloth/TextileDoc.html)
attrs = ['filter_classes', 'filter_html', 'filter_ids', 'filter_styles',
'hard_breaks', 'lite_mode', 'no_span_caps', 'sanitize_html']
r = RedCloth.new(content)
# Set attributes in r if they are NOT nil in the config
attrs.each do |attr|
r.instance_variable_set("@#{attr}".to_sym, @config['redcloth'][attr]) unless @config['redcloth'][attr].nil?
end
r.to_html
end
end

View File

@ -10,6 +10,7 @@ require 'set'
# self.data=
# self.ext=
# self.output=
# self.name
module Jekyll
module Convertible
# Returns the contents as a String.
@ -26,14 +27,13 @@ module Jekyll
def read_yaml(base, name)
self.content = File.read(File.join(base, name))
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = $POSTMATCH
begin
begin
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = $POSTMATCH
self.data = YAML.load($1)
rescue => e
puts "YAML Exception reading #{name}: #{e.message}"
end
rescue => e
puts "YAML Exception reading #{name}: #{e.message}"
end
self.data ||= {}

View File

@ -14,19 +14,24 @@ module Jekyll
# 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"
QUERY = "SELECT n.nid, \
n.title, \
nr.body, \
n.created, \
n.status \
FROM node AS n, \
node_revisions AS nr \
WHERE (n.type = 'blog' OR n.type = 'story') \
AND n.vid = nr.vid"
def self.process(dbname, user, pass, host = 'localhost')
def self.process(dbname, user, pass, host = 'localhost', prefix = '')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
if prefix != ''
QUERY[" node "] = " " + prefix + "node "
QUERY[" node_revisions "] = " " + prefix + "node_revisions "
end
FileUtils.mkdir_p "_posts"
FileUtils.mkdir_p "_drafts"
@ -73,12 +78,18 @@ EOF
# 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 "---"
aliases = db["SELECT dst FROM #{prefix}url_alias WHERE src = ?", "node/#{node_id}"].all
aliases.push(:dst => "node/#{node_id}")
aliases.each do |url_alias|
FileUtils.mkdir_p url_alias[:dst]
File.open("#{url_alias[:dst]}/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
end

View File

@ -0,0 +1,53 @@
require 'rubygems'
require 'sequel'
require 'fileutils'
require 'yaml'
# NOTE: This migrator is made for Joomla 1.5 databases.
# 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 Joomla
def self.process(dbname, user, pass, host = 'localhost', table_prefix = 'jos_', section = '1')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
FileUtils.mkdir_p("_posts")
# Reads a MySQL database via Sequel and creates a post file for each
# post in wp_posts that has post_status = 'publish'. This restriction is
# made because 'draft' posts are not guaranteed to have valid dates.
query = "SELECT `title`, `alias`, CONCAT(`introtext`,`fulltext`) as content, `created`, `id` FROM #{table_prefix}content WHERE state = '0' OR state = '1' AND sectionid = '#{section}'"
db[query].each do |post|
# Get required fields and construct Jekyll compatible name.
title = post[:title]
slug = post[:alias]
date = post[:created]
content = post[:content]
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
slug]
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header.
data = {
'layout' => 'post',
'title' => title.to_s,
'joomla_id' => post[:id],
'joomla_url' => post[:alias],
'date' => date
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
# Write out the data and content to file
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
end
end
end
end
end

View File

@ -29,12 +29,17 @@ module Jekyll
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
when Net::HTTPForbidden then
retry_after = response.to_hash['retry-after'][0]
puts "We have been told to try again after #{retry_after} seconds"
sleep(retry_after.to_i + 1)
fetch(uri_str, limit - 1)
else response.error!
end
end
def self.process(email, pass, api_token, blog = 'primary', tags_key = 'categories')
@email, @pass , @api_token = email, pass, api_token
@email, @pass, @api_token = email, pass, api_token
FileUtils.mkdir_p "_posts"
posts = JSON.parse(self.fetch("/api/2/sites/#{blog}/posts?api_token=#{@api_token}").body)

View File

@ -0,0 +1,47 @@
# Created by Kendall Buchanan (https://github.com/kendagriff) on 2011-12-22.
# Use at your own risk. The end.
#
# Usage:
# (URL)
# ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('http://yourdomain.com/your-favorite-feed.xml')"
#
# (Local file)
# ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('./somefile/on/your/computer.xml')"
require 'rubygems'
require 'rss/1.0'
require 'rss/2.0'
require 'open-uri'
require 'fileutils'
require 'yaml'
module Jekyll
module MigrateRSS
# The `source` argument may be a URL or a local file.
def self.process(source)
content = ""
open(source) { |s| content = s.read }
rss = RSS::Parser.parse(content, false)
raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless rss
rss.items.each do |item|
formatted_date = item.date.strftime('%Y-%m-%d')
post_name = item.title.split(%r{ |!|/|:|&|-|$|,}).map { |i| i.downcase if i != '' }.compact.join('-')
name = "#{formatted_date}-#{post_name}"
header = {
'layout' => 'post',
'title' => item.title
}
File.open("_posts/#{name}.html", "w") do |f|
f.puts header.to_yaml
f.puts "---\n"
f.puts item.description
end
end
end
end
end

View File

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

View File

@ -1,119 +1,195 @@
require 'rubygems'
require 'nokogiri'
require 'open-uri'
require 'fileutils'
require 'CGI'
require 'iconv'
require 'nokogiri'
require 'date'
require 'json'
require 'uri'
require 'jekyll'
module Jekyll
module Tumblr
def self.process(url, grab_images = false)
current_page = 0
while true
f = open(url + "/api/read?num=50&start=#{current_page * 50}")
doc = Nokogiri::HTML(Iconv.conv("utf-8", f.charset, f.readlines.join("\n")))
puts "Page: #{current_page + 1} - Posts: #{(doc/:tumblr/:posts/:post).size}"
FileUtils.mkdir_p "_posts/tumblr"
(doc/:tumblr/:posts/:post).each do |post|
title = ""
content = nil
name = nil
if post['type'] == "regular"
title_element = post.at("regular-title")
title = title_element.inner_text unless title_element == nil
content = CGI::unescapeHTML post.at("regular-body").inner_html unless post.at("regular-body") == nil
elsif post['type'] == "link"
title = post.at("link-text").inner_html unless post.at("link-text") == nil
if post.at("link-text") != nil
content = "<a href=\"#{post.at("link-url").inner_html}\">#{post.at("link-text").inner_html}</a>"
else
content = "<a href=\"#{post.at("link-url").inner_html}\">#{post.at("link-url").inner_html}</a>"
end
content << "<br/>" + CGI::unescapeHTML(post.at("link-description").inner_html) unless post.at("link-description") == nil
elsif post['type'] == "photo"
content = ""
if post.at("photo-link-url") != nil
content = "<a href=\"#{post.at("photo-link-url").inner_html}\"><img src=\"#{save_file((post/"photo-url")[1].inner_html, grab_images)}\"/></a>"
else
content = "<img src=\"#{save_file((post/"photo-url")[1].inner_html, grab_images)}\"/>"
end
if post.at("photo-caption") != nil
content << "<br/>" unless content == nil
content << CGI::unescapeHTML(post.at("photo-caption").inner_html)
end
elsif post['type'] == "audio"
content = CGI::unescapeHTML(post.at("audio-player").inner_html)
content << CGI::unescapeHTML(post.at("audio-caption").inner_html) unless post.at("audio-caption") == nil
elsif post['type'] == "quote"
content = "<blockquote>" + CGI::unescapeHTML(post.at("quote-text").inner_html) + "</blockquote>"
content << "&#8212;" + CGI::unescapeHTML(post.at("quote-source").inner_html) unless post.at("quote-source") == nil
elsif post['type'] == "conversation"
title = post.at("conversation-title").inner_html unless post.at("conversation-title") == nil
content = "<section><dialog>"
(post/:conversation/:line).each do |line|
content << "<dt>" + line['label'] + "</dt><dd>" + line.inner_html + "</dd>" unless line['label'] == nil || line == nil
end
content << "</section></dialog>"
elsif post['type'] == "video"
title = post.at("video-title").inner_html unless post.at("video-title") == nil
content = CGI::unescapeHTML(post.at("video-player").inner_html)
content << CGI::unescapeHTML(post.at("video-caption").inner_html) unless post.at("video-caption") == nil
end # End post types
name = "#{Date.parse(post['date']).to_s}-#{post['id'].downcase.gsub(/[^a-z0-9]/, '-')}.html"
if title != nil || content != nil && name != nil
File.open("_posts/tumblr/#{name}", "w") do |f|
f.puts <<-HEADER
---
layout: post
title: #{title}
---
HEADER
f.puts content
end # End file
end
end # End post XML
if (doc/:tumblr/:posts/:post).size < 50
break
else
current_page = current_page + 1
def self.process(url, format = "html", grab_images = false,
add_highlights = false, rewrite_urls = true)
@grab_images = grab_images
FileUtils.mkdir_p "_posts/tumblr"
url += "/api/read/json/"
per_page = 50
posts = []
# Two passes are required so that we can rewrite URLs.
# First pass builds up an array of each post as a hash.
begin
current_page = (current_page || -1) + 1
feed = open(url + "?num=#{per_page}&start=#{current_page * per_page}")
json = feed.readlines.join("\n")[21...-2] # Strip Tumblr's JSONP chars.
blog = JSON.parse(json)
puts "Page: #{current_page + 1} - Posts: #{blog["posts"].size}"
posts += blog["posts"].map { |post| post_to_hash(post, format) }
end until blog["posts"].size < per_page
# Rewrite URLs and create redirects.
posts = rewrite_urls_and_redirects posts if rewrite_urls
# Second pass for writing post files.
posts.each do |post|
if format == "md"
post[:content] = html_to_markdown post[:content]
post[:content] = add_syntax_highlights post[:content] if add_highlights
end
end # End while loop
end # End method
File.open("_posts/tumblr/#{post[:name]}", "w") do |f|
f.puts post[:header].to_yaml + "---\n" + post[:content]
end
end
end
private
def self.save_file(url, grab_image = false)
unless grab_image == false
FileUtils.mkdir_p "tumblr_files"
File.open("tumblr_files/#{url.split('/').last}", "w") do |f|
f.write(open(url).read)
end
return "/tumblr_files/#{url.split('/').last}"
else
return url
# Converts each type of Tumblr post to a hash with all required
# data for Jekyll.
def self.post_to_hash(post, format)
case post['type']
when "regular"
title = post["regular-title"]
content = post["regular-body"]
when "link"
title = post["link-text"] || post["link-url"]
content = "<a href=\"#{post["link-url"]}\">#{title}</a>"
unless post["link-description"].nil?
content << "<br/>" + post["link-description"]
end
when "photo"
title = post["photo-caption"]
max_size = post.keys.map{ |k| k.gsub("photo-url-", "").to_i }.max
url = post["photo-url"] || post["photo-url-#{max_size}"]
ext = "." + post[post.keys.select { |k|
k =~ /^photo-url-/ && post[k].split("/").last =~ /\./
}.first].split(".").last
content = "<img src=\"#{save_file(url, ext)}\"/>"
unless post["photo-link-url"].nil?
content = "<a href=\"#{post["photo-link-url"]}\">#{content}</a>"
end
when "audio"
if !post["id3-title"].nil?
title = post["id3-title"]
content = post.at["audio-player"] + "<br/>" + post["audio-caption"]
else
title = post["audio-caption"]
content = post.at["audio-player"]
end
when "quote"
title = post["quote-text"]
content = "<blockquote>#{post["quote-text"]}</blockquote>"
unless post["quote-source"].nil?
content << "&#8212;" + post["quote-source"]
end
when "conversation"
title = post["conversation-title"]
content = "<section><dialog>"
post["conversation"]["line"].each do |line|
content << "<dt>#{line['label']}</dt><dd>#{line}</dd>"
end
content << "</section></dialog>"
when "video"
title = post["video-title"]
content = post["video-player"]
unless post["video-caption"].nil?
content << "<br/>" + post["video-caption"]
end
end
date = Date.parse(post['date']).to_s
title = Nokogiri::HTML(title).text
slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
{
:name => "#{date}-#{slug}.#{format}",
:header => {
"layout" => "post",
"title" => title,
"tags" => post["tags"],
},
:content => content,
:url => post["url"],
:slug => post["url-with-slug"],
}
end
# Create a Hash of old urls => new urls, for rewriting and
# redirects, and replace urls in each post. Instantiate Jekyll
# site/posts to get the correct permalink format.
def self.rewrite_urls_and_redirects(posts)
site = Jekyll::Site.new(Jekyll.configuration({}))
dir = File.join(File.dirname(__FILE__), "..")
urls = Hash[posts.map { |post|
# Create an initial empty file for the post so that
# we can instantiate a post object.
File.open("_posts/tumblr/#{post[:name]}", "w")
tumblr_url = URI.parse(post[:slug]).path
jekyll_url = Jekyll::Post.new(site, dir, "", "tumblr/" + post[:name]).url
redirect_dir = tumblr_url.sub(/\//, "") + "/"
FileUtils.mkdir_p redirect_dir
File.open(redirect_dir + "index.html", "w") do |f|
f.puts "<html><head><meta http-equiv='Refresh' content='0; " +
"url=#{jekyll_url}'></head><body></body></html>"
end
[tumblr_url, jekyll_url]
}]
posts.map { |post|
urls.each do |tumblr_url, jekyll_url|
post[:content].gsub!(/#{tumblr_url}/i, jekyll_url)
end
post
}
end
# Uses Python's html2text to convert a post's content to
# markdown. Preserve HTML tables as per the markdown docs.
def self.html_to_markdown(content)
preserve = ["table", "tr", "th", "td"]
preserve.each do |tag|
content.gsub!(/<#{tag}/i, "$$" + tag)
content.gsub!(/<\/#{tag}/i, "||" + tag)
end
content = %x[echo '#{content.gsub("'", "''")}' | html2text]
preserve.each do |tag|
content.gsub!("$$" + tag, "<" + tag)
content.gsub!("||" + tag, "</" + tag)
end
content
end
# Adds pygments highlight tags to code blocks in posts that use
# markdown format. This doesn't guess the language of the code
# block, so you should modify this to suit your own content.
# For example, my code block only contain Python and JavaScript,
# so I can assume the block is JavaScript if it contains a
# semi-colon.
def self.add_syntax_highlights(content)
lines = content.split("\n")
block, indent, lang, start = false, /^ /, nil, nil
lines.each_with_index do |line, i|
if !block && line =~ indent
block = true
lang = "python"
start = i
elsif block
lang = "javascript" if line =~ /;$/
block = line =~ indent && i < lines.size - 1 # Also handle EOF
if !block
lines[start] = "{% highlight #{lang} %}"
lines[i - 1] = "{% endhighlight %}"
end
lines[i] = lines[i].sub(indent, "")
end
end
lines.join("\n")
end
def self.save_file(url, ext)
if @grab_images
path = "tumblr_files/#{url.split('/').last}"
path += ext unless path =~ /#{ext}$/
FileUtils.mkdir_p "tumblr_files"
File.open(path, "w") { |f| f.write(open(url).read) }
url = "/" + path
end
url
end
end
end

View File

@ -11,52 +11,284 @@ require 'yaml'
module Jekyll
module WordPress
def self.process(dbname, user, pass, host = 'localhost', table_prefix = 'wp_')
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
# Main migrator function. Call this to perform the migration.
#
# dbname:: The name of the database
# user:: The database user name
# pass:: The database user's password
# host:: The address of the MySQL database host. Default: 'localhost'
# options:: A hash table of configuration options.
#
# Supported options are:
#
# :table_prefix:: Prefix of database tables used by WordPress.
# Default: 'wp_'
# :clean_entities:: If true, convert non-ASCII characters to HTML
# entities in the posts, comments, titles, and
# names. Requires the 'htmlentities' gem to
# work. Default: true.
# :comments:: If true, migrate post comments too. Comments
# are saved in the post's YAML front matter.
# Default: true.
# :categories:: If true, save the post's categories in its
# YAML front matter.
# :tags:: If true, save the post's tags in its
# YAML front matter.
# :more_excerpt:: If true, when a post has no excerpt but
# does have a <!-- more --> tag, use the
# preceding post content as the excerpt.
# Default: true.
# :more_anchor:: If true, convert a <!-- more --> tag into
# two HTML anchors with ids "more" and
# "more-NNN" (where NNN is the post number).
# Default: true.
# :status:: Array of allowed post statuses. Only
# posts with matching status will be migrated.
# Known statuses are :publish, :draft, :private,
# and :revision. If this is nil or an empty
# array, all posts are migrated regardless of
# status. Default: [:publish].
#
def self.process(dbname, user, pass, host='localhost', options={})
options = {
:table_prefix => 'wp_',
:clean_entities => true,
:comments => true,
:categories => true,
:tags => true,
:more_excerpt => true,
:more_anchor => true,
:status => [:publish] # :draft, :private, :revision
}.merge(options)
if options[:clean_entities]
begin
require 'htmlentities'
rescue LoadError
STDERR.puts "Could not require 'htmlentities', so the " +
":clean_entities option is now disabled."
options[:clean_entities] = false
end
end
FileUtils.mkdir_p("_posts")
# Reads a MySQL database via Sequel and creates a post file for each
# post in wp_posts that has post_status = 'publish'. This restriction is
# made because 'draft' posts are not guaranteed to have valid dates.
query = "SELECT post_title, \
post_name, \
post_date, \
post_content, \
post_excerpt, \
ID, \
guid \
FROM #{table_prefix}posts \
WHERE post_status = 'publish' AND \
post_type = 'post'"
db = Sequel.mysql(dbname, :user => user, :password => pass,
:host => host, :encoding => 'utf8')
db[query].each do |post|
# Get required fields and construct Jekyll compatible name.
title = post[:post_title]
slug = post[:post_name]
date = post[:post_date]
content = post[:post_content]
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
slug]
px = options[:table_prefix]
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header.
data = {
'layout' => 'post',
'title' => title.to_s,
'excerpt' => post[:post_excerpt].to_s,
'wordpress_id' => post[:ID],
'wordpress_url' => post[:guid],
'date' => date
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
posts_query = "
SELECT
posts.ID AS `id`,
posts.guid AS `guid`,
posts.post_type AS `type`,
posts.post_status AS `status`,
posts.post_title AS `title`,
posts.post_name AS `slug`,
posts.post_date AS `date`,
posts.post_content AS `content`,
posts.post_excerpt AS `excerpt`,
posts.comment_count AS `comment_count`,
users.display_name AS `author`,
users.user_login AS `author_login`,
users.user_email AS `author_email`,
users.user_url AS `author_url`
FROM #{px}posts AS `posts`
LEFT JOIN #{px}users AS `users`
ON posts.post_author = users.ID"
# Write out the data and content to file
File.open("_posts/#{name}", "w") do |f|
f.puts data
f.puts "---"
f.puts content
if options[:status] and not options[:status].empty?
status = options[:status][0]
posts_query << "
WHERE posts.post_status = '#{status.to_s}'"
options[:status][1..-1].each do |status|
posts_query << " OR
posts.post_status = '#{status.to_s}'"
end
end
db[posts_query].each do |post|
process_post(post, db, options)
end
end
def self.process_post(post, db, options)
px = options[:table_prefix]
title = post[:title]
if options[:clean_entities]
title = clean_entities(title)
end
slug = post[:slug]
if !slug or slug.empty?
slug = sluggify(title)
end
date = post[:date] || Time.now
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month,
date.day, slug]
content = post[:content].to_s
if options[:clean_entities]
content = clean_entities(content)
end
excerpt = post[:excerpt].to_s
more_index = content.index(/<!-- *more *-->/)
more_anchor = nil
if more_index
if options[:more_excerpt] and
(post[:excerpt].nil? or post[:excerpt].empty?)
excerpt = content[0...more_index]
end
if options[:more_anchor]
more_link = "more"
content.sub!(/<!-- *more *-->/,
"<a id=\"more\"></a>" +
"<a id=\"more-#{post[:id]}\"></a>")
end
end
categories = []
tags = []
if options[:categories] or options[:tags]
cquery =
"SELECT
terms.name AS `name`,
ttax.taxonomy AS `type`
FROM
#{px}terms AS `terms`,
#{px}term_relationships AS `trels`,
#{px}term_taxonomy AS `ttax`
WHERE
trels.object_id = '#{post[:id]}' AND
trels.term_taxonomy_id = ttax.term_taxonomy_id AND
terms.term_id = ttax.term_id"
db[cquery].each do |term|
if options[:categories] and term[:type] == "category"
if options[:clean_entities]
categories << clean_entities(term[:name])
else
categories << term[:name]
end
elsif options[:tags] and term[:type] == "post_tag"
if options[:clean_entities]
tags << clean_entities(term[:name])
else
tags << term[:name]
end
end
end
end
comments = []
if options[:comments] and post[:comment_count].to_i > 0
cquery =
"SELECT
comment_ID AS `id`,
comment_author AS `author`,
comment_author_email AS `author_email`,
comment_author_url AS `author_url`,
comment_date AS `date`,
comment_date_gmt AS `date_gmt`,
comment_content AS `content`
FROM #{px}comments
WHERE
comment_post_ID = '#{post[:id]}' AND
comment_approved != 'spam'"
db[cquery].each do |comment|
comcontent = comment[:content].to_s
if comcontent.respond_to?(:force_encoding)
comcontent.force_encoding("UTF-8")
end
if options[:clean_entities]
comcontent = clean_entities(comcontent)
end
comauthor = comment[:author].to_s
if options[:clean_entities]
comauthor = clean_entities(comauthor)
end
comments << {
'id' => comment[:id].to_i,
'author' => comauthor,
'author_email' => comment[:author_email].to_s,
'author_url' => comment[:author_url].to_s,
'date' => comment[:date].to_s,
'date_gmt' => comment[:date_gmt].to_s,
'content' => comcontent,
}
end
comments.sort!{ |a,b| a['id'] <=> b['id'] }
end
# Get the relevant fields as a hash, delete empty fields and
# convert to YAML for the header.
data = {
'layout' => post[:type].to_s,
'status' => post[:status].to_s,
'published' => (post[:status].to_s == "publish"),
'title' => title.to_s,
'author' => post[:author].to_s,
'author_login' => post[:author_login].to_s,
'author_email' => post[:author_email].to_s,
'author_url' => post[:author_url].to_s,
'excerpt' => excerpt,
'more_anchor' => more_anchor,
'wordpress_id' => post[:id],
'wordpress_url' => post[:guid].to_s,
'date' => date,
'categories' => options[:categories] ? categories : nil,
'tags' => options[:tags] ? tags : nil,
'comments' => options[:comments] ? comments : nil,
}.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
def self.clean_entities( text )
if text.respond_to?(:force_encoding)
text.force_encoding("UTF-8")
end
text = HTMLEntities.new.encode(text, :named)
# We don't want to convert these, it would break all
# HTML tags in the post and comments.
text.gsub!("&amp;", "&")
text.gsub!("&lt;", "<")
text.gsub!("&gt;", ">")
text.gsub!("&quot;", '"')
text.gsub!("&apos;", "'")
text
end
def self.sluggify( title )
begin
require 'unidecode'
title = title.to_ascii
rescue LoadError
STDERR.puts "Could not require 'unidecode'. If your post titles have non-ASCII characters, you could get nicer permalinks by installing unidecode."
end
title.downcase.gsub(/[^0-9A-Za-z]+/, " ").strip.gsub(" ", "-")
end
end
end

View File

@ -22,6 +22,8 @@ module Jekyll
attr_accessor :data, :content, :output, :ext
attr_accessor :date, :slug, :published, :tags, :categories
attr_reader :name
# Initialize this Post instance.
# +site+ is the Site
# +base+ is the String path to the dir containing the post file
@ -131,7 +133,7 @@ module Jekyll
"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('/'),
"categories" => categories.map { |c| URI.escape(c) }.join('/'),
"output_ext" => self.output_ext
}.inject(template) { |result, token|
result.gsub(/:#{Regexp.escape token.first}/, token.last)

View File

@ -4,7 +4,7 @@ module Jekyll
class Site
attr_accessor :config, :layouts, :posts, :pages, :static_files,
:categories, :exclude, :source, :dest, :lsi, :pygments,
:categories, :exclude, :include, :source, :dest, :lsi, :pygments,
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts
attr_accessor :converters, :generators
@ -23,6 +23,7 @@ module Jekyll
self.pygments = config['pygments']
self.permalink_style = config['permalink'].to_sym
self.exclude = config['exclude'] || []
self.include = config['include'] || []
self.future = config['future']
self.limit_posts = config['limit_posts'] || nil
@ -173,7 +174,10 @@ module Jekyll
self.posts.sort!
# limit the posts if :limit_posts option is set
self.posts = self.posts[-limit_posts, limit_posts] if limit_posts
if limit_posts
limit = self.posts.length < limit_posts ? self.posts.length : limit_posts
self.posts = self.posts[-limit, limit]
end
end
# Run each of the Generators.
@ -305,7 +309,7 @@ module Jekyll
# Returns the Array of filtered entries.
def filter_entries(entries)
entries = entries.reject do |e|
unless ['.htaccess'].include?(e)
unless self.include.include?(e)
['.', '_', '#'].include?(e[0..0]) ||
e[-1..-1] == '~' ||
self.exclude.include?(e) ||

View File

@ -3,14 +3,19 @@ module Jekyll
class HighlightBlock < Liquid::Block
include Liquid::StandardFilters
# We need a language, but the linenos argument is optional.
SYNTAX = /(\w+)\s?([\w\s=]+)*/
# The regular expression syntax checker. Start with the language specifier.
# Follow that by zero or more space separated options that take one of two
# forms:
#
# 1. name
# 2. name=value
SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=\w+)?)*)$/
def initialize(tag_name, markup, tokens)
super
if markup =~ SYNTAX
if markup.strip =~ SYNTAX
@lang = $1
if defined? $2
if defined?($2) && $2 != ''
tmp_options = {}
$2.split.each do |opt|
key, value = opt.split('=')
@ -23,7 +28,7 @@ module Jekyll
end
tmp_options[key] = value
end
tmp_options = tmp_options.to_a.collect { |opt| opt.join('=') }
tmp_options = tmp_options.to_a.sort.collect { |opt| opt.join('=') }
# additional options to pass to Albino
@options = { 'O' => tmp_options.join(',') }
else
@ -36,9 +41,9 @@ module Jekyll
def render(context)
if context.registers[:site].pygments
render_pygments(context, super.join)
render_pygments(context, super)
else
render_codehighlighter(context, super.join)
render_codehighlighter(context, super)
end
end
@ -53,9 +58,7 @@ module Jekyll
#The div is required because RDiscount blows ass
<<-HTML
<div>
<pre>
<code class='#{@lang}'>#{h(code).strip}</code>
</pre>
<pre><code class='#{@lang}'>#{h(code).strip}</code></pre>
</div>
HTML
end

View File

@ -0,0 +1,38 @@
module Jekyll
class PostComparer
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)$/
attr_accessor :date, :slug
def initialize(name)
who, cares, date, slug = *name.match(MATCHER)
@slug = slug
@date = Time.parse(date)
end
end
class PostUrl < Liquid::Tag
def initialize(tag_name, post, tokens)
super
@orig_post = post.strip
@post = PostComparer.new(@orig_post)
end
def render(context)
site = context.registers[:site]
site.posts.each do |p|
if p == @post
return p.url
end
end
puts "ERROR: post_url: \"#{@orig_post}\" could not be found"
return "#"
end
end
end
Liquid::Template.register_tag('post_url', Jekyll::PostUrl)

View File

@ -1,177 +0,0 @@
/* ---------------------------------------------------------------------------
Bare AsciiDoc styles
Ryan Tomayko <r@tomayko.com>
--------------------------------------------------------------------------- */
body {
font-family:verdana,helvetica,arial,sans-serif;
font-size:81.25%; /* 13px */
line-height:1.538; /* 20px */
margin:40px auto 50px auto;
max-width:53.8461538462em; /* 790px */
color:#333;
}
em {
font-style:italic;
}
strong {
font-weight:bold;
color:#000;
}
tt {
font-family:consolas, 'lucida console', 'bitstream vera sans mono',
'courier new', monospace;
color:#000;
}
p, ul, ol, dl {
margin:10px 0
}
dl {
margin-left:40px
}
dt {
font-weight:normal;
color:#000;
}
h1, h2, h3, h4, h5 {
font-family:'lucida grande',georgia,verdana,helvetica,arial,sans-serif;
font-weight:normal;
color:#000;
}
h1 {
font-size:40px;
line-height:1.428;
margin:20px 0;
}
h2 {
font-size:30px;
line-height:1.36363636; /* repeating, of course */
margin:60px 0 20px 0;
}
h2 + .sectionbody {}
h3 {
font-size:24px;
line-height:1.1;
margin:30px 0 10px 0;
}
h4 {
font-size:18px;
line-height:1.1;
margin:20px 0 5px 0;
}
h5 {
font-size:13px;
font-style:italic;
line-height:1.1;
}
#header {
text-align:center;
margin-bottom:30px;
}
#header h1 { margin-bottom:0 }
.title, .sidebar-title {
font-weight:normal;
color:#000;
margin-bottom:0;
}
.admonitionblock .title {
font-weight:bold;
}
.admonitionblock {
margin:30px 0px;
color:#555;
}
.admonitionblock td.icon {
width:30px;
padding-right:20px;
padding-left:20px;
text-transform:uppercase;
font-weight:bold;
color:#888;
}
.listingblock {
margin: 13px 0;
}
.listingblock .content {
border:1px solid silver;
background:#eee;
padding:5px;
}
.listingblock .content pre {
margin:0;
}
.literalblock .content {
margin-left:40px;
}
.verseblock .content {
white-space:pre
}
.sidebarblock .sidebar-content {
border:1px solid silver;
background:#FFFFEE;
padding:0 10px;
color:#222;
font-size:smaller;
line-height:1.5;
}
.sidebar-title {
margin:10px 0;
font-weight:bold;
color:#442;
}
.quoteblock-content {
font-style:italic;
color:#444;
margin-left:40px;
}
.quoteblock-content .attribution {
font-style:normal;
text-align:right;
color:#000;
}
.exampleblock-content *:first-child { margin-top:0 }
.exampleblock-content {
border-left:2px solid silver;
padding-left:8px;
}
#footer {
font-size:11px;
margin-top:40px;
border-top:1px solid silver;
color:#555;
}
#author {
color:#000;
text-transform:uppercase
}

View File

@ -141,6 +141,19 @@ class TestPost < Test::Unit::TestCase
end
end
context "with space (categories)" do
setup do
@post.categories << "French cuisine"
@post.categories << "Belgian beer"
@post.process(@fake_file)
end
should "process the url correctly" do
assert_equal "/:categories/:year/:month/:day/:title.html", @post.template
assert_equal "/French%20cuisine/Belgian%20beer/2008/09/09/foo-bar.html", @post.url
end
end
context "with none style" do
setup do
@post.site.permalink_style = :none

86
test/test_redcloth.rb Normal file
View File

@ -0,0 +1,86 @@
require File.dirname(__FILE__) + '/helper'
class TestRedCloth < Test::Unit::TestCase
context "RedCloth default (no explicit config) hard_breaks enabled" do
setup do
@textile = TextileConverter.new
end
should "preserve single line breaks in HTML output" do
assert_equal "<p>line1<br />\nline2</p>", @textile.convert("p. line1\nline2").strip
end
end
context "Default hard_breaks enabled w/ redcloth section, no hard_breaks value" do
setup do
config = {
'redcloth' => {}
}
@textile = TextileConverter.new config
end
should "preserve single line breaks in HTML output" do
assert_equal "<p>line1<br />\nline2</p>", @textile.convert("p. line1\nline2").strip
end
end
context "RedCloth with hard_breaks enabled" do
setup do
config = {
'redcloth' => {
'hard_breaks' => true # default
}
}
@textile = TextileConverter.new config
end
should "preserve single line breaks in HTML output" do
assert_equal "<p>line1<br />\nline2</p>", @textile.convert("p. line1\nline2").strip
end
end
context "RedCloth with hard_breaks disabled" do
setup do
config = {
'redcloth' => {
'hard_breaks' => false
}
}
@textile = TextileConverter.new config
end
should "not generate break tags in HTML output" do
assert_equal "<p>line1\nline2</p>", @textile.convert("p. line1\nline2").strip
end
end
context "RedCloth w/no_span_caps set to false" do
setup do
config = {
'redcloth' => {
'no_span_caps' => false
}
}
@textile = TextileConverter.new config
end
should "generate span tags around capitalized words" do
assert_equal "<p><span class=\"caps\">NSC</span></p>", @textile.convert("NSC").strip
end
end
context "RedCloth w/no_span_caps set to true" do
setup do
config = {
'redcloth' => {
'no_span_caps' => true
}
}
@textile = TextileConverter.new config
end
should "not generate span tags around capitalized words" do
assert_equal "<p>NSC</p>", @textile.convert("NSC").strip
end
end
end

View File

@ -126,12 +126,20 @@ class TestSite < Test::Unit::TestCase
should "filter entries with exclude" do
excludes = %w[README TODO]
includes = %w[index.html site.css]
files = %w[index.html site.css .htaccess]
@site.exclude = excludes
assert_equal includes, @site.filter_entries(excludes + includes)
assert_equal files, @site.filter_entries(excludes + files)
end
should "not filter entries within include" do
includes = %w[_index.html .htaccess]
files = %w[index.html _index.html .htaccess]
@site.include = includes
assert_equal files, @site.filter_entries(files)
end
context 'with orphaned files in destination' do
setup do
clear_dest

View File

@ -9,6 +9,11 @@ class TestTags < Test::Unit::TestCase
Jekyll::DEFAULTS.merge({'pygments' => true}).merge(override)
end
site = Site.new(Jekyll.configuration)
if override['read_posts']
site.read_posts('')
end
info = { :filters => [Jekyll::Filters], :registers => { :site => site } }
@converter = site.converters.find { |c| c.class == converter_class }
payload = { "pygments_prefix" => @converter.pygments_prefix,
@ -31,6 +36,41 @@ CONTENT
create_post(content, override)
end
context "language name" do
should "match only the required set of chars" do
r = Jekyll::HighlightBlock::SYNTAX
assert_match r, "ruby"
assert_match r, "c#"
assert_match r, "xml+cheetah"
assert_match r, "x.y"
assert_match r, "coffee-script"
assert_no_match r, "blah^"
assert_match r, "ruby key=val"
assert_match r, "ruby a=b c=d"
end
end
context "initialized tag" do
should "work" do
tag = Jekyll::HighlightBlock.new('highlight', 'ruby ', ["test", "{% endhighlight %}", "\n"])
assert_equal({}, tag.instance_variable_get(:@options))
tag = Jekyll::HighlightBlock.new('highlight', 'ruby linenos ', ["test", "{% endhighlight %}", "\n"])
assert_equal({'O' => "linenos=inline"}, tag.instance_variable_get(:@options))
tag = Jekyll::HighlightBlock.new('highlight', 'ruby linenos=table ', ["test", "{% endhighlight %}", "\n"])
assert_equal({'O' => "linenos=table"}, tag.instance_variable_get(:@options))
tag = Jekyll::HighlightBlock.new('highlight', 'ruby linenos=table nowrap', ["test", "{% endhighlight %}", "\n"])
assert_equal({'O' => "linenos=table,nowrap=true"}, tag.instance_variable_get(:@options))
tag = Jekyll::HighlightBlock.new('highlight', 'ruby linenos=table cssclass=hl', ["test", "{% endhighlight %}", "\n"])
assert_equal({'O' => "cssclass=hl,linenos=table"}, tag.instance_variable_get(:@options))
end
end
context "post content has highlight tag" do
setup do
fill_post("test")
@ -137,4 +177,25 @@ CONTENT
end
end
end
context "simple page with post linking" do
setup do
content = <<CONTENT
---
title: Post linking
---
{% post_url 2008-11-21-complex %}
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
should "not cause an error" do
assert_no_match /markdown\-html\-error/, @result
end
should "have the url to the \"complex\" post from 2008-11-21" do
assert_match %r{/2008/11/21/complex/}, @result
end
end
end