前回はgrinderを使ってタスクを実行するという記事を書きました。
その時に、compile
タスクを定義しました。dartをjsに変換、sassをcssに変換、rspを変換などです(rspはrikuloのテンプレート機能。JSPみたいなやつ)。
@Task() void compile() { dart2js(new Directory(path.absolute('web/client/js'))); sass2css(new Directory(path.absolute('web/client/css'))); compileRsp(new Directory(path.absolute('web/client'))); }
実はこれらを定義してちゃんとコンパイルが通るようになって、いい気になっていたのですが、欠陥がありました。例えば、sassファイルが正しくない状態でコンパイルされたらコンパイルエラーが発生して、grinderで実行中のタスクごと落ちてしまうという不具合です(しかもrikulo streamのプロセスは生きたまま…)。
つまりはエラー制御をしっかりやれって話です、はい。
エラーになったコード
エラーになったコードを書いておきます。 やっていることはシンプルで、非同期でsassをcssにコンパイルしています。
/// sassをcssにコンパイルする /// /// [file]がDirectoryだった場合は、再帰的に呼び出す。 /// Fileだったら非同期でコンパイルする。 void sass2css(FileSystemEntity file) { file.exists().then((bool exists) { if (file is Directory) { Directory dir = file; dir.list().listen((FileSystemEntity fse) { sass2css(fse); }); } else if (file is File && (file.path.endsWith('.scss') || file.path.endsWith('.sass'))) { // この処理でコンパイルに失敗したら本体を巻き込んで落ちる! new File("${path.withoutExtension(file.path)}.css").writeAsString( sass.compile(file.path), mode: FileMode.WRITE_ONLY ); } }); }
エラー制御を追加したコード
Dartの非同期処理のエラーハンドリングにはZoneという概念があり、エラー制御はそのZoneの中で行うことができます。今回はそれをしていなかったために、全体を巻き込んで落ちていました。シンプルなZoneの例だと、非同期処理をrunZoned
でくくります。
runZoned(() { // 非同期処理 }, onError: (e) { // 非同期処理でエラー発生したら実行されるブロック // なんらかのエラー制御を行う });
これを元に、エラーになっていたコードを直します。
/// sassをcssにコンパイルする /// /// [file]がDirectoryだった場合は、再帰的に呼び出す。 /// Fileだったら非同期でコンパイルする。 void sass2css(FileSystemEntity file) { file.exists().then((bool exists) { if (file is Directory) { Directory dir = file; dir.list().listen((FileSystemEntity fse) { sass2css(fse); }); } else if (file is File && (file.path.endsWith('.scss') || file.path.endsWith('.sass'))) { runZoned(() => new File("${path.withoutExtension(file.path)}.css").writeAsString( sass.compile(file.path), mode: FileMode.WRITE_ONLY ), onError: (e) { log(e.toString()); }); } }); }
エラーが起きたらその原因をgrinderのログに出力するように変更しました。 これで、コンパイルエラーが起きてもgrinderのタスクを巻き込んで落ちることはなくなりました。
編集後記
非同期処理のエラー制御まで頭が働いていませんでした。たしかにそれでアプリケーション自体を巻き込んで落ちてしまったら、ウェブサーバーとかの場合は洒落になりません…。しかしrunZoned
を使えば見通しよくエラー制御できるのでいいですね。