高尾宏治日記 on はてなブログ

プログラミング言語Rubyを中心とした技術情報を扱うブログです(Microsoft Surfaceはやめました)。

which-keyが遅すぎる

最近MacBook 12インチに乗り換えた高尾です。ついでに、「Emacs + 自前の.emacs.d」から「Emacs + Spacemacs」にも乗り換え済み。マシンのメモリ不足からちょっともっさりした感じはありますが、所有欲は十二分に満たされていますので大丈夫です!なにも気にならない、気にしない!!!

さて、今回は、Emacs + Spacemacsに関する記事です。

見つけたぞ、which-key!

次のファイルを編集するよ。Control-x Control-f...あらま、また固まったか...一休みだな。

これはEmacs + Spacemacsを使っているときによく起きることです。

マシンの性能によると思いますが、Spacemacsのコマンド候補の表示(Control+xを押してしばらく待つとminibufferに表示されるやつ)が遅すぎます。少なくとも、手元のMacBook '12 Early 2016だと、候補が多い場合は5秒程度はEmacsが応答しなくなる。これでは困ります。

なんとか改善できないか、調査してみます。そもそも、どのパッケージでそれを実現しているかを調べるところからですね。

ここは、eww-search(Emacs での テキストブラウザ eww を使えるレベルにする | Futurismo)の出番ですね。まだ慣れておらず、うっかりSafariを使ったりしてしまいますが、ewwのほうがよいこともたくさんあります。特に、0キーでマークダウン形式のURLをコピーできるのは使いやすいですね。

で、ググってもなかなか見つけられず...そもそもよいキーワードが思いつきませんね。「spacemacs minibuffer command list」ではだめでした。

それならばということで、 ~/.emacs.d/elpa/ を探索。それっぽいのはないかな。ace-windowってなんだろ、completeを含むものかな、なんて考えながら見ていくと、which-keyというそれっぽいものがあるでないですか。

which-key.elにもそれらしい記述があります。これで間違いなさそうです。

カスタマイズド、which-key。

which-key.elを読むと、defcustomでいろいろ設定項目が定義されています。 気になったのは、コマンド入力をやめてから候補を表示するまでの待ち時間 which-key-idle-delay だけですね。さて、defcustomってどうやって設定するんだったかな?setqでいいのかな???

ここでもeww-search。役立つね。で、defcustomで定義された変数はsetqではなくcustom-set-variablesで設定すべき理由 - kawamuray's blogを見つけました。

.spacemacsのdotspacemacs/user-config ()に以下を追加して様子見です。さて、どうかな。

(custom-set-variables '(which-key-idle-delay 10.0))

WindowsのGitで異常終了するようになったら...一時ファイルの削除と再起動でOK!

はじめに

今日、一日ぶりにWindowsEmacsからmagitを使ってmagit-refresh(magit-statusの画面でgを押す)すると「sh.exeが異常終了しました」というダイアログがでてmagit-refreshが失敗するようになりましたorz

なにか変ったことといえば、昨日、スリープからの復帰に失敗してブルーバックになりました。あぁ、スリープに失敗しまくっているので、いつかはこうなるよな~と思いつつ、対処方法を探します。(一瞬、Windowsのリフレッシュが頭をよぎりましたが、アプリケーションのセットアップなどに数日かかるため恐ろしくてできません...)

作業ログ

インターネットで「sh.exe.stackdump」のキーワードを検索して、次のブログの記事を見つけました。

http://d.hatena.ne.jp/miau/20131229/1388301913 より

git のコマンドを叩くと

 0 [main] sh.exe" 6768 handle_exceptions: Exception: STATUS_ACCESS_VIOLATION
 1620 [main] sh.exe" 6768 open_stackdumpfile: Dumping stack trace to sh.exe.stackdump

こんなエラーが出る状態になってしまったので、対策方法のメモ。ちなみに試したバージョンは Git-1.8.4-preview20130916.exe です。

まさにこれですね。

上記の記事で紹介されている「Process monitor」は http://technet.microsoft.com/ja-jp/sysinternals/bb896645.aspx からダウンロードできます。

Process monitorの操作説明を読んで、「Process Nameにsh.exeを含む」でフィルタリングすると、たしかに %TEMP% へのアクセス時に異常終了しているようですね。

じゃあ、あとは一時ファイルを削除すればよさそうです。Windows 7までは「ディスク クリーンアップ」でできたようなのですが、Windows 8では違うようです。

http://121ware.com/qasearch/1007/app/servlet/relatedqa?QID=013968 より

Windows 8.1の場合

「検索」ボックスに「不要」と入力し、表示された一覧から「不要なファイルの削除によるディスク領域の解放」をクリックして、手順5に進みます。

ふむふむ。

「不要なファイルの削除によるディスク領域の解放」で、一時ファイルのみをチェックして「OK」ボタンを押すと一時ファイルが削除されます。

あら、それでもまだmagit-refreshが失敗します。で、PC自体を再起動するとmagit-refreshが成功するようになりましたとさ。

まとめ

magit-refreshに失敗するようになったら、「不要なファイルの削除によるディスク領域の解放」で一時ファイルを削除してから再起動すればOK!

「Process Monitor」を知ることができたのはよかったね。

Surface Pro 2/Windows 8.1から、Surface Pro 3/Windows 8.1へのデータの移行 #SurfaceJP

基本的には http://takaokouji.hatenablog.com/entry/2013/12/30/213104 に従って作業します。違う点だけを以下に挙げますね。

  • 当該Emacs (gnupackemacs-24.3-20130503.exe) が見つけられませんでした。そのため、Surface Pro 2の当該ディレクトリ (C:\emacs-24.3-20130503/) をそのままコピーしました。
  • ドキュメントもコピーしました。
  • VirtualBoxVMはめっちゃファイルが大きいので今後どうするか考えないと...
  • 忘れていたけど、Autohotkey_LがC:\toolsにありますので、それを実行しないとね♪

Surface pro 3の感想

と、ここまでの作業でのSurface Pro 3の使用感を書いておきます。

良い点

  • 軽い
  • 薄い
  • かっこいい!
  • 画面が広い
  • Type Cover 3のタッチパッドが使いやすくなった。クリックできる。以前のものは右クリックができないに等しかったのですが、これは 右クリック は誤認識はしませんね。
  • すげぇ~!10分以上時間がかかっていたsmalruby-editorの自動テストが3分で終わっている!!!Core i7はすごいですね♪

悪い点

  • Windows UpdateをするまではまともにSurface Penが使えませんでした。Surface Penの上部のボタンを2回押してもスクリーンショットが撮影できず、初期不良かと思ってとても焦りました。
  • 発熱がすごい。これ、持てないじゃん。もしかして、初期不良!?
  • Type Cover 3を浮かすようにするとキー入力がやりずらい。
  • 親指の腹の部分がType Cover 3のタッチパッドに触れてしまい、文字の入力中に誤ってクリックが発生してしまう。タップが作動するまでの待ち時間を長く設定しても解決できなかった orz

Surfaceグループの紹介

Surface全般を扱うはてなグループ Surface に参加してみませんか? みなさんの参加をお待ちしております!!

そういえば、BASICのデバッグってどうやってたんだっけ?

私はラッキーなことに、子供のころにプログラミングというものを知った。それは「日清焼そばUFO」のおかげであった。 今では考えられないが、「ポケコン」というプログラミングをすればゲームにでも電卓にでもなるようなコンピュータを「日清焼そばUFO」の景品として配っていたのだ。(http://ameblo.jp/iemotolovexxx/entry-11713213543.html で紹介されていた!?)

たとえるならば、ベビースターラーメンの景品がRaspberry Piなのだ!(ベビーちゃんタオルもほしいけどもね♪)

http://www.oyatsu.co.jp/product/babystar.html

http://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Raspberry_Pi_Photo.jpg/800px-Raspberry_Pi_Photo.jpg

縁があって、それを手にした私は親指でしか押せないようなキーボードを使って、ちまちまと50行のプログラムを入力したのだ。当然、一発で入力できるわけもなく、「RUN」と入力しては問題がある行が表示され、紙に印刷されたプログラムとにらめっこして入力間違いを修正したのだ。そして、そして、あのプログラムが正しく動いた時の感動はたまらなかった。50行の文字がゲームになるのだ。4,800円のポケモンのカセットではなく、50行の文字と、2日間の子どもの労働(お手伝いの相場は10円なので、20円程度だろうwww)によってゲームを手に入れたのだ。

このような経験を子供の時にできてことが今につながっているのかな~と思う今日この頃。

ふと思い返してみると、あのときはどうやってプログラムの問題を見つけること(デバッグ)ができたのだろう?いまならruby-debug、gdbなどなど、いろんなものを駆使するだろう。当時は1行しかないディスプレイでどうやってデバッグしたんだろうね。今と違って、体力がありあまっていたんでしょうね♪

おわり

Google 製 Visual Programming Editor「Blockly」の調査

Blockly とは

smalruby-editor の視覚的なプログラムエディタ(Visual Programming Editor)の実装には Blockly を使うことを想定しています。 BlocklyGoogle 製の Visual Programming Editor です。Apache License, Version 2.0 にしたがって配布されているオープンソースソフトウェアです。また、いわゆるタダ乗りにも寛容なフリーソフトウェアの思想を感じます。

まずは デモサイト Code - Export a Blockly program into JavaScript, Python or XML. にアクセスして Blockly がどのようなものか確認するといいでしょう。

f:id:kouji0625:20140119011930p:plain

さて、ざっと Blockly がどのようなものかわかったところで smalruby-editor の実装に必要な次の情報を調査していきましょう。

  • デモサイトをローカルマシンで動作させる方法
  • Blockly を改良する方法
  • Rubyソースコードを出力する方法
  • 任意のブロックを作成する方法
  • ブロックをカテゴリ分けする方法
  • インタフェースをカスタマイズする方法
  • インタフェースを日本語にする方法

デモサイトをローカルマシンで動作させる方法

まずは git clone します。

git clone https://github.com/google/blockly

そして、 \path\to\blockly\apps\code\index.html をブラウザで開きます。

あらま、これだけで Blocklyデモサイト が手元でも動作します。

Blockly を改良する方法

Blocklyを改良するために必要なソフトウェアをインストールします。

これで準備が完了しました。

Blocklyをビルドします。

PS C:\Users\kouji\work\smalruby\blockly> python build.py
('Error running i18n/js_to_json.py: ', WindowsError(193, '%1 \x82\xcd\x97L\x8c\xf8\x82\xc8 Win32 \x83A\x83v\x83\x8a\x83P\x81[\x83V\x83\x87\x83\x93\x82\xc5\x82\xcd\x82\xa0\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x81B'))
SUCCESS: blockly_uncompressed.js
SUCCESS: blockly_compressed.js
Size changed from 1814 KB to 439 KB (24%).
SUCCESS: blocks_compressed.js
Size changed from 91 KB to 53 KB (59%).
SUCCESS: javascript_compressed.js
Size changed from 76 KB to 44 KB (58%).
SUCCESS: python_compressed.js
Size changed from 62 KB to 29 KB (47%).

なんかエラーが出ていますね。失敗しているのは以下の箇所です。 i18n/js_to_json.py の実行に失敗していますが、Windows では shebang を指定してもスクリプトは実行できないのでどうにかする必要があります。

        subprocess.check_call([
            os.path.join('i18n', 'js_to_json.py'),
            '--input_file', 'msg/messages.js',
            '--output_dir', 'msg/json/',
            '--quiet'])

以下のように修正して回避します。

diff --git a/build.py b/build.py
index 4dbbf89..92f917b 100755
--- a/build.py
+++ b/build.py
@@ -334,6 +334,7 @@ class Gen_langfiles(threading.Thread):
                       ['en.json', 'qqq.json', 'synonyms.json']]):
       try:
         subprocess.check_call([
+            'python',
             os.path.join('i18n', 'js_to_json.py'),
             '--input_file', 'msg/messages.js',
             '--output_dir', 'msg/json/',
@@ -350,6 +351,7 @@ class Gen_langfiles(threading.Thread):
     try:
       # Use create_messages.py to create .js files from .json files.
       cmd = [
+          'python',
           os.path.join('i18n', 'create_messages.py'),
           '--source_lang_file', os.path.join('msg', 'json', 'en.json'),
           '--source_synonym_file', os.path.join('msg', 'json', 'synonyms.json'),

これでBlocklyをビルドできるようになりました。

PS C:\Users\kouji\work\smalruby\blockly> python build.py
WARNING: definition of LOGIC_NEGATE_TOOLTIP in msg\json\th.json contained a newline character.
SUCCESS: msg\js\ar.js
SUCCESS: msg\js\az.js
(省略)
Size changed from 91 KB to 53 KB (59%).
SUCCESS: javascript_compressed.js
Size changed from 76 KB to 44 KB (58%).
SUCCESS: python_compressed.js
Size changed from 62 KB to 29 KB (47%).

Blockly が正しく動作しているかどうかは、 https://code.google.com/p/blockly/wiki/UnitTesting に従って操作することで確認できます。なお、Windows 8.1/Internet Explorer 11 では確認用の HTML が正しく動作しません。Firefox or Chromeで試しましょう。

ついでにちょっと修正してみます。意味は特にないのですが文字列のダブルクォーテーションを2つにしてみます。

以下の修正を行って、ビルドして、demos/fixed/index.html をブラウザで開くと修正内容が反映されていることが確認できます。

diff --git a/blocks/text.js b/blocks/text.js
index 3bdd714..9797353 100644
--- a/blocks/text.js
+++ b/blocks/text.js
@@ -36,6 +36,8 @@ Blockly.Blocks['text'] = {
     this.appendDummyInput()
         .appendField(new Blockly.FieldImage(Blockly.pathToBlockly +
         'media/quote0.png', 12, 12))
+        .appendField(new Blockly.FieldImage(Blockly.pathToBlockly +
+        'media/quote0.png', 12, 12))
         .appendField(new Blockly.FieldTextInput(''), 'TEXT')
         .appendField(new Blockly.FieldImage(Blockly.pathToBlockly +
         'media/quote1.png', 12, 12));

Rubyソースコードを出力する方法

Blocklyは、JavaScriptPythonソースコードを生成できます。しかしながら、Rubyには対応していません。 でもね、 https://github.com/velniukas/blockly.cascading.jrubyRubyソースコード生成に対応したものが公開されています。 とっても参考になります。

レポジトリをリモートに追加しておきます。

cd path/to/blockly
git remote add velniukas git@github.com:velniukas/blockly.cascading.jruby.git

また、 https://code.google.com/p/blockly/wiki/CustomBlocks から参照できる以下の文書も参考になります。

まとめると、BlocklyRubyソースコードを生成できるようにするには、

といったことをすればいいようですね。

任意のブロックを作成する方法

チュートリアルに従ってタートルグラフィックス用のブロックを作ってみました。ブロックのカスタマイズを GUI で行える ことには驚きました。よくできています。

ただし、WindowsPowerShell だと最後のコマンドの --srcs オプションの引数を以下のように " で括ってやる必要がありました。

PS C:\Users\kouji\work\smalruby\blockly\apps\turtle> java -jar ../_soy/SoyToJsSrcCompiler.jar --outputPathFormat generated/en.js --srcs "../common.soy,template.soy"

ブロックを作るときに使うAPIのリファレンス もあります。

ブロックをカテゴリ分けする方法

Blockly ではブロックを選択するところを Toolbox と呼びます。このサンプルのように Toolbox にずら~とブロックを並べたり、タートルグラフィックス用のブロック のようにカテゴリ分けしてブロックを配置することができます。

そのやり方は https://code.google.com/p/blockly/wiki/Toolbox に記述されています。この文書の最後のサンプルには、あらかじめいくつかのブロックを組み合わせたものを Toolbox に配置する方法が説明されていますね。

インタフェースをカスタマイズする方法

Blockly のインタフェースのうち、ブロックやブロックを配置するためのキャンバスは SVG で表現されているようですので、それをカスタマイズするのは簡単ではなさそうです。 しかしながら、Toolbox は HTML で表現されていますので、CSS で簡単に見た目を変えることができそうです。

smalruby-editor では、できればScratchのように選択中のカテゴリのブロックを常に表示させたいのですが、そのためには core/toolbox.js を編集するか、それを継承した独自の Toolbox を作成する必要がありそうですね。

インタフェースを日本語にする方法

Blockly で利用している Soy というテンプレートエンジンがメッセージの翻訳に対応しているようです。 前述した「任意のブロックを作成する方法」で使ったコマンド java -jar ../_soy/SoyToJsSrcCompiler.jar --outputPathFormat generated/en.js --srcs "../common.soy,template.soy" のようにして、テンプレートから翻訳対象の文字列を抽出して、 JavaScript のメッセージカタログを出力できます。

実際には、SoyMsgExtractor.jar/xliff_to_json.py というコマンドを使って翻訳対象のメッセージをJSON形式で抽出して、ja.json などの各国語に翻訳したものを作成後、 json_to_js.pyJSON から JavaScript のメッセージカタログを出力するようです。

ここまでの準備ができたら、以下のJavaScriptの処理によってメッセージカタログを読み込みます。これでインタフェースを日本語にできます。

document.write('<script type="text/javascript" src="generated/' +
               BlocklyApps.LANG + '.js"></script>\n');

詳細は TranslationForDevelopersApplications and Tutorials に書いてあります。

Surface Pro 2/Windows 8.1で、スリープから復帰しない人のためのとりあえずの解決方法と、これからやるべきたったひとつのこと #SurfaceJP (追記:1/22時点で解決したかも?)

(1/22追記)本日、ファームウェアの更新がありました。こちらの記事にもあるように下記の問題が解決しているかもしれません。私の手元でも調査して問題がなければその旨を追記する予定です。

Twitterの#SurfaceJPでもちらほら話題に挙がっているようですが、Surface Pro 2ではスリープから復帰しません。後述するようにMicrosftからの公式な発表はありませんし、Microsoftのサポートでもガン無視されるんですけど、スリープから復帰しないのは事実なんですよね。

スリープから復帰しないってどういうこと?

「はぁ、俺のSurface Pro 2はスリープから復帰するけど、なに言ってんの?」って人も多いと思いますが、いくつかの条件がそろうとあなたのSurface Pro 2もスリープから復帰しなくなるでしょう。その条件とは以下のようなものです。

  • 電源プランがバランスか省電力
  • なにかしらのハードウェアを接続している(私の場合は純正のドッキングステーションとマルチタッチ対応ディスプレイです)
  • スリープしてから1時間以上経過

これだけで、あら不思議!?スリープから復帰しなくなります orz

また、復帰しないといっても起動しないのではありません。通常の再起動はします。当然、スリープしたときの状態を完全に破棄してしまいます。レジストリや保存前のアプリケーションのファイルも破棄するのです。

それでも、「はぁ、俺のSurface Pro 2はスリープから復帰するけど、なに言ってんの?」って人がいると思いますが、それは運がいいだけです。10回に1回くらいの割合で復帰しないんですよ。1000回に1回なら、たまたまだから仕方がないとも思えますが 10回に1回はありえんでしょ!

スリープ前の状態によっては、復帰に失敗したせいで、すべてのファイルが読めなくなるかもしれないんですよ、もうスリープなんて怖くてできなくなりますよ。

で、とりあえずの解決方法

実はこの現象、スリープではなく「休止状態」に問題があるのです。Surface Pro 2/Windows 8.1は、なにかの条件がそろうと休止状態から復帰しないようなのです。

「えっ、俺は休止状態になんてしてないけど?」って思うかもしれませんが、Surface Pro 2の初期設定では、スリープ後に一定時間経過すると休止状態になります。バッテリ駆動時は60分、電源接続時は180分です。バッテリでの稼働時間を伸ばすための機能だと思いますが、この設定のせいで、スリープから一定時間経過する前にスリープから起こしてやらないと復帰しなくなっちゃうという残念なことになっています。

なので、それらの設定を変更してどちらの場合も休止状態にならないようにすればOKです。(いや、全然OKじゃないんだけど...) また、休止状態にしない場合は、スリープ中もどんどんバッテリを消費していき一晩で電源が切れてしまうこともあるでしょう。 寝る前には必ず電源に接続するようにしましょう!

設定方法は以下のような感じです。私は電源に接続しているときだけSurface Pro 2にハードウェアを接続していますので、バッテリ駆動の設定は変更せず、電源に接続のみ0(なし)に設定しています。

f:id:kouji0625:20140118160053p:plain

f:id:kouji0625:20140118160109p:plain

f:id:kouji0625:20140118160116p:plain

これからやるべきたったひとつのこと

さて、スリープから復帰しない不具合のために、毎日電源に接続しないといけないなんて残念ですよね。そして、 これはいつかきっと修正されるはずなんて思っているなら大間違いです!

私がこの問題についてSurfaceのサポート窓口に連絡したところ、とても親切に対応してくださり、再現確認もしてくださったのですが、残念ながらSurface Pro 2単体では再現しませんでした。私の手元でもSurface Pro 2単体では再現しないため、これ以上サポート窓口に問い合わせても無駄だと思いました。

で、そのときに「このような問題はあなたからしか報告されていません」と言われたのです!

私はSurfaceのサポート窓口に連絡する前に、インターネットで調べたところ少なくとも2件はスリープから復帰しないという事例がありました(ごめんなさい、情報元が不明です)。今調べてみると結構事例がありそうです - surface pro スリープから復帰しない/Google

みんながインターネットで騒いでいてもMicrosoftは見ていないかもしれないんですよ!!現時点(1/18)でもこの問題に対応していないことからも、Microsftからの公式な発表はありませんし、Microsoftのサポートでもガン無視されている事象なんですよ。

だから、この問題で困っているのであれば、あなたがこれからやるべきたったひとつのことは、 Contact Us マイクロソフトへのご意見・ご要望 にアクセスしてこの問題が発生していることを報告することです。 もしかすると、私が報告した1件しかカウントされていないかもしれません。もしそうならば対応されなくても当然ですよね。

ぜひみなさんの声を Microsoft に届けましょう。そうすればいつかきっとこの問題が解決されるはずです!

Surfaceグループの紹介

Surface全般を扱うはてなグループ Surface に参加してみませんか? みなさんの参加をお待ちしております!!

Turnipによる受け入れ試験中に、Rails.envをtestではないのもの(standalone)に変更する方法 (その2) #RubyJP

この記事はその1の続きです。

受け入れ試験の任意のタイミングで app/assets/javascripts/*.js.coffee.erb を再コンパイルする方法を調べています。

後述するようにとりあえずの方法はわかりましたが、あとでいろいろ考え直します。

[ダメ] Poltergeist の session.driver.quit

まずはPhantomJSのセッションを削除してみることにします。

jonleighton / poltergeist - Memory leak より

If you run a few capybara sessions manually please make sure you've called session.driver.quit when you don't need session anymore. Forgetting about this causes memory leakage and your system's resources can be exhausted earlier than you may expect.

spec/spec_helper.rb に以下を追加。

config.before(standalone: true) do
  page.driver.quit # 追加
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
  page.driver.quit # 追加
end

さっぱり feature が通らなくなった。セッションが切れたらだめだよね。

[ダメ] sprockets のキャッシュをクリア

tmp/cache/assets/test/sprockets/ 以下のファイルを削除してみる。

spec/spec_helper.rb に以下を追加。

config.before(standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) } # 追加
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) } # 追加
end

再コンパイルはしない。 ただし、sprockets のキャッシュが削除されれば次回のテスト実行時に assets を再コンパイルすることが分かった。

[ダメ] feature を分けてみる

test と standalone の feature を分けてみたが、ダメだった。

[ダメ] feature を分けてbefore/after(:all)で sprockets のキャッシュをクリア

test と standalone の feature を分けて、なおかつ spec/spec_helper.rb に以下を追加したがダメだった。

config.before(:all, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.after(:all, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

(そろそろ Poltergeist/PhantomJS または Turnipソースコードを読まないといけないかな)

[ダメ] Capybara::Poltergeist::Driver#restart いろいろ

以下、ダメ。

config.before(:all, javascript: true, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.after(:all, javascript: true, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.before(javascript: true, standalone: true) do
  page.driver.restart
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(javascript: true, standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
  page.driver.restart
end

以下もダメ。

config.before(:all, javascript: true, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.before(javascript: true, standalone: true) do
  page.driver.reset!
  page.driver.restart
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(javascript: true, standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
end

結局 sprockets 関連のソースコードを読んだ

sprockets の結果はメモリにキャッシュされるようだ。

  • sprockets-rails-2.0.1/lib/sprockets/railtie.rb:26 キャッシュのディレクトリを指定
  • sprockets-rails-2.0.1/lib/sprockets/railtie.rb:113 キャッシュをSprockets::Indexに変更している
  • その他もごにょごにょ <- 面倒になって記録をしなかった

なので(?)、以下のようにするとキャッシュが消えて、assets が再コンパイルされるようになった!

# assetsのキャッシュを削除する
#
# ただし、このメソッドを呼び出した後で page.driver.restart を実行しなけ
# ればブラウザ側のキャッシュが有効になったままとなる。
def expire_assets_cache
  env = Rails.application.assets
  if Rails.application.config.cache_classes
    env.instance_variable_set('@assets', {})
    env.instance_variable_set('@digests', {})
    env = env.instance_variable_get('@environment')
  end
  env.send(:expire_index!)
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f|
    FileUtils.remove_entry_secure(f)
  }
end

config.before(:all, javascript: true, standalone: true) do
  expire_assets_cache
end

config.after(:all, javascript: true, standalone: true) do
  expire_assets_cache
end

config.before(javascript: true, standalone: true) do
  page.driver.restart

  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(javascript: true, standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env

  page.driver.restart
end

とりあえずはこれで様子を見よう。

でもね、これはRails実装依存なのがとてもよくないですね。そもそも Rails.env が standalone のときだけ動作を切り替えるというのは実装がまずいのかもしれませんね。あとで考え直すというタスクをTODOに積んでおこう♪