タグ「node.js」が付けられているもの

先日の記事で、 ブログシステムとして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もまた使いたいです。

色々考えた結果、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の範囲が狭まることになりましたし、厳しい情勢です。

以前の記事で、 x-rayというライブラリを使ってスクレイピングをしてみた話を書きました。 その時に、ページネーションだけでなく、リンク先取得もできればいいと思ったのですが、 先日バージョンアップしていた2.0.0ではすばらしいことに複合的な処理が書けるようになっていました。

さっそく、試してみましょう。 サンプルををちょっといじって実行してみます。

var xray = require('x-ray')();
xray('https://www.google.com/', {
  title: 'title',
  image: xray('#gbar a:nth-child(2)@href', 'title'),
  map: xray('#gbar a:nth-child(3)@href', 'title'),
  play: xray('#gbar a:nth-child(4)@href', 'title'),
  youtube: xray('#gbar a:nth-child(5)@href', 'title')
})(function(err, obj) {
  console.log(obj);
});

ちゃんとそれぞれのページのタイトルが取れました。 「画像検索」はなぜか文字化けしました。

これは便利です。しかも、delayやthrottleの設定までできます。 今度勉強がてらコードも読んでみるといいかもしれません。

また、小ネタでした。

ところで、x-rayってだけだと検索キーワードとしてはあいまいすぎますね。

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

今さらながら、node-v0.12.3を入れました。 vmが新しくなったことはどこかで聞いたのですが、timeoutが指定できるようになっているというのは認識できていませんでした。

これまで、 rss-pipes や codeonmobile で、vmモジュールを使ってなんちゃってsandbox環境を作っていました。 その際、無限ループだけは避けたかったため、safeCode.jsというのを作って ループのカウントをしてexceptionを投げるようにしていました。

ところが、node-v0.12で新しくなったvmはtimeoutの指定ができるので、簡単にできるようになりました。 今後はこれを使うことにします。

サンプルコードはこちら。

var vm = require('vm');
var x = vm.runInNewContext('x=0;for(i=0;i<100;i++){x++;}x', {}, {
  timeout: 10
});
console.log(x);
x = vm.runInNewContext('x=0;for(i=0;i<10000;i++){x++;}x', {}, {
  timeout: 10
});
console.log(x);

実行するとこんな感じ。

$ node vm-test.js
100

vm.js:38
  return this.runInContext(context, options);
              ^
Error: Script execution timed out.
    at Error (native)
    at ContextifyScript.Script.runInNewContext (vm.js:38:15)
    at Object.exports.runInNewContext (vm.js:69:17)
    at Object.<anonymous> (/tmp/vm-test.js:6:8)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)

というわけで、なんちゃってsandboxが簡単に作れるようになりました。 ドキュメントにはuntrusted codeは別processで走らせるべきだというように書いてあります。確かに、メモリを大量に消費するようなmalicious codeには対処できないと思います。他にはどういう穴があるのでしょうか。globalオブジェクトだけを渡しているうちはそこまで悪いことはできないような気もしますが、どうなんでしょう。

http://dailyjs.com/2015/02/05/xray/ で紹介されていたx-rayというツールを試してみました。

https://github.com/lapwinglabs/x-ray のサンプルをほぼそのまま実行しました。

すこし脱線しますが、yieldableというのを知りました。利用者がcallbackタイプとyieldタイプを使い分けられるのはいいですね。

x-rayはpaginationがサポートされているのが便利でした。もう一歩進んで、一つリンクを進んで詳細ページも扱えるといいのですが。

さて、プロクシの設定をしたかったのですが、x-ray自体にはサポートされていません。しかし、内部で使われているsuperagentを差し替えることはできます。superagent-proxyというライブラリがあるのでそれを使えばできそうです。 今回はいつも使っているrequestを使ってみました。下記にコードスニペットを載せておきます。

var xray = require('x-ray');
xray.prototype.request = function(url, callback) {
  require('request').get(url, function(err, response, body) {
    callback(err, body);
  });
};

あまりうまいやり方ではないですが、一応こんな感じで動いています。

小ネタでした。

今まで、Chromeアプリにはそれほど興味を持っていなかったのですが、 下記の記事でangular.jsと組み合わせているのを見て興味が湧きました。

https://developer.chrome.com/apps/angular_framework

これは、手軽に作れそうです。 Chromeのディベロッパーモードをオンにすると、 Chromeアプリをパッケージする前にフォルダ指定でアプリを実行できます。 しかし、コードをいじるたびにリロードボタンを押さないといけないです。

これは不便、なんとかして先日作った easy-livereload を使いたいと思って、Chromeアプリをexpressと組み合わせて 開発してみることにしました。

manifest.jsonやbackground.jsはサンプル通りに作って、 jadeはパッケージ化するときにコンパイルするようにしました。 package.jsonのscriptsはこんな感じです。

"build": "mkdir -p build; cp manifest.json public/*.js public/*.css build; jade views/main.jade --out build"

Chromeアプリのパスと合わせるようにexpessのルートを設定します。 ひと手間必要だったのは、httpで外部サイトにアクセスする場合です。 Chromeアプリではmanifest.jsonに書けば制限を外せるのですが、 expressでブラウザで動作させる場合はcross domainで引っかかります。 そこで、express側で中継するようにしました。

app.get(/^\/proxy-(http|https)\/(.*)/, function(req, res) {
  request.get(req.params[0] + '://' + req.params[1], {
    encoding: null,
    qs: req.query
  }, function(error, response, body) {
    if (!error && response.statusCode === 200) {
      res.type(response.headers['content-type']);
      res.send(body);
    } else {
      console.log(error, body);
      res.status(500).send('proxy error');
    }
  });
});

こんな感じです。場合によっては、ヘッダーなども中継する必要があるでしょう。 これをクライアント側から呼び出すときは、例えば次のようにしました。

var serverMode = $location.host() === 'localhost';
$http.get((serverMode ? 'proxy-http/' : 'http://') + 'www.foo.bar/hoge.json')

もっといい書き方があるかもしれませんが、まあとりあえずはいいでしょう。これで開発効率が格段に上がりました。

LiveReloadをご存知でしょうか。Web開発をするときに、ブラウザのリロードを省く機能を提供するものです。 類似のものとしては、Live.jsというのもあります。 また、Meteorでも実装されています。

一言で言うと、websocketでコネクションを維持して、サーバ側のファイル変更を通知して自動でリロードするものです。 CSSの場合はリロードせずに変更を適用することもできます。 livereloadは商用のようですが、そのコアのソースコードはMITライセンスで公開されています。

livereloadはブラウザ拡張を使うことがメインのようですが、javascript版のクライアントもあります。 javascript版だと、mobile safariでも動きます。 livereloadのnode.js用のサーバは、https://github.com/livereload/livereload-serverで公開されていますが、 これはそのまますぐには使うことができません。 平たく言うとプロトコルが実装されているだけで、実際に更新通知する機能は含まれていません。

そこで、簡単に使えるライブラリとしてまとめてみました。特徴をまとめると、

  • livereloadのサーバコードの最新版を使用(現在npmに登録されているlivereload系のモジュールはほとんど旧バージョン)
  • livereloadのクライアントコードの最新版を使用(npm化されていないのでnapaというモジュールで導入)
  • fs.watch()を使ってファイル変更を迅速に監視(node-watchというモジュールを使用)
  • node-devを使うことでサーバ側のファイル変更を監視して、クライアントを自動リロードする機能を追加
  • これらの機能をたった一行を加えるだけで使えるようにパッケージ化

となります。

プロジェクトページは下記です。

https://github.com/dai-shi/easy-livereload

最低限の使い方は、

app.use(require('easy-livereload')())

ですが、細かい設定はオプションを引数に与えることでできます。

本質的には他人が作ったライブラリを結合して簡単に使えるようにしただけですが、 相当便利だと思いますので、ぜひお試しください。

夏ごろに「暑さをみんなでふっとばせ!」というアプリを作りました。

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

に経緯が書いてあります。

当時は、Famo.usやF/Aのライブラリに手を入れないといけなかったのですが、その後バージョンアップして不具合が解消されました。 新しいバージョンでは読み込み速度なども改善しています。

せっかくなので、GitHubにソースをアップロードしました。 Famo.us/Angularとsocket.ioの組み合わせでこんなことができるというサンプルによいかと思います。

ついでに、「暑」を「寒」に変更する機能もつけました。タイトルはそのままですが。

GitHubのページはこちらです。

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

実際に動作しているアプリのページはこちらです。

http://atsufut.axlight.com/#?color=blue&label=%E5%AF%92