Hatena::ブログ(Diary)

mizchi log

@mizchiの雑記帳

2011-12-24

サーバーサイドJSのための、JSONで復元できるモデルクラス *追記あり*

ほとんど自分のためのメモ。
サーバサイドJSを使っているとクライアントにwebsocket経由で渡したりmongodbやnstoreで永続化する際にピュアなJSONとしてデータを宣言しなおす必要があって面倒。

じゃあModelクラス自身が読み込み/書き出しできると嬉しいのでは。
coffee-scriptで書いてみた。

class Model 
  _iparam_ : {}

  constructor : (data={})->
    for k,v of @_iparam_
      @[k] = data[k] or @_iparam_[k] 

  toJson : (cls)->
    return @toJson(@) if cls is undefined 
    return cls unless cls instanceof Object

    json = {}
    for k,v of cls
      if typeof v is 'function' or k[0] is '_'
      else if typeof v in ['string','number','boolean']
        json[k] = v
      else if v instanceof Array 
        json[k] = (@toJson(v[i]) for i in [0...v.length])
      else if v instanceof Object
        json[k] = @toJson(v)
    return json

ルール

継承して_iparam_というプロトタイプに初期化用パラメータを詰め込む(実際のモデルスケルトン)
モデルの引数にインスタンス(JSだからオブジェクトだけど)を作ると元のパラメータが復元される
アンダースコア(_)をプレフィックスに持つ変数は無視
Model#toJsonでJSONに書き出し

サンプル

class A extends Model
  _iparam_ :
    x : 1
    y : 2
    _y : 5  #無視
    z : [1,3,5]

  constructor : (data={})->
    super data

  func : ->
    # 関数定義してもjson化する際には無視する
    @x+@y

使う

> a = new A
> alt = new A { x: 330 }
> console.log alt
{ x: 330, y: 2, _y: 5, z: [ 1, 3, 5 ] }
> console.log alt.toJson()
{ x: 330, y: 2, z: [ 1, 3, 5 ] }

toJsonしたJSONを引数にモデルクラスを作成すると元のパラメータが復元される
そのままコントローラをmixinしたりして使うと何かと楽

追記


ちょっと改良
undefinedのみスルーするようにしないとfalseや0のとき失敗する
いらないパラメータは_をフラグにするのではなく_ignore_で宣言する

class JsonLoader
  _init_ : {}
  _ignore_: {}

  constructor : (params={})->
    for k,v of @_init_
      if params[k] is undefined
        @[k] = @_init_[k] 
      else 
        @[k] = params[k]

  toJson : (cls)->
    return @toJson(@) if cls is undefined 
    return cls unless cls instanceof Object

    json = {}
    for k,v of cls
      if typeof v is 'function' or @_ignore_[k]?
      else if typeof v in ['string','number','boolean']
        json[k] = v
      else if v instanceof Array 
        json[k] = (@toJson(v[i]) for i in [0...v.length])
      else if v instanceof Object
        json[k] = @toJson(v)
    return json

サンプル

class MyClass extends JsonLoader
  _init_ : 
    n : 1
    str : 'test'
    bool : false
  _ignore_: 
    ng : 0

  constructor:(data)->
    super data
    @ng = 0
    
myclass = new MyClass
myclass.toJson() #ngを含まない

実際には別のクラスを参照したりして再ロード時に邪魔になるパラメータが多いということにきづいた。

2011-12-21

coffee-script v1.2.0で--watch と --joinが併用できるようになった


複数ファイルにまたがって自動ビルドしつつ1つのファイルにまとめることができます
http://jashkenas.github.com/coffee-script/#changelog

--watch は指定したファイルを監視し、自動的にjsにコンパイルする
--join はファイルを指定した順番で連結
--output で出力するディレクトリの指定


こんなディレクトリ構成だとします

src/
 - a.coffee
 - b.coffee
 - c.coffee
lib/
 - all.js

こんな感じでファイルの変更をwatchする

coffee -wcb -j all.js -o lib src/a src/b src/c

lib/all.jsがエディタで変更される度にビルドされます
ブラウザではall.jsさえロードしておけば、リロードされる度にすべてのファイルがコンパイルされたものが適用されます

-b,-l の細かいオプションは本家ドキュメントを読んでください
http://jashkenas.github.com/coffee-script

自動テストとかは http://d.hatena.ne.jp/mizchi/20110523/1306161220を参照