Chaplin.jsを試している

Brunchで生成されるスキャフォールドよくわからんわーっていうのはChaplin.jsがわからんわーっていうことと同義だった。

Hello Worldのサンプルostioのコードを読んでみたんだけど、いまいちわかってないところがある(HandlebarとかStylusをどこで組み込んでいるのかとか)ので、自分で一つ書いてみないとダメだなぁ。

Chaplin.jsとは

いまのとこの理解だと、Chaplin.jsというのはBackbone.jsを土台にしたフレームワーク。backbone.layoutmanagerはBackbone.jsの拡張っぽいなぁと思ったがこっちはフレームワークな感じなので規約とかお作法っぽいものが多い。

  • View,Mode,Utilsのディレクトリ構造は大体決まっている。
  • View,Modelにはbaseっていうディレクトリが掘ってあって、共通の処理を書く。ほんでもって継承する。
  • Layoutはbackbone.layoutmanagerとは異なり、Viewを継承していない。

他にドキュメントからだと、

  • Applicationクラスは単なるブートストラップ
  • mediatorがpub/subを司る
  • ControllerがModelと対応するViewをインスタンス化する

という感じで役者が増えている。

grunt-bbbでtodoリストをつくってみた

Yeomanはちょっとデカすぎるなぁってことで、Yeomanよりは小さめな感じのgrunt-bbbをいじってみた。

todomvc用のスキャフォールドが用意されているので

bbb init; bbb init:todomvc

というコマンドが使えるんだけど、これは動かない

というわけでbbbのお作法の勉強も兼ねて自分で最初から書いてみた。

mkdir bbbtodos
cd bbbtodos
bbb init
bbb init:module todo

とかこんな感じで書いていけばいい。

bbbtodos

  • backbone-localstorages.jsをvendor/jam以下に置いてしまったがまぁいいかな。
  • 実際のコードはapp/modulesに置くのがお作法らしい。bbb init:moduleもあるしな。
  • bbb serverで127.0.0.1:8000でサーバーが立ち上がるので便利
  • main.jsではrouterの設定をしている
  • app.jsではLayoutの設定とか、useLayoutといった便利関数を追加しているだけ。
  • router.jsが頑張っている

grunt-bbbとは何か?

backbone.layoutmanagerを含んだbackbone.jsのボイラープレートのフレームワーク。

backbone.layoutmanagerはPhilosophyを読めば分かるようにBackbone.Viewを拡張したものでキャッシュ機構を入れたりテンプレートをよろしく管理してくれたりとかそんな感じ。

なので、Backbone.jsで開発している感をだしつつ、弱いところを補ってくれる感じのBackbone.js++って感じのツールかな。

MODERN WORKFLOWS FOR MODERN WEBAPPSなYeomanで利用されているパッケージを調べた

Yeomanはrobust and opinionated set of tools, libraries, and a workflowなので、使われているパッケージを調べることでjavascript界で今イケてるツールの情報をゲットできるわけだ。

というか、ざっと見たところgrunt.jsしかわからん。

grunt.js

makeのjavascript版みたいな位置付け。ほとんど使ったことがない。

BOWER

ブラウザサイドのパッケージマネージャ。プロキシ通らないなぁと思ったら、fixされていた

AMD関連

underscore.jsがAMD対応されていないので対応版としてlodashってのがあるらしい。他にrequire.jsの軽い版でalmond.jsがある。

PhantomJS

Headless Webkitというわけで、テストしたりスクレイピングできたりする。

ブラウザを使ったスクレイピングをやりたい時にはWWW::Mechanize::Firefoxを使っていたが、PhantomJSベースのCasperJSに乗り換えるかもしれない(WWW::Mechanize::FirefoxはFirefoxをインストールしないといけないのでちょっと面倒臭いので)。

Yeoman使うのか?

養鰻だけにShizuoka.jserは使うよね的なtweetしたけど、僕は使わないと思う。ツールの寄せ集めだと追随していくの大変だしねー。

自分の用途だとボイラープレートで十分なんじゃないかと思う。

Asynchronous Module Definition

今年はBackbone.jsとかCSS(Stylus)なんかを頑張ろうかなぁと思っている。

理解があいまいなAMD(Asynchronous Module Definition)について調べてみた。

underscore.jsではAMDのサポートが外されている。

Nodeクックブックを予約した

LoLも気になっているが、Nodeクックブックを先に読むべきだなという結論になった(LoLは電子版が出たら買う)。

Nodeクックブックというタイトルだったので、Nodeの本はもういいやと思っていたが、目次を見たら実用Expressのほうがふさわしいんじゃないかと。

  • 1章 Webサーバを立てる
  • 2章 HTTPオブジェクトをより深く
  • 3章 データのシリアル化
  • 4章 データベース接続
  • 5章 Websocketを使ってAjaxを超える
  • 6章 Expressで開発をスピードアップ
  • 7章 セキュリティ、暗号化、認証
  • 8章 ネットワークサービスの実装
  • 9章 自分のNodeモジュールを作成する
  • 10章 本番環境に展開するために

というわけで買いですな。

ProductName Nodeクックブック
David Mark Clements
オライリージャパン / 3570円 ( 2013-02-23 )


Stylusのフレームワーク

Stylusのフレームワークといえばnibだろうが、他にもいくつかあったので調べてみた。プラグインっぽい拡張とかもあったんだけど、Stylusの公式からリンクも張ってないしnpmも探しにくいしどうしたらいいのかなぁと。

Framework

nib

お約束。でもtext-shadowがないのはちょっと困るかな。

nibble

nibの上にのっかる感じのフレームワークらしい。ドキュメントが殆ど無いけど、foundationに強く影響を受けているらしいので、グリッドシステムとかレスポンシブ・ウェブデザインが充実しているのかな。僕の場合はGridのためにtwitter-bootstrapを使っているようなもんだから、こっちでいけるようだったら乗り換えてもいいかなと思うので、あとできちんと読んでみる。

fluidity

こちらもグリッドシステム。

Unlike other CSS grids, Fluidity does not use the same table-like concept of rows & columns. Instead of the usual print inspired multi-column style grid where the designer has to think about how wide a column is, what margins exist between them, and the usual "wait... how many columns wide is 315px?" Fluidity focuses on the screen and the varying screensizes in our world by describing things in terms of percentage of screen size or exact pixels which is much easier to visualize, plan around, and ultimately understand.

klipspringer

これもドキュメントがない。ソース見た感じではグリッドシステムとレスポンシブ・ウェブデザインは備えているっぽいがどこを目指しているのかちょっとわからない。

プラグインっぽいやつ

フレームワークほど大きくないモジュール

stylus-lemonade

Compass/SASSでいうところのSpritingにあたる機能を提供する。こういうやつだ。

stylus-images

@2xとかをよろしくやってくれるようだ。

  • Merges duplicated data URIs
  • Loads resolution dependent images when available
  • Moves non-inlined relative url's into the output directory

エディタとか更新時コンパイルするもの

stylusにはwオプションがあって、これを使えば更新時にコンパイルしてくれる。

lyvus

これはstylusのwオプションに加え、ブラウザもリロードしてくれる。利用するためには10行くらいのjsコードを埋め込む、lyvusコマンドで表示されるのでそれをコピペすればいい。

lyvus

ただ、僕がよく使うpyhttpd(python -m SimpleHTTPServerのalias)だとsocket.ioに対応してないからreloadしなくて残念。と思ったが、これのせいではない気もしているので後でちょっと調べる。

styler

こちらはオンラインエディタ。触ってみたところ良い感じなんだけどEmacs使いたいからなぁ。

Building Node Applications With Mongodb and Backbone

Backbone.jsでクライアントサイド、Express+mongoose(MongoDB)+socket.ioでサーバーサイドで、チャットアプリをつくるという内容。

ProductName Building Node Applications With Mongodb and Backbone
Mike Wilson
Oreilly & Associates Inc / 1666円 ( 2012-12-31 )


誤字が多かった。コードはGithubにあるのでIssueみるとどこが修正されたか分かるので、確かめながら本を読んだほうがイイ。

あと、実際書いてみるとBackbone.jsのモデルとmongoのモデルの名前付けが紛らわしい。実際、本書のタイポはここらの名前付けルール(小文字かキャメルケースか)だったりするので、サーバーとクライアントの言語は変えたほうがいいのかなぁと思った。

あとmongooseみたいにスキーマ定義するのはどうなんだろう。それだったらSQLでいいんじゃないかなと思ったりするが。

こっちの本を参照しながらひと通り読んだのだけど、全文公開されているのでありがたい。

ProductName Developing Backbone.js Applications
Addy Osmani
Oreilly & Associates Inc / 2778円 ( 2013-05-22 )


Kindleに入れて気になった時に見れるようにしておきたいかも。

npm startのデフォルトはnode server.js

モデルにgoogle tasksのapiを使ってbackbone.jsでアプリをつくるチュートリアルを読んだ。

package.jsonのscriptsのセクションにstartがないのにnpm startでサーバーが立ち上がるのはなんでじゃろかー?って不思議だったので、ぐぐってみた。

npm startのデフォルトはnode server.jsらしい。

btask

Backbone.sync

RESTfulなAPIじゃない場合とか他のライブラリを使いまわす場合どうするのかなと思って調べてみたらBackbone.syncを上書きすればいいらしい。modelのほうはいじらないでsyncでよろしくやればいいみたい。

Backbone.sync = function(method, model, options) {
  var requestContent = {};
  options || (options = {});

  switch (model.url) {
    case 'tasks':
      requestContent.task = model.get('id');
    break;

    case 'tasklists':
      requestContent.tasklist = model.get('id');
    break;
  }

  switch (method) {
    case 'create':
      requestContent['resource'] = model.toJSON();
      request = gapi.client.tasks[model.url].insert(requestContent);
      Backbone.gapiRequest(request, method, model, options);
    break;

    case 'update':
      requestContent['resource'] = model.toJSON();
      request = gapi.client.tasks[model.url].update(requestContent);
      Backbone.gapiRequest(request, method, model, options);
    break;

    case 'delete':
      requestContent['resource'] = model.toJSON();
      request = gapi.client.tasks[model.url].delete(requestContent);
      Backbone.gapiRequest(request, method, model, options);
    break;

    case 'read':
      request = gapi.client.tasks[model.url].list(options.data);
      Backbone.gapiRequest(request, method, model, options);
    break;
  }
};

Backbone.jsのソースコードを読んだ

日本語のアノテーションがついているソースコードを読んだので、ちょっとわかりにくかったあたりをメモっておく。

コールバックリスト(107行目)

リスト構造にしてあんのかなと思ったがtailってなんだろうと思ったら最後尾を常に指すようにしているらしい。

while (event = events.shift()) {
  list = calls[event];
  node = list ? list.tail : {};
  node.next = tail = {};
  node.context = context;
  node.callback = callback;
  calls[event] = {tail: tail, next: list ? list.next : node};
}

これは最後尾のobjectのnextとtailが同じ物を指すようにしているのでさくっと最後に空のオブジェクトの参照を保持している。全てのコールバックを実行するのはnextでたどればいいようになっている。Spine.jsは普通に配列使っていたけどリストにするメリットはなんかあるのかなぁ。

実際の動きはこんな感じ。

> node = {}
{}
> node.next = tail = {}
{}
> node.context = "ctx1"
'ctx1'
> node.callback = "callback1"
'callback1'
> calls = {tail: tail, next: node}
{ tail: {},
  next: 
   { next: {},
     context: 'ctx1',
     callback: 'callback1' } }
> list = calls
{ tail: {},
  next: 
   { next: {},
     context: 'ctx1',
     callback: 'callback1' } }
> node = list.tail
{}
> node.next = tail = {}
{}
> node.context = "ctx2"
'ctx2'
> node.callback = "callback2"
'callback2'
> calls = {tail: tail, next: list.next}
{ tail: {},
  next: 
   { next: 
      { next: {},
        context: 'ctx2',
        callback: 'callback2' },
     context: 'ctx1',
     callback: 'callback1' } }

tailとnextは同じ空のオブジェクトを指しているので、常に次の参照を保持したまま大きくなっていく。

void 0 (283行目)

void 0はundefinedになる。

> void 0
undefined

success = option.success (342行目)

if (success) success(model, resp);って何やっているのかなぁと思ったのだが、opttion.successを上書きしてしまうので、元々のoption.successを残しておいてコールバックで呼び出しているだけだった。

var model = this;
var success = options.success;
options.success = function(resp, status, xhr) {
  if (!model.set(model.parse(resp, xhr), options)) return false;
  if (success) success(model, resp);
};
options.error = Backbone.wrapError(options.error, model, options);
return (this.sync || Backbone.sync).call(this, 'read', this, options);

},

element (1190行目)

$elとelが存在する。

this.$el = (element instanceof $) ? element : $(element);
this.el = this.$el[0];

ステートフルJavascriptを読んであればそんなに難しくないので、コードリーディングにちょうどいいかも

FlaskでRESTful

ちょっと色々あってSpine.jsからBackbone.jsに乗り換えることにした。で、REST-APIが使いたかったので、Flask-RESTfulで書いてみた。

from flask import Flask, request
from flask.ext.restful import Resource, Api

app = Flask(__name__)
api = Api(app)

todos = {}
i = 0

class TodoSimple(Resource):

    def get(self, tid):
        return {"tid": tid, "todo": todos[tid]}

    def put(self):
        global i
        tid = i
        i = i + 1
        todos[tid] = request.form['data']
        return {"tid": tid, "todo": todos[tid]}

    def post(self, tid):
        todos[tid] = request.form['data']
        return {"todo": todos[tid]}

    def delete(self, tid):
        deldata = todos[tid]
        del(todos[tid])
        return {"tid": tid, "todo": deldata}

api.add_resource(TodoSimple, '/', '/<int:tid>')

if __name__ == '__main__':
    app.run(debug=True)

curlで確かめた。

$ curl http://localhost:5000/ -d "data=Remember the Flask" -X PUT
{"tid": 0, "todo": "Remember the Flask"}
$ curl http://localhost:5000/ -d "data=Remember the Python" -X PUT
{"tid": 1, "todo": "Remember the Python"}
$ curl http://localhost:5000/0
{"tid": 0, "todo": "Remember the Flask"}
$ curl http://localhost:5000/0 -d "data=Remember the Sphinx" -X POST
{"todo": "Remember the Sphinx"}
$ curl http://localhost:5000/0
{"tid": 0, "todo": "Remember the Sphinx"}
$ curl http://localhost:5000/0 -X DELETE
{"tid": 0, "todo": "Remember the Sphinx"}
$ curl http://localhost:5000/0
{"status": 500, "message": "Internal Server Error"}

今回はMongoDBを使いたかったので Flask-RESTfulを検討しているのだけど、SQLAlchemyでいいんだったらFlask-RESTlessでいいかもしれない。メソッド自分で書かなくていいし。