Visual Studio のカスタムツール(2010向け)を2012で使えるようにした
以前の記事で書いた、Textile形式のテキストをHTMLにコンバートする拡張がきがついたら動かなくなっていました。2012ですからあたりまえです。なので動くようにしました。
そもそも拡張機能それ自体のソリューションが2010のものなので、まずはVisual Studio 2012 SDKをダウンロードしてインストール。つぎにソリューションを開くと一方通行のコンバータがでたのでコンバート。これで開けるようになりました。
つぎに、vsixmanifestという拡張子のついたファイルを探します。
<SupportedProducts> <VisualStudio Version="10.0"> <Edition>Ultimate</Edition> <Edition>Premium</Edition> <Edition>Pro</Edition> </VisualStudio> </SupportedProducts>
こんなふうになっています。Version 10.0ってのが2010ですね。なので2012を追加します。
<SupportedProducts> <VisualStudio Version="10.0"> <Edition>Ultimate</Edition> <Edition>Premium</Edition> <Edition>Pro</Edition> <Edition>Express_All</Edition> </VisualStudio> <VisualStudio Version="11.0"> <Edition>Pro</Edition> <Edition>Express_All</Edition> </VisualStudio> </SupportedProducts>
ついでにExpressでも動くようにしました(リファレンスをちゃんと見てないので間違ってるかも。とりあえずProfessionalでは動いた)
直したものは、前回同様 BitBucket で 公開されています。
WCFをIISでテストをする環境を再作成した
あけましてだいぶたちましてございます。
昨年なんか作ろうとしたんですが、どうもWebページをどうやって設計するかとかそういうところがさっぱり分からず、かなり時間を費やして格闘したものの、結果的にAPS.NET MVCの基本からコツコツやらんとこれはダメだぞと言う気分になりました。最初からやり直します(ので前の記事は「その1」のままでいったんオシマイです)
それはそれとして、ひさしぶりにWCFのテストをしようとしたら、なぜか設定が全部消えていて、どうしようもなくなっていたので、メモ。IISは7.5をつかっています。
普通はサイトの下にDefault Web Siteってのがあるんですが、これがなぜか消えているのでまずこれを作ります。名前とか、場所とかは適当で良いと思うのですが、デフォルトを復元しておくことにします。
こんなかんじ。
次にいま追加したDefault Web Siteを右クリック、アプリケーションの追加をします。
- エイリアス:http://localhost/以下のパスを入れます。hogeって書いたらhttp://localhost/hoge/以下にホストされます
- アプリケーションプール:アプリケーションの実行環境などです。ここで.NET Frameworkのバージョンが2.0とかになっていると、4.0でつくったWCFサービスは動きません。
- 物理パス:WCFサービスのプロジェクトのローカルパスです。ここでプロジェクトフォルダをちゃんと指定しておけば、いちいちプロジェクト側で「発行」とかしなくてもサービスがホストされるようになります。
さてこれで終わりのはずなのですが、ここで、なぜか「ファイルが使用中です」などと出てしまいアプリケーションが起動しません。どうもファイルが使用中なのではなく、ポートがバッティングしているせいのようです。ポート80なんてものを使おうとする図々しいアプリなんか起動してたかなあ、とおもって検索したら、ありました。
おい。なんなんでしょうこいつは。さっそく「接続」という設定にあった「上記のポートに代わり、ポート80番を使用」のチェックを外したらうまく動きました。なんだこの設定は。いつのまにこんなところにチェックが入っていたんだ。
無事環境が戻ってきました。よかった。
なんかつくるよ(その1:Web APIを使う)
ASP.NETを使っていたのは5年くらい前で、いまやすっかり様相が変わっております。新しいASP.NETをちょっと勉強しつつ、こちらにメモを書いていこうと思います。
まずさっそくどの技術を使えばいいのかすらわからない。うーん。いろいろさまよったあげくたぶんASP.NETはASP.NET MVCってのになったんだろうと見当を付けて、ASP.NET MVC 4をやることにしました。しかしまあ、ベータ情報がまざって出てきて実際にリリースされたものがそれをそのまま踏襲できるのかどうかがよく分からないなあ。まあいいか。とりあえずはじめてみます。
環境はVS2012+.NET 4.0。ソリューションを作って、ASP.NET MVC 4プロジェクトを作成します。作成後にいきなりいくつかの選択肢が出てきます。今回APIをちゃんと作りたいと思うのでWeb APIに進むことにしました(すでに道を逸れている感じがものすごくしますね!)
プロジェクトが出来ると自動的に出来たファイルが山のように。
ValuesController.csというのがすでに開かれていて、RESTの各HTTPメソッドからここに飛んでくるんだなと言うことが見て取れます。何も触らずF5をおもむろにおしてみると
いきなりなんか起動します。
ひとまずAPIってことですから、UIは別にどうでも良いのでとりあえずモデルを作ってみることにします。Modelsの下にPOCOのファイルを適当に追加します。
今回はコンテスト主催アプリを作ろうと思ったので、まずコンテストそのものを定義するためのクラスを作ってみました。
namespace ContestOrganizerAPI.Models { public class Competition { public long Id { get; set; } public string Name { get; set; } public string Description { get; set; } } }
つぎにValuesController.csをCompetitionsController.csに書き換え、クラスも書き換えます。中身も適当に変更。[FromBody]とかよくわからん属性があるな。
namespace ContestOrganizerAPI.Controllers { public class CompetitionsController : ApiController { // GET api/competitions public IEnumerable<Competition> Get() { return new Competition[] { new Competition(){Id = 1L, Name = "value1",Description = "value2" }}; } // GET api/competitions/5 public Competition Get(long id) { return new Competition() { Id = 5L, Name = "value1", Description = "value2" }; } // POST api/competitions public void Post([FromBody]Competition value) { } // PUT api/competitions/5 public void Put(long id, [FromBody]Competition value) { } // DELETE api/competitions/5 public void Delete(long id) { } } }
さて、これでF5を押して……もトップページが出るだけなのでAPIがどうなったかはわかんないですね。こういうときはF12っていうIEのツールを使うらしい。IEを起動してF12を押すと出てきた。
[ネットワーク]タブで[キャプチャ]ボタンを押すと、ネットワークのやりとりがキャプチャできます。ふむふむ、JSONでデータが取得できていて中身は「[{"Id":1,"Name":"value1","Description":"value2"}」らしい。ちゃんと出来ています。
さて、次はUIか。
NUnit 2.6 で実装された Action 属性について
リリースされていたので目玉のひとつになりそうな Action 属性についてちょっと調べてみました。というか、公式ページの解説を抄訳しただけともいえますが。
すごく簡単なしくみです。 Action 属性を使うことで、これまで SetUp/TearDown/FixtureSetUp/ FixtureTearDown みたいな属性をつけた上でメソッドとして書いてきた準備とか後始末を、Action クラスにカプセル化して、属性ベースでテストに適用できるようになります。
テストの眼目は当然テストコードにあって、その準備や後始末については、テストの本質ではありません。ただ、どういったコンテキストかでのテストであるかということを示す情報として意味はありますから、完全に隠蔽するのも良くありません。そこで、準備や後始末自体が抽象化され、名前がついた状態で再利用できることはたしかに意味がありそうです。
前述の通り、これまでは SetUp など属性を特定のメソッドにつけて、その中で実際の準備をおこなっていました。メソッドは複数書けますからメソッド名という形でコンテキストに名前をつけて、テストの環境を示すことは確かに出来ます。また、そのメソッドから Global な共通処理メソッドを呼べば複数のテストで共有することも出来るには出来ます。ただ、どうやっても初期化と後片付けのコードが(意味としてセットになっていたとしても)分離してしまいます。よくやるのはSetupでトランザクションを開始して、TearDownでロールバックする、みたいなのでしょうか。これらはどのテストであってもセットで取り回したいのですが、SetUp/TearDown 方式ではセットで書くことを強制できません。そんなわけで、本来テストしたいコード以外の夾雑物が多くなればテストは読みにくくなってしまいますし、テストを書くために気をつけないと行けないことが増えるのもテストを書くことのハードルを上げることになります。
そんなわけで、準備と後片付けがカプセル化される仕組みは欲しかった機能です。「あるテストを走らせるための環境を宣言的に示す」ために、Action属性をテストに付与するという実装は確かにスマートに感じます。
サンプル
マニュアルには次のような例が載っていました。
[TestFixture, ResetServiceLocator] public class MyTests { [Test, CreateTestDatabase] public void Test1() { /* ... */ } [Test, CreateTestDatabase, AsAdministratorPrincipal] public void Test2() { /* ... */ } [Test, CreateTestDatabase, AsNamedPrincipal("charlie.poole")] public void Test3() { /* ... */ } [Test, AsGuestPrincipal] public void Test4() { /* ... */ } }
これはわかりやすい。このテストフィクスチャはResetServiceLocatorを行ってから、開始されます。
テスト1の前は、CreateTestDatabase が前提となるテストです。中野実装はともかく、ようするにデータベースが設定され居なければテストの意味がないことを宣言している。テスト2には AsAdministratorPrincipal とありますから管理者権限で実行されるというのもわかりますね。テスト3も4も前提条件が書かれていることが一目でわかります。
作り方は ITestAction を継承したアクションクラスを使います。
こんなの
public interface ITestAction { void BeforeTest(TestDetails details); void AfterTest(TestDetails details); ActionTargets Targets { get; } }
ActionTargets は、そのアクションがテストクラスに対するものなのか、テストメソッドに対するモノなのかを示すようですね。 ActionTargets.Suite が TestFixtureSetUp/TestFixtureTearDown で、ActionTargets.Test が SetUp/TearDown に対応している感じですね。ActionTargets.Default はどちらにも付与できます。挙動の違いは自分で実装せよってことですね。
で、呼ばれたときの状況を携えて TestDetails クラスが BeforeTest/AfterTest にやってくるので、準備メソッド、後片付けメソッドでそれぞれ利用するわけですね。
例としてこんなクラスがサンプルにあります。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Assembly, AllowMultiple = true)] public class ConsoleActionAttribute : Attribute, ITestAction { private string _Message; public ConsoleActionAttribute(string message) { _Message = message; } public void BeforeTest(TestDetails details) { WriteToConsole("Before", details); } public void AfterTest(TestDetails details) { WriteToConsole("After", details); } public ActionTargets Targets { get { return ActionTargets.Test | ActionTargets.Suite; } } private void WriteToConsole(string eventMessage, TestDetails details) { Console.WriteLine("{0} {1}: {2}, from {3}.{4}.", eventMessage, details.IsSuite ? "Suite" : "Case", _Message, details.Fixture != null ? details.Fixture.GetType().Name : "{no fixture}", details.Method != null ? details.Method.Name : "{no method}"); } }
(なんかNUnitのページにあるサンプルそのままだとWriteToConsole メソッドの中にミスがあったのでビルド通るよう修正)
単純なテストをつくってみます。
[TestFixture] public class ActionOnTestTests { [Test] [ConsoleAction("Actionからこんにちは!(Testに付与)")] public void SimpleTest() { Console.WriteLine("テスト本体が実行されました"); } }
で、
Before Case: Actionからこんにちは!(Testに付与), from ActionOnTestTests.SimpleTest. テスト本体が実行されました After Case: Actionからこんにちは!(Testに付与), from ActionOnTestTests.SimpleTest.
こんなふうになります。上記は TestCase に属性をつけた場合です。
次に、同じ属性を TestFixture につけた場合は、少し挙動が違います。
[TestFixture] [ConsoleAction("Actionからこんにちは!(Suiteに付与)")] public class ActionOnSuiteTests { [Test] public void SimpleTest() { Console.WriteLine("テスト本体が実行されました"); } }
結果はこう。
Before Suite: Actionからこんにちは!(Suiteに付与), from ActionOnSuiteTests.{no method}. Before Case: Actionからこんにちは!(Suiteに付与), from ActionOnSuiteTests.SimpleTest. テスト本体が実行されました After Case: Actionからこんにちは!(Suiteに付与), from ActionOnSuiteTests.SimpleTest. After Suite: Actionからこんにちは!(Suiteに付与), from ActionOnSuiteTests.{no method}.
こんなふうに、TestFixture でも TestCase でも呼ばれるようになります。これは Action の
public ActionTargets Targets { get { return ActionTargets.Test | ActionTargets.Suite; } }
こう定義しているためです。Action 属性を各テストケースに付与しなくても毎回呼ばれるわけですね。なので、この場合、 BeforeTest/AfterTest で、TestFixutre から呼ばれた場合と TestCase から呼ばれた場合で処理を変える必要があります。全体を通して1回で良いものと、毎 TestCase ごとに初期化し直さないといけないものを、常にセットで扱うことが出来ます。
なお、これを
public ActionTargets Targets { get { return ActionTargets.Suite; } }
とすると、
Before Suite: SuiteOnlyActionからこんにちは!(Suiteに付与), from OnlySuiteActionOnSuiteTests.{no method}. テスト本体が実行されました After Suite: SuiteOnlyActionからこんにちは!(Suiteに付与), from OnlySuiteActionOnSuiteTests.{no method}.
TestFixture に含まれる全テストケースの開始前と終了後によばれます( TestFixtureSetUp/TestFixtureTearDown の挙動と同じ)
もうひとつ強力そうなのが、 Assemby に対して Action を定義できます。
using System; using NUnit.Framework; [assembly: ActionSampleProject.ConsoleAction("Actionからこんにちは!(Assemblyに付与)")] namespace ActionSampleProject { [TestFixture] public class AssemblyActionTest{ [Test] public void SimpleTest() { Console.WriteLine("テストが実行されました"); } } }
これがあると同じアセンブリのすべての TestCase にこの Action 属性を定義したものとおなじ挙動を示すようです。
Before Suite: Actionからこんにちは!(Assemblyに付与), from {no fixture}.{no method}. Before Case: Actionからこんにちは!(Assemblyに付与), from AssemblyActionTest.SimpleTest. テスト1が実行されました After Case: Actionからこんにちは!(Assemblyに付与), from AssemblyActionTest.SimpleTest. After Suite: Actionからこんにちは!(Assemblyに付与), from {no fixture}.{no method}.
静的プロパティなどの初期化によさそうだなとは思いますが、うーん、ちょっと不思議ですね。この場合は、 ActionTargets.Test | ActionTargets.Suite と定義されていてもTestFixutreごとには発火しないようですね。TestDetails#IsSuite が true であって TestDetails#Fixture が null のときは Assembly 単位で呼ばれたと判断できますが……これ将来改善されそうだなあ。
おまけ
ちなみに、Action と従来の SetUp/TearDown とどっちが先に呼ばれるのかなと思って
[TestFixture] [ConsoleAction("Actionからこんにちは!(Suiteに付与)")] public class AllTests { [SetUp] public void SetUp() { Console.WriteLine("SetUpからこんにちは!"); } [TearDown] public void TearDown() { Console.WriteLine("TearDownからこんにちは!"); } [TestFixtureSetUp] public void SetUpFixture() { Console.WriteLine("TestFixtureSetUpからこんにちは!"); } [TestFixtureTearDown] public void TearDownFixture() { Console.WriteLine("TestFixtureTearDownからこんにちは!"); } [Test] [ConsoleAction("Actionからこんにちは!(TestCaseに付与)")] public void SimpleTest() { Console.WriteLine("テスト本体が実行されました"); } }
クラスを作ってみたところ、こんな風になりました(上で試した Assembly 単位の付与もしてある)
Before Suite: Actionからこんにちは!(Assemblyに付与), from {no fixture}.{no method}. TestFixtureSetUpからこんにちは! Before Suite: Actionからこんにちは!(Suiteに付与), from AllTests.{no method}. SetUpからこんにちは! Before Case: Actionからこんにちは!(Assemblyに付与), from AllTests.SimpleTest. Before Case: Actionからこんにちは!(Suiteに付与), from AllTests.SimpleTest. Before Case: Actionからこんにちは!(TestCaseに付与), from AllTests.SimpleTest. テスト本体が実行されました After Case: Actionからこんにちは!(TestCaseに付与), from AllTests.SimpleTest. After Case: Actionからこんにちは!(Suiteに付与), from AllTests.SimpleTest. After Case: Actionからこんにちは!(Assemblyに付与), from AllTests.SimpleTest. TearDownからこんにちは! After Suite: Actionからこんにちは!(Suiteに付与), from AllTests.{no method}. TestFixtureTearDownからこんにちは! After Suite: Actionからこんにちは!(Assemblyに付与), from {no fixture}.{no method}.
ひとつの TestFixture 内では、どうも先に TestFixtureSetUp が呼ばれるようですね。
Ubuntu の Redmine をやっと 0.1.0.0 から 0.1.3.0 へバージョンアップした
重い腰を上げて、いろいろ作業をした。腰が痛くなった。apt-getで得られるライブラリがいろいろ不足しているのでまず Ubuntu 自体を 8.04 LTS から 10.04 LTS にあげて、そのあとでやることにした。Ubuntu自体はUIからさくっとアップグレード。いくつかのファイルがデフォルトに戻ったけど、特に問題なし。
その先はちょうど同じようなことをされているブログを最終的にいくつか参照してなんとかなりました。
まずはいろいろ指定されているバージョンのライブラリを入れた。
$ sudo gem install rubygems-update -v 1.5.3 $ sudo gem install rack -v=1.1.2 $ sudo gem install rake -v=0.8.7 $ sudo gem install rdoc -v=2.4.3
と対象バージョンを入れた。 rubygemsは新しすぎるとダメなので、わざわざ古いのを入れて、新しいのをアンインストール。
$ sudo gem uninstall rubygems-update
Redmine を落としておく
$ sudo wget http://rubyforge.org/frs/download.php/75597/redmine-1.3.0.tar.gz $ sudo tar xzf redmine-1.3.0.tar.gz
設定とプラグインをコピー(Emailは使ってなかったのでemail.ymlはコピーせず。プラグインも一つだけ)
$sudo cp /var/redmine/config/database.yml /var/redmine-1.3.0/config/ $sudo cp /var/redmine/vendor/plugins/redmine_work_time /var/redmine-1.3.0/vendor/plugins/
添付ファイルを移動(過去からの蓄積でフォルダ全体のサイズがえらいことになっててびびったけど、1テラHDDなのでいまのところ問題なし)
$sudo mv /var/redmine/files/* /var/redmine-1.3.0/files || セッションデータを生成する。 >|| $ sudo rake generate_session_store
データベースをマイグレーションする。
$ sudo rake db:migrate RAILS_ENV="production" $ sudo rake db:migrate:upgrade_plugin_migrations RAILS_ENV=production $ sudo rake db:migrate_plugins RAILS_ENV=production
キャッシュとセッションファイルのクリアをする。
$ rake tmp:cache:clear $ rake tmp:sessions:clear
最後に、Redmine をさしかえ。
$ sudo mv redmine redmine-old $ sudo mv redmine1.3.0 redmine
アクセスしてみると
no such file to load -- net/https (LoadError)
とかでるので、
$ sudo apt-get libopenssl-ruby
で、Apacheを再起動もせずに見てみるとPassengerの何かがないといって落ちている。再起動していないせいだろうと思って再起動したら今度はサーバエラーになってしまった。一瞬見えていたのは何だったのか。いずれにせよ困ったので、passengerから最新にすることにした。
$ sudo gem install passenger
で 0.2.6 だった passenger が最新の 0.3.11 になった。しかし、
$ sudo passenger-install-apache2-module
は
で失敗するので、エラー内で言われたとおりに、libcurl4-openssl-devを入れることにする。
$ sudo apt-get libcurl4-openssl-dev
ぶじ成功。しかし、この先どうも、古いバージョンを参照する設定が何処かに残ってしまう。これまたちゃんと指定されたとおりに
/etc/apache2/mods-available/passenger.conf
に
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-3.0.11/ext/apache2/mod_passenger.so PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-3.0.11 PassengerRuby /usr/bin/ruby1.8
と書いてもどっか古いのを見ているようだ。
実は一行目は
/etc/apache2/mods-available/passenger.load
に書くのが正解(というか、そこに古い参照が残っていたので修正した)これでなんとか動いた。
特定の名前を持つ外部キー制約があるかどうか調べる
たんに目で見て確認したいだけならば、システムストアドプロシージャの sp_fkeys を使えばいろんな情報が取れるんですが、スクリプトファイルや別のストアドからその結果を使うにはいったんテーブルに入れてからでないとダメなので(ストアドの戻り値はそのままではテーブルとして使えないため)さすがに使いにくいわけです。
で、こんなの。
SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk ON ref.constraint_name = fk.constraint_name
INFORMATION_SCHEMA というシステムテーブルは SQL 2003で規定されてるらしいので、汎用的な書き方になるというのがポイントですかね(別に外部キーじゃなくてもいいのですが)
でも、あるかないかを名前から調べるだけなのであれば、SQL Server なら OBJECT_ID('Hoge') が手っ取り早いってことに、後から気づきました。どのテーブルに関連してるかと、とか、そこまで調べたければ先述のクエリが良いと思いますが……。
Visual Studio のカスタムツールを作ってみた
いま開発しているソフトで、RedmineのWikiに書かれている情報をテキストファイルにコピペして、ソフトにReadmeとして付けていました。そうなると、RedmineのTextile形式のままで同梱することになって見た目があまり良くありません(決して読めないわけじゃないですが)なので、テキストファイルを作成するまでは仕方ないとして、HTMLに変換させたい。いちいち手作業でやるのは馬鹿馬鹿しいので、Visual Studio のカスタムツールをつくってみました。
といっても、TextileをHTMLにするのは、http://textilenet.codeplex.com/:Title=Textile.NETをほとんどそのまま利用しましたし、カスタムツールとして作成する部分もhttp://kazuktnd.wordpress.com/2011/02/14/implementing-visual-studio-custom-tool-step-by-step/:Title=こちらの記事を参考にして、ほとんどそのまま利用できました。なので、とりたててここに書くことはほとんどありません……。なお、検索すると2008までのやりかたも数多く見つかってそれで少し混乱しました。
あ、カスタムツール実行後のファイル拡張子を替えるために、BaseCodeGeneratorWithSite 継承したクラスでGetDefaultExtensionメソッドをオーバーライドしたくらいでしょうか。
protected override string GetDefaultExtension() { return ".html"; }
こんなの。
それにしてもあまりにも簡単に作れるのでコード生成をT4とかでやるよりもコードで書いちゃった方が楽な場合がけっこうありそうな気がしています。とくにこういったコンバータみたいなタイプのものは、カスタムツール化するのは悪くないなーと思いました。
作ったプロジェクトを Bitbucketに置いておきます。さっきBitbucketのアカウントを作ったばかりなので、使い方間違っておかしなことになってないといいけど。ライセンスは http://creativecommons.org/licenses/by-sa/3.0/:Title=CC BY-SA 3.0です(Textile.NETのライセンスをそのまま継承してあります)
追記
PC環境を新しくしたので、ひさしぶりにこのプロジェクトを開こうとしたらエラーになって開けません。どうも Download Visual Studio 2010 SP1 SDK from Official Microsoft Download Center がないとダメのようです。最初にやったのにすっかり忘れていました。