Hatena::Groupcside

Cside::StudyMemo このページをアンテナに追加 RSSフィード

メインブログに書くまでもない自分用メモを垂れ流す。日々是勉強也。

カテゴリー

2012-03-29

[]Titaniumなjsファイルをコンソールで編集中に文法チェック

エミュレータを10秒近くかけて起動したら、表示されたのはJSのしょーもない文法エラーだった…というのはあるある。文法チェックはコンソールで編集中に済ませて、エミュレータを使ったデバッグはiOS/AndroidのAPIを正しく使えているかとか表示の調整とかそういうのだけに集中したい。

ということで、コンソールでTitaniumなjsファイルを実行してみると…。当然 Ti(tanium)オブジェクトが定義されてなくて

Titanium is not defined

みたいなエラーで死ぬ。


そこで、Titaniumオブジェクトのモックを作って、コンソールで実行した場合のみこいつを読みこませるようにする。

https://gist.github.com/2233668

こいつを app.js でrequireしてやるだけでOK。これでTi(tanium)オブジェクトがコンソールでjsを実行した場合に勝手に定義してくれるので、上のエラーで死ぬことはなくなる。文法のエラーのみがエラーメッセージに出力されるようになる。

あとは、自分のエディタのreplプラグインを使えばいい。


TiのAPIをドキュメントから全部ぶっこ抜くのに使ったスクリプトを一応貼る。俺俺リファレンスとか作りたくなった時に再利用できそう。

2012-03-24

[]User-Agentは requestオブジェクトをopenした後でないとsetできない

var req = Ti.Network.createHTTPClient();
req.onload = function() { ... };
req.open('GET', url);
// ここで呼ぶ
req.setRequestHeader('User-Agent','Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3');
req.send();

req.openより前でsetReqHeaderしてもセットされない。

また、リファレンスをぐぐるとTi.setUserAgentなるメソッドがあるが、これではセットできない罠。

結論

なんかおかしいなーと思ったらKitchenSinkの動くソースをackして参照するよい。無駄にはまらずに済む。

2012-03-22

[][]Titaniumのrequireにおける相対パスの起点がおかしい件とその対策

Titaniumで外部のJSを読み込むとき、Ti.include の代わりに require を使うと、CommonJSで定義されたJSのモジュールを読み込むことができる*1。しかしこの方法には1つ大きな問題がある。

require('./path/to/module') の相対パス起点は、CommonJSではrequireを呼んだファイルが起点とされている対して、Tiではすべて Project/Resources/ が起点になる

もう少し具体的にいうと、こういうディレクトリ構成だったとして、

Resources/
├── app.js
└── lib1
    ├── module1.js
    └── lib2
        └── module2.js

module1.js 内で

require('./lib2/module2');

とmodule2を呼びだそうとしても、Titaniumは Resources/lib2/module2.js を読み込もうとしてしまうのでFile Not Foundでエラーになるということだ。この例でいうとパスを './lib1/lib2/module2' と書きなおさなければ動かない。


これの何がまずいかというと、CommonJSで書かれたJSのモジュールをTitaniumに移植してもこの問題のせいで動かないということだ。頑張ってパスを Resource/ を起点としたものに書き換えれば当然動くが、馬鹿馬鹿しいし、元のソースには極力手を入れたくないというものだ。

そこで、元のソースに手を入れずに require の挙動を変更する。app.js の最初でこのような定義をすればよい。

var origRequire = require;
var PathResolver = function (cwd) { 
    this.cwd = cwd || '.';
    var self = this;
    require = function (path) {
        return (new PathResolver(self.cwd)).require(path);
    };
};
PathResolver.init = function (cwd) { new PathResolver(cwd) };
PathResolver.prototype.require = function (path) {
    var newPath = this.cwd + '/' + path;
    this.cwd  = newPath.substr(0, newPath.lastIndexOf('/'));
    return origRequire(newPath);
};
PathResolver.init();

// Write your code below...

これで、require の相対パスの起点が呼び出したファイルになる。


TitaniumにはNode.jsでいう __filename のような、そのソースファイルの場所を取れる予約変数が無かったので、若干ややこしい実装になったし時間も食ってしまったのだけ残念。


Note

自分用メモ。

Tiに依存しない部分をNodeで動作チェックしつつCommonJSなモジュール化し、それをTiに移植する、というのを今回試みている。いくつかの罠(上で挙げたのも当然)を意識すれば、Tiで毎回10秒近くかかるエミュレータ再起動くりかえしな開発よりもだいぶ効率上がる感じなので、これについてはアプリが完成した段階でまとめたい。


てか最近、メインブログよりすっかりこっちが元気になってるな…。

[][]CommonJSの仕様 ⊂ Node.jsの仕様?

Nodeでは動いていたコードが一部Tiで動かなかった。たとえばこれ。

var module = require('module.js'); // error

CommonJSの仕様を見たところ .js を外さないと駄目なようだった。

var module = require('module'); //=> ok

ほかにも結構、NodeにはCommonJSから外れた仕様がある。(詳しくはここ ttp://d.hatena.ne.jp/t_43z/20110626/1309082158 。)


僕はTiで動かしたいモジュールを、Nodeで動作チェックしながら書いていた。何故でTiで実行しないかというと、エミュレータの再起動に毎回10秒近くかかるからだ。それはだるい。

しかしNodeにはCommonJSにない独自仕様があるから、Nodeで動いてたのにTiで動かないコードをデバッグするのに時間を取られる。本末転倒感ある。

CommonJSの仕様を脱してないかどうかcheckできるJSLintのようなものは無いのだろうか。ちょっと困っている。

そういうのが無いなら、diff Node.js CommonJS の主なところを知りたいが、探してもいまいち見つからない…。