2014年7月16日水曜日

JSON のオブジェクトを ID でリンクする(5)

JSON のオブジェクトを ID でリンクする(4) - SakuraCrowd’s blog の続きです。


jsonId を利用しようとしていた仕様が変わってしまい、必要なくなったのでいったん保留します。


とはいっても、 {"$ref":"id1"} などの ID から、その ID を持つオブジェクトの参照への変換、逆変換の簡単なテストをクリアする実装もできたので、記念として gist にしました。長いので最後にはっときます。


 


jsonId の実装について


gist のコメントに書いてありますが、循環参照は jsonId の置換では問題にならないのでエラー検出はしないことにしました。


JSONPath でルートの "$" のときに jsonPath.eval の第一引数を対象にしたかったのですが、空の配列が返されてしまっていたので、その部分だけ条件文で特別に処理しました。


"$[0]" などはルート配列の1番目の要素の参照を取得できるので、 jsonPath.eval(...)[0]を使っています。


反省点


jsonId を使わない仕様をなぜ最初に気づけなかったか。


それは、なるべく作成する機能に汎用性を持たせたいという悪いくせがでたためだと思います。


tmx のプロパティの編集がやりづらいので、他の json ファイルに細かい設定を書いてそれを参照させようというのがきっかけでした。


しかし、実際に自分が必要とする設定は tmx のプロパティに CSV などで4つくらいのパラメータを書くだけですむものでした。


それがなぜか、もっと複雑なものも簡単に設定したいというあいまいな仕様になってしまいました。


実際に使うデータを処理できるだけで十分だということを念頭において仕様を考えるようにしたいです。


 


cocos2d-js-3.0-rc0 


が使えるようになりました(∩´∀`)∩ワーイ


setup.py をいちいち管理者権限で呼び出すのが面倒なのでバッチファイルを作りました。


2.2 のころと違って html を起動するだけというわけではなく、 cocos コマンドで実行するのが微妙に面倒ですが、ノードの親子関係に依存しないで奥行きを設定できたりするのが便利です。


project.json の module に extensions を追加しないと使えないのがわからず苦戦しましたが、ScrollView で簡単にスクロールできるようになってよかったです。


 



jsonId の実装とテストケース


2014年7月13日日曜日

JSON のオブジェクトを ID でリンクする(4)


gistb28ba8146f829f3f3254


JSON のオブジェクトを ID でリンクする(3) - SakuraCrowd’s blog の続きです。前回まではテストケースをクリアする実装までいけていたのですが、今回はテストケースを書いたまでで、それを満たす実装は勉強中です。


いつもどおり、仕様の説明は gist のコメントに書いてあります。


簡単に言うと、複数のオブジェクトが集まっている構造の中で一部だけを処理できるようにしました。


それだけならば、その一部のオブジェクトを引数にくわせりゃいいじゃんと思うんですが、 ID からデータに置換するには JSONPath の絶対パスを利用しています。


そのためルートが変わってしまうと ID からデータに置換できなくなってしまうのです。


 


JSONPath とか XPath とかパスもいろいろあるね(`・ω・´)


そんなかんなで jsonPath を使って処理を開始するノード要素を取得する処理を追加しました。


最初にはまったのはルートは "$" だから jsonPath.eval(target, "$") とすれば target の値が返されると思ったところでした。


実際は空の配列が返って来ます。


XPath の仕様などの説明を読んだところ、どうやらルートノードとルート要素というものは別物らしいです。


そして JSONPath でルート要素を表す場合は "$.*" とします。


これで target の値が取得できました。


 


eval だからって、えばる(eval)な!←いってみたかっただけです><


しかし、今度は ret = jsonPath.eval(target, "$.*") が ret == target ですが ret !== target になっています!


これでは、共通のオブジェクトへリンクする処理ではなく、同じ値を持つ別のオブジェクトへのリンクとなってしまいます。


どうすればいいのかなー。


現状はそんなところです。


 


2014年7月11日金曜日

JSON のオブジェクトを ID でリンクする(3)


gist8e9a924f3090183b6a41


JSON のオブジェクトを ID でリンクする(2) - SakuraCrowd’s blog の続きです。


詳しくは上記の gist のコメントに書きました。


前回は参照→オブジェクトへの変換だけでしたが、逆の変換の機能も追加しました。


参照とオブジェクトが混ざった状態から、オブジェクトだけに置換してしまうので、その後、どれがもともとオブジェクトだったのかわかりません。


そのため、変換の結果でそれを記録しておき、逆変換のときに利用します。


ない場合は、最初にみつけたオブジェクトの位置以外を参照に置き換えます。


 


git 苦手だったけどちゃんとできたよ(`・ω・´)


subversion も使いこなしてたってわけではありません。


しかし git はもっとわからなかったしやってませんでした。


そんなでしたが、いい機会だしとやってみると以外に簡単にやれました。


tortoiseGIT っていうコンテキストメニュー拡張のソフトを使いました。


バージョン管理したいファイルの置いてあるフォルダを選んで、「Git ここにリポジトリを作成」を選ぶ。


管理したいファイルを選択して Git > 追加 メニューで追加する。


あとはコミットすればOK。


ついテンパったりめんどくなるとコピーして日付つけて「どきっ!コピーだらけの開発フォルダ///」になってしまうのですが、なるべく git とかで管理しようといつも思っています。


 


jasmine-node が動かないヽ(`Д´)ノウワァァァン!!


以前に、 npm install -g jasmine-node でちゃちゃちゃっとインストールしておいて、以前はたしかコマンドプロンプトから jasmine-node でいけたはずなのに、「cannot find module」とか「ファイルとして認識されません」とかでちゃった(∀`*ゞ)テヘッ


なんか、いじっちゃったかなー、って環境変数とか確認してたけど、よくわからないまま3時間経過ヽ(`Д´)ノウワァァァン!!


自分の中ではこういう問題を「環境系バグ('∀`)」と呼び恐れています。


環境様を怒らせたらもう何がなんだかわからないバグがいっぱいでて(゚∀゚)アヒャヒャヒャヒャヒャヒャとなってしまうのですよ。


怖いですね(´・ω・`)


そんなときは思い切って再インストールすればいいんだよ!って思って node.js はしてみたんだけど、 npm のほうは残ってしまいました。


そこでたまたま見つけた node.js メニューの node.js command prompt を起動してみました。


Visual Studio の command prompt みたいに環境変数をちょこちょこと追加したコマンドプロンプトでした。


これで jasmine-node と実行するとちゃんと認識できたー(∩´∀`)∩ワーイ


 


next ('∀`)'s hint


ID で JSON のオブジェクトをリンクさせる機能は sakuraCrowd.jsonId という名前で作成したので、これを使う上位の関数 loadTmx(仮)を作ろうと思います。


loadTmx は node で動くので jasmine-node を動かそうといろいろやってたのです。


おおざっぱにいうと tmx を読み込んで、オブジェクトレイヤーのオブジェクトに jsonId を適用します。他にそのオブジェクトを拡張するのですが、それはまた今度書こうと思います。


2014年7月9日水曜日

JSON のオブジェクトを ID でリンクする(2)


gistcf43df70ff1d946640af


 


JSON のオブジェクトを ID でリンクする - SakuraCrowd’s blog の関数のテストケースの作成と実装を行いました。


関数の説明は gist のコメントに書きました。


 


JSONPath でいいんじゃね?->やっぱり ID もいるよね!


リンクを展開していく際に、循環参照を検知したらエラーにして、発生した場所をメッセージで伝える仕様にしました。


その場所の表し方を検討していたところ、 JSONPath という XPathJSON 版がみつかりました。


「あれ? ID とか独自仕様作らないで JsonPath でリンクすればいいんじゃね?(`・ω´・)」と思い始めました。


実際、それですんだら独自仕様なんて面倒なものから解放されるのでよいと思ったのですが、ぐぬぬと思う残念なところがあって、先日の提案どおり ID によるリンクにしました。


残念なところとは、上位へのアクセスを指示する .. が使えないことです。


.. 自体はあるのですが、再帰の指示で使われていて意味が異なりました。


これがないと、同じ階層にある他のオブジェクトを指すために絶対パスが必要になると思います。


ID はプロパティを割り振る手間や重複などのデメリットがありますが、ユーザが簡単にオブジェクトを指すことができます。


もともと、この機能は TMX の中のオブジェクトの設定を簡単にするために作っています。


TMX 自体は XML で表されていて、それを tmx-parser で JavaScript のオブジェクトに変換します。


TMX の編集中に JSONPath を意識するのは難しいので、 ID によるリンクでよいと思いました。


 


開発環境とかコーディングルールとか


実際のところ、関数の作成よりも開発環境や基本的な書き方の勉強に時間がかかりました。


しかも、まだふわふわしています。


 


JavaScript のライブラリの書き方 (まだふわふわ)


JavaScript によるライブラリの書き方は underscore.js を参考にしました。


無名関数の中で、ライブラリ用のプロパティをグローバル変数に追加します。


同じ名前の変数がすでにある場合は直前まで割り当てられていた値に戻せる関数 noConflict も underscore.js や jQuery を参考にしてやろうと思いましたが、まだ必要性がないと思いやってません。


最低限の機能として、 node.js の場合は exports 変数にライブラリ用の変数を設定するようにしました。


 


テストフレームワーク Jasmine を使ってみた


Jasmine についてはサンプルを適当にいじっただけです。


Jasmine-standalone はダウンロードして specrunner.html を起動するだけでサンプルが動くし、その中を見れば直感的にカスタマイズできる感じでわかりやすいです。


 


IDE になじめず軽くへこむ


aptana とか VS express 2013 for web などの IDE も使ってみたのですが、インテリセンスやブレークポイントがうまく使えませんでした。


「俺、向いてないのかな(´・ω・`)」とまじで落ち込むくらいに、正しい使い方がわかりませんでした。


 


ブラウザのデバッガがすごい


結局、デバッガは FireFox の F12「要素を調査」を使いました。


chrome の「要素を検証」でもよかったのですが、 cocos2d のサンプルが chrome ではなぜか動かず FireFox では動いたので、 FireFox でやっています。


Jasmine でテストして、失敗したとこだけ、ブレークポイントをはって実行というパターンがわかりやすくてデバッグしやすかったです。


 


テスト駆動のにわかの感想


テスト駆動だと、修正確認を短いサイクルで行えるので不安要素が少なくなっていじりやすいです。


2014年7月6日日曜日

JavaScript のテストフレームワーク Jasmine の勉強

Jasmine というテストフレームワークを勉強中です。


Jasmine を選んだ理由



  • クライアントサイドだけでなくサーバサイドもサポートしている

  • クライアントサイドは jasmine standalone 、サーバサイドは jasmine-node が提供されている。
  • わりと人気がありそう

  • 主観ですが、検索すると結構使われていそうでした。よらば大樹の陰。

Jasmine standalone を使ってみる


バージョン 1.3 と 2.0 系があったので 2.0 系をダウンロードしてみました。
SpecRunner.html というそれっぽいのを起動すると、同梱しているサンプルのテストの実行結果が表示されました。
SpecRunner.html 内で、フレームワークの .js とテスト対象の .js とテストコードの .js を指定して利用します。


jasmine-node を使ってみる


jasmine-node のバージョンは 1.14.3 となっていて、standalone と足並みそろってないのかなと思いました。
こちらは実行対象をファイル単位で指定せず、ディレクトリ単位です。


クライアントサイドのテストコードとの大きな違い




    • クライアントサイドでは script タグで読み込むファイルを require で読み込む。

    • テスト対象の関数の代入先が違う。

    • クライアントサイドでは単純に関数を定義するだけでした。 var func1 = function(){...}



      jasmine-node では、 exports.func1 = func1; としたりして、 exports のプロパティに設定する必要がある。

      これは Jasmine というよりクライアントサイドと node.js の違いかもしれません。


できたらクライアントサイドもサーバサイドもコードを共用したい


クライアントサイドとサーバサイドでそれぞれネットで見つけた FizzBuzz のテストケースを実行できました。
できましたが、まだ不満があります。前述した違いを除くと、テストコードが同じであることです。


同じようなコードをクライアントサイドとサーバサイドの両方で書くことは、DRY のポリシーに反するような気がします。


そこで、同じファイルで実行できないか模索中です。


これは、 Jasmine というよりもクライアントサイドと node.js の話になり、まだまだ難航しているので別に書きたいです。
ぐぐってみると、 typeof window === 'undefined' という条件で分岐させるなどの方法がでているのですが、やり方が悪いらしくうまくいっていません。
クライアントサイドと node.js でコードとそのテストケースを共用できるようにもう少し調べてみます。


2014年7月2日水曜日

JSON のオブジェクトを ID でリンクする

複数のプロパティに同じ値のオブジェクトを設定するのは面倒だし同じであることがわかりにくい。


{"prop1":{"value":12345},


 "prop2":{"value":12345},


 "prop3":{"value":12345}}


 


いちいち同じ値のオブジェクトを記述しないで、共通のオブジェクトを参照したくなりました。


あれ={"value":12345}


{"prop1":あれ, "prop2":あれ, "prop3":あれ}


 


 そこで、 ID というプロパティを持たせて、オブジェクトを ID で表せないか考えました。


共通のオブジェクトを指定したい場合は、そのプロパティにオブジェクトの ID を指定します。


{"id":"hoge", "value":12345}


{"prop1":"hoge", "prop2":"hoge", "prop3":"hoge"}


 


ID といってもただの文字列なので、読み込み側でオブジェクトが指定されるはずのプロパティで文字列があったら、その文字列を ID とするオブジェクトを割り当てる処理が必要です。


 「オブジェクトが指定されるはずのプロパティ」かどうかを判別する手段として、あらかじめ読み込み側に教えておくか、プロパティ名にプレフィックスなどをつけて表すことが考えられます。


さらに何かないかと調べていると、JSON のハイパースキーマ仕様には参照型というものがある事を知りました。


JSONだってハイパーメディア -- JSONハイパースキーマ仕様をなんとかしたい - 檜山正幸のキマイラ飼育記


URL をただの文字列ではなく "$ref" というプロパティの値として指定することで、その文字列が参照であることを表します。


 


これを先ほどの例に適用すると、さきほどよりも参照であることが明確にわかるようになりました。


{"id":"hoge", "value":12345}


{"prop1":{"$ref":"hoge"}, "prop2":{"$ref":"hoge"}, "prop3":{"$ref":"hoge"}}


 


$ref をいちいち打つのが面倒とか思ってしまいそうですが、今後も特別な意味を持つ文字列を扱いたい場合などにこの方法は有効だと思いました。


直接の関係はありませんが、ECMA script 5 の property descriptor も値をオブジェクトに包んで情報を付加しています。


 


これならば読み込み側でプロパティによって処理を変える必要はないので、扱うデータによって読み込み側を変更する必要はありません。


「$ref プロパティだけのオブジェクト」が値の場合は参照として処理すればよいだけです。


 


今日は仕様をある程度形にできました。明日は実装までいきたいです。


JavaScript 自体まだ不慣れなところがありますが、テストケースのフレームワークがあればそれを使ってみたいです。