snippets from shinichitomita’s journal RSSフィード Twitter

2013-01-06

[][] SOQL Console

SOQLのオートコンプリーションができるSOQLコンソールアプリを作りました。

Ctrl+Space or Tabキーでコンプリーションを実行できます。

アプリケーション

https://soql-console.herokuapp.com/

機能

FROM句でオブジェクト名の補完

f:id:shinichitomita:20130106114743p:image

SELECT句での項目名の補完

f:id:shinichitomita:20130106114745p:image

親リレーション内の項目名の補完の例

f:id:shinichitomita:20130106114746p:image

子リレーション名の補完

f:id:shinichitomita:20130106114747p:image

子リレーションの内部クエリでの項目名の補完

f:id:shinichitomita:20130106114748p:image

WHERE句/GROUP BY句/ORDER BY句での項目名の補完

f:id:shinichitomita:20130106114749p:image

関数名、キーワードの補完

f:id:shinichitomita:20130106114750p:image

その他

自分の組織にあるカスタムオブジェクトを参照して補完もできます(要ログイン)

ソース

https://github.com/stomita/soql-console


Enjoy!

2012-12-06

[][] 忘年会・二次会向け「Party Roulette」というソーシャルアプリを作りました

忘年会・二次会向け「Party Roulette」というソーシャルアプリを作りました

https://party-roulette.herokuapp.com/

これは何か

端的に言うと、ルーレットです。スタートボタンを押して、ストップボタンを押すと、事前に選んだ参加者の中から適当にランダムにカーソルが止まります。ChromeなどHTML5 Audioに対応したブラウザでは音も出ますので注意。

ルーレットのWebアプリというと、リクルートさんが作った「パパパメーターR」っていうものがありますが、そのソーシャル版とおもっていただければとおもいます。やっぱ顔が出たほうが盛り上がりますよね?なので顔写真をソーシャルネットワークから引っ張ってきます。

ソーシャルアプリでFacebook対応ってのは多いと思いますが、やっぱりこれからの社畜2.0としては社内ソーシャルに対応すべきです!ということでSalesforce Chatterに対応してます。

使えるシーンとしては、メンバーがあらかじめわかっていて、抽選が必要な集まりなどが良いでしょう。たとえば結婚式の2次会の景品とか、忘年会・納会での部署の罰ゲームとか。

ちなみにherokuで動いてますが、APIコールをサーバプロキシしてないのでIP制限ある組織でも使えます。これなにげに重要。

使い方

最初に顔写真と名前を取り込むソーシャル・ネットワークから選びます。現在Facebook EventとChatter Groupに対応してます。

f:id:shinichitomita:20121206171356j:image

プルダウンからグループを選ぶと、メンバーがリストされるので、取り込むメンバーを選択してインポートします。

f:id:shinichitomita:20121206171357j:image

顔写真の読み込みにちょっと時間がかかりますが、しばらくするとアイコンがずらっと表示されます。

f:id:shinichitomita:20121206171358j:image

「Start Roulette」ボタンを押すとルーレット開始です!適当なところでStopボタンを押しましょう。

f:id:shinichitomita:20121206171359j:image


まとめ

これ、先日行った自分の結婚披露宴の2次会パーティで作ったものです^^

まあちょうど12月ですので、忘年会、納会、クリスマスパーティの抽選あるいは部署の罰ゲームにお使いいただけたらと思います?

(なお、Force.com APIを使ってますので、この記事はForce.com Advent Calendarにも参加しています!)

Enjoy!

2012-09-27

[][][] iOS6でメガピクセル画像をCanvasに描画するとおかしくなってしまう件と、その対処

iOS6によるアップデート

まず最初に、iOS6において、Safari上のWebアプリから簡単にカメラ&フォトライブラリの写真にアクセスできるようになりました。いままでカメラにアクセスするにはPhoneGapなり何なりでネイティブ化する必要があったので、写真共有サービスなどにはかなり有用なアップデートです。

File API、HTML Media Capture への対応

HTML Media Capture に対応し、Safari から カメラを起動してのファイルアップロードが可能になりました。

type 属性値に file を指定すれば単体ファイルのアップロードが簡単に。

(略)

ファイルを選択したあとは何ができるか...

  • action による Multipart post
  • Ajax (XMLHttpRequest 2) を使用したファイル送信(プログレス表示にも対応)
  • File API に対応したので JavaScript で色々できるよ
HTML5 開発者向け iPhone 5 / iOS 6 での変更点等まとめ | WWW WATCH

発生する問題

さて、フォトライブラリ内のカメラ画像にアクセス可能になりましたが、iPhone5などの高解像度カメラを備えた端末では、写真として保存されるファイルは数メガピクセルに軽く達してしまいます。そして、iOSの制限として、2Mピクセルを超えるJPEG画像はサブサンプルの対象となり、情報が間引かれてしまいます。

JPEG images can be up to 32 megapixels due to subsampling, which allows JPEG images to decode to a size that has one sixteenth the number of pixels. JPEG images larger than 2 megapixels are subsampled―that is, decoded to a reduced size. JPEG subsampling allows the user to view images from the latest digital cameras.

Know iOS Resource Limits

情報が間引かれて表示されるだけならまだいいですが、この画像をCanvasに対してdrawImageなどしてリサイズする場合、不都合が起きます。サブサンプルされる場合とされない画像で描画の結果が異なってしまいます。

例)iOS6のSafariで2Mピクセル以下の写真をdrawImageで描画した時:

f:id:shinichitomita:20120927145232p:image

例)iOS6のSafariで2Mピクセル以上の写真をdrawImageで描画した時

f:id:shinichitomita:20120927145233p:image

テスト環境:

http://jsbin.com/upivad/4/

回避方法

いくつかのテストの結果、メガピクセル画像をCanvasにdrawImageする際にリサイズを伴う場合、つまりContext#drawImage(img, x, y, width, height) のwidth, height に元画像と異なる値を設定していると発生することがわかりました。そのため、回避策として、一旦小さなCanvasに元画像の一部をコピーし、それを張り合わせて対象のCanvasにdrawImageすることで対応しました。またその際、サブサンプルされている画像とそうでないものを描画結果から判別し、サブサンプルされているものには縮尺を補正してあげるようにしています。

ライブラリ化しましたので、ご参考まで。

ソース:

http://github.com/stomita/ios-imagefile-megapixel

テスト環境:

http://jsbin.com/ovupil/1/

上記ソースでは、画像ファイルにEXIFのOrientation(画像の向き)が90度回転方向に設定されている際、縦方向にひしゃげて表示されるバグについても対処しています(これは画像サイズに関係なく発生する模様)。

なお、こちらはiOSの問題の解決のために作成しましたが、同じソースをそれ以外のブラウザで利用しても特に挙動は変わりません。PCのChrome, Safari, Firefoxなどで正常に動作します。

2012-05-23

[][] Heroku上でスクリーンショットサーバを動かす

前回PhantomJSのbuildpackを作成したので、Heroku上でPhantomJSのプロセスを自由に稼働させることができるようになった。

PhantomJSはGUI環境のない(Headless) WebKitブラウザであるため、ブラウザ上のJavaScriptの単体テストor結合テストをサーバ上で走らせて、継続的インテグレーションに組み込むなどの利用方法もあるだろう。これも興味深いトピックではあるけど、ここでは触れない。

今回はPhantomJSの画面レンダリングの機能を使って、スクリーンショットサーバをHeroku上に構築する。

構成と処理フロー

f:id:shinichitomita:20120523202004j:image:w360

PhantomJSにはwebサーバ機能も含まれており、単独でHTTPサーバとしてリクエストを受け取る事もできるが、実サービスで利用するようなシロモノではない。そのためクライアントからリクエストを受け取るNode.jsのサーバを別に立てる。今回はこれもHeroku上で行なっている。

Node.jsのアプリケーションは、Socket.IOサーバとして稼働し、2つのchannelを持つ。

1つめのrequest channelでは、ブラウザからのスクリーンショット取得リクエストを受け取り、ページのURLをキューに入れる。

もう1つのrender channelでは、PhantomJSのスクリーンショットサーバと接続する。PhantomJSはスクリーンショットサーバであるが、Socket.IO的にはクライアントになる。スクリーンショットのリクエストは、接続中のPhantomJSクライアントのうちの1つにdispatchされる。

PhantomJSのアプリケーションは、PushサーバとしてのNode.jsにSocket.IOで接続し、スクリーンショットのリクエストを待つ。ページのURLがプッシュされた時点で、PhantomJSはページにアクセスし、スクリーンショットのレンダリングを開始する。

スクリーンショットは画像ファイルとしてPhantomJSのローカルファイルシステムに保存されるが、そのままではどこからもアクセスできないので、イメージサーバであるAmazon S3にアップロードする。ここで、ファイルデータをNode.jsに戻すことなく、PhantomJSから直接Browser Post Formを利用してS3にアップロードする(postの際に必要になるsignatureは事前にNode.js側で生成されており、プッシュ通知の際にページURLと共に同時に渡ってくる)

最後に、PhantomJSはアップロードしたスクリーンショットのURLをNode.jsに通知する。その後、Node.jsはrequest channelに接続しているクライアント全てに対してスクリーンショットのURLをブロードキャストする(ブラウザはそれを受け取って描画する)


利点

スクリーンショットサーバをクラウド上で構築する場合、今まではEC2などのIaaS上でGUI環境を含むOSを稼働させることが多かったが、常時起動によるリソースの有効利用およびスケール時のVMの上げ下げのコストが問題点としてあった。

これを、HeadlessのPhantomJSでスクリーンショットを撮り、それをHeroku上で動作させることにより、スクリーンショットサーバのスケールアウトがスピーディかつ容易になっている。

たとえば、以下のコマンドでインスタンスを瞬時に*1上げ下げ可能である。

$ heroku ps:scale renderer=4

資料

*1:VMの起動停止に比べて

2012-05-19

[][] Heroku の PhantomJS buildpack

つくりました。

stomita/heroku-buildpack-phantomjs ? GitHub

何をする?

PhantomJS (www.phantomjs.org) はheadless(GUI環境を必要としない)のWebKitで、JavaScriptおよびCoffeeScriptでスクリプトを書くことができます。

このPhantomJSをheroku cedar stackで実行可能にしたものが、heroku-buildpack-phantomjs です。

※ heroku devcenter ドキュメントの 3rd party buildpack リストに載っけていただきました

https://devcenter.heroku.com/articles/third-party-buildpacks

利用方法

READMEに書いてあるように、以下のコマンドでheroku cedar上にアプリを作成できます。

$ heroku create --stack cedar --buildpack http://github.com/stomita/heroku-buildpack-phantomjs.git

とりあえず試しにスクリーンショットを取得するサーバを書いてみます。

screenshot-server.js:

var fs = require('fs');

var formHtml = [
  '<html><body>',
  '<form method="get">',
  'URL: <input type="text" name="url"><br>',
  'Width: <input type="text" name="width" value="320"><br>',
  'Height: <input type="text" name="height" value="480"><br>',
  'Filetype: <select name="ftype">',
  '<option>jpg</option><option>png</option><option>pdf</option>',
  '</select><br>',
  'Wait: <input type="text" name="sleep" value="400"><br>',
  '<input type="submit" value="Get Screenshot">',
  '</form>',
  '</body></html>'
].join('');

function toNumber(str, defaultValue) {
  var n = Number(str);
  if (isNaN(n)) { return defaultValue; }
  return n;
}

function parseQueryString(url) {
  var params = {};
  var qstr = url.split('?', 2)[1];
  if (qstr) {
    var pairs = qstr.split('&');
    for (var i=0; i<pairs.length; i++) {
      var pair = pairs[i].split('=');
      params[pair[0]] = decodeURIComponent(pair[1]);
    }
  }
  return params;
}


var port = phantom.args[0];

var server = require('webserver').create();
console.log('server listening port: '+port);
var service = server.listen(port, function (request, response) {
  var params = parseQueryString(request.url);
  if (!params.url) {
    response.statusCode = 200;
    response.write(formHtml);
    response.close();
    return;
  }
  var url = params.url;
  var ftype = params.ftype || 'jpg';
  var width = toNumber(params.width, 320);
  var height = toNumber(params.height, 480);
  var sleep = toNumber(params.sleep, 400);
  var page = new WebPage();
  page.viewportSize = { width: width, height: height };
  page.open(url, function (status) {
    if (status !== 'success') {
      response.statusCode = 500;
      response.write('Unable to load the address!');
      response.close();
    } else {
      setTimeout(function() {
        var file = "/tmp/image_"+ Math.random().toString(36).substring(2, 10) + "." + ftype;
        page.render(file);
        var contentType =
          ftype === 'jpg' ? 'image/jpeg' :
          ftype === 'png' ? 'image/png' :
          ftype === 'pdf' ? 'application/pdf' : 'application/octet-stream';
        response.statusCode = 200;
        response.setHeader("Content-Type", contentType);
        var stream = fs.open(file, "rb");
        response.write(stream.read());
        stream.close();
        fs.remove(file);
        response.close();
      }, sleep);
    }
  });
});

Procfile:

web:  phantomjs screenshot-server.js $PORT

heroku上に配布したものがこちらになります。

http://growing-lightning-3614.herokuapp.com/

これまで

実はすでにPhantomJS buildpackは PhantomJS 1.4.1ベースで作成していましたが、Xvfb(X11のheadless)を必要としていました。そのため、buildpack内にXvfb環境を埋め込んで提供していたので、slug sizeが90MB近くになっていました(これでも結構削った結果)。

今回、1.5.0ベースにしたことで、pure headless (X11を必要としない)となり、slug sizeは17MBほどまで減りました。

制限

まだ日本語フォントが使えません。headlessなPhantomJSでフォント追加する方法を調査中です。

なお @font-face でWeb Font なら使えるかな、と思いましたが、対応してないようです。

(追記)

fontconfigを使う環境では追加のTTFフォントを ~/.fonts に入れておくだけで大丈夫な模様。(参考:書体関係 Wiki - unixuser200403-2

なので、日本語フォント対応するには、IPAフォントなりをダウンロードし、.fontsディレクトリを作成してTTFファイルを突っ込んだ上でherokuにpushしておけばOK。

(容量の問題もあるのでbuildpackには含めていません)

ビルド方法メモ

以下、個人的なメモとしてのbuildpack作成の手順。

https://gist.github.com/2725125

https://gist.github.com/2725122

vulcanに以上の2つのスクリプトを載せてビルドプロセスを走らせれば良いわけだけど、どうもvulcanはビルドの途中で落ちてしまうことが多い。PhantomJSのソースコードコンパイルはQtを含んでおりheroku上で優に1時間くらいはかかるので、落とされると大変困る。heroku run bash で対話的にやるほうがよいかとおもう。ビルド結果は自分でアーカイブしてscpで適当なサーバに転送した。