IE9でBackboneアプリが動かない

ChromeやFFで問題なく動作していたBackboneアプリがIE9では動かない。。
デバッグツールを立ち上げるとこんなエラーが出ていました。

SCRIPT1028: 識別子、文字列または数がありません。

var ItemView = Backbone.View.extend({
	render: function(){
		$(this.el).html(this.template(this.model.toJSON()));
		return this;
	}, //こんなかたちでカンマが残っているとエラーになります。
});

IE9でも互換モードになっているとChromeFirefoxのように動かないので、ヘッダーを入れておく必要があります。
http://stackoverflow.com/questions/4811801/force-ie9-to-emulate-ie8-possible

Source:
http://d.hatena.ne.jp/rockstar2007/20081102/1225590395

Jasmine+sinon.jsでjQuery.ajaxが呼ばれているかテストする

sinon.spyを使うと$.ajaxが呼ばれているかをテストできます。
このときはBackboneのCollectionでfetchが正しいパラメータで呼び出されているかをテストしてみました。

describe("some ajax test", function(){
	beforeEach(function(){
		this.ajaxSpy = sinon.spy($, "ajax");
		this.paramSpy = sinon.spy($, "param");
		this.collection = new MyCollection();
	},
	
	afterEach(function() {
		$.ajax.restore();
		$.param.restore();
	});
	
	it("should make some ajax request using these parameters", function(){
		var start = "2012/6/1";
		var theEnd = "2012/6/31";
		this.collection.someAjaxCall(start, theEnd); 
		expect(this.ajaxSpy).toHaveBeenCalledOnce();
		expect(this.paramSpy).toHaveBeenCalledWith({start: "2012/6/1", theEnd: "2012/6/31"});
	});
});

Jasmine+BackboneでViewのテストをするときにダミーのHTML要素を使う

jasmine-jqueryのsandboxを使うと、テストの間だけ使えるDIV要素を作ってくれます。
各テストの間でちゃんと掃除をしてくれるので、わざわざ削除する必要もありません。

beforeEach(function(){
	setFixtures(sandbox());
});

sandbox()を使うとdiv要素にidとしてsandboxが付けられます。
idを変更したい場合はsandbox({id: "someId"})とするとIDを変更できます。
DIV以外の要素を指定したい場合はこんな感じでappendすると使えます。

	setFixtures(sandbox());
	$("#sandbox").append("<input type='text' id='search' />");

https://github.com/velesin/jasmine-jquery/

Jasmineで非同期のリクエスト終了後にテストを実行する

今日はJasmineを使ってBackbone.Viewのテストを書いていました。
サンプルに載っていた通り、非同期リクエストでViewのテンプレートを読み込んでいたのですが、テンプレートの読み込みが終了した時点でテストを実行しようとして、うまくいかずにハマりました。Jasmineを使った非同期リクエストのテストについて調べて「waitsFor」と「runs」を使うことで、思い通りに実行できました。

waitsFor(function, message, timeout)
https://github.com/pivotal/jasmine/wiki/Asynchronous-specs
waitsForを使うとtimeoutで指定した秒数(ミリ)が経過するまで、引数に渡したfunctionからtrueが返ってくるのを待ってくれます。
もしtrueが返ってくるまえにtimeoutに達した場合はmessageで指定したエラーメッセージが戻ってきます。

下の例では、テンプレートを非同期で読み込んで、読み込みが完了したらloadedがtrueになります。
waitsForで500ミリ秒間、loadedの値がtrueになるのを待ってから、次のrunsを実行しています。

beforeEachの時点でviewを用意しているので、itが実行されるときにはテンプレートの読み込みが終わって、viewの準備ができた状態になっています。

describe("nice test", function(){
	beforeEach(function(){
		var self = this;
		this.loaded = false;
		this.model = new Backbone.Model();
		
		templateLoader.load(["nice-template"], function(){
			self.loaded = true;
		});	
		
		waitsFor(function(){
			return self.loaded;
		}, "template loading timed out", 500);
		
		runs(function(){
			self.view = new MyView({model: self.model});
		});
	}
	
	it("should be testing the view", function(){
		expect($(this.view.render().el).html()).contains("hoge");
	});
});

Source:
http://pivotal.github.com/jasmine/
https://github.com/pivotal/jasmine/wiki/Asynchronous-specs
http://d.hatena.ne.jp/hagino_3000/20111009/jasmine

PlayFramework 別のモデルと関連があるモデルにGsonのtoJsonが使えないとき

Gsonを使うとモデルをそのままJSON形式に変換したい時に、こんな形で一発変換できます。

Gson g = new Gson();
Person p = Person.findById(myId);
g.toJson(p); // {"name": "Taro Yamada", "birthday": "1920/06/30"}

ただ、Personが別のモデルとManyToOneとかOneToManyなど、何らかの関連があるときには「gson circular reference error」でJSON形式に変換できません。最初はモデルを一度Hashに変換してからJSON形式に変換していたのですが、なにか違うのかもしれないと思い、回避する方法を探してみたところ、Exposeアノテーションを使う方法と、ExclusionStrategyを実装する方法の2つ方法がありました。

@Exposeを使う方法
http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/annotations/Expose.html

モデルでJSON形式として含めたいプロパティだけにExposeアノテーションを付けます。
この場合はsecretだけtoJSONしても出てきません。

@Expose public String name;
@Expose public String address;
@OneToMany(mappedBy="person", cascade=CascadeType.ALL)
public List<Hoge> secrets;

モデルの設定が終わったらGsonBuilderでexcludeFieldsWithoutExposeAnnotationを使ってGsonインスタンスを生成します。

	Gson g = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()  
        .create();
	Person p = Person.findById(id);
	renderJSON(g.toJson(p));

うまくいったと思ったのですが、idが出力されないので使えません。。
いろいろごにょごにょした結果、最終的にもうひとつの方法を使いました。
http://stackoverflow.com/questions/4802887/gson-how-to-exclude-specific-fields-from-serialization-without-annotations

ExclusionStrategyを実装します。
さきほどと同じく他のモデルと関連があるsecretsを対象から外します。

package models;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

public class PersonExclusionStrategy implements ExclusionStrategy {

    public boolean shouldSkipClass(Class<?> arg0) {
        return false;
    }

    public boolean shouldSkipField(FieldAttributes f) {
        return (f.getDeclaringClass() == Person.class && f.getName().equals("secrets"));
    }

}

でGsonをインスタンス化するときにこのExclusionStrategyを使うように指定します。

	Gson g = new GsonBuilder()
	.setExclusionStrategies(new PersonExclusionStrategy())
       .create();
       Person p = Contact.findById(id);
	renderJSON(g.toJson(p));

無事に変換できました。

その他ソース:
http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/GsonBuilder.html#excludeFieldsWithoutExposeAnnotation()
https://sites.google.com/site/gson/gson-user-guide

Ajaxリクエストをテスト

PlayのFunctionalTestでAjaxリクエストをテストしてみました。
ここではJSON形式でデータを渡すので、Gsonを使ってHashをJSON形式に直してからPOSTしています。

	HashMap dataToSend = new HashMap();
	dataToSend.put("key1", "value1");
        dataToSend.put("key2", "value2");
	
	Gson g = new Gson();
	Response resp = POST("/niceAction","application/json",g.toJson(dataToSend).toString());
	
	// check if desired value is returned
	assertContentEquals("expected response!", resp);

Eclipseでvim/textmateライクなテーマを使う

仕事ではJavaが多いのでEclipseを使います。
画面があまりにもシンプルで寂しいので、Eclipseのテーマをいじれないか探してみたところ、プラグインを使ってVimTextmateっぽい感じにすることができました。wombatやmonokaiなども最初から用意されています。インストールも設定も非常に簡単でした。

Help -> Install New Softwareで次のサイトを登録
http://eclipse-color-theme.github.com/update

Eclipse Color Themeをインストール

General -> Appearance -> Color Themeから好きなテーマを選ぶだけ

Eclipse Color Theme Plugin
https://github.com/eclipse-color-theme/eclipse-color-theme

Stackoverflow
http://stackoverflow.com/questions/96981/color-themes-for-eclipse
http://stackoverflow.com/questions/96981/color-themes-for-eclipse/4815649#4815649