MarsEdit や Marked で kramdown を用いた変換が機能しなくなったけど回避できたという話

このエントリーをはてなブックマークに追加 Pocket

先日、MacBook Pro の Jekyll をバージョンアップしたところ、MarsEdit や Marked に外部 Markdown エンジンとして指定している kramdown が機能しなくなってしまいました。

挙動を探ってみると以下のような状況です。

  • ASCII 文字のみを入力すると機能する。
  • ターミナルで実行すると機能する。

つまり、MarsEdit に非 ASCII 文字を入力する(Marked の場合は非 ASCII 文字が含まれるファイルを開く)と、何も出力されなくなってしまったのです。

MarsEdit - the blog editor for WordPress, Tumblr, Blogger and more.
バージョン: 3.6.2
価格: ¥4,000(2013.12.28 時点)
サイズ: 7.9 MB
カテゴリ: ソーシャルネットワーキング, 仕事効率化
販売元: Red Sweater Software
全てのバージョンの評価: ★4.0(10件の評価)

Marked
バージョン: 1.4.3
価格: ¥400(2013.12.28 時点)
サイズ: 7.7 MB
カテゴリ: 仕事効率化, ユーティリティ
販売元: Brett Terpstra
全てのバージョンの評価: ★4.5(14件の評価)

これは困ったということで、解決(回避)に向けて試行錯誤してみました。

目次

文字コードの問題だろう

ASCII 文字は OK で非 ASCII 文字は NG となるのであれば、文字コード(encoding)の問題でしょう。

以下のようにして、スクリプトの標準エラー出力を確認することにしましょう。

#! /bin/sh
CMD=/usr/bin/kramdown
CMDARGS=""
$CMD $CMDARGS 2> $HOME/Desktop/kramdown.log

やはり、エンコーディング関連のエラーが発生していました。

/Library/Ruby/Gems/2.0.0/gems/kramdown-1.3.0/lib/kramdown/parser/base.rb:91:in `adapt_source': The encoding of the source text is not valid! (RuntimeError)
	from /Library/Ruby/Gems/2.0.0/gems/kramdown-1.3.0/lib/kramdown/parser/kramdown.rb:89:in `parse'
	from /Library/Ruby/Gems/2.0.0/gems/kramdown-1.3.0/lib/kramdown/parser/base.rb:67:in `parse'
	from /Library/Ruby/Gems/2.0.0/gems/kramdown-1.3.0/lib/kramdown/document.rb:107:in `initialize'
	from /Library/Ruby/Gems/2.0.0/gems/kramdown-1.3.0/bin/kramdown:59:in `new'
	from /Library/Ruby/Gems/2.0.0/gems/kramdown-1.3.0/bin/kramdown:59:in `<top (required)>'
	from /usr/bin/kramdown:23:in `load'
	from /usr/bin/kramdown:23:in `<main>'

Ruby のバージョン

ところで、Jekyll のバージョンアップ時に gem のアップデートではなく新規インストールが必要だったのですが、これが今回の件と関連がありそうです。

前述のスクリプトの標準エラー出力を見るとわかるように、OS X 10.9(Mavericks) では Ruby のバージョンが 2.0 に変わっています(以前は 1.8 だったと記憶しています)。

さらに、Ruby 1.8 も(少なくとも私の環境では) Mavericks には残っているようです。

試しに Ruby 1.8 で kramdown を実行すると今回の件が回避できることを確認しました。 Mavericks にしてから kramdown をインストールしたことが今回のエラー発生のきっかけであるということです。

これで回避はできるのですが、Ruby 1.8 はもうメンテナンスされないので、ここは Ruby 2.0 での回避策を確立したいところです。

Ruby の文字コードの扱い

Ruby の文字コードの扱いについて調べてみると、Ruby 1.9 で変更があったことがわかりました。

Encoding.default_external は IO のデフォルトの外部エンコーディングを、Encoding.default_internal は IO のデフォルトの内部エンコーディングを返します。これらは標準入出力、コマンドライン引数、open 等で開くファイル等で、明示的な指定を行わなかった場合に外部または内部エンコーディングとして用いられます。

via Rubyist Magazine - Ruby M17N の設計と実装

kramdown でエラーが発生している箇所を見ると、入力データのエンコーディングと外部エンコーディングが一致していないためにエラーになっているのでしょう。kramdown は外部エンコーディングは指定していないので Encoding.default_external の値がポイントですね。

ということで、以下のようにしてスクリプト実行時の Encoding.default_external の値を確認してみると…

#! /bin/sh
CMD=/usr/bin/kramdown
CMDARGS=""
ruby -e "puts Encoding.default_external" > $HOME/Desktop/kramdown.log
$CMD $CMDARGS

Encoding.default_external の値は US-ASCII でした。

一方、ターミナルで Encoding.default_external の値を確認してみると、UTF-8 でした。

入力データのエンコーディングは UTF-8 なので、上記の確認結果とあわせて考えると、ここが原因と見てよいでしょう。

回避策

アプリから kramdown を実行したときも Encoding.default_external が UTF-8 になるように指定できれば回避できそうです。

Encoding.default_external を指定するには以下の方法があるとのことです。

default_external
コマンドラインオプションの -E / -U / -K > RUBYOPT の -E 等 > shebang の -E 等 > locale
via Rubyist Magazine - Ruby M17N の設計と実装

今回はシェルスクリプト内で指定したいので、環境変数 RUBYOPT-E オプションを指定することにしました。

Marked からも kramdown を使いたいので、$HOME/bin/kramdown.sh を作って、Marked からはこれを直接呼び出し、MarsEdit は TextFilter からこのスクリプトを呼び出すようにしました。

#! /bin/sh
CMD=/usr/bin/kramdown
export RUBYOPT=-EUTF-8
$CMD $@

試してみるとうまくいきました。MarsEdit でこれまでと同様に kramdown が使えるようになりました。

ちなみに

アプリからの呼び出しで Encoding.default_external の値が US-ASCII になるのは、環境変数 LANG が空であるため(?)、Encoding.locale_charmapnil になるからだと思われます。

Encoding.default_external の値は以下のように決定されるとのことから上記のように判断しています。

default_external -> Encoding[permalink][rdoc]

規定の外部エンコーディングを返します。

入出力において、外部エンコーディングが指定されていない場合の規定値として利用されます。Rubyはロケールまたは -E オプションに従って default_external を決定します。ロケールの確認・設定方法については各システムのマニュアルを参照してください。

default_external は必ず設定されます。Encoding.locale_charmap が nil を返す場合には US-ASCII が、 ロケールにRubyが扱えないエンコーディングが指定されている場合には ASCII-8BIT が、default_external に設定されます。

via singleton method Encoding.default_external

関連記事

0 コメント: