Debuginfo

思考とアウトプット

Redisをインストールしてみた

@miyagawaさんpodcastでもMentionされてたアルゴリズムとの親和性が高いRedisをインストールしてみました。バイナリサイズはmemcached並みに小さい。

$ yum install redis
....
Installing:
redis                               x86_64                               2.4.10-1.el6                                
...
Total download size: 213 k
Installed size: 668 k

とりあえず、まだname spaceをどうやって区切るか良く理解していないので自分の権限でアプリ用に起動する。

$ cd <dev dir>
$ cp /etc/redis.conf etc/
$ redis-server etc/redis.conf

何もデータがない状態だとメモリの使用量(resident)は7Mぐらいでいい感じ^^

$ pmap -x 15674 | grep total
total kB           39932    6988    6360

PerlモジュールRedisを入れる。クライアントが色々あるみたい。githubとか調べて使われてそうなのでRedisを使うことに。

$ vi cpanfile # add requires ‘Redis’
$ carton install 

テストを書いて確認。

use Test::More;
use FindBin qw($Bin);
use lib "$Bin/../lib";
use YAML;
use Data::Dumper;

BEGIN { use_ok( 'TSS::API::KVS::Redis' ); }

my $kvs = new TSS::API::KVS::Redis;
$kvs->redis->set('foo' => 'bar');
is($kvs->redis->get('foo'), 'bar', 'set/get ok'); 
$kvs->redis->del('foo');
is($kvs->redis->get('foo'), undef, 'del ok'); 

done_testing();

ListとSetとかの使い方がわからない。帰りにWebDBpress買ってよまないと^^;;

sendAsBinaryをChrome/Safariでやる

xmlHttpRequest.sendAsBinaryはfirefoxでしか実装されてないことを知りました^^;; 同じことをどうやるかぐぐってみると下記のリンクを見つけました。がっつりprototypeをいじってますね。firefoxで動けば問題ないでしょうってことで採用。

XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
    function byteValue(x) {
        return x.charCodeAt(0) & 0xff;
    }
    var ords = Array.prototype.map.call(datastr, byteValue);
    var ui8a = new Uint8Array(ords);
    this.send(ui8a.buffer);
}

参考

javascriptでFlickrへの写真のアップロードを実装してみた

トピックとしてはかなり今更感がありますが。。これをするのに一週間かかりました。私の技術力不足もありますが、情報がなくてかなり苦労しました。苦労した分、とっても勉強になりました。

同じようなことをする人にこの努力をさせたくないのでブログに書いておきます。

説明はいらないから、今すぐアップロードしたい人はgithubを見てください。

https://github.com/shoheik/flickrUpload

長いので目次..

目的

手元にある写真をFlickerにブラウザを使ってアップロードする。このとき、画像データをサーバー経由ではなくて、ブラウザから直接アップロードできるようにする。クライアント側に処理をさせてサーバの負荷軽減をするのが目的です。Flickr APIのページには各言語でAPIを実装したモジュールがありますが、サーバ側の処理、またはアプリで行うのが一般的のようです。

以後、時系列的に何をしたかを記します。

準備

API Keyを取得

ここからAPI KeyとShared Secretを取得します。そのプロセスは省略しますが、商用と個人利用を選べるようになっています。スタートアップやテストに利用するときは個人利用の方で良いと思います。

Oauth Token と Oauth Secretを取得

Flickr+技術用語ででググると仕様のベージではなくて、Flickr Discussonのページがヒットします。そして、Samという人が頻繁に質問に答えています。(彼がFlickrの開発者なのかな?)。で、彼曰く、Tokenはexpireしないと言っているので一度認証させたものをアプリに仕込むのはありです。ユーザのトークンを取得して、データベースに保存する場合はセキュリティに注意を払いましょう(もし漏洩したら,,, ユーザの写真をdelete/writeできてしまう。。こ、怖い。。)

で、Oauth Token/Secretの取得方法ですが、一回作って保存しておけばいいので、既存モジュールでスクリプトを書いて、それを使います。私はPerl Mongerですが、Ruby勉強中なので、あえてRubyのモジュール、flickrawを使いました。

使うというよりもコピペです^^

exampleの中のauth.rbをコピーしてきてAPI_KEYとSHARED_SECRETに代入して走らせるだけ。

$ vi Gemfile
source 'https://rubygems.org'
gem 'flickraw'
$ bundle install
$ cp /Users/kamesho/.rbenv/versions/2.0.0-p0/lib//ruby/gems/2.0.0/gems/flickraw-0.9.6/examples/auth.rb .
$ vi auth.rb # add your API key
$ ./auth.rb
# 指示にしたがい URLをコピーして、そこに表示された数字をコピーしてエンター。

表示されたキーをメモっておきます。

アップロードできるか試してみる

flickrawを使って試してもいいのですが、中身を知りたかったのでググってヒットしたajido.hatenablog.comのOAuthでFlickrにアップロードのスクリプトを使いました。

またまたコピペですが、アップロードできるか確認しました。

アップロード

調べてみたところFlickrからブラウザからアップロードする方法はいくつかありそうでした。

  1. HTML Formを利用してアップロード
  2. XmlHttpRequestを利用したアップロード
  3. Flashを利用したアップロード

Flashはやや拒否反応してしまうので、1が駄目だったら2をやってみるという方針で行きました。

その前にOauthの仕様を知る

Flickr公式のUpload APIのページに行くと情報が載ってますが、flickr.people.getUploadStatus methodは古い情報なのでOauth認証とは関係がありません。。私はここで2日無駄にしました orz... Depreciatedぐらい書いておいて欲しいものです。ここで有用な情報はphoto以外は(oauth)signatureの生成に利用するということです。

で、flickrの場合のoauth認証は?というと、例のSamさんが下記のブログにまとめています。

簡単に言うと、各パラメータをソートして、2つのshared_secretとoauth_secretで暗号化したものがoauth_signatureでこれを一つのキーとしてauthorizationに利用するということです。

javascriptoauth.jsというモジュールがあります。ここのexample/signature.html がすごく役に立ちます。

f:id:shoheik:20130323114005p:plain

このスクリーンのように代入していくとsignature等が生成されるのでこれで確認できます。(散々悩んだ後にこのページを知りました。もっと早く知っていれば、oauthの問題かxhr(後述)の問題か住み分けができて、早く解決できたように思います。

HTML Formを利用したアップロード

私はトライしましたが、結論をいうと駄目でした。というより、駄目だと思ってXmlHttpRequestに移った後にバグや思い違いを変更してうまくいったのでFormを利用してもうまくいく可能性はあると思います。

このDiscussionの途中あたりにSamさんのiframe+formを使ったアップロードのサンプルがあります。Oauth認証ではなく以前の認証方法で書かれていますが、これをOauthに当てはめれば使えると思いました。 以下のようなjavascritptでフォームを作りました。動作確認はしてませんが、写真をinput fileに入れてそれを送信する的にはできる可能性はあります。しかし、私が今後やりたいことの一つのcanvasで作ったイメージを送信することがあります。これにはinput fileにデータを挿入する必要があるのですが、input fileはreadonlyなので、XmlHttpRequestで実装することにしました。Ajax, form共にmultipart/form-dateである必要があります。(実はFormの送信方法に二通りあるというのをここで初めて知りました^^; )

var form = document.createElement("form");
form.setAttribute("method", 'POST');
form.setAttribute("action", 'http://api.flickr.com/services/upload/');
form.setAttribute("enctype", 'multipart/form-data');
//form.setAttribute("target", 'iframe');

var p = {
    description: "desc1",
    oauth_consumer_key : "aaa"
    oauth_nonce : "bbb"
    oauth_signature : "ccc"
    oauth_signature_method : "HMAC-SHA1",
    oauth_timestamp : "1363492728",
    oauth_token: "ddd"
    oauth_version: "1.0",
    tags: "tags1",
    title: "title1"
};

for (var i in p) {
    var i = document.createElement("input");
    i.setAttribute("type", "hidden");
    i.setAttribute("name", i);
    i.setAttribute("value", p[i]);
    form.appendChild(i);
}

//var file_input = document.createElement("input");
//file_input.setAttribute("type", "file");
//file_input.setAttribute("value", dataURL); //input fileはReadOnly
var file_input = document.getElementById(‘file_input');
form.appendChild(file_input);
document.body.appendChild(form);
form.submit();

Ajax(XmlHttpRequest)を利用したアップロード

Formがうまく動かなかったので重い腰を上げて一番複雑そうなXmlHttpRequestで実装することにしました。(後でわかったのですが、Formで駄目だったのはOauth Signatureが間違ってたように思います。もしかしたら、動く可能性があるので試してみる価値はあると思います)

JQuery.ajaxで実装しなかった理由は、Discussionのページを見るとSamさんが改行\r\nを突っ込んだりしてて、かなり細かい仕様のようで、この振る舞いをJQueryでしているかを確認してくより、実際生のXHRで実装した方が早いと思ったからです。(ブラウザ互換を気にする必要が以後ありますが)

XmlHttpRequest(XHR)は使えるか?

皆さん、ご存知のようにXHRはクロスドメイン制約があります。デフォルトでは同じドメインのサーバしかアクセスできません。しかし、サーバ側(この場合はFlickr側)がそれを許しているとXHRでアクセスできるます。下記が 確認するヘッダのようです。

* Access-Control-Allow-Origin: 
* Access-Control-Allow-methods:
* Access-Controll-Allow-Headers:

何か投げてみてFirebugみたいなツールで レスポンスヘッダを見てみましょう。

f:id:shoheik:20130323114038p:plain

この写真を見るとAccess-Control-Allow-Origin:*となっているので、XHRは使えそうです。しかし、Access-Control-Allow-Headersはありません。Access-Control-Allow-Headers: authorizationの記述がないとAuthorizationヘッダを使う事ができません。しがたって、Flickrでjavacriptを用いるためにはbodyにOauthキーを入れて認証を行う必要があります。

javascript code

githubに上げておきました。基本的にDiscussionで話していることを実装します。Samさんはjavascriptでバイナリを送る方法は知らないと言っていますが、sendAsBinaryで投げればうまくデータを送信することができました。

実行例:

HTML Canvausに画像を入れてバイナリファイルを作ってから送信する。バイナリファイルは他にも取得方法があると思います。OAuth keyを含んだパラメータはサーバ側から取得するようにしてます。セキュリティ的にもクライアントでキーを算出するのはどうかと思うので^^;;

まとめ

冒頭にも書きましたが、https://github.com/shoheik/flickrUploadにモジュールとして上げてみました。 OAuth keyのサーバエンドの実装が知りたい要望があればのちのち書こうと思います。

It’s sooooo tired but it’s always good to think something new :)

javascriptでモジュール/ライブラリを自作する方法

ちょっとしたライブラリを作ってgithubで公開しようと思ってます。 そこで、javascriptのライブラリをどうやって作成しようか調べてました。 javasriptは関数で閉じないとグローバル変数になってしまうので、なるべくグローバル変数を使わず、汚染しないというのがポイントです。

色々やり方はあるようですが、Javascript patternsにも出てきているモジュールパターンを使うのが定石のようです。(初心者の私が)すんなり理解できるものの方が読みやすいのかも、と調べていたら下記がサイトの一つの例がよかったのでこれを使います。引数もわかりやすいですし。

http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript

var myNamespace = (function () {
    var myPrivateVar, myPrivateMethod;
    // A private counter variable
    myPrivateVar = 0;
    // A private function which logs any arguments
    myPrivateMethod = function( foo ) {
        console.log( foo );
    };
    return {
        // A public variable
        myPublicVar: "foo",
        // A public function utilizing privates
        myPublicFunction: function( bar ) {
            // Increment our private counter
            myPrivateVar++;
            // Call our private method using bar
            myPrivateMethod( bar );
        }
    };
})();

Perlユーザがruby on railsに挑む - インストール編

先程のポストと前後してしまうが、rails入れました。 コンセプトはrubyからperlに来たものが多いけど、最初にperlで開発しているので色々困惑してしまう。まず押さえておくところは、

  • perlbrew — rbenv
  • cpanm — gem
  • carton — bundler
  • Mojolicious等のWAF - ralis

というところなんだと思う。コンセプトを知ってるのでI can understand very very smoothly :D

rbenvを入れる

OSXユーザなので

$ brew update
$ brew install openssl
$ brew install readline
$ brew install rbenv
$ brew install ruby-build
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

rbenvでruby2.0.0-p0を入れる

$ rbenv install -l # make sure 2.0.0-p0 is available
$ RUBY_CONFIGURE_OPTS="--with-readline-dir=`brew --prefix readline` --with-openssl-dir=`brew --prefix openssl`" rbenv install 2.0.0-p0
$ rbenv global 2.0.0-p0
$ rbenv rehash
$ ruby —version

rbenvでbundlerを入れる

$ rbenv exec gem install bundler

project毎にbundle install including rails :)

ref : http://qiita.com/items/a60886152a4c99ce1017

$ cd ~/dev
$ cat  > Gemfile
source "http://rubygems.org"
gem "rails", "3.2.11" 
# ctl^c
$ bundle install --path vendor/bundle
$ bundle exec rails new a_proj --skip-bundle
$rm -f Gemfile
$rm -f Gemfile.lock
$rm -rf .bundle
$rm -rf vendor/bundle
$cd a_proj
$ vi Gemfile # add yours
$bundle install --path vendor/bundle
$ echo '/vendor/bundle' >> .gitignore
# git push  
# use always bundle exe to run the commands
$ bundle exec rails s # start server