js-model チュートリアル

js-modelとは

公式 : http://benpickles.github.com/js-model/
js-modelはjQuery上で動く、JavaScriptでモデルを扱うためのフレームワーク
以下の文章は公式サイトのチュートリアルの勝手訳。

オブジェクトの生成

モデルクラスはModel()ファクトリーを使用して生成する。

var Project = Model("project")

これで"project"モデルのインスタンスを生成できるようになる。

オブジェクトの操作

attr()メソッドで属性を読み書きすることができる。

var project = new Project({ title: "stuff" })
project.attr("title", "nonsense")
project.save()

オブジェクトの探索

save()を呼ぶと、モデルはクラスの"コレクション"に追加され、firt()を呼ぶことで取り出すことができる。

Project.first()
// => project

すべてのモデルをコレクションから取り出すにはall()を使用する。

Project.all()
// => [project]

カスタムプロパティ

モデルにメソッドとプロパティを付与することもできる。

クラスプロパティ

モデルを設定するときに、2番目の引数にオブジェクトを渡す。プロパティはこのクラスに定義される。

var Project = Model("project", {
  find_by_title: function(title) {
    return this.detect(function() {
      return this.attr("title") == title
    })
  }
})

Project.find_by_title("stuff")
// => "stuff" project model
インスタンスプロパティ

モデルを設定するときの3番目の引数はインスタンスプロパティの定義に使用される。これはモデルのprototypeに追加されるので、デフォルトを書き換えることもできる。

var Project = Model("project", {}, {
  markAsDone: function() {
    this.attr("done", true)
  }
})

Project.find(1).markAsDone()
// "stuff" project marked as done

関連

単純な関連はインスタンスメソッドを追加することで真似ることができる。CatはMatに所属し、MatはCatを複数もつ。

var Cat = Model("cat", {}, {
  mat: function() {
    var mat_id = this.attr("mat_id")

    return Mat.detect(function() {
      return this.id() == mat_id
    })
  }
})

var Mat = Model("mat", {}, {
  cats: function() {
    var id = this.id()

    return Cat.select(function() {
      return this.attr("mat_id") == id
    })
  }
})

イベント

オブジェクトのライフサイクルベースで、イベントをリッスンすることができる。UI要素とデータを結びつけるために使用するのが典型的だ。

クラスイベント

コレクションにオブジェクトが追加/削除されるイベントにバインドすることができる。

Post.bind("add", function(new_object) {
  add_object_to_ui(new_object)
})

Post.bind("remove", function(removed_object) {
  remove_object_from_ui(removed_object)
})
インスタンスイベント

特定のインスタンスの特定のイベントに対してバインドもできる。

post.bind("update", function() {
  my_ui_elem.text(this.attr("name"))
})

インスタンスが削除されたときにも可能。

post.bind("remove", function() {
  my_ui_elem.remove()
})
カスタムイベント

UI要素と結びついてるオブジェクトに対しカスタムイベントを設定する、なんてこともできる。

post.bind("turn_blue", function() {
  my_ui_elem.css("background", "blue")
})

post.trigger("turn_blue")

永続化

REST

jQueryajax()メソッドを利用し、GET/POST/PUT/DELETEでモデルのデータをサーバーとJSONでやりとりする。

var Project = Model("project", {
  persistence: Model.REST("/projects")
})

save()またはdestroy()で適切なRESTリクエストが送られる。

var project = new Project({ name: "stuff" })
project.save()                            //   POST /projects
project.attr("name", "nonsense").save()   //    PUT /projects/1
project.destroy()                         // DELETE /projects/1
localStorage

localStorageにデータを保存することもできる。Safari,Chrome,Firefox,Opera,IE8,Safari Mobileがサポートされている。

var Project = Model("project", {
  persistence: Model.localStorage()
})
データのロード

保存したデータはload()で読み出すことができる。

// wait for the document to load
$(function() {
  Project.load(function() {
    // do something with the UI
  })
})

Content ScriptでJSONPは使えない

Content Scriptから見えるwindowはWebページから見えるwindowとは異なる。
このため、Content ScriptでJSONPを使用しコールバック関数にContent Script内で定義した関数を指定すると、関数が見つかりません的なエラーになる。JSONPのコールバック関数はWebページのコンテキストで実行されるからだ。。
これを回避するためには、Background Pageを使えばいい。

Chrome拡張機能のレコメンダー


Extension RecommenderというChrome拡張機能を公開しました。
ボタンを押すと、見ているサイトに適したExtensionをお薦めしてくれます。
Firefoxのアドオンでサイトに応じてアドオンを推薦してくれるもの(Add-on Recommender)があったので、Chromeにも同じようなものがあったら面白いかと思って作ってみました。


便利な拡張機能とか変てこな拡張機能が発見できて中々楽しいと思うので、使ってみてはいかかでしょうか。

Plan to Throw One Away

Stoyan Stefanov(YUIの開発者)「Javascript Patterns」より引用。

Talking about first drafts, there's also the idea of "plan to throw one away."
It may look a little extreme at first, but it makes a lot of sense,especially when you have mission-critical project at hand(and human lives depend on it).
The idea is that the first solution you come up with should be thrown away and then you start from scratch.
The first solution may be working solution,but it's nothing more than a draft,one example way to solve a problem.
The second solution will always be better,because you now have a much deeper understanding of the problem.
In the second solution,you're also not allowed to copy-paste from the first one,which helps prevent from taking shortcuts or settling for the nonperfest solution.

最初の下書きについて言えば、”それを捨てる計画をする”というアイディアがあります。
最初はちょっと極端に映るかもしれませんが、これは道理にかなっています。特にあなたがミッションクリティカルなプロジェクトを担当している場合は。
あなたが最初に思いつた解決法を捨てて、一から作り直すのです。
最初の解決法はちゃんと動くかもしれませんが、それは下書きで、問題を解決する方法の1つにすぎません。
より深く問題について理解するようになるため、2つ目の解決法は常により良いものになります。
2番目の解決法では、ショートカットや不完全な解決法に妥協しないためにコピー&ペーストは禁止されます。

アジャイルとかスパイラルモデルも同じような思想に基づいていると思うし、「人月の神話」にも最初に作ったシステムは捨てようみたいな話があった。
Stoyanの主張が独特なところなコピペを許さないってとこ。
小さなシステムだったらコピペなしで作り直しってもの現実的に考えられるけど、大規模システムだとどうかなあ。

Pythonでワイルドカードマッチング

Pythonワイルドカードマッチングしたい!そんなあなたはfnmatchを使いましょう。

>>> fnmatch.fnmatch('http://www.google.com/', 'http://*/*')
True
>>> fnmatch.fnmatch('http://www.google.com/foo/baz/bar', 'http://*.google.com/foo*bar')
True
>>> fnmatch.fnmatch('hoge', 'hoge')
True

うん便利。

Chrome ExtensionでGAEのChannel APIを使えるようにしてみる

Chrome ExtentionでGoogle App EngineのChannel APIを使ってみよう思い、channel.jsを読み込もうとしたらエラーが。

Uncaught Error: URI chrome-extension://oejekmbenpogkdncmmjlnjebpinjcnac/_ah/channel/xpc_blank is invalid for field ppu

channel.jsへchrome-extensionプロトコルでアクセスすると拒絶されてしまう模様。
どうにか解決する方法がないかなーと思ってとりあえず検索してみたら、解決方法を見つけたんでメモ。

解決方法

  1. 修正されたchannel.jsをダウンロード
  2. channel.jsを開発しているChrome Extensionのフォルダーに配置
  3. channel.jsの2行目を修正 var myHost = "http://yourappid.appspot.com";
  4. Extension内での代わりに編集済みのchannel.jsを読み込むようにする
  5. Enjoy!

ネタ元

App Engine Channels and Chrome Extensions:http://www.paddyforan.com/2010/12/app-engine-channels-and-chrome.html