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

リンク切れてるっていう連絡をもらったので更新しておきました(2018/01/30)。


モデルに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;
  }
};

HUNTER×HUNTER 蟻編

Nexus7を導入してから漫画をよく読むようになった。ブックオフにいかんでもいいしな。蟻編は面白いので最後のほうをまとめて買ってしまった。ワンクリック恐るべし。

ProductName HUNTER×HUNTER 19: No.19 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 20: No.20 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 21: No.21 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 22: No.22 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 23: No.23 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 24 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 25 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 26: NO.26 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 27 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 28 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 29 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


ProductName HUNTER×HUNTER 30 (ジャンプコミックスDIGITAL)
冨樫 義博
集英社 / ?円 ( 2012-12-03 )


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でいいかもしれない。メソッド自分で書かなくていいし。

Think Simple

先月の広島出張の行きの新幹線のなかで4冊読んだけど、これが一番面白かった。

ProductName Think Simple―アップルを生みだす熱狂的哲学
ケン・シーガル
NHK出版 / 1680円 ( 2012-05-23 )


特に会議の考え方がすごかった。

プロジェクトの成果の質は、そこにかかわる人間の多さに反比例する

というように、意思決定に必要ない人間は徹底的に排除するというのは、会議が4W1Hを決めるために存在すると考えるのであれば合理的だなと思った。報告というのは意思決定をするためのおまけみたいなものだからな。

ブロッコリーとカリフラワーを畑とベランダで育てているが成長が全然違った

久しぶりに畑にいって、ブロッコリーとカリフラワーの成長具合を見に行ったけど、全然育ってなかった。

1358145667

しかも茎があかっぽくなってるし。

まったくおなじポット苗からベランダのプランターで育てているのは順調に大きくなっている。たまにハイポネックス与えたりしてるし、日当たりがいいからだろうなぁ。

1358145669

来期はベランダ菜園も併用したほうが楽しめるかもしれないなぁ。

1358145670

畑は来年の契約が終ったら再抽選になるので、当選するかどうかわからんし、ベランダ菜園の比重も増やしておこうっと。

ジグソーパズルで精神が削られた

娘と息子がジグソーパズルをやりたがって買ってきたのだけど、子供たちは予想通り途中で飽きてしまい、同時進行していた僕の苦行にしかならなかった。

こびとづかんシリーズのホトケアカバネ。鳩の餌が大好物らしい。

1358145162

娘のワンピースは難易度低め(顔とか図柄がわかりやすい)。

1358145164

やるならラージピースのほうがいいな。

C言語ではじめる音のプログラミング

ひと通り読んだので、javascriptで何か作ってみたい。

macbookをクリーンインストールして娘に贈呈した

MBAに買い換えたらMacbook(2008 early@Lion)を娘にあげる約束をしていた。

今日は時間があったのでクリーンインストールしてから娘に贈ろうとしたら、バッテリーがイカれていたので、安いやつを探して発注しておいた。

クリーンインストールは簡単で、optionを押しながら起動してディスクユーティリティからHDDのデータを削除した後、再インストールをすればいいだけ。

最近とびだせどうぶつの森を手に入れてドット絵職人の道を歩きはじめたので、Pixenを入れてやったのと、ポケモングローバルリンクのブックマークを追加しておいてやった

あとはScratchとGimpを入れといてやらなきゃいけないなぁ。

AlloyでiPhoneのカメラを扱う

カメラで写真を取ってデータベースに格納するアプリを作ってみた。スクロールビューで画像が入れ替わるのがよくわからんがひと通りできた。

alloy camera

スキャフォールドはこことかここを参照。

controllers/index.coffee

カメラ用のボタンを押したら内蔵カメラを起動して、写真をファイルに保存したら、パスをデータベースに保存する。

pts = Alloy.Collections.photo

cameraButton = Ti.UI.createButton
  systemButton: Ti.UI.iPhone.SystemButton.CAMERA

$.win1.rightNavButton = cameraButton

cameraButton.addEventListener 'click', ->
  Ti.Media.showCamera
    success: (event) ->
      now = (new Date).getTime()
      file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,
              String.format("%d-%d", now, Math.floor(Math.random() * 1000)))
      file.write(event.media)
      photo = Alloy.createModel('photo', { path: file.nativePath })
      photo.save()
      Alloy.Collections.photo.add photo
      return
    mediaTypes:[Ti.Media.MEDIA_TYPE_PHOTO]

pts.fetch()

$.index.open()

models/photo.js

モデルの定義

exports.definition = {
  config: {
    "columns": {
      "path": "string"
    },
    "adapter": {
      "type": "sql",
      "collection_name": "photo"
    }
  }
}

views/index.xml

コレクションの定義をしておく

<Alloy>
    <Collection src="photo"/>
    <TabGroup>
        <Tab id="tab1" title="写真" icon="dark_book.png">
      <Window id="win1" title="写真">
            <ScrollView id="scroll" dataCollection="photo">
              <Require src="photo"/>
            </ScrollView>
      </Window>
        </Tab>
        <Tab id="tab2">
      <Window id="win2">
      </Window>
        </Tab>
    </TabGroup>
</Alloy>

views/photo.xml

個々の画像

<Alloy>
  <ImageView image="{path}" width="100%">
  </ImageView>
</Alloy>