2009年3月11日水曜日

最近のお気に入り

IMG_0457 IMG_0460 

Bluetoothスピーカー。

キラキラボディーでお気に入り。PC本体のスピーカーだとシャリシャリ感が気になるし、あんまり音も大きくならないもんね。ヘッドホンも聞きやすくていいんだけど、いちいち線をつなげて、耳に当ててとかっていうのが面倒。移動するとき外すのも面倒だし。音楽を聴いたりするわけじゃないから、音質はそんなに気にならないよ。ハンズフリーのプロファイル(?)は削除しとかないとなんか調子悪かった。

Amazon.co.jp: Bluetooth ステレオスピーカー BIT-STB2825S シルバー A2DP・HFP・HSP・ワンセグ音声SCMS-T: 家電・カメラ

IMG_0467 IMG_0468

でろ~ん。スライムじゃないよ~。キーボードを掃除するCyber Cleanっていう製品(スライムだな...)。こないだハンズで売ってるのを見かけて買っちゃった。ベトベトしないし、ちゃんとゴミを吸着してくれるし、なんか楽しいし。

Amazon.co.jp: アイリスオーヤマ サイバークリーン 135gプラスチックボトル入り PCP-135: 家電・カメラ

2009年3月8日日曜日

第2戦(Brass Division)

前回の反省を生かし、今回の試合では前半から強気の攻めで、試合の流れを持ってくることを心がけよう。そんな目標を事前に立てての試合。試合前にはメンバーが全然集まって無くて唖然としたけど、試合始まる直前にはなんとか2セット揃ってホッとした。目標を立てたのに人がいないことにアワアワし過ぎて、すっかり目標のことを忘れちゃってたよ。 前半からガンガンがんばれ作戦っていうこともあって、セットの組み合わせも1stタケ・ミズ・セト、2ndオグ・シゴ・ミチと今期から参加のシンタローと組むことになり、走りまくらなきゃいけない予感。前回の試合で走らなすぎたら見方からのブーイングで心折れそうになったのを思いだし、今回はちゃんと最初から攻め気も見せつつプレーしなきゃね。 珍しくイマイちゃんとユミちゃんがいないおかげで5メンズ1ウーマン。オニャノコいないと、ブラスではブーイングがでちゃうS40なんだけど...。 まぁ、そんなこと心配してもしょうがね。 試合開始するまで全然気がつかなかったけど、開始2分で3点...。おかしい。いや、得点できることはいいんだけど、いくらなんでもおかしい。そんなアッサリ目標通り得点できるなんておかしすぎる。相手ゴーリーをよくよく見てみると、なんかスケーティングがゴーリーのそれとは違う。横移動も前後移動もままならない感じで、ほぼゴールライン上で微動だにしてない。もしや、プレーヤーゴーリー?ちゃんとオスギに確認しとけば良かったけど、試合は始まっちゃってるし、相手ベンチに「ゴーリーの人プレーヤー?」なんて聞けるわけもなく。 そのまま、前半から攻め続けて、と言うか我守らずを全員がモットーとしてるんじゃないのかってくらいの守備放棄。カネコさんが初戦に引き続いての鉄壁っぷりを発揮してくれてたからよかったものの。そんな流れで8-0で前半終了。どうも外野からのブーイングが気になる...。 後半も相変わらずノーガード。流石に守りきれず目の前でリバウンドを叩かれて失点。自分の目の前っていうのが責任感じる。守ってたのに守ってなかったんじゃないのかっていう失点の仕方だったのが余計にたちが悪い。それでも、攻めに攻めて得点を重ねるんだけど、終了間際にまたしても自分がリンクに乗ってるときに失点...。なんかもう、プレーオフの時からそうだけど、こうも終了間際の失点が続くと切なくなるね。 試合終了後、オスギに確認したら、急遽ゴーリーがこれなくなったそうで、プレーヤーがゴーリーをしてたんだそうな。単純に得点しまくってたのはそんな理由からですよ。17-2と勝ったはいいけど全然勝った気もせず。失点には常に絡む自分を励ましたい。 今回の試合では自陣ベンチからのブーイングがなかったものの、外野からのブーイングが炸裂。日曜に戸塚に行っても「昨日アメージングに鬼が出た」だの「悪魔っているんだね」だの、さんざんな言われよう。これからは演技派を目指そう。必死のプレーでゴール前でこけるとか、シュートをカラぶるとか、見てて一生懸命なのは伝わるけど、コレといって活躍しないっていう...。もう、それしか、自分を守る方法が思いつかない。ぎゃふん。

2009年3月4日水曜日

ASP.NET MVC RC2リリース

when its going to be released - ASP.NET Forums

↑ここで発見。最後の発言が...。まぁ、そうですけど...みたいな。

いろいろ気になるところがあったんでしょうね。安易にRTMを出さず、品質上げるためにRC2リリースの決断に敬意を表します。

ASP.NET MVC Release Candidate 2

とはいえ、RC1との違いが単にインストーラだけなわけ無いですよね。しっかりとリリースノートを確認しましょう。

CodePlexにソース/Futuresも上がってるんですが、その下にDataAnnotationsを使ったModelBinderのサンプルが...。もっと早く出してくれれば、自分でサンプル書かなくても良かったのに...。

このDataAnnotationsModelBinderの実装を確認してみると、その内容がまた超かっこいいです。IDataErrorInfo使わずに、DefaultModelBinderを派生させて、OnPropertyValidatingのオーバーライドメソッド内でValidationAttribute属性の取得・実行とModelStateへのエラー投入を行ってる。入力検証の集約というより、DataAnnotationsはそれだけで検証を完結させておき、モデル自体の検証や、ビジネスルールの検証は切り離して行うという設計。IDataErrorInfoならErrorのオーバーライドでモデル検証も集約できるから、どっちが分かりやすいコード(このサンプルだとモデル検証は別途モデル層に実装しましょうということになる)になるかは、アプリケーション設計者の好みでどうぞ、ってことですかね。

リリースノートに書かれてる変更点(それほど多くない)をさらっと確認してみると、以下のような感じです。

  • .NET Framework 3.5SP1必須です。入れといてね。
  • サーバーインストールモードを用意しました。VS関係のインストールがないのでデブロイ環境へのインストールはこれを使いましょう。 msiexec /i AspNetMVC1-RC2.msi /l*v .\mvc.log MVC_SERVER_INSTALL="YES"
  • GACに入れるよ。
  • AntiForgeryのCookie出力時にパスを指定できるようにしました。なので、デプロイ環境(ルートからとかサブフォルダあるとか)に合わせてパスを調整できます。
  • DefaultModelBinderが出力するエラーメッセージをローカライズできるようにしました。resxファイルを独自に定義して分かりやすいエラーメッセージを登録しておきましょう(InvalidPropertyValueとPropertyValueRequiredの2個だけ)。
  • ValidationSummaryのオーバーロードが増えたよ。メッセージリストをul/liタグで展開する前にspanタグでのタイトル表示できるなり。
  • jQuery1.3.1にしたよ。
  • あとバグフィックス。DropDownListの例外。web.configのauthenticationの値をLogOnに。Site.Masterとかで使ってるheadタグがrunat="server"をちゃんと動くように。checkboxとradiobuttonをModelStateからちゃんと復元。ルートのDefault.aspx(ルーティングでコントローラ+アクション指定してるとルートアクセスでデフォルトのコントローラ+アクションが実行されるけど、これにOutputCacheを指定しても効かない)でOutputCacheがちゃんと効くように。

基本的にRC1のコードならそのまま動くっぽいですね。

追記:2009/03/04 16:50

System.Web.AbstractionsとSystem.Web.Routingの2つのアセンブリがSP1のものを利用するように変わったんですね。なので、System.Web.Mvcだけ(Futuresを利用するならMicrosoft.Web.Mvcも)を配布すればよくなりました。グッジョブ!

2009年3月2日月曜日

ASP.NET MVCの新しいサンプル&チュートリアル

ASP.NET MVC Sample Application at www.ASP.net/MVC

ASP.NET MVCでの新しいサンプル。チュートリアルにそって順に良くなっていく。

これはMVCerはみとくべき。

イテレ-ション1 Entity Frameworkをモデルにして、アドレス帳サンプルを作成。この手順はオーソドックスな最初の作り方。これでMVCでの作成ってこれまでとどう違うのかが分かるはず。

イテレーション2 デザインギャラリーからデザインテンプレートを取得したり、自分でデザインしたものを1で作ったものに適用させて、見た目を綺麗にしましょう。ヘルパーなんかも書いたりしてデザインしやすくしておきましょう。

イテレ-ション3 フォームの入力検証を実装。シンプルな検証方法で、DataAnnotationsなどを使うわけではないです。どこで検証を実施すればいいのか、検証結果がどうViewに反映されるのかをしっかり押さえておきます。

イテレーション4 機能を疎結合にしましょう。モデル層はRepositoryに任せ、ロジック層はServiceに移動させる。それぞれインターフェイスと実装を分けて書きましょう。

イテレーション5 ユニットテスト。これまで書いたコードにたいするテストコードを書きましょう。モックを使うとテストコードを楽にかけるのでお気に入りのモックを使いましょう。ここではMoqを使います。他にもRhino MocksやTypeMockなんかもあるよ。テストの初期化時にインターフェースのインスタンスを生成し、コンストラクタインジェクション。 Service層だけじゃなく、コントローラもテストコードを書きましょう。

イテレーション6 5でテストコードを書いたけど、ここからはテストドリブンな開発にしましょう。Group機能を追加する様子をテストを書くところから始めます。その後でテストをパスするコントローラコードを書き、繰り返して機能を実装していく。一通り出来たら、テストコードもあることだし、リファクタリングしてコードをService層に移動したり、Repository層に移動して、テストをパスしないコードが発生しないようにしましょう。

イテレーション7 最後にアプリケーションをAjax化して、使いやすさも向上させましょう。ページの部分更新をしたりするので、共通部分はコントロール化して、Ajaxでの更新時にPartialViewでページ全体じゃなく、必要な部分だけを返すようなコードにしましょう。

テーブルも2個しかないし、規模としては凄く小さいけど、これがASP.NET MVCの開発スタイルの基本になると思います。しっかり身につけて、テストもはしょったりしないようにしましょう(自分で言っときながら耳が痛い...)。

カスタムViewEngineを試す

ASP.NET MVCに限った話ではないんですが、JavaScriptをページで使う場合、外部ファイル(scriptタグのsrc属性で指定する)にするか、インラインでページに直接書く(scriptタグ内にコードを書く)かどっちかになる。

今時の作り方ならJavaScriptはページの最後で外部ファイルを取り込むのが、パフォーマンス的にもよろし、ということになってますね(ハイパフォーマンスWebサイト)。

ASP.NET MVCでも、もちろん外部JSファイルを使ったアプリケーションを作るわけですが、ここで少し悩みが出てきました。

処理コード(静的)としてのJavaScriptは簡単に外部に出せるから問題にはならないんですが、サーバーサイドで生成したデータを元に処理するコードの場合、そのデータをどうやって外部ファイルで使えばいいでしょうか。分かりにくい説明ですが、例えばサーバー上でデータベースから名前一覧を取得してViewDataに入れておいたとします(こればっかりは動的)。そのViewDataを外部JSファイルではもちろんそのまま参照できません。と、いうのもscriptタグで取り込んだ外部JSファイルとページそのもののリクエスト(MvcHandler)は別物だから、いくらコントローラのアクションでViewDataに値を入れたとしても外部JSファイルを取得したリクエスト(StaticFileHandler)では参照できないというのと、そもそもJSファイルが<%=~%>を解析して処理してくれないから。あたりまえですね~。

じゃぁ、どうすればデータ(サーバーサイドで動的生成)をコード(JavaScriptを静的に取得)に簡単に渡せるんだろう。

ページ専用のJavaScriptコードはどうしても、アクションで生成されるデータを使った動的コードにしたくなってしまう。でも、それだと外部ファイルじゃなくViewにscriptタグを書いてしまうことになって、なんかスッキリしない。

  1. 外部JSファイルを拡張子JSじゃなくASPXで作成し、JavaScript用のコントローラを作成(レスポンスのコンテンツタイプをapplication/x-javascriptに変更)し、動的にJSコードを生成するようにして(ViewファイルにJavaScriptコードを書く)、scriptタグでsrc指定。
  2. 外部JSファイルにはコードだけを書き、Viewに出力されるHTMLにscriptタグを書いておいて適当な変数にデータ(JSON)で入れておいて、外部JSファイルからはこれを参照する。

1の方法をとる場合、凄く分かりにくくなるのがコントローラが違うからViewとJavaScriptそれぞれでデータ取得のロジック(もちろんJavaScript用のコントローラではクライアントで必要なデータのみですが)を書かなきゃいけないからコードが散らばるうえに、VSで開発してるにも関わらずコードハイライトもインテリセンスも効かなくなる。

例えば...

// CharaControllerのViewアクション
public ActionResult Character() {
return View();
}

// JsControllerのJavaScriptアクション
public ActionResult Character() {
ViewData["chara"] = ToJson(new {firstName="ルフィ",lastName="モンキー"});
return View();
}

// ビュー
<body>
<!-- viewの定義 -->
</body>
<script type="text/javascript" src="JsController/Character"></script>

// Js/Character.aspx(JavaScript)
var chara = <% = ViewData["chara"] %>;
// 以降charaを使った処理
これだと2つのコントローラが必要になるし、ViewDataの生成と利用が離れすぎ。

綺麗にコードとデータを分離するなら、2の方法が正解ですよね。その場合Viewページにscriptタグを書く必要がありますが、あくまで処理コードは外部に分離できる。

例えば...

// CharaControllerのViewアクション
public ActionResult Character() {
ViewData["chara"] = ToJson(new {firstName="ルフィ",lastName="モンキー"});
return View();
}

// ビュー
<body>
<!-- viewの定義 -->
</body>
<script type="text/javascript">
var viewData = <% = ViewData["chara"] %>;
</script>
<script type="text/javascript" src="chara.js"></script>

// chara.js
var chara = viewData;
// 以降charaを使った処理

Viewアクション内でJavaScriptで利用するデータを生成しておき、外部スクリプトでのデータ参照はグローバル(この場合ならviewData)を見る。

どうも1の方法に固執しすぎてて、これを解決するためにカスタムのViewEngineを作ればいいんじゃないの?というおかしな路線に走ってしまって...。結局は2の方法にすることでViewEngine作る必要は無かったことに気がついたんだけど、その過程で今ネットで見つかるViewEngineの作り方が少し古いやり方な事に気がついたので、無駄にしないために、ココにメモとして残しておきます。

ASP.NET MVC Tip #25 – Unit Test Your Views without a Web Server Maarten Balliauw {blog} - Creating a custom ViewEngine for the ASP.NET MVC framework SingingEels : Creating a Custom View Engine in ASP.NET MVC Brad Wilson: Partial Rendering View Engines in ASP.NET MVC

ViewEngineを作るといっても、単にViewパスの検索場所を変更するためだけの目的もあれば、テンプレートエンジンを置き換えてしまう目的もあると思います。

パスを変えるだけならIViewEngineの実装はせず、PhilさんのAreasデモソース(Grouping Controllers with ASP.NET MVC)のようにWebFormViewEngineを派生させてViewLocationFormatsとMasterLocationFormatsをセットして、IViewは標準のWebFormViewを使えばいいですね。

テンプレートエンジンを変えてしまいたい場合は、IViewEngineとIViewそれぞれを実装することになりますが、今回は少し楽をしてIViewEngineにはVirtualPathProviderViewEngineを使うことにします。

何を作るかというと、拡張子jsの中に/$Key$/という形でテンプレートを入れとくと、ViewDataCollection内の同名Key値を埋め込むというテンプレート。これならJavaScriptのインテリセンスもコードハイライトも有効。

IViewEngineの実装。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Hosting;

namespace Sample.Libraries
{
public class JavaScriptViewEngine : VirtualPathProviderViewEngine
{
public JavaScriptViewEngine()
{
  MasterLocationFormats = new string[0];

  ViewLocationFormats = new[]{
   "~/ViewScripts/{0}.js",
   "~/ViewScripts/Shared/{0}.js"
  };

  PartialViewLocationFormats = ViewLocationFormats;
}

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
  return CreateView(controllerContext, partialPath, null);
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
  return new JavaScriptView(viewPath);
}
}
}

IViewの実装。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.IO;
using System.Web;
using System.Text.RegularExpressions;

namespace Sample.Libraries
{
public class JavaScriptView : IView
{
private string _templatePath;

public JavaScriptView(string templatePath)
{
  _templatePath = templatePath;
}

public void Render(ViewContext viewContext, System.IO.TextWriter writer)
{
  var appPath = viewContext.HttpContext.Request.PhysicalApplicationPath;
  var filePath = VirtualPathUtility.ToAbsolute(_templatePath).Substring(1).Replace("/", "\\");
  var fullPath = Path.Combine(appPath, filePath);

  if (!File.Exists(fullPath))
    throw new InvalidOperationException("not exits javascript template file.");

  var template = File.ReadAllText(fullPath);

  writer.Write(Parse(template, viewContext.ViewData));
}

public string Parse(string contents, ViewDataDictionary viewData)
{
  return Regex.Replace(contents, @"\$\/(.+)\/\$", m => GetMatch(m, viewData));
}

protected virtual string GetMatch(Match m, ViewDataDictionary viewData)
{
  if (m.Success)
  {
    string key = m.Result("$1");
    if (viewData.ContainsKey(key))
      return viewData[key].ToString();
  }
  return String.Empty;
}
}
}

置換部分の処理はまるっきりStephenさんのコードです...。

拡張子jsのファイルはViewScriptsフォルダに入れておくようにしたものです。コントローラ名もフォマットに含めようと思ったんですが、そこはルーティングの登録を以下のようにしておくことでとりあえず必要無いな、と。でも、ViewScriptsフォルダ内はコントローラ名フォルダ/スクリプト名.jsでファイルを入れておきます。

      routes.MapRoute(
    "ViewScripts",
    "ViewScripts/{*path}",
    new { controller = "ViewScripts", action = "Index" }
  );

ViewEngineの登録も忘れずに。

ViewEngines.Engines.Add(new JavaScriptViewEngine());

あとはViewScriptsControllerを書くだけ。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

namespace Sample.Controllers
{
public class ViewScriptsController : Controller
{
//
// GET: /ViewScripts/

public ActionResult Index(string path)
{
  ViewData["test"] = "{data:'sample'}";

  return View(path);
}

}
}

まずは動くコードを、という簡単なサンプルです。

テスト用にViewScripts/Test.jsを作成。

// test.js
var viewData = /$Test$/;
alert(viewData);

あとは、通常のViewでこれをインクルード。

<script type="text/javascript" src="/ViewScripts/Test.js"></script>

これで一応動くものが出来たわけですが、ここで2の方法でいいじゃん、と思い直してコードを破棄...。しかもこの方法だとViewScriptsControllerでいろんなViewDataを入れるためのコードが必要になって、面倒なことに。もっと早い段階で気がつけば良かったけど、カスタムViewEngineを書いてみる勉強になったから良しとします。

ちなみにカスタムViewEngineで最高のサンプルは今書いたこんな中途半端なコードじゃなくてStringTemplate Template Engineじゃないかと思われます。作ってみたい方は是非そちらを参照してみてください。

string-template-view-engine-mvc - Google Code

2009年3月1日日曜日

Crystalized Intelligence

結晶性知性。 【コラム】IT資本論 (26) 8つのパラドクス - 学習パラドクス(4) 持続学習 | 経営 | マイコミジャーナル これを読んでいて、流動性知性はPCやネットで補完できる(外部記憶や計算能力)が、結晶性知性は「コンピュータによって模倣できない」と書かれてる。 専門知や経験知からしか発達していかないインテリジェンスだから、それもそうだろうとは思うけど(画期的なAIっていうドラえもん級の夢のソフトウェアが出れば別)、外部知性というかたちでPCとネットで有効活用出来るとは思う。 Wikiとかナレッジベースとかっていう形での実装ではなくて、もっとリアルタイムに近い形で。 久しぶりにネガティブレガシーと出会いました。 工業経済至上主義を未だ貫き(それはいい)、統計を完全否定し、ネットを不安視(あえて無視)することで現時点での自分の正当性を閉じた世界で証明しようとするその姿勢が面白かった。

2009年2月28日土曜日

Base64でエンコード

Url encoded slash in URL - Stack Overflow

この投稿から始まって、以下のエントリにたどり着く。

Allowing special characters (forward slash, hash, asterisk etc) in ASP.Net MVC URL parameters

まさに、同じ問題に直面した内容。

Double/incomplete Parameter Url Encoding - Stack Overflow

↑ここへのリンクもあったから覗いてみたら、Uri.EscapeDataString メソッド (System)を使ったエンコーディングがあるということを初めて知る。なにこれ~、と思って試したけど、Server.UrlEncodeと同じだった。ガッカリ。いや、違う方が驚きか。

結局、Philさんの投稿のように"/"か他の文字に置換して区切ってしまうか、Base64エンコードで渡すのがいいのかな。Base64といえばFuturesに入ってるLinqBinaryModelBinderが使えるんだもんね。

というわけで試してみた。

登録するルートは以下の通り。

routes.MapRoute(null,
"Proxy/{*base64url}", 
new { controller = "Images", action = "Proxy" } 
); 

Controllerはシンプルに以下。LinqBinaryModelBinderを使うので、参照設定にFuturesのアセンブリを含めるのを忘れずに(単純に文字列で渡しておいて、Binary型にしなくてもConvert.FromBase64Stringでも可)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Net;
using System.Text;
using System.Data.Linq;

namespace Mvc.RC.Controllers
{
 public class ImagesController : Controller
 {
   public ActionResult Proxy(Binary base64url)
   {
     var web = new WebClient();
     var url = Encoding.UTF8.GetString(base64url.ToArray());

     var bytes = web.DownloadData(url);
     return File(bytes, web.ResponseHeaders["Content-Type"]);
   }
 }
}

Viewでリンクを作る時にBase64エンコードしたパラメータを渡す。
  <% = Html.ActionLink("いぬ", "Proxy", "Images", new {
 base64url = Convert.ToBase64String(Encoding.UTF8.GetBytes(
         @"http://farm1.static.flickr.com/131/353753310_1ed04f694c_m.jpg"
         ))
 }, null)%>

これが出力されると以下のHTML。

<a href="/Proxy/aHR0cDovL2Zhcm0xLnN0YXRpYy5mbGlja3IuY29tLzEzMS8zNTM3NTMzMTBfMWVkMDRmNjk0Y19tLmpwZw==">いぬ</a>

リンクをクリックする。

base64

ちゃんと犬が表示されました。一応、ブレークポイントセットして変数の中身を確認。

base64_2

ちょと見にくいけど、引数のbase64UrlにはBase64エンコードされたBinary型の値が入ってて、変数urlには元のURLがデコードされてる。

問題はぱっと見どこのURLを参照してるのかを判断出来ないところ。フレンドリURLとは言い難し。

ところで、Windows Live Writerから投稿すると、毎回無駄な改行が入るのはなんでなんだろう。

dotnetConf2015 Japan

https://github.com/takepara/MvcVpl ↑こちらにいろいろ置いときました。 参加してくださった方々の温かい対応に感謝感謝です。