けさらんぱさらん

方向性は定めず、ただ思いつくままに

Knockout.jsの小ネタ

Knockout.jsでは通常、値をバインドしたい時は

<div data-bind="hoge">

このようにプロパティ名を指定します。


Knockout.jsには、とある条件の時にCSSクラスをバインドする機能があります。
例えばこんな感じに書きます。

<div data-bind="css: { css-no-class: foo() }">

このfooは、bool値を持つobservableだとします。

foo = ko.observable(true);


この時によくやってしまうのが

<div data-bind="css: { css-no-class: foo }">


fooが関数として実行されないというミス。
しかもJavaScriptは、この形でもtrueとして判断するので割と気づかない
falseにした時にあれれってなることが多いので注意が必要なのです。

訂正
コメントいただいて調べなおしたら上記はどうやら私の勘違いだったようです。

<div data-bind="css: { css-no-class: !foo }">

このパターンではうまく動かないと思っていたのですがちゃん動きました。
なんで動かなかったんだろう???

関数呼び出しをしなければいけないパターンは

<div data-bind="css: { css-no-class: count() < 10 }">

こんな感じで比較を行った場合でした。

Chutzpahを使ったTypeScriptのテストで困ったこと

VisualStudioでJavaScript単体テストもやってくれるようにするアドインのChutzpahですが
TypeScriptを使ったプロジェクトだといくつか困った問題が発生します。

こんなテストが有ったとします。
JavaScriptではなくTypeScriptで書いてます。
(この例だと分かりづらいですけど・・・)

/// <reference path="typings/jquery/jquery.d.ts" />
/// <reference path="typings/jasmine/jasmine.d.ts" />

describe("test_", () => {

    it("hoge", () => {
        spyOn($, "ajax").andCallFake(
            function (_) {
                var d = $.Deferred();
                d.resolve([
                    { id: 1, name: "佐藤" },
                    { id: 2, name: "鈴木" }
                ]);

                return d.promise();
            });;

        // なんかテスト
    });
});

これをテストエクスプローラで見ると
f:id:cer1974:20131014213612p:plain:w300
こんな感じで1つのテストが3つに見えてしまいます。
しかも1つは文字化けしてます。

1つは、WebEssentialsというアドインがTypeScriptをコンパイルした時に
生成されるminifyの方もテスト対象になってしまっているからです。
ASP.NET MVCの場合は、minifyは必要ないのでこれは生成されないように設定を変更します。

[ツール]→[オプション]から[Web Essentials]を選択し[Minify generated JavaScript]をfalseにします。
f:id:cer1974:20131014214418p:plain
※既に出来てしまっているminifyファイルは削除してください。

もう1つが分かりにくかったのですが、Chutzpahは生成されたJavaScriptではなくTypeScriptでもテストが出来るようで
JavaScriptとTypeScriptのどちらもテストをしてしまっています。
これを設定でJavaScriptだけにします。(TypeScriptの方は文字化けしてしまっている方なので使いません)

[ツール]→[オプション]から[Chutzpah]を選択し[Testing Mode]をJavaScriptにします。
f:id:cer1974:20131014215357p:plain

これで1つになりました。
f:id:cer1974:20131014215554p:plain:w300


実はもう1つ困ったことが起こるんです。
このテストを実行するとこんな感じでエラーになります。
f:id:cer1974:20131014220219p:plain:w300
$という変数が無いと言ってますね

TypeScript的な参照は1行目で行ってますが
生成されたJavaScriptにはjQueryJavaScriptファイルがどこにあるのか分かりません。

jQueryJavaScriptファイルを参照できるように1行目に追記します。
普通のrefernceだとコンパイルエラーになるので注意です。

/// <chutzpah_reference path="jquery-2.0.3.js" />
/// <reference path="typings/jquery/jquery.d.ts" />
/// <reference path="typings/jasmine/jasmine.d.ts" />

これでテストが成功します。
f:id:cer1974:20131014221735p:plain:w300

TFSのwiqlではまったこと

TFSの作業項目をプロジェクト横断的に取得したくて
ここ見て作ってたら見事の嵌められたのでメモっておきます。

例に倣ってこんな感じで作ります。

var tpc = new TfsTeamProjectCollection(
        new Uri("http://localhost:8080/tfs/DefaultCollection"));
var workItemStore = (WorkItemStore)tpc.GetService(typeof(WorkItemStore)); 
var queryResults = 
    workItemStore.Query("Select [State], [Title] From WorkItems Where [Work Item Type] = 'User Story' Order By [State] Asc, [Changed Date] Desc");

これを実行すると
f:id:cer1974:20131008223036p:plain:w400
Stateが無いとかエラーが出ます。絶対にあるのに!!


なんだかんだ色々試した結果

var tpc = new TfsTeamProjectCollection(
        new Uri("http://localhost:8080/tfs/DefaultCollection"));
var workItemStore = (WorkItemStore)tpc.GetService(typeof(WorkItemStore)); 
var queryResults =
    workItemStore.Query("Select [System.State], [System.Title] From WorkItems");

カラム名の前にSystem.を付けるとちゃんとデータが取得できる!
でもカラム名に半角スペース付いているとエラーになる! まだ回避方法がわからん
でも今、作っているプログラムは半角スペースがあるカラムは必要ないので掘り下げないかもね

ASP.NET MVCでJSONのプロパティをLowerCaseで返す方法

ASP.NET MVCでアプリケーションを作成しているときに
ちょっと困るのがAJAXでオブジェクトを取得したときに
プロパティがUpperCaseで返ってくることです。JavaScriptではLowerCaseで扱いたい><
今まではしょうがないって思ってたんですけどちょうどJavaScriptリファクタリングをする必要があったので
C#側でLowerCaseで返すようにしてみました。


こんな感じのメソッドがあるとします。
クラスはMVCのControllerクラスを継承してAppControllerクラスを継承しています。

public class HomeController : AppController
{
    public ActionResult GetData()
    {
        var model = new Model { ID = 1, Name = "佐藤" };

        return Json(model, JsonRequestBehavior.AllowGet);
    }
}


通常はこんな感じでそのまま頭大文字でJSONが返ります。

{"ID":1,"Name":"佐藤"}


頭を小文字に変更します。
まずMVCのJsonResultクラスを継承したAppJsonResultクラスを作成し
ExecuteResultメソッドをオーバーライドします。
ほとんどMVCのメソッドまるパクリなんですけどJSON文字列を生成するところだけ変更しています。
JSONの生成にはJson.NETを利用します。

public class AppJsonResult : System.Web.Mvc.JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        if (!String.IsNullOrEmpty(ContentType))
        {
            response.ContentType = ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data != null)
        {
            var json = JsonConvert.SerializeObject( Data, Formatting.Indented,
                new JsonSerializerSettings 
                { ContractResolver = new CamelCasePropertyNamesContractResolver() } );

            response.Write(json);
        }
    }
}


AppControllerクラスでJsonResultメソッドをオーバーライドしてAppJsonResultを返すようにします。

public class AppController : Controller
{

    protected override JsonResult Json(object data, string contentType, 
            System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
    {
        return new AppJsonResult
        {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            JsonRequestBehavior = behavior
        };
    }

}


これでLowerCaseになったJSONを返すことができました。

{ "id": 1, "name": "佐藤" }


APIフレームワークだともっと簡単にできるみたいなんですけど
MVCだとなんかいい方法が見つからなかったんでこんな方法にしてみました。
まあこれから作るんだったらAPIの方で作れってことですね。

jQuery.ajaxで配列を送信するのに嵌った

jQueryajaxメソッドを使って配列をサーバに送信するのに嵌ったのでメモ

サーバサイドは、ASP.NET MVCです。

まずサーバサイドのコード

[HttpPost]
public ActionResult Post(string[] values)
{
    return View();
}

引数で文字列の配列を取ります。
予定では、モデルバインダーで自動的にvaluesに値が入るはずです。

で、問題のJavaScriptのコード

$(function () {
    $.ajax({
        url: "Home/Post",
        type: "POST",
        datatype: "json",
        data: { values: ["a", "b"] },
    });
});

これを実行すると
f:id:cer1974:20130801215728p:plain:w400
valuesはnullです。
バインドが出来ていませんね

Resuestの中身を見てみると
f:id:cer1974:20130801220520p:plain
なんかパラメータ名に変なの付いてる!
実はこれ大かっこなんです。

jQueryのやつが勝手に大かっこなんて付けやがるんですね
ASP.NET MVC的には大迷惑です。

でも回避方法がありました。

$(function () {
    $.ajax({
        url: "Home/Post",
        type: "POST",
        datatype: "json",
        data: { values: ["a", "b"] },
        traditional: true,
    });
});

ajaxメソッドのオプションに
traditional: true
を追加します。

すると
f:id:cer1974:20130801221534p:plain:w400
今度はちゃんと値が入っています。

まああんまりこんな感じで配列を送りたい時がそうそう無いんですけどね

mongoDBにバイナリデータを登録する

ひさしぶりにmongoDBネタで

mongoDBに画像データを登録したかったのですが
C#から実行する方法があまり情報が無かったのでメモっておきます。

まあ流れ的にはSQLServerとか他のDBと同じみたいですね。
(自分はバイナリデータをDBに登録するとか経験ないんですけどそうみたいです)

めんどいので適当に

var imageStream = new FileStream(@"sample.png", FileMode.Open, FileAccess.Read);

// streamからbyte配列を作成
var imageData = new byte[imageStream.Length];
imageStream.Read( imageData, 0, Convert.ToInt32( imageStream.Length ) );

// byte配列を持つモデル
model.Image = imageData;
// mongoDBに更新する処理
・・・

てな感じでバイナリデータをmongoDBに登録することができます。

TFSビルドプロセスの小ネタ

TFSでビルドプロセスを作成していると引数や変数の値が知りたい時があります。
でも通常の設定だと確認することができません。
じゃーどうするかっていうと
ビルド定義の編集から「プロセス」を選択して
「ログの詳細度」を「Diagnostic」に変更します。

f:id:cer1974:20130619203650p:plain


するとこんな感じでパラメータの値がログに出力されます。

f:id:cer1974:20130619203700p:plain

上図は、初期値ですが変更されたりした場合もすべて表示されます。