Chienomi リニューアル! 〜 裏話編
開発::web
計画
PureBuilder Simplyのデビューは2017年の年末。 当初WordPressでスタートしたMimir Yokohamaのサイトを「ほぼそのままに」移植するのが目標だった。
実はこの時期にChienomiはWordPressデザインを変更している。
PureBuilder Simply以前にはPureBuilder2を使用していた。 PureBuilder2はMimir Yokohamaで制作サイトを販売・サポートしていたので、商用利用に耐えるプロダクトではあったのだが、 個人的には「めんどくさい」プログラムだった。たくさん設置したいとは思えない。
ドキュメントそのものは、PureBuilder2になってKramdownによるMarkdownサポートが追加されており、Markdownで書いていたのだが、サイト構築がめんどくさい。
そこでこれを改善するのがPureBuilder Simplyであった。 Simplyの名の通り、複雑な方法で機能を提供していたPureBuilder2と違い、Pandocを使用して非常にシンプルなアプローチを実現している。
そして、このソフトウェアがとてもよかった。これこそ商用利用に適しているし、広くオープンソースとして展開できると思った。 超急ピッチで開発された新しいMimir Yokohamaのウェブサイト(原稿執筆、WordPressでの展開からPureBuilder Simplyの開発、PureBuilder Simplyでの展開まで含めて12月に、というか1週間程度である)だが、この感触の良さから他のサイト(PureBuilder2, またはWordPressで構築されている)の置き換えが検討された。
だが、新しいMimir Yokohamaのサイトは既にMarkdownで書かれており、置き換えが容易だったのに対して、それ以外のサイトは多くの問題があった。 仕事を含めいくつかのサイトをPureBuilder Simplyに置き換えたし、元がWordPressサイトだったものもあるが、「正攻法で」…というか、汎用性のある方法で移行したものはなく、あまりノウハウとしての蓄積がなかった。
現在私のサイトとしては5つあるが、「旧サイトからの移行」がなされたのは、Harukamy’s Memorandaだった。 つまり、実行はごく最近である。 Harukamy’s MemorandaはWordPressからの移行ではあるが、記事の選定でごく一部の記事しか残らなかったため、手作業で移植している。
そして、最も移行「したかった」のがChienomiだ。 Chienomiは実用的なニーズが高く、アクセス数も非常に多い。ここ数ヶ月は月間PVは約15万ほどである。 だからこそ、軽量化したいし、負荷にも耐えられるようにしたい。また、ヴィジターをトラッカーに晒す事態からも避けたい。 これだけ負担のあるサイトで、広告をつけることも避けているのだから、それを台無しにするような要素をなくしたい。
だが、なかなか実行できないまま、2年が経とうとしていた。
作業は2日
着手したのは金曜日の夜だった。この時点では何もしていない、まっさらの状態だ。 そして、新しいChienomiをお披露目したのは日曜日の夜中。およそ2日の作業である(2回寝ているので、体感的には3日)。この間
- 「HTMLを抜き出してアーカイブとして設置し、テンプレートにはPureBuilder Simplyから “生成した” HTMLファイルを使う」という発想に至る
- WordPressのXMLを眺める
- 余り使ったことのないREXMLライブラリを試し、うまくいかずに挫折する
- XML処理について調べ、ActiveRecordに行き着く
- WPImporterを開発する
- Chienomiをデザインする
- Chienomiのテンプレートを書く
- ChienomiのCSSを書く
- サイドバーのデザインにおいて、「タブ切り替え」を試すも、Dilloで正しく表示されず、試行錯誤の末リンク切替式に変更する
- WordPressのXMLが本文はHTMLでないことに気づき、「中途半端なHTML」を含めHTMLに変換するコードを追加する
- WPImporterのドキュメントを書いてGitLabで公開
- ConoHa WINGに新しいChienomiのドメインを登録
- 検索機能の開発を開始し、エクストラクタを書く
- 検索用ライブラリを書く
- 検索用ライブラリを使ったCGIをとりあえず書く
- ConoHa WINGにアップロードして、CGIが動くまで試す
- CGIを汎用化したものを書く
- ドキュメントを書く
- GitLabで公開する
- テンプレートにWordPressにあった案内を追加する
- WordPressサイトに案内を掲載する
- WordPressサイトでこの1年で人気の高い記事を確認し、テンプレートに追加する
- 変換した記事をビルドドキュメントに追加する
- Twitterのシェアリンクの仕様を調べ、素材を用意してシェアリンクを追加する
- 現状の仕様だとシェアリンクが作れないことが分かったので、PureBuilder Simplyを改良する
wp-content/uploads
をソースドキュメントに追加する- リニューアルの記事を書く
- ビルドしてリポジトリ化し、ConoHa WING上でテストする
- CGIのデバッグ
- 公開
- 旧サイトのVPS側でサーバーリダイレクトを設定
という作業である。
眠かったり、低気圧もあり具合が悪かったりした中であったため、「だらだら作業していて遅々として進まない」という印象だったのだが、 こうしてみると結構がんばっている。いつもよりかなり多めの家事をこなし、台風の対応もしつつであったことを考えると割と褒め称えられてもいいと思う。
ちなみに、ドキュメントはコードを書きながらちゃんと書いている
一番きつかったのはREXML
REXMLのXPathが目的のノードを全く見つけてくれなくてとても困った。
例えば
a>
<b>
<c>
<c>
</b>
</b>
<c>
<c>
</b>
</b>
<c>
<c>
</b>
</a> </
というドキュメントに対して
require 'rexml/document'
= REXML::Document.new(ARGF.read)
doc = doc.elements
elem
.each("//b") do |child|
elemp child["//c"]
end
の結果が
nil
nil
nil
である。c
要素が見つけられていない。
これがなぜか全くわからず、悪戦苦闘した。さらにいえば、#each_element
も通らない。
実はこれ、REXML::Elements
とREXML::Element
があるという罠であった。つまり、
require 'rexml/document'
= REXML::Document.new(ARGF.read)
doc = doc.elements
elem
.each("//b") do |child|
elemp child.elements["//c"]
end
なら良い。
結局、サーバーサイドで動かすようなものではないので、ActiveRecordを採用したのだけれども。
速くするはずが…
実は検索のコードは、「改善」としてもっと無駄がなく速そうなこんなコードも書いていた。
#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-
require 'yaml'
require 'shellwords'
class PureBuilderSearch
def initialize(config)
@config = YAML.load(File.read(config))
@andsearch = false
end
:andsearch, true
attr
def search(words)
= Shellwords.shellsplit words.downcase
cond = []
hits Dir.glob("**/*", base: @config["destdir"]).each do |stfile|
= File.join(@config["destdir"], stfile)
stfile next unless File.file? stfile
= File.read(stfile)
doco = doco.downcase
doc if ( andsearch ? cond.all? {|i| doc.include? i} : cond.any? {|i| doc.include? i} )
if @config["incl_digest"]
= doc.each_line.to_a[1 .. -1]
doc .each_with_index do |i, index|
docif cond.any? {|c| i.include? c}
= doco.each_line.to_a[(index + 1), 3]
lines =~ /\A(.*)\n(.*)\n/
doco .push({path: stfile, lines: lines.compact, title: $2, date: $1})
hitsend
end
else
=~ /\A(.*)\n(.*)\n/
doco [$1, stfile, $2]
end
end
end
hitsend
end
実際のライブラリでは一旦リストを作ってから改めてファイルを読み直すという方法をとっている。 downcaseしたことのある文書をもう一度downcaseしており、しかも後半は1行ずつdowncaseしているのでかなり効率が悪いように見えた。
#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-
require 'yaml'
require 'shellwords'
class PureBuilderSearch
def initialize(config)
@config = YAML.load(File.read(config))
@andsearch = false
end
:andsearch, true
attr
def search(words)
= Shellwords.shellsplit words.downcase
cond = Dir.glob("**/*", base: @config["destdir"]).select do |stfile|
hits = File.join(@config["destdir"], stfile)
stfile next unless File.file? stfile
= File.read(stfile).downcase
doc if andsearch
.all? {|i| doc.include? i}
condelse
.any? {|i| doc.include? i}
condend
end
if @config["incl_digest"]
= []
rv .each do |hf|
hits= File.foreach(File.join(@config["destdir"], hf)).to_a
doc .each_with_index do |i, index|
doc= i.downcase
idc if cond.any? {|c| idc.include? c}
= doc[(index + 1), 3]
lines .push({path: hf, lines: lines.compact, title: doc[1], date: doc[0].strip})
rvbreak
end
end
end
return rv
else
.map do |i|
hits= nil
title File.open(File.join(@config["destdir"], i)) do |f|
= f.gets
date = f.gets
title end
[date, title, i]
end
end
end
end
そこでできるだけ処理をまとめ、無駄なループが発生しないように書き直した…のだが、結果的には同条件計測で0.179秒から0.221秒へと悪化してしまった。 なんで遅くなったのかさっぱり分からない。 パフォーマンスチューニングは想像でやってもうまくいかないことを見せつけられた。
ハイアクセシブル
私の伝統である「Dilloとw3mでチェック」がChienomiにも適用された。
加えて、Chienomiは印刷のニーズも高いため、普段より入念にprinting CSSを練ってある。
主には
- 余計な要素の除去 (サイドカラムだけでなくヘッダーやシェアアイコンもなくなる)
- ページをフルに使うレイアウト設計
- グレースケールのカラーテーマ
- 画像への折返しの配慮
- ソースコードの表示のされ方の調整
- セリフ体フォントの指定
である。
デザイン的にはデフォルトを尊重するようなものでそんなにいじってはいないが、余計な要素を適用させないようにしつつ、 より見やすいように微調整を加えている形である。
「印刷しやすいテック系サイト」というのは、魅力のひとつになるかな、と思っている。