「heroku」と一致するもの

先日の記事で、 ブログシステムとしてGhostを使ってherokuにデプロイする話を紹介しました。

ところが使い始めてみるといくつか問題がありました。

  1. herokuはアクセスが無いとスリープするので、サイトを初めて開くときに時間がかかる
  2. 常にアクセスが来るとスリープはしなくなるが、Freeプランの範囲を越えてしまい、Hobbyにアップグレードするように催促が来る
  3. 画像ファイルを置けないのはやはり多少不便である

そこで、hexoに移行してみることにしました。デプロイ先はgithub.ioです。 github.ioがどこまで使えるのかは分かりませんが、少なくともherokuのfree planよりはよさそうです。

hexoはghostと違ってスタティックなサイトを生成するだけですが、markdown形式のファイルを入力とするので、 移行は比較的楽かと期待しました。 Ghostのmarkdownプレビュー機能は便利だったのですが、それは残念ながら諦めることにしました。ちゃんと探していませんが、オーサリング側の拡張もありそうです。

移行時に気づいたことをまとめておきます。 全体的に、Ghostの方が出来が良くて、hexoでやりたいことを再現するのに苦労した感じです。

markdownの変換が不完全な場合がある

Ghostでは問題なかったmarkdownの変換がhexoではうまく行かないケースがいくつかありました。 今回テンプレートにhandlebarsを使ったのでそれが原因かもしれません。 例えば、{{foo}}のような記述がうまく変換できませんでした。&#x7b;{foo}}とすることで解決。 他にも、<code>の変換結果が異なりました。これは問題という訳でもないですが。

handlebarsがサポートされていない

Ghostで標準のhandlebarsがhexoではコミュニティサポートであり、サポートされていませんでした。 どうやら、hexo v2からhexo v3で大幅な変更があったようで、プラグインなどが追従できていないようです。 これはなんとかしました。

Casperの移植が大変だった

Ghostの標準テーマであるCasperががシンプルで使いやすくて気に入っていたのですが、 hexoのテーマには似たようなものがありませんでした。以前のCasperを移植したものはありましたが、 機能やデザインがいまいちでした。 Ghostに戻ろうかと何度も挫折しましたが、結果的になんとか自分が使いたいCasperの機能とデザインは移植できました。

まとめ

当初の目的であった、herokuの課題への対応は、github.ioになったことで解消されました。 hexoで苦労したのはどれもCasperを使おうとしたことに起因するので、もっと素直に使う分には問題ないかもしれません。 苦労したおかげでテーマのテンプレートやCSSは理解が進んでいじりやすくなりました。

最後に一つ学んだこと、Ghostすごい。WordPressを置き換えることができるかもしれません。 (ただ、単純に代替として使うという意識ではきっとダメです、新世代に移行するという意識でないと)

機会があったらどこかでGhostもまた使いたいです。

あまりにも特殊な話なので書くか迷ったのですが、記録のために書いておきます。

Ghostはnode.jsベースのブログシステムです。 先日、Ghostをherokuにデプロイする話を書きましたが、 使ってみて一つだけ不便に感じることがありました。

エディタでC-hが効かないのです。

C-hというのは普段Backspaceの代わりに使っているキーバインドで、 Ctrlキーを押しながらhを押します。ないと困る人には困るのです。 ブラウザでも使えるようにしています。しかし、これが困るのはLinuxユーザだけかもしれません。 OSXの場合はCtrlではなくてCommandがありますし、Windowsの場合はそもそもC-hが効かないと思います。

GhostのMarkdownエディターは気に入ったので、なんとかしてC-hを使えるようにしたいところ。 ちなみに、C-hが使えない理由はC-hがGhostのショートカットに割り振られていて、 ヘッダータグの挿入になるからです。 ちょっと調べてみましたが、本家Ghostではすぐには解決しそうにない(あまりに特殊環境か)ので、 forkすることにしました。

https://github.com/dai-shi/Ghost

です。修正したコードはほんの2行です。 C-hとC-bに設定されていたショートカットをC-S-hとC-S-bにしました。つまり、Shiftキーも同時に押した時だけショートカットが有効になるようにしました。

さて、これをghost-on-herokuでも使いたいのですが、一筋縄では行きませんでした。 というのも、gitリポジトリからcloneしてデプロイするには、gruntのタスクを走らせて ビルドする必要があるからです。手動でやるのは簡単ですが、これをnpmでやる必要がありました。

結果的には、ghost-on-herokuのpackage.jsonを次のように修正することで解決できました。

diff --git a/package.json b/package.json
index 3af466a..aec84be 100644
--- a/package.json
+++ b/package.json
@@ -10,14 +10,37 @@
   "private": true,
   "version": "0.6.4",
   "dependencies": {
-    "ghost": "0.6.4",
+    "ghost": "https://github.com/dai-shi/Ghost/archive/0.6.4-for-emacs-keybinding.tar.gz",
     "ghost-s3-storage": "~0.2.1",
     "pg": "latest"
   },
+  "devDependencies": {
+    "bower": "1.4.1",
+    "csscomb": "3.0.4",
+    "grunt": "0.4.5",
+    "grunt-bg-shell": "2.3.1",
+    "grunt-cli": "0.1.13",
+    "grunt-contrib-clean": "0.6.0",
+    "grunt-contrib-compress": "0.13.0",
+    "grunt-contrib-copy": "0.8.0",
+    "grunt-contrib-jshint": "0.11.2",
+    "grunt-contrib-uglify": "0.9.1",
+    "grunt-contrib-watch": "0.6.1",
+    "grunt-docker": "0.0.10",
+    "grunt-express-server": "0.5.1",
+    "grunt-jscs": "1.8.0",
+    "grunt-mocha-cli": "1.13.0",
+    "grunt-mocha-istanbul": "2.4.0",
+    "grunt-shell": "1.1.2",
+    "grunt-update-submodules": "0.4.1",
+    "matchdep": "0.3.0",
+    "top-gh-contribs": "2.0.2"
+  },
   "engines": {
     "node": "~0.10.0"
   },
   "scripts": {
+    "postinstall": "(cd node_modules/ghost/node_modules && ln -s -b ../../grunt-* .) && (cd node_modules/ghost/node_modules/.bin && ln -s -b ../../../.bin/bower) && (cd node_modules/ghost && grunt --force init && grunt prod)",
     "start": "node server.js"
   }
 }

devDependenciesはもう少し削れるかもしれません。 この修正に加えて、heroku configの設定も行いました。

$ heroku config:set NPM_CONFIG_PRODUCTION=false NODE_MODULES_CACHE=false

これで期待通りになりました。

色々考えた結果、herokuを使うのが一番手軽だと思い、Ghostをデプロイしてみることにしました。 file storage問題は既に取り組まれていて、storage moduleでfile以外の方法も使えるようになったようです。 具体的には、Ghost S3 Storageというのがあるようです。 個人的には、mongodbをバックエンドにしたstorageとかできないかなと期待します。

さて、Ghostをherokuにデプロイする簡単な方法はないか調べたところ、 https://github.com/cobyism/ghost-on-heroku というのがありました。 これは便利と思って早速試そうとしたところ、メール機能の有料アドオンを使う設定になっていて それを外す方法が分かりませんでした。

そこで、通常の方法でデプロイすることにしようと思いました。 参考にしたサイトはhttp://www.autodidacts.io/host-a-ghost-blog-on-heroku/です。 ところが、作業を開始してから思いの外手順が多いことに気づき、 作業メモの利用価値が低くなってしまいそうでした。 これでは再利用性がなくなってしまいます。

そこで、方針を戻して、ghost-on-herokuをforkすることにしました。 有料アドオンを使っているところをやめて、GMailでメール機能を使えるようにします。 Ghostのソースを調べると思っていたように、NodeMailerが使われていたため、簡単そうです。 ghost-on-herokuをみようみまねで編集して、ボタンひとつでデプロイできるようにしました。

こちらです。

https://github.com/dai-shi/ghost-on-heroku

ボタンひとつでデプロイできます。動作確認はこれからですが。

このherokuのテンプレート機能便利だと思いました。他にも使えそうです。

node.jsのデプロイ先としてはherokuを使うことが多いのですが、 他にはどういう候補があるのか調べてみようと思いました。 昔の記事を見ると手軽に使えそうだった、Nodejitsu, AppFog, Node Ninjaなど 使えなくなった(もしくは、以前より使いにくくなった)ものもあります。 また、最近Ghostを試してみたいと思ったのですが、 herokuがサポートされていない(ファイルアクセスができない)こともあり、 他の候補を調べてみることにしました。

条件は、無料で使えるプランがある(一定期間でも)ことです。 網羅はできていないと思います。順不同です。 実際に試したわけではなく、サイトから得られる情報をまとめただけです。

flow.ch

Flow App Engineは、Java, PHP, Ruby, Node.js, Python or .NET (Beta) を動作させることができて、dockerのイメージも動かせるそうです。 各種データベースも用意されているとのこと。 webベースのdashboardで簡単に操作できるようです。

free trialは14日間です。pricingは、usage-basedとfixedの2種類とのこと。

modulus.io

Modulusは、Node.js,PHP, Javaが使えます。Meteorがサポートされているのも特徴のようです。 modulusというコマンドをインストールして、デプロイ作業をするようです。 その他必要そうなものはひと通り揃っているように見えます。

sign upすると30日間使える$15分のクレジットがもらえるようです。 しかし、一番安い構成でも月に$28.80になるので、30日連続運用はできないことになります。

dotCloud

dotCloudは、Python, Java, Node.js, PHPが使えるようです。 デフォルトはMySQLですが、他のストレージのadd-onもあります。MongoDBもMongoSoupというのがあるようです。 特徴的なのは、プラットフォームへのアクセスで、 CLIのツールと、Webコンソールと、REST APIが用意されているとのこと。

Sign up for Freeとありますが、実際無料でどこまで使えるか分かりませんでした。 一番小さい構成では、月$9.90のようです。

cloud.google.com

Google Cloud Platformは、IaaSに近い感じですが、 Cloud Launcherを使って簡単にStackを構成できるようです。MEANも使えるようです。

Free trialでは、$300分を60日間使えるそうです。

bluemix

IBM Bluemixは、OSSのCloud FoundryをベースとしたPaaSです。 色々使えるようですが、一例として、Node.js, PHP, Python, Rubyがあげられています。

フリートライアルでは、30日間使えるそうです。

現在、アプリ開発コンテストが開催中とのこと。

まとめ

残念ながら、期間限定なしでfreeで使えるサービスは見つけられませんでした。 freemiumは最近は流行りではないのでしょうかね。 herokuもfreeの範囲が狭まることになりましたし、厳しい情勢です。

Gunosy RSSのURL変更その他

  • 投稿日:
  • by

久しぶりにgunosy-rssのコードをいじりました。

まず、URLを変更しました。新しいURLは、

http://gunosy-rss.axlight.com/

です。以前のURLでアクセスした場合は301 Redirectされます。 RSSリーダーがちゃんとURLを書き換えてくれればよいのですが、 おそらくすべてのRSSリーダー/アグリゲータが対応しているということはないでしょう。

今後、旧URLではアクセスできなくなることがあるかもしれません。 というのも、herokuの無料枠で動かしているのですが、その条件が変わるためです。 将来的にDNSでアクセス先を変更したりすることを想定してのことです。

しかし、当面はそんな面倒なことをするつもりはありません。 とりあえず、無料枠で動かし続けるためには、6時間スリープさせればよいようなので、 rssのttlを設定してみました。今後RSSリーダー/アグリゲータがttl正しく解釈してくれれば、朝の5時以降と夕方の5時以降に取得するようになるはずです。 しかし、これはもっと早く実装しておけばよかったですね。 ちょっと様子を見た感じだとアクセス頻度がだいぶ減ったように思います。 503 Retry-Afterが返ることも減ったようですし、本家Gunosyへのアクセス負荷も下がるかもしれません。

JavaScriptのDateのタイムゾーンが手元とherokuで異なっていて、はまりました。 ちょっとスマートでない方法で解決したのと、もともとのコードがスマートでないので見苦しいですが、ttlの算出周りに興味があればコードを見てみてください。 たぶんきっともっといい書き方があるはずです。

他にも今から見ると書き直したい部分があったり、そもそもライブラリのアップデータもしたいのですが、そこまでモチベーションがわきません。まあそのうち、ついでがあれば。


5/24追記。

タイムゾーンを設定する方法がありました。

$ env TZ=Asia/Tokyo node app.js

これでコードはシンプルになりました。

https://github.com/dai-shi/gunosy-rss

Browserify CDNはよさそう

  • 投稿日:
  • by

先日、Browserifyを使ってみたという記事を書きましたが、 その後、Browserify CDNというもをの知りました。Browserify界隈では普通なのかもしれませんが、Browserifyというのはてっきりローカルで動かすものという先入観があったので、眼から鱗でした。

初めに知ったのは、

https://wzrd.in/

です。 browserify-as-a-serviceとのこと、いいですね。URLも短くてよいです。 説明も分かりやすく、APIも直感的です。

これはいいと思っていたところ、さらに、

https://www.brcdn.org/

を見つけました。 こちらは後発ですが、wzrd.inとの比較が書かれています。 コンパイルしたファイルはAmazon S3に置かれるということで、productionにも使えそうです。 minifyもproductionを想定しているとのことです。

なるほどと思ってhttps://github.com/jfhbrook/browserify-cdnを見直すと、 Heroku installが書いてあったり、キャッシュが消えると書いてあったり、 どちらかというとa serviceであって複数のインスタンスが立ち上がることを想定しているようです。

というわけで、今後はhttps://www.brcdn.org/を積極的に使ってみようかと思います。

最近、Angular以外も試そうと思って、2012年春にオープンソースになったときから気になっていたMeteorを使うことにしました。 当時は、FirefoxかChromeかどちらかでしか動かず、動かない方で見ると、背景画像しか見えないというすさまじいものでした。 一方、そのコンセプトはとても共感できて、social-cms-backendを自分で開発したときも同じではないものの似た問題意識でした。

さて、Meteorのサイトは、

http://meteor.com/

です。いいドメイン持ってますね。Meteorの発音は「ミーティア」です。 T.M.Revolutionの歌にありましたね。よし、それを聞きながらコーディングしよう。

さて、Meteorの基本的な説明は他に任せるとして、今回はちょっと変わった使い方をしようと思います。そもそも、普通のWebアプリを作るなら慣れたAngularの方が早いのですが、canvasベースのアプリを作りたいと思ったのがきっかけでした。canvasベースのアプリだとAngularの恩恵があまり得られないので、Angular以外を試してみようと。細かい経緯は省きますが、下記のサイトを見てやってみようと思いました。

http://www.html5gamedevs.com/topic/4886-how-to-use-phaser-with-meteor/

Phaserというのはhttp://phaser.io/で、WebGL/Canvasでゲームを作るフレームワークです。Phaserの説明も他に任せます。上記の記事はちょっと古くて、今では、

https://atmospherejs.com/robertlowe/meteor-phaser

が使えます。MeteorのパッケージがAtmosphereというのは、かっこいいですが、慣れるまでは戸惑いそうですね。

さて、お題は1bitコミュニケーションツールを考えています。 夏の暑いときに作った「暑さをみんなでふっとばせ!」というアプリを移植してみようと思います。このアプリについては、下記を参照。

1bitで使い捨てで匿名のコミュニケーションツールを作ってみた

Famo.us/AngularのサンプルアプリをGitHubに置きました

これは、Famo.us/Angularで作りました。とてもお手軽だったのですが、ちょっとゲームのようなものに使うにはパフォーマンスに難がありました。そこで今回は同じものをMeteor/Phaserで作ってみようと思います。

この記事を書きながらコーディングするのでうまく行くか分かりません。また、読みにくくなってしまうかもしれませんがご了承ください。

さて、まずはMeteorをインストールします。https://www.meteor.com/installに従います。このチュートリアルはよくできてますね。

$ curl https://install.meteor.com/ | sh

1.0.1になってroot権限も要求しなくなりました。1.0のときも、sudoのパスワードを入れなければ同じだったのですが、これはシンプルでよいです。

冬なのでアプリは「寒さをみんなでふっとばせ!」にします。ちょっとふっとばすというのは合わないかもしれませんが。アプリ名はsamufutにします。

$ meteor create samufut
$ cd samufut

チュートリアル通りです。ここで、phaserのライブラリを入れてしまいます。

$  meteor add robertlowe:meteor-phaser

これができるようになったのは最近のようですね。

$ meteor &

で起動して準備完了です。コーディング開始。


とりあえず、Phaserを使ったコードを書くところで時間がかかっています。これはあまりMeteorは関係ないところです。 一つだけ気になったこととして、本体のjsを編集すると毎回パッケージのアップデートをしようとして遅いので、Phaserのコードはclient配下に移動しました。この場合はrefreshするだけなので比較的早いです。

Meteorの話が出てくるのは明日になりそうなので、本記事はとりあえず一時保存します。


さて、一日経過しました。その後、Phaserのコードはほぼ完成し、次に通信部分のコードに着手します。Meteorは既にsockjsを使って通信を行う仕組みになっているのですが、それをアプリからも使えるとよいです。調べたところ、

http://arunoda.github.io/meteor-streams/

というのがありました。パッケージはこちらですね。

$ meteor add lepozepo:streams

でインストールします。簡単です。サンプルコードもそのまま貼り付けます。

chatStream = new Meteor.Stream('chat');

if(Meteor.isClient) {
  sendChat = function(message) {
    chatStream.emit('message', message);
    console.log('me: ' + message);
  };

  chatStream.on('message', function(message) {
    console.log('user: ' + message);
  });
}

直感的ですね。これで実装してみましょう。今回はinsecureは入れたままにします。

完成しました。コードは237行でした。 それでは、デプロイしてみます。

% meteor deploy samufut.meteor.com

できました。メールアドレスが要求されました。

しかし、このURLはgithub pagesやherokuと同じように問題になるのではないでしょうか。そのうち、*.meteorapp.comとかになるかもしれませんね。

デプロイされたサイトは、

http://samufut.meteor.com/

です。ぜひ、遊んでみてください。使い方は書きませんでしたので、暑さをみんなでふっとばせ!の方を参照してください。

少し試した範囲だと、環境によっては通信がうまく行かないケースがあるようです。遅延というか、反映されないのです。またの機会に調べましょう。

ソースコードは下記にアップしました。

https://github.com/dai-shi/samufut

当初は、もっとMeteorのことを書こうと思っていたのですが、苦労したのはPhaserまわりだったのであまり書くことがなくなってしまいました。まだ、やったことないですが、insecureを外してまじめに実装するといろいろコード量が増えて、結局大変っていうことにならないか心配です。それでも、プロトタイプが手軽に作れるのは利点だと思います。

気になったことを一つ思い出しました。普段非同期処理で書くことが多く、比較的好んでいるのですが、Meteorのクライアント側のmongoは同期的なので戸惑いました。キャッシュされているからなせるわざなのでしょうか。事前にpublishできないようなデータの場合どうなるかなど気になります。もう少し勉強しなければいけません。

それでは、Meteorの今後に期待します。


12/28追記。

sudoがいらないと思ったのは勘違いでした。たまたま、その時の環境が/usr/local/binにwrite権限があったため。

RSS Pipesを使って、実用的なことをしよう、というお話です。

以前、RSS Pipesで広告エントリの除去という記事を書いたのですが、あまり自分でも使っていませんでした。今回、どうしてもやりたいことがあったので、久しぶりにRSS Pipesをいじりました。

やりたいことというのは、RSSのURLの修正です。RSSのURLにリダイレクトURLが使われていることがあります。例えば、bit.lyなどの短縮URLが使われている場合です。通常は困らないのですが、そのRSSをプログラムで処理しようとする場合に、短縮URLではなく最終的なURLが欲しい場合があります。めずらしいケースだとは思います。

URLのリダイレクトをたどるにはこれまでのRSS Pipesの仕組みの上だけではできず、新たな関数を作り込む必要がありました。具体的には、getRedirectURL()という関数を作りました。

ところが、このgetRedirectURL()は同期的には結果を返すことができません。nodeでは、通常callbackを使って非同期処理を書くのですが、RSS Pipesのフィルターは同期的に書くように作られていたので困りました。仕方なく、promiseを使うことにしました。

フィルターの方でもpromiseを扱えるようにする必要があるため、Qを使えるようにしました。しかし、これでブロックするようなコードも書けてしまうので、悪意コード対策がいずれ必要になりますね。promiseにタイムアウトの概念を持ち込めれば簡単にできるかしら。

さて、新しくなったRSS Pipesで書いたフィルターがこちらです。

function rssPipesFilterFunction(articles) {
  var promises = [];
  articles.forEach(function(article) {
    var maxRedirects = 5;
    var loop = function() {
      var promise = getRedirectURL(article.link).then(function(link) {
        if (article.link === link || --maxRedirects < 0) {
          return article;
        } else {
          article.link = link;
          return loop();
        }
      });
      return promise;
    };
    promises.push(loop());
  });
  return Q.all(promises);
}

https://gist.github.com/dai-shi/8934864

にも同じものを同じものを置いてあります。

リダイレクトが多重になっている場合を想定してループにしてみましたが、promiseをちゃんと理解していればもっとうまく書けるのかもしれません。どなたか改良してくれたら、うれしいです。

しかしこれでサーバ側の処理がますます重くなるので、キャッシュ機構を入れる必要がでてきそうです。そのうち、気が向いたらやりますか。今のところ、それほどユーザがいないのでほっておいても大丈夫です。それはそれでさびしいものの。

Gunosy RSS がしばらく前から調子悪く、RSSが取得できていなかったので調べました。

初めの原因は、GunosyページのHTML構造が変わったからでした。 (ところで、何で変わったのでしょう。前バージョンのクリーンで先進的な感じでした。)

HTML構造はすぐに対応できたのですが、大変なのはそれからでした。 なぜか、リクエストがタイムアウトしてしまう現象が発生しました。

https://devcenter.heroku.com/articles/request-timeout

に書いてあるように、herokuは30秒しか待ってくれません。 それを大幅にオーバーしているようなのです。

試しに、30秒ごとに1バイトずつデータを送ってタイムアウトしないようにしてみました。しかし、そのうちメモリがあふれてしまいました。

次に、リクエストを並列で処理しないようにしました。処理中にリクエストが来た場合には503レスポンスを返します。親切にRetry-Afterヘッダもつけました。

これで多少改善されましたが、それでも1つの処理に20秒くらいかかることもあり、30秒を超えることもしばしばでした。

ところで、なぜ、この問題が簡単に解決できなかったかと言うと、 ローカル環境では再現しないからです。ローカルでは数秒で処理が終わるものが、herokuにデプロイした状態では数十秒から場合によっては1分以上かかってました。

これは仕方ないと思い、本番環境でデバッグしました。直るまでは正常に使えないことには変わりないという割り切りで。

で、発見しました。遅さの原因はJSONPathというライブラリでした。これが数十秒もかかってました。ローカルでは数秒。

https://github.com/s3u/JSONPath/issues/14

で報告されているのを発見し、古いバージョンを使うことにしました。 そうしたら、なんと数秒で処理が終わるようになりました。 ローカル環境では、1秒もかかりません。めでたし、めでたし。

しかし、ローカル環境とherokuで10倍から100倍くらいの差があるのは、なぜでしょう。単なるスペックの問題でしょうか。そうであれば納得はしますが、その場合はローカルでも低スペックを再現できるような環境が欲しいですね。

リクエストを並列で走らせないようにする処理は残したままです。完全に取ってしまうと、また不具合があったときに波及が大きいと考えたためです。ただ、せめて数本同時に走らせるようにしないと、503になるケースが多すぎる気もするので、それは今後対応できたらよいかと思います。#2

ところで、Gunosy RSSの不具合は誰も気づいていなかったのでしょうか。 それはそれでいいような、さびしいような。

突然ですが、例えば、githubにリポジトリを置いて、herokuにデプロイするというケースは多いのではないでしょうか? そういうときは大抵、

$ git push master origin
$ git push master heroku

と2つコマンドを打つことになると思います。 それぞれ、git remoteでoriginとherokuが設定されているとして。

これが、一発で済むと楽ではないですか? もちろんローカルでshell scriptを書いてもいいかもしれませんが、 例えば、githubにはPUSH権限があるけど、herokuにはPUSH権限がないような ユーザがいるケースでは、どうでしょう? そういう場合に、githubからherokuに自動でデプロイできたら便利でしょう。

いくつか解決法はあるようですが、CodeShipというのを使ってみました。

http://blog.codeship.io/2013/10/02/how-to-deploy-a-ruby-on-rails-app-from-bitbucket-to-heroku.html

チュートリアルに沿って言われたとおりに設定するだけでできるようになりました。 どうやら、双方にアクセスするための鍵を登録して、 さらにPUSH時のhookを登録することで実現しているみたいです。 鍵の元はCodeShipにあるわけなので、CodeShip側に悪意があればいろいろできちゃいそうですが。

CodeShipはデプロイだけではなく、テストもまわせるなど、使いこなせばいろいろできそうです。