从wordpress迁移到ruhoh的心得。

终于换成用 ruhoh 写博客了。

之前的 blog 使用 WordPress 来编写。但使用 Wordpress 的在线编辑系统是非常低效的。为了加快效率,我习惯先使用 org-mode 来编写内容。org-mode的强大不必多说,用它可以方便的生成本地页面,通过定制甚至还可以导出成pdf。为了在我编写文章的同时备份 org-mode 格式的草稿,我使用了与 Evernote 结合的方案:先利用 emacs-evernote-mode 在 Evernote 上创建笔记,然后开 org-mode 编辑内容。这样,编写的文章除了在博客上可以看到,也可以在 Evernote 上阅读。一旦博客托管的服务器出了问题甚至数据全部丢失,我还能从Evernote上找回我的所有文章。有些文章即使不想发布出去,我也可以保存在 Evernote 上,作为自己的私人笔记。这种方式一直用得挺爽,所以我坚持用了一年多的时间。

但不幸的是,几个月前 Evernote 换了认证协议,以致于 emacs-evernote-mode 不再能够访问 Evernote 的数据。而该项目已然很久没有更新了。没有了 Evernote 的备份支持,我失去了编写文章的冲动,以至于从今年一月份到现在,我的文章产出率几乎为零。

没办法之下,我只好选择弃用 WordPress,投入到 Static Blog Generator 的怀抱。接触静态网站并不是第一次,在这之前我曾经用 webgen 写过一个个人主页,但搭建过程非常繁琐,依赖项太多,给我留下不太好的印象。后来那个项目也停止更新了,我就决定不再使用它。后来看到 Pluskid 又开了一个博客,一来就是一篇洋洋洒洒追述自己如何折腾静态博客的经历,看完之后我对他所使用的一个称为 ruhoh 的项目来了兴趣。于是自己克隆了一个 ruhoh 的 Github 仓库,按照官方说明简单执行了几条命令,就看到了成功生成的主页,顿时挺有好感。

ruhoh 比起 WordPress ,有不少优点:

  1. 简洁轻便。 WordPress太臃肿了,默认就加载一堆的脚本,不符合简单就是美的思想。相比之下,ruhoh 要清爽很多,只提供最基本的功能,剩下的交给用户决定是否添加。
  2. 支持多种文档格式。ruhoh 默认使用 RedCarpet 作为格式转换器,以支持使用流行的 Markdown 来编写文档。只要你愿意,你也可以自己重写格式转换器,使之支持 Textile、reStructuredText、DocBook、MediaWiki 等格式。
  3. 定制性强。使用 Mustache 来做模板语言。这样,高级用户可以很自由的对博客的功能进行定制,而不用担心会影响到文章的内容。
  4. 免费托管。通过与 Github 等支持 Page 服务的托管商来免费托管你的博客,省下白花花的银两。同时依赖于Git(也可以方便的更换为mercurial),博客也能拥有强大的版本控制能力。

不过,ruhoh 也不是十全十美的。不可否认 ruhoh 的学习成本较高,如果想要把它打造的更加顺手,那就更加需要花时间折腾了。而且 ruhoh 还处于开发阶段,比起同类的系统比如 Octopress 而言成熟度要差一些。因此如果你只想尽快用上静态博客,我推荐你选择 Octopress。但如果你需要的是一个更为灵活的框架,那么不妨试试 ruhoh 。

安装配置 Ruhoh

安装 ruhoh 可以使用 bundle 安装和直接安装 gem 两种。前者的更新要快一些,而且用 bundle 升级 ruhoh 系统是采用全新安装的方式,不会覆盖原来的 ruhoh 系统,显得比较安全。特别是现在 ruhoh 还处于开发阶段,说不定某次升级就出问题了,所以强烈推荐使用第一种方案。

安装 Ruby

ruhoh依赖Ruby,Arch用户安装Ruby的方法非常简单,只需要一条命令:

1
$ yaourt -S ruby

换用淘宝 RubyGems 镜像

由于国内网络原因(你懂的),导致 rubygems.org 存放在 Amazon S3 上面的资源文件间歇性连接失败。为了避免安装 ruhoh 失败,最稳妥的方法还是将 rubygems 源改成淘宝的镜像。

1
2
$ gem sources --remove https://rubygems.org/
$ gem sources -a http://ruby.taobao.org/

安装 Ruhoh

先把 ruhoh 的github仓库克隆下来:

1
2
$ git clone 'git://github.com/ruhoh/blog.git' 'blog'
$ cd blog

同样为了让ruhoh的安装顺利,修改一下 Gemfile 文件如下:

1
2
3
source 'http://ruby.taobao.org/'
gem 'psych', "~> 1.3"
gem 'ruhoh', :git => 'git://github.com/ruhoh/ruhoh.rb.git'

安装 bundle:

1
$ gem install bundle

之后安装 ruhoh:

1
2
$ bundle install
$ bundle exec rackup -p 9292

如果遇到 psych 等依赖项没有安装报错的问题,手动 gem install 之。完成后打开 http://localhost:9292/ 就可以看到本地的一个主页了。

端口占用问题的解决

有时候会遇到端口占用问题 (Errno::EADDRINUSE) 而无法rackup成功,可以使用 lsof 命令查看 9292 端口被什么程序占用:

1
$ lsof -i:9292

解决方法是换用其他端口即可:

1
$ rackup -p <其他端口>

文章搬家

接下来要做的事情就是把 WordPress 上的内容搬到ruhoh上,包括文章和评论。如果你之前没有用过 WordPress 搭建过博客,可以跳过这一步。评论的搬家比较容易,使用 Disqus 就可以完成多个网站的同步。如何把WordPress上的文章导出并转成ruhoh这类静态博客所解析的 Markdown,也有不少文章已经讨论过:

  1. 从WordPress迁移到Ruhoh的经验
  2. Ruhoh札记

大致是先利用 WordPress 自带的文章导出功能将文章导出成 xml 文件,然后利用 pandoc 将文档转换成 markdown 脚本。但要无痛完成转换是非常困难的。如果之前的 WordPress 文章添加了不少特殊的 shortcode 的话,你就需要自己修改转换脚本将这些 shortcode 转换成 Markdown 下该有的格式。反正这个过程真是挺折腾的。真是后悔当初不懂事,用了不少特殊的 shortcode,把自己的博客弄得太 重量级 ,给以后的搬家带来不便。

定制 ruhoh

下面简单罗列一下自己做了哪些定制吧。

更换Converter

标准的 Markdown 实在太简单,连对表格的支持都缺乏。关于这个问题已经有很多人吐槽过了。

相比之下,Pandoc-Markdown 就要强大许多。而且 Pandoc 支持很多格式的转换,所以选择使用 Pandoc 取代原来的 RedCarpet 对我而言更有诱惑力些。

要改用 Pandoc 并不复杂,这要感谢 pandoc-ruby 项目实现了 ruby 的接口。我只需要 blog 目录下新建 plugins/converters 目录,在里面编写一个 pandoc.rb 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
require "pandoc-ruby"

class Ruhoh

module Converter

module Markdown

def self.extensions
['.md', '.markdown']
end #self.extensions

def self.convert(content)
converter = PandocRuby.new(content).to_html(:smart,:mathjax,:email_obfuscation => :javascript)
converter
end #self.convert

end # Markdown

module Textile

def self.extensions
['.textile']
end #self.extensions

def self.convert(content)
converter = PandocRuby.new(content).to_html(:smart,:mathjax,:'email-obfuscation' => :javascript)
converter
end #self.convert

end # Textile

module RST

def self.extensions
['.rst']
end #self.extensions

def self.convert(content)
converter = PandocRuby.new(content).to_html(:smart,:mathjax,:'email-obfuscation' => :javascript)
converter
end #self.convert

end # RST

module Html
def self.extensions
['.htm', '.html']
end
def self.convert(content)
content
end
end # Html


end # Converter


end # Ruhoh

重写后的 Converter 可以支持使用 Pandoc-Markdown、Textile、reStructuredText 等几种主流的格式来编写博文。与原来的 Markdown 相比强大多了 11 考虑到以后 Markdown 可能会推出更加完善的标准,我也会尽量少用到 Pandoc-Markdown 的特殊标记。以保持向上兼容。

显示公式

要在文章中显示公式,MathJax 依然是最优雅的方案。不过这一块也让我折腾了不少时间。鉴于 Pandoc 本身就支持生成 MathJax ,我一开始的想法是在转换器里添加 --mathjax 选项,但发现即使这样做还是不能渲染出公式。后来看了一下 Pandoc 的文档,试着加上 -s 选项为页面添加单独的头部信息就可以显示出公式了。但这样的做法又会使得文章出现了两处头部信息,显得很 ugly。最终我还是回到 Widget 的方案,手动实现了一个 MathJax 的 Widget,然后在 layouts 中引进来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
version: 'latest'
style: 'TeX-AMS-MML_HTMLorMML'
---
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [["\\(", "\\)"]],
displayMath: [["\\[", "\\]"]]
}
});
</script>
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/{{this_config.version}}/MathJax.js?config={{this_config.style}}">
</script>

这样,公式的插入就很很轻松了:

  1. 对于 Markdown,行内公式的格式是 $公式$ ,居中公式的格式是 $$公式$$
  2. 对于 restructedText,行内公式的格式是 :math:公式,居中公式的格式是 ..math::公式
  3. Textile 的公式插入比较蛋疼,因为 Pandoc 对 Textile 并没有完全支持,所以只好用 <notextile>$公式$</notextile> 或者 <notextile>$$公式$$</notextile> 来转义。

在实际测试中,我发现 Pandoc 的 --mathjax 选项并不是一无是处的,因为它可以检测当前文档中是否有公式的标记,而决定是否在这一页加载 MathJax 脚本!这样就不会影响到没有公式的文章的加载速度了。

语法高亮

ruhoh 自己已经默认集成了 google_prettify 作为语法高亮的插件,但我在使用 ruhoh 2.1.1 版本的时候始终弄不出来高亮效果,后来看 ruhoh 的源码发现这是一个bug:在 ruhoh.rb 项目里有人提出将 google_prettify 移动到一个子目录里,相当于对它降了级,将它作为一个可选的高亮插件,以方便更换其他高亮插件,具体见 这个commit 。但这个建议被采纳后随即引来了一个问题:原来的 config.ymllayouts/default.html 两个文件中对 google_prettify 的引用信息没有更新,致使 ruhoh 找不到被移动了位置的 google_prettify 插件。于是我就向 ruhoh 项目报了这个 bug ,作者 @plusjade 很快就确认了这个问题,并且恢复了 google_prettify 的默认地位。现在这个 bug 在 ruhoh 2.2 里已经解决。不过这个解决方式并不彻底:plusjade 只是恢复了 google_prettify 作为默认的插件的设置,但实际上 config.ymllayouts/default.html 中对 google_prettify 的引用仍然是有问题的。我已经再次向该项目发了一个 pull request ,建议使用我的修改。

个性化

ruhoh 自带的主题比较单调。要想自己的主页更有个性一些,还是得手动调整一番。最理想的状态当然是找到个现有的主题,省的自己折腾。可惜,目前为止我所能搜索到的第三方主题都是基于 ruhoh 1.x 的。而 ruhoh 升级到2.0后就宣布不再兼容1.x版本的主题了 --bb。于是我只好动手。不得不赞一下 twitter-bootstrap ,真是很好很强大,让像我这种没有美工基础的 nerd 也可以做出很好看的页面。

因为非常喜欢 pluskid 的页面布局,所以我的主题在很大程度上借鉴了他的样式 2 2为了表示感谢,我在 footer 处加上了 theme inspired by pluskid ,当然也加上了自己的一些调整。我的博客主题可以在 这里 找到。

RSS

之前听说 ruhoh 生成 RSS 会有一堆没用的信息,但这个问题在 ruhoh 2.0 开始已经被解决了。但为了生成一个正确的 rss,首先你得在 config.yml 里设置好。比如你的文章基本都放在 posts 这个 collection 里,那么你就应该在 posts 那一部分将 rss 的 enable 选项设为 true 。千万不要把 _root 那一部分的 rss 选项开启。这样生成的 rss.xml 文件就只有标题和正文,而不会有其他无关的信息。文件存放在 posts/rss.xml 目录下。如果使用其他的 collection,同样使用类似的设置即可。

自动刷新

虽然 ruhoh 提供了预览的能力,但并不能实时预览用户的修改,这样就达不到所见即所得。最好的方式就是一边写文章,一边就能在另一个窗口里看到效果。为此,pluskid在他的文章里推荐使用 guard-livereload 来实现这个效果。试了一下,体验很好呀。这个小工具的原理也很简单,就是执行一个监听器,监听用户指定的文件,一旦检测到有文件发生改动,就向浏览器发送刷新指令。监听器就是 Guard ,负责发送刷新指令的就是 livereload。

配置的过程也很简单,先安装好 Guard,然后安装 livereload。接收到刷新后还得让浏览器执行刷新指令,因为是 Chrome 的 heavy user,我选择了安装相应的 Chrome 插件 来帮我完成刷新。你也可以根据你的需求选择 Safari/Firefox 的对应插件,或者直接使用 rack-livereload 。安装完成后,进入博客目录,然后执行下面的命令:

1
$ guard init livereload

将启动 Guard 监听器,同时会在当前目录下创建一个 Guardfile 文件。将里面的内容修改如下:

1
2
3
4
5
6
7
8
9
10
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'livereload' do
watch(%r{\.(yml|ru)})
watch(%r{_root/.+\.(html|md)})
watch(%r{widgets/.+\.(html|md)})
watch(%r{posts/.+\.(md|textile|markdown|rst)})
# Rails Assets Pipeline
watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html))).*}) { |m| "/assets/#{m[3]}" }
end

根据需要还可以继续添加规则,比如如果要监测 wiki 目录下的文件,可以添加下面这行:

1
watch(%r{wiki/.+\.(md|textile|markdown|rst)})

完成后就可以在你的修改文章后自动刷新页面了。特别适合像我一样拥有两个显示器的人,可以用一个显示器编写文章,用另一个显示器直接预览效果。进一步提高写博客的效率。

其他技巧

因为使用 bundle 安装方式,ruhoh 的命令都变得好长,做什么都要在前面敲上长长的 bundle exec ,这点的确有点泯灭人性啊!作为一个热爱偷懒的人,当然是不允许这样的情况存在的。

设置 alias

首先先设置 alias ,将常用的几条命令用最简单的命令名称来替代。将这段文本加到终端配置文件里,如果用的是 bash,就加到 ~/.bashrc ,如果用的是 zshell,就加到 ~/.zshrc 里。

1
2
3
4
5
6
export RUHOH=/home/ehome/Documents/blog
export PUBLISH=/home/ehome/Documents/wzpan.github.io
alias _rp='cd $RUHOH && bundle exec rackup -p 9294'
alias _gd='cd $RUHOH && guard'
alias _dft='cd $RUHOH && bundle exec ruhoh posts draft' # 快速添加草稿
alias _tp='cd $RUHOH && bundle exec ruhoh posts titleize' # 快速为草稿文件命名

其中注意将 $RUHOHPUBLISH 两个环境变量更改为你机器上对应的博客源文件仓库和存放编译后的页面的仓库。

交互式更新

因为使用 Github Page 服务来托管我的网站,每次 compile 后我还得手动将我的改动提交到 Github 上,才能在网站上看到最新的修改。为了进一步简化这个过程,我编写了一个 shell 脚本用于完成一系列编译和交互式更新的任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/zsh
cd $RUHOH
rm -r compiled
bundle exec ruhoh compile
cd -
echo -n "Copy the compiled result to your site repo?[y/n]"
read copy_yn
if [ "$copy_yn" = "y" ]; then
cp -av $RUHOH/compiled/* $PUBLISH/
echo -n "Update your site repo?[y/n]"
read update_site_yn
case "$update_site_yn" in
y )
cd $PUBLISH
git add --all
echo -n "Please enter commit message:"
read commit_message
git commit -m $commit_message
git push
cd -
echo "Congrats! The site repo has been updated"
;;
n )
echo "Don't copy them this time."
;;
*)
echo "Please enter y or n."
exit 1
;;
esac
elif [ "$copy_yn" = "n" ]; then
echo "Don't copy them this time."
else
echo "Please enter y or n."
exit 1
fi
echo "Update your blog repo?(y/n)"
read update_blog_yn
if [ "$update_blog_yn" = "y" ]; then
cd $RUHOH
git add --all
git commit -m $commit_message
git push
cd -
echo "Congrats! The blog repo has been updated"
elif [ "$update_blog_yn" = "n" ]; then
echo "Don't update the blog repo this time."
else
echo "Please enter y or n."
exit 1
fi
echo "All done!"
exit 0

建议将上述脚本保存为 ~/ruhoh_compile ,并执行下面的命令将之设为可执行:

1
$ chmod +x ~/ruhoh_compile # 需要 root 权限

然后,可以将这个命令追加到 alias 设置中:

1
alias _cp='ruhoh_compile' # 编译和交互式更新

这样,每次要编译只需要执行 _cp 指令,系统会自动完成编译工作,并询问是否将更新提交到远程仓库,根据情况回答 y/n 即可。

总结

总的感觉,ruhoh 的定制性的确很强,逻辑视图也比较清晰,不过因为对 Mustache 不太熟悉,暂时还不能很自如的编写模块来实现其他一些想法。比如 Octopress 提供了简单的方法来方便的插入 gist 上的代码片段,但在 ruhoh 中这个功能暂时没有实现。虽然作者在 1.x 的时候就信誓旦旦地说要实现这个功能(见1.0的文档),但到了 2.0 的时候还是没有实现它,而且作者索性把这个想法都从 2.0 的文档里删掉了--bb 。只能期待作者能够在未来的版本中提供更加丰富的模板功能,还有最重要的:一份更加清晰的文档 3 3在软件开发过程中,文档的滞后性真是一件残酷的事实。文档总是跟不上最新的改动,这点在 ruhoh 项目也可见一斑。比如现在 ruhoh 已经更新到 2.2 了,官方的文档还停留在 2.1 。

最后罗列一下经过定制后,我经常用到的命令:

1
2
3
4
5
$ _rp # 启动ruhoh服务器
$ _gd # 开始监听改动
$ _dft # 快速添加草稿
$ _tp # 快速为草稿命名
$ _cp # 编译和交互式更新

Comments