JekyllからWordpressへ移行しました。
Jekyllは生成が遅すぎて耐えきれなかったというのが第一の理由です。一つ記事を書き終わってから更新をかけると、ほぼすべてのページを再生成するため、どうしても数十秒かかります。すべてのページを再生成しなくても良いように思うかもしれませんが、各ページにあるナビゲーションリンクやタグごとの記事数なんかを書き直さなければなりません。このサイトには1500くらい記事があるのですが1分くらいはかかります。関連記事や記事数といった情報はいったん全部の記事を読み込んでからで無いと求まりませんし。incrementalな再生成も検討されているようなのですが、できるのはまだ先のようなので。
さくらのレンタルサーバで簡単にWordpressを入れる方法が紹介されていたのも移行を後押ししました。
外出先からなど、もっと自由に記事を書きたかったですし。
移行手順は次のような感じでした。
記事の変換はいくつかハマったところがありました。
まずはインポートできる形式を調べたのですが、めぼしいものが見当たりません。結局WordPress間でやりとりするのに使われるであろう形式にこれまでの記事を変換することにしました。
これまでの記事はJekyllのyamlヘッダー付きhtml形式です(昔の独自マークアップ形式やorg-mode形式からhtmlへ変換されたものです)。それをWordpressでインポートできるWXR(WordPress eXtended RSS)へ変換しました。変換に使ったEmacs Lispは次の通り。
(defun jtow-parse-html (file)
"yamlヘッダー付きhtmlファイルを解析して(記事URL slug タイトル 時刻 タグ文字列リスト 内容文字列)を返します。"
(with-temp-buffer
(insert-file file)
;; ---
(beginning-of-buffer)
(if (not (looking-at "^---\n")) (error "syntax error. '---'"))
;; yaml header
(next-line)
(let ((yaml-header nil))
(while (not (looking-at "^---"))
(if (not (looking-at "^\\([^:]+\\): *\\(.*\\)$"))
(error "syntax error. %s %s" file (buffer-substring (point-at-bol) (point-at-eol))))
(push (cons (match-string-no-properties 1) (match-string-no-properties 2)) yaml-header)
(next-line))
(next-line)
;; check
(let ((title (cdr (assoc "title" yaml-header)))
(date (cdr (assoc "date" yaml-header)))
(tags (cdr (assoc "tags" yaml-header)))
(content (buffer-substring-no-properties (point) (point-max))))
(if (not title) (error "title not found. %s" file))
(if (not date) (error "date not found. %s" file))
;; result
(list
;; link
(concat "http://example.jp/blog/" file)
;; postname
(if (string-match "^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-\\([^.]+\\)\\.html$" file ) (match-string 1 file) nil)
;; title
(substring title 1 -1)
;; date
(jtow-parse-date date)
;; tags
(if tags (split-string tags " " t))
;; content
content))
)))
(defun jtow-parse-date (d)
"2014-01-02という形式の文字列からEmacsの時刻形式へ変換します。"
(let ((tm (parse-time-string d)))
(encode-time 0 0 0 (nth 3 tm) (nth 4 tm) (nth 5 tm) (* 9 3600))))
(defun jtow-format-date-rfc2822 (time)
(format-time-string "%a, %d %b %Y 00:00:00 %z" time))
(defun jtow-format-time (time &optional universal)
(format-time-string "%Y-%m-%d %H:%M:%S" time universal))
(defun jtow-make-item (item)
"jtow-parse-htmlが返したリストをWXRのitem要素へ変換します。"
(let ((link (nth 0 item))
(postname (nth 1 item))
(title (nth 2 item))
(date (nth 3 item))
(tags (nth 4 item))
(content (nth 5 item)))
(concat
"<item>"
(if link (concat "<link>" link "</link>\n"))
(if title (concat "<title>" title "</title>\n"))
(if date (concat "<pubDate>" (jtow-format-date-rfc2822 date) "</pubDate>\n"))
(if tags (loop for tag in tags concat (format "<category domain=\"post_tag\" nicename=\"%s\"><![CDATA[%s]]></category>\n" (downcase tag) tag)))
(format "<wp:post_date>%s</wp:post_date>\n" (jtow-format-time date))
(format "<wp:post_date_gmt>%s</wp:post_date_gmt>\n" (jtow-format-time date t))
(concat "<wp:post_name>" postname "</wp:post_name>\n")
"<wp:post_type>post</wp:post_type>\n"
"<wp:status>publish</wp:status>\n"
;; content
"<content:encoded><![CDATA[" content "]]></content:encoded>\n"
"</item>\n")))
(defun jtow-dir-html-to-xml (dir outfile)
"ディレクトリ内のyamlヘッダー付きhtmlファイルからWXRファイルを作成します。"
(with-temp-file outfile
(insert
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>
<rss version=\"2.0\"
xmlns:excerpt=\"http://wordpress.org/export/1.2/excerpt/\"
xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"
xmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"
xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
xmlns:wp=\"http://wordpress.org/export/1.2/\"
>
<channel>
<title>Example Blog</title>
<link>http://example.jp/blog</link>
<description></description>
<pubDate>Thu, 25 Sep 2014 05:56:52 +0000</pubDate>
<language>ja</language>
<wp:wxr_version>1.2</wp:wxr_version>
<wp:base_site_url>http://example.jp/blog</wp:base_site_url>
<wp:base_blog_url>http://example.jp/blog</wp:base_blog_url>
<wp:author>
<wp:author_id>1</wp:author_id>
<wp:author_login>foo</wp:author_login>
<wp:author_email>foo@example.jp</wp:author_email>
<wp:author_display_name><![CDATA[foo]]></wp:author_display_name>
<wp:author_first_name><![CDATA[]]></wp:author_first_name>
<wp:author_last_name><![CDATA[]]></wp:author_last_name>
</wp:author>
<generator>http://wordpress.org/?v=4.0</generator>
")
(loop for file in (directory-files dir nil "\.html$") do (insert (jtow-make-item (jtow-parse-html file))))
(insert "</channel></rss>")))
;; ex)カレントディレクトリにある.htmlからposts.xmlを作る。
;;(jtow-dir-html-to-xml "." "posts.xml")
私が使っていたyamlヘッダーは主にtags, date, titleだけでしたので、それだけ考慮してあります。
CDATAの障害になる]]>という文字列が使われていないことも確認しました。
一番困ったのが、slug(postname)が重複してはいけないということでした。昔の記事では/blog/2006-01-01-a.htmlのようなURLが多いのですが、aの部分は他の日付でも沢山使われているので重複してしまいます。重複すると自動的にa-2、a-3のように補正されてしまいます。仕方ないので/blog/2006-01-01-060101a.htmlのように確実にユニークになるようにファイル名を置換しました。以前のURLと変わってしまいますが諦めました。
外観のカスタマイズはBootpressテーマをベースにして、気に入らないところをひたすら書き換えては表示してみる繰り返しでした。
それにしてもphpは久しぶりです。しかしまぁ、見えないグローバル変数に依存するコードが多くて分かりづらいですね。WP_Queryが割と万能な模様。
一通りカスタマイズが終わったらテスト用ディレクトリから本番ディレクトリへ移し替えて完成です。
投稿は今まで事実上Emacsからしか出来ませんでしたが、これからはブラウザ、Androidアプリ、Emacsと色々なところから出来るようになりました。
AndroidのWordPressアプリは面白いですね。写真を撮ったそばから投稿できます。
Emacsからの投稿はOrg2blogを導入。el-get経由でインストールしました。bzrが実行できないと言われたのでCygwinのインストーラからbzrをインストール(xml-rpc-elのインストールに必要)。でもWindowsのEmacsを使っていて、かつ、bzrはPythonスクリプトなのでEmacsからexecutable-findで見つからず。bzr.batを作ってsh -c '/bin/bzr %*'のようにしてなんとかel-getからインストールできました。(MELPAをpackageに追加してそこからインストールした方が良かったかも)
Org2blogもAndroidのWordPressアプリもですがslugが入力できません。URLが日本語になってしまいますが、まぁ、これも諦めるしかないでしょうか。(Org2blogでは#+PERMALINKでslugを設定できました)
忙しさにかまけて切りに切りまくった今期ですが、最後まで見て面白かったなと思ったのは次の三つでしょうか。
グラスリップは、一話を見たときは良くあるテキトーな青春グループ恋愛もの(誰と誰がくっつくか、というだけで延々話を消費してこれといって中身がないもの)なのかなと思ってスルーしかけたのですが、意外と中身がありました。
野崎くんは、「千代ちゃんかわいい」というだけですね。ええ。……いや、コメディなので、色々と可笑しかったです。
ベイビーステップは、勉強が得意な主人公が真面目に考えながらテニスに打ち込む話です。スポーツものとしてはちょっと変わってますよね。
detfcというのを作りました。
普段はGNU Makeで$(wildcard)とかを使ってなんとかしているのですが、どうにもビミョーな場合が良くあるので。
でもわざわざ作っても、結局なんだかビミョー。
wavファイルの合計時間が欲しかったのだけどsoxとかではあんまりうまく出来ないみたいなので自分で書いてみた。バイナリファイルの処理なので、なんとなくPython。
import sys import os import struct import fractions if len(sys.argv) != 2: print "Usage: python %s <directory>" % sys.argv[0] quit() dir = sys.argv[1] files = [y for y in [os.path.join(dir, x) for x in os.listdir(dir)] if os.path.isfile(y) and os.path.splitext(y)[1].upper()==".WAV"] totalSeconds = fractions.Fraction() for file in files: print file f = open(file, 'rb') riff = f.read(4) if riff == "RIFF": riffSize, riffType = struct.unpack('<I4s', f.read(8)) samplesPerSec = 0 blockSize = 0 while True: chunkType,chunkSize = struct.unpack('<4sI', f.read(8)) if chunkType == "fmt ": formatId,channels,samplesPerSec,bytesPerSec,blockSize,bitsPerSample = struct.unpack('<HHIIHH', f.read(chunkSize)) elif chunkType == "data": totalSeconds += fractions.Fraction(chunkSize / blockSize, samplesPerSec) break else: f.seek(chunkSize, os.SEEK_CUR) f.close() print "totalSeconds " + str(float(totalSeconds)) print "totalMinutes " + str(float(totalSeconds / 60))
なんて書いてみたら、後からwaveライブラリなんてものがあることに気がついたorz。
import wave w = wave.open("a.wav") w.getframerate() w.getnframes()
みたいな感じでサンプリングレートとサンプル数が取得できるんだって。
ポピュラスみたいなゲームがやりたくて探していたら Godus というのを見つけたのでプレイしてみました。
ヒーター・モリニューの最新作でまだベータ版らしいです。
とりあえず一通り見終わりました。
第一話の段階で特に目を引いたものはありませんでした。
最近、伊藤園の緑茶のティーバッグを色々試しています。これまでに次の三種類を飲みました。
どれも十分美味しいと思います。味の評価としては、
深蒸し=ぞっこん>抹茶入り
という感じですね。価格相応です。
抹茶入りは伊右衛門みたいな味がします。いかにも抹茶でごまかされたという感じがしてしまいますが、安いことを考えれば妥当かもしれません。
ぞっこんは最近ペットボトルのおまけでティーバッグが付いていたので、飲んだ方も多いと思います。とにかくまろやかさを前面に出したような味ですね。
深蒸しはちゃんと深蒸し茶っぽい香りと味がします。綺麗な緑色、強い甘みと弱めだが確かに感じる渋みのバランス。
どれを買っても後悔はしないと思います。
個人的にはやっぱり普段から飲み慣れている深蒸し茶が好きかなぁ。
深蒸しだと20袋で36gと書いてますね。お茶屋さんで1000円/100gくらいで買った方が安いのですが、いつも全部飲みきる前に悪くなってしまうので、それを考えるとそんなに高くはないのかも。
Amazonで見かけて最初の数ページ読んでみて、続きが気になったので購入しました。
なかなか読ませるじゃないですか。ちゃんと魔法使いの弟子の成長物語になっています。最後まで一気に読んでしまいました。
物語中では「オートマトン」とかその手の専門用語は極力出てこないようになっていて、物語中の設定に従った用語に置き換えられています。
なので知識の無い人でも小難しい単語に翻弄されることはありません。
一応巻末で物語中に出てきた概念と計算理論での概念との対応付けや、それについて詳しく学びたい場合の参考文献が紹介されています。
私はこのあたりの理論は大学や独学で少しかじったり、プログラミング言語などを製作する上で必要な部分を多少知っていたりするだけですが、物語を読みながら「ああ、これはε遷移のことを言ってるんだな」とか記憶を呼び起こされながら読みました。
でもそういう知識が無くても普通に物語として読めるのでは無いかと思います。
後半になるにつれて少し難しくなっていきますが、そういう所は適当に流して読んでも問題ないと思います。
時間があれば、主人公が直面する問題について一緒に解きながら読んでみても面白いかもしれません。
この手の抽象的な理論は、身近な応用と結びつけるとより分かりやすくなると思うのですが、そういった例は自動販売機の例くらいでしょうか。
そういう例がもっとあると、読んだ後にもっとスッキリするのかなぁと思いました。
少し疑問に思ったのは、偽クフ語と万能機械について。偽クフ語の詩集は万能機械に入れる命令を表しているということだと思うのですが、それは偽クフ語をそのまま万能機械に入力すると言うことなのでしょうか。万能機械は、入力された偽クフ語を構文解析して実行する?
偽クフ語は、作中において手作業で解析をしていましたが、字句の区切りを見つけるのが難しく、また、かなり曖昧な部分が出る言語です。
なので、実際に万能機械を作るとしたら、どのようになるのだろうというのが気になりました。
おそらく命令として有効な文にはさらなる制約があるのでしょうね。
GIGAZINEで紹介されてた冷やし味噌ラーメンを食べてみました。まぁ、普通のコンビニの冷やし麺かなぁ。
食べログ評価4.1を超えるラーメン店麺屋 彩未監修の「冷し味噌ら~めん」をローソンで買って食べてみた - GIGAZINE