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

node.jsとかブラウザで簡単な統計処理をしようと考えて使えそうなライブラリがないか調べました。 できるだけ依存ライブラリがないものがいいです。

構築にMakeを使うと書いてアイルからネイティブコードがあるのかと思いましたが、そうではなくpure JavaScriptでした。 http://jstat.github.io/にドキュメントがあります。かなり色々な関数がそろっているようです。 検定などもできるようです。JavaScriptのArrayをそのまま渡すこともできるので、お手軽です。 GitHubのStarは712です。

こちらも同じような感じです。Arrayを使うのが基本です。回帰や分類もできるようです。 これが紹介ページですが、フォントがいい感じです。 GitHubのStarは821なので、jStatとこれがメジャーどころでしょうか。

こちらはCollectionやVectorといったクラスを導入して抽象化しているようです。 と思いましたが、作られたオブジェクトは普通のArrayとしても扱うことができ、とても便利そうです。 READMEにはJavaScript標準のMathと連携しているサンプルが載っています。 JavaScriptのprototype chainのおかげですね。魅力的です。 Star数は327です。

ドキュメントページはこちらです。 Star数は711なので初めの2つと同じくらいですね。ここまで来るとStar数だけでは判断できないでしょう。 Vector and Matrix math for JavaScriptというふれこみで、他のものよりMatrix系が充実しているようにも見えます。

READMEにはほとんど情報がないのに、Star数が530という状態が不思議です。最近更新されていないところを みると過去に集めたStarでしょうか。それともどこか別のところにドキュメントページがあるのでしょうか。 Rっぽいことができるらしいです。コードとしては枯れているのかもしれません。

これまでのライブラリと比べると小規模なもので、Arrayを拡張して基本的な関数群を追加してくれます。 機能は少ないですが、これだけがやりたい場合はいいかもしれません。Star数は23です。


最後に、こんなページを見つけました。 中身はよく理解していませんが、JavaScript(というかv8)は意外と速いと言う気付きだそうです。

http://julialang.org/には新しく、もう少し見やすいベンチマークの表が載っています。

上記のライブラリたちのベンチマークも気になるところです。

ブラウザ側に情報を取っておく手段としてlocalStorageがあります。 昔はcookieを使っていたことが多く、今でもまだよく使われているのですが、 モダンブラウザ対応ならlocalStorageの方が適しているのではないかと思います。

以前、JavaScript nativeでlocalStorageを使うコードは書いたことがあって、 別に不満はなかったのですが、AngularでlocalStorageを簡単に扱えるライブラリがあると 思って調べてみました。

このあたりがメジャーそうです。順当に行くとStar数が一番多いangular-local-storageを選ぶのですが、 じっくりREADMEを読んでみます。APIは自明で分かりやすいです。bindはangularならではのAPIで便利そうです。 localStorageが使えない場合はcookieにfallbackするそうです。APIは抽象的に作られているということですね。

さて、2つ目のangularLocalStorageはどうでしょう。名前が似ていますが別物です。 bindもあるしAPIもシンプルで分かりやすいです。cookieへのfallbackもあるそうです。 angular-local-storageと比較すると若い分、Star数が少ないだけでしょうか。issueも処理されて アクティブに見えます。でも違いがない分、ドキュメントが充実しているangular-local-storageを選びたくなります。

最後の、ngStorageは、in the Angular Wayということで少し変わった感じです。

No Cookie Fallback - With Web Storage being readily available in all the browsers AngularJS officially supports, such fallback is largely redundant.

だそうです。確かにangularを使うようなブラウザでは、localStorageをサポートしていないケースは無いですね。 これは思い切りがよいです。気に入りました。

$scope.$storage = $localStorage;

使い方もこれだけです。APIとかそういうレベルではない感じ。とてもいいです。

And use it like you-already-know

だそうです。気に入りました。 これはつまり、localStorageを加えた3-way data bindingということですね。 これは応用が広がりそうです。時間があったらコードも読んでみたいです。

というわけで、おすすめはngStorageでした。

以前の記事で、 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オブジェクトだけを渡しているうちはそこまで悪いことはできないような気もしますが、どうなんでしょう。

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/を積極的に使ってみようかと思います。

今さらながらbrowserifyを使ってみました。 存在は知っていたものの普段はcdnを使ってライブラリを読み込むようにしていたので、 必要性を感じていませんでした。 今回、ライブラリをローカルに保存したかったので使ってみました。 bowerと比較してnpmだけで済むのがいいです。

しかし、npm上のライブラリが対応しているかどうか分からないというのは難点ですね。 実際に使おうとしていたライブラリがうまくラップしていないものがありました。 そこで、browserify-shimを使いました。 これは、CommonJSでexportしていないライブラリをexportしたかのように使えるようにするものです。 関連して、napaを思い出しました。 うまくやればnapaとbrowserify-shimの組み合わせは最強ではないでしょうか。

browserify-shimの使い方はあまり検索しても見つからなかったので、メモしておきます。 基本的にはREADMEに書いてあるのがそのままなのですが、一瞬理解しにくかったです。

以下、package.jsonの中身の書き方です。まず、

{ 
  "browserify": {
    "transform": [ "browserify-shim" ]
  }
}

これが必須です。これにより、browserify-shimが認識されるようです。 transformは他にも用途があるのでしょうか。それも興味あります。 次に、

{
  "browserify-shim": {
    "./node_modules/foo/bar.js": { "exports": "Bar" }
  }
}

これはどのファイルのどのようにラップするのかを記述します。 詳細は調べていませんが、これはbar.jsのファイルの最後に

exports = Bar;

を追加したような感じです。もちろん、function(){}でスコープを切っているでしょうが。 さて、一番悩んだのは、どうやってこれを利用するかでした。 状況としては、./public/javascripts/hoge.jsというファイルでどのように使うかというものです。 これは、

var bar = require('../../node_modules/foo/bar.js');

としたら動きました。パスが違うのが曲者です。

改めて、これはnapaと組み合わせると便利かもしれません。 最近自分でよく使っているcdnとhttp://rawgit.com/の組み合わせに匹敵するかも。


5/13追記。

よりよい設定方法が分かりました。というか単にbrowserify自体の設定方法をよく理解していなかっただけでした。 package.jsonを、

{ 
  "browserify": {
    "transform": [ "browserify-shim" ]
  },
  "browser": {
    "bar": "./node_modules/foo/bar.js"
  },
  "browserify-shim": {
    "bar": { "exports": "Bar" }
  }
}

のように書けば、

var bar = require('bar');

として利用することができました。 分かってしまえば簡単なのですが、browserify-shimのドキュメントだけでは理解できなかったです。

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);
  });
};

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

小ネタでした。