msn email google-talk twitter tumblr flickr

搬家第一篇 使toto支持中文的技巧分享

缘起

上周决定把blog搬家,目标是放到heroku上.图的就是免费,但是heroku的免费空间DB有5MB的限制,放些Demo还可以,起个用DB的blog恐怕用着会捉襟见肘.网上考察了下,似乎有很多人都是用的基于rack的轻量级博客引擎在github或heroku上起的blog,后台基于文档系统,没有5MB的限制(记不得看到哪里说文档有100MB的限额 不清楚 就算是真的也足够用了).总之大方向就这么定了.

开始瞄上的是jekyll,只是一个静态页面生成工具,不过我git下来的模板就已经包含了config.ru,跑在rack上果然很轻快.但是每次改动都要成静态文件才能预览,麻烦.如果不做太大的前端修改,直接用起来应该也是足够了.

jekyll不能满足我对页面的要求,继续上网扫,发现介绍toto的文章,嗯,ERB,感觉有戏.牵回toto,又请回dorothy(toto的blog模板 作者的命名很有意思 Dorothy是我们熟悉的OZ国历险记的主人公 而Toto是她的宠物的名字 一定有童鞋想到卡拉赞的歌剧院吧 心照不宣 心照不宣 : ).

rackup起来没怎么试就开始套样式,直到我把样式搞完,试着发博文才发现原来toto和dorothy对中文水土不服,问题还挺多.google了下,基本没有什么能参考的,只好自己动手;也是这个原因,才有了这搬家第一篇的分享.

开工

上面全是废话,想解决问题的人从这里看起.toto也不介绍了,实在需要的google下就会有很多.直奔主题.遇到toto的中文问题主要就是标题的中文化.这里的标题指的是articles/下的文件名以及文件头部YAML中的title.要求文件名是类似这样的格式:

2010-08-24-foo-bar.txt

后缀是可以配置的,前面的部分就是toto能找到文章的关键.toto内部是根据url定位相应日期及名称的日志文件的.我们rake new然后输入中文的标题,日期之后是空的,没有生成我们想要的英文标题.这个问题可以手工解决,不过每次手工起名字很烦,我的解决方案是挂了一个汉字转拼音的工具包在dorothy的Rakefile中.这样每次输入完标题就会自动生成相应拼音组成的文件名.RUBY的转拼音包有好几个,就不细说了.

下面重点来了,因为toto还是不能正常的找到文章.问题出在这:

   1      #in ERB
   2      <a href="<%= article.path %>"><%= article.title %></a>
   3      #in Toto
   4      class Article
   5          ...
   6          def path
   7              "/#{@config[:prefix]}#{self[:date].strftime("/%Y/%m/%d/#{slug}/")}".squeeze('/')
   8          end
   9  
  10          def slug
  11              self[:slug] || self[:title].slugize
  12          end
  13          ...
  14      end

toto内部默认会拿YAML的title来slugize,英文标题没有问题,只是格式改改就跟文件名一样了;但是我们要用中文标题,难不成要我们给toto gem也加上一个汉字转拼音的包?没那么麻烦.答案就在toto代码里self[:slug].这说明如果我们的YAML有叫slug项的时候,toto就不用title项slugize了.

toto本身有解决方案,只是文档中只字未提…其实我们只要在生成日志文件的时候将拼音化的文件名再存一份到YAML中的slug属性,点击日志链接就能正确到达日志内页了.依旧修改dorothy的Rakefile:

   1  slug = title.empty? ? nil : py.to_permlink(title).strip.slugize
   2  
   3  article = {'date' => Time.now.strftime("%d/%m/%Y"),'slug' => slug,'title' => title}.to_yaml
   4  
   5  path = "#{Toto::Paths[:articles]}/#{d.strftime("%Y-%m-%d")}#{'-' + slug if slug}.#{@config[:ext]}"

to_yaml时多生成一个slug项,值与生成文件名的标题部分相同.这样再新生成一个日志文件,首页里用article.path生成的链接就能正确的找到文章了.

打开刚生成的日志文件.效果如下:

   1      --- 
   2      slug: zhong-wen
   3      title: !binary |
   4        5Lit5paH
   5  
   6      date: 24/08/2010

首先是我们的title被二进制化,不可读了,本身这个问题不大.但是还有个并发症,二进制化是to_yaml的行为,它还附带着为我们加了两个"\n"换行,而toto会将空行之后内容一概看作body的内容,行为代码如下:

   1  meta, self[:body] = File.read(@obj).split(/\n\n/, 2)

且to_yaml的结果是无序的,这样的话,页面渲染日志body时就会出现date: 24/08/2010.这不是我们想要的.当然,这个问题手工也能解决:把空行后的yaml全部移动到空行前就行了.这里提供我的解决方案,不是很优雅,算是hack技巧:

   1    article = "--- \n"
   2    hash = {'title' => title,
   3            'date' => date.strftime("%d/%m/%Y"),
   4            'slug' => slug,
   5            'category' => "foo",
   6            'tags' => "foo,bar,baz"
   7            }
   8    hash.each{|key,value| article << ("#{key}: #{value}\n")}
   9  # article = {'date' => Time.now.strftime("%d/%m/%Y"),'slug' => slug,'title' => title}.to_yaml

不使用to_yaml方法,只模拟yaml的格式拼成一个字符串.这个方法避免了之前两个问题,标题名不可读和title后的空行,并且在页面中title也能够正常显示.

抛砖

经过以上几个步骤,你的blog应该能比较省事的支持中文了(如果不嫌麻烦 可以每次手工修改相应的地方 原理上面也都说到了).toto的功能还不够丰富,除了日志 RSS功能就只有归档的功能,也没有像WP那样复杂的界面,毕竟才300+行的核心代码,不能要求太高不是.不过也正是因为toto的简单,才让我有机会感受它的魅力,自己动手为它添加功能吧;目前我已经实现了分类标签功能,这个blog就是living demo.这就跟本篇要讨论的无关了,需要添加toto的引擎的功能,本篇提到的修改都是在dorothy中,只是修改了生成日志文件的rake,不涉及toto的行为.有兴趣的可以自己看看代码,或者mail我一起为toto添砖加瓦吧.