location object と anchor element

JavaScript で Unit Test を書こうとした時、その関数が location object を
処理したり、URL の文字列からホスト名などを取得したい場合、anchor element を生成して location object の代わりに使うという方法が stack overflow にあった。

testem とか使ってテストをする場合、location.href に値を入れると画面遷移してしまうので、別のオブジェクトを用意して使えばよい。

例: URL 文字列からホスト名を取得

var mylocation = document.createElement('a');
mylocation.href = 'http://example.com/path/to/file';

var hostname = mylocation.hostname;

例: アクセスしているURLのパラメータを削除した文字列を返す関数とそのテスト

// Test
describe('callMyFuncTest', function(){
    beforeEach(function(){
        myNS.location = document.createElement('a');
    });

    afterEach(function(){
        myNS.location = window.location;
    });

    it('should return url without query string', function(){
        myNS.location.href = 'http://example.com/path?query=string';

        expect(myNS.removeQueryStr()).toBe('http://example.com/path');
    });
});

// Function
var myNS = {
    location: window.location
};

myNS.removeQueryStr = function(){
    var l = myNS.location;

    return l.protocol+'://'+l.hostname+l.pathname;
}

コードの方で少し工夫する必要はあるけど、一応これでテストできる。

参照: MDN

location object と anchor element

JavaScript で CSV をテンプレートにあわせて 1 行 1 ファイルに変換

例えば以下のような CSV から

title,date,text
Convert CSV into some files,2013-04-12,How to convert each record in CSV to...
Sample script,2013-04-11,Here is the sample script that you want...

以下の様なテンプレートを使って

<html>
<head>
    <title>{{title}}</title>
</head>
<body>
    <h1>{{date}} {{title}}</h1>
    <p>{{text}}</p>
</body>
</html>

以下のようなファイルを生成するスクリプト(各行につき 1 ファイル)を書いた。自分以外に必要とする人がいるか分からないが。。。

<html>
<head>
    <title>Convert CSV into some files</title>
</head>
<body>
    <h1>2013-04-12 Convert CSV into some files</h1>
    <p>How to convert each record in CSV to...</p>
</body>
</html>

Node.js と mu、fast-csv モジュールを使う。

$ npm install mu2
$ npm install fast-csv

convert.js

var mu  = require('mu2');
var csv = require('fast-csv');
var fs  = require('fs');

var jsondata = [];
var contents = [];
var datafile = 'data.csv';
var template = 'template.html';
var outfile  = 'page';

csv(datafile, {headers: true})
.on('data', function(data){
    jsondata.push(data);
})
.on('end', function(){
    for (var i=0, l=jsondata.length; i<l; i++) {
        render(template, jsondata[i], i);
    }   
})
.parse();

function render (template, data, contentIndex) {
    contents[contentIndex] = "";

    mu.compileAndRender(template, data)
    .on('data', function(output){
        contents[contentIndex] += output.toString();
    })
    .on('end', function(){
        fs.writeFileSync(outfile+'_'+contentIndex+'.html', contents[contentIndex]);
    });
}

ちょっと必要になったので作ってみた。そして後で少しいじって使うかもしれないので、メモとして残してみた。

JavaScript で CSV をテンプレートにあわせて 1 行 1 ファイルに変換

Oblique Strategies

Oblique Strategies のカードをランダムに引くページを作った。iOS のブラウザと、Safari、Chromeで動くことを確認した。Android 2.xのブラウザだと、CSS のアニメーションが上手く動かない。。

最初にアクセスしたら、ページのどこかをタップ、またはクリックすると、カードが引ける。別のカードを引きたい時は、表示されているカードをフリックして(上とか)消してから、また画面をタップする。

Oblique Strategies が何か、というのはこの辺を参考に。

今回も JsTestDriver のテストを書きながら進めたのだが、CSS のアニメーションとかは地道に手で確認するしか無いよね、と思った(当たり前)。

Oblique Strategies

TDD で jQuery plugin を作ってみた

指定した element のサイズに合わせて 1 行ごとにフォントサイズを調整する jQuery のプラグインを書いた。結果はサンプルを見た方がわかりやすいと思う。

指定したフォントや、使っている文字とブラウザの組み合わせによっては、上手くサイズが合わなくて折り返しちゃったりする事があるので、完璧ではないけど。。一応、Mac の Safari、Chrome、Firefox でテストした。

これを作る前に テスト駆動JavaScript を読んでたので、JsTestDriver のテストを書きながら作ってみた。

ちょっとハマったのが、JsTestDriver の charset が iso-8859-1 で決め打ちされてるので、日本語のテストができなかった。一応 Accepted になってる からいつかは直ると思うけど、結構放置気味…。

TDD を初めて自分でやってみたんだけど、テストを先に書く良さというのが (説明はできないまでも) 何となく体感できたような気がする。

後は、JsTestDriver のテストは、”test…“ で始まらないものが無視されるようなので、考えた仕様をメモ的に “should ….” とかで空のテストとして思いついたものから書いておいて、1 つづつ順番にテスト → 実装を繰り返すようなこともした(邪道かもしれないが)。

TDD で jQuery plugin を作ってみた

JavaScript の関数呼び出しと this

Yehuda Katz のブログで 2011/08/11 に post された記事 の概要。

大本の関数呼び出し

Function の call メソッドは次のように動作する

  1. すべての引数のリストを作る (argList)
  2. 最初のパラメータが thisValue
  3. function が this を最初の引数、次の引数に argList を指定して呼び出される

例:

   function hello(thing) {
     console.log(this + " says hello " + thing);
   }

   hello.call("Yehuda", "world") //=> Yehuda says hello world

他の呼び出し方は、この形式に変換できる。

単純な関数呼び出し

例:

   function hello(thing) {
     console.log("Hello " + thing);
   }

   // これは:
   hello("world")

   // このように変換できる:
   hello.call(window, "world");

ECMAScript 5 (ES5)の strict モードでは、 hello.call(undefined, “world”) になる。

メンバ関数

例:

   var person = {
     name: "Brendan Eich",
     hello: function(thing) {
       console.log(this + " says hello " + thing);
     }
   }

   // this:
   person.hello("world")

   // desugars to this:
   person.hello.call(person, "world");

hello メソッドがどのような方法でオブジェクトにアタッチされているかは関係ない。動的に追加された場合を見ると、次のようになる。

   function hello(thing) {
     console.log(this + " says hello " + thing);
   }

   person = { name: "Brendan Eich" }
   person.hello = hello;

   person.hello("world") // これも person.hello.call(person, "world") と同じ意味になる

   hello("world") // これは "[object DOMWindow] says hello world" となる

Function.prototype.bind を使った場合

常に同じ this を持つように、closure を使うテクニックが良く使われる。

   var person = {
     name: "Brendan Eich",
     hello: function(thing) {
       console.log(this.name + " says hello " + thing);
     }
   }

   var boundHello = function(thing) { return person.hello.call(person, thing); }

   boundHello("world"); // "Brendan Eich says hello world"
</code></pre>
このテクニックをもう少し汎用的にして、次のように書くことができる。
<pre><code>   var bind = function(func, thisValue) {
     return function() {
       return func.apply(thisValue, arguments);
     }
   }

   var boundHello = bind(person.hello, person);
   boundHello("world") // "Brendan Eich says hello world"

これを理解するためのポイントは、arguments は配列に似たオブジェクトだということと、apply メソッドは引数を配列に似たオブジェクトとして扱う以外は call メソッドと同じだということ。

尚、ES5 には Function に bind メソッドが追加されている。

   var boundHello = person.hello.bind(person);
   boundHello("world") // "Brendan Eich says hello world"

これはもとの関数をコールバックとして渡す時に便利である。

   var person = {
     name: "Alex Russell",
     hello: function() { console.log(this.name + " says hello world"); }
   }

   $("#some-div").click(person.hello.bind(person));

   // div がクリックされたら、 "Alex Russell says hello world" が表示される
JavaScript の関数呼び出しと this

ngCore最速チュートリアルの続き

前回の記事の後に、Twitterのタイムラインを取得するサンプルを iOSシミュレータで動かしたので、メモ。

前回の記事: ngCore最速チュートリアル

参考にしたのは、ngCoreの 開発者サイト の「リソース」> 「エンサイクロペディア」の「iOSSimulatorを使ってサンプルを動かす」というページ。開発者サイトのドキュメントは、学習コンテンツよりもエンサイクロペディアを見た方が分かりやすい気がする。

ちなみにシミュレータで動かすだけなら Developer Program に登録しなくても OK (サンプル動かす為にお金払うのはちょっと、ていう俺みたいな人も動作確認できる)。

手順:
1. SDK のディレクトリ以下、iOS/Release-iphonesimulator/webgame_US_LIVE_SAND_SDK-iPhone-Simulator.app を右クリックして「パッケージの内容を表示」
2. Contents/Resources/EmbeddedApp の中の webgame.app を右クリックして「パッケージの内容を表示」
3. bootconfig.xml を開くと以下のような内容になっているので

<resources>
    <string name="NgStartingServer">http://nggame.ngmoco.com:8002</string>
    <string name="NgStartingGame">Samples/Launcher</string>
    <string name="NgIntentAction"></string>
    <string name="_gameKey">game</string>
    <string name="_serverKey">server</string>
    <string name="_appidKey">com.ngmoco.webgame</string>
    <string name="useOpenSL">true</string>
</resources>

最初の 2 つ、NgStartingServer と NgStartingGame を自分の環境に合わせて書き換える。例えば、Twitter のタイムラインを取得するサンプルを Projects/Twitter に保存してたら、

<string name="NgStartingServer">http://localhost:8002</string>
<string name="NgStartingGame">Projects/Twitter</string>

のようにする
4. 修正を保存したら、SDK のディレクトリで sudo make server でサーバを起動
5. webgame_US_LIVE_SAND_SDK-iPhone-Simulator.app をダブルクリックして iOS シミュレータを起動

ちなみに Mac OSX が Snow Leopard の人は、ngCore の SDK が 1.6 だと iOS シミュレータが起動しないので、1.4.2 とかを使う事になる (1.6のやつだと、iOS 5 を要求されるが、XCode 3 では iOS のバージョンが 4 だから)。

ngCore最速チュートリアルの続き

ngCore最速チュートリアル

「ngCore最速チュートリアル」という冊子を貰ったのでサンプルコードなどを動かしてみようとしたら、いろいろ情報が足りなくてつまずいたのでメモを残す。環境はMac OSX。

Part 1 は概要の説明なので大丈夫だった。開発者サイトに登録して、SDKを落としてくればいい。また、サンプルコードが CodeZineからダウンロードできるので落としておく(サンプルで使われてる画像なども含まれている)。

準備

Part. 2 で Hello World のサンプルがあるのだが、その説明に

このコードを適当なディレクトリに配置して、ブラウザからアプリのURLを開いてみましょう。

とあるのだが、必要なファイルや指定のファイル名などがあるので、どこでも何でもいい訳じゃない。これは開発者サイトの「リソース」> 「はじめに」へ行くと書いてある。

とりあえず、SDKディレクトリ配下に Projectsというディレクトリを作り、各サンプル用のディレクトリをその下に、必要なファイルと共に作っておくと良いと思う。以下のような感じ。

SDK/
 +- Projects/
 |   +- MyApp
 |   |   +- Code/
 |   |   |   +- Main.js
 |   |   |
 |   |   +- Content/
 |   |   +- NGCore -> ../../NGCore
 |   |   |
 |   |   +- configuration.json
 |   |   +- manifest.json

NGCoreはSDKディレクトリ下のNGCoreへのシンボリックリンク。configuration.jsonとmanifest.jsonは以下のような感じにしておけば良い。

configuration.json サンプル:

{
    "appId": "MY_APP_ID"
}

mafifest.json サンプル:

{
    "code": [
        "./NGCore/Client/Legacy.js",
        "./Code/Main.js"
    ],
    "textures": [],
    "audio": [],
    "others": []
}

後は Main.js を編集して、冊子に載ってるサンプルコードを書いて、ブラウザで localhost:8002/Projects/MyApp/ を開けば動く。configuration.json とかは、ファイル内容が空だと make server で動かしたサーバが落ちるみたいだ。

Part. 2 のサンプル

LIST5にあるコードで、剣の軌跡を描く部分のコードがちょっと間違ってる。

bg.addChild( this._trajectory._node );

じゃなくて、

bg.addChild( this.trajectory.getNode() );

かな。

LIST6 では DnLib というライブラリのを使っているのだが、それは開発者サイトの「リソース」 > 「エンサイクロペディア」 > 「DnLib ドキュメント」からダウンロードできる。

Part. 3 のサンプル

Twitter のタイムラインを取得するサンプルは、注意点に書かれているように、ブラウザでは動かない(Flash のクロスサイトドメインの問題)。リクエストしている URL の出力結果 (json) をローカルに保存して、その Path を指定すれば、ブラウザでも雰囲気はつかめるけど。

iOSのシミュレータなどではまだ試してないので、また今度試したら何か書くかも(しれないし、書かないかもしれない)。

追記 (2012.03.15): 続きを書いた。

ngCore最速チュートリアル