さとやn Blog

試合はRuby,勝負はC#!

プログラミング

UNIXという考え方―その設計思想と哲学

以前から読もう読もうと思って読めてなかった。


タイトルUNIXという考え方―その設計思想と哲学
著者/監督/ArtistMike Gancarz
価格, 出版¥ 1,680 オーム社
評価評価なし
2012-07-27

【Amazon.co.jp】

   UNIX系のOSは世界で広く使われている。UNIX、Linux、FreeBSD、Solarisなど、商用、非商用を問わず最も普及したOSのひとつであろう。そしてこのOSは30年にわたって使用され続けているものでもある。なぜこれほど長い間使われてきたのか? その秘密はUNIXに込められた数々の哲学や思想が握っている。    そもそもUNIXはMulticsという巨大なOSの開発から生まれたものだ。あまりに巨大なMulticsはその複雑さゆえに開発は遅々として進まず、その反省からケン・トンプソンが作ったのがUNIXの初めとされる。その後デニス・リッチーら多数の開発者が携わり、UNIXは発展した。本書はこのUNIXに込められた「思想と哲学」を抽出し、数々のエピソードとともにUNIXの特徴を浮き彫りにしていく。    たとえば本書で述べられているUNIXの発想のひとつとして「過度の対話式インタフェースを避ける」というものがある。UNIXのシステムは初心者には「不親切」なつくり、つまり親切な対話式のインタフェースはほとんどなく、ユーザーがコマンドを実行しようとするときはオプションをつける形をとっている。この形式はオプションをいちいち覚えねばならず、初心者に決してやさしくない。しかしこれはプログラムを小さく単純なものにし、他のプログラムとの結合性を高くする。そして結果としてUNIXのスケーラビリティと移植性の高さを支えることになっているのだ。このような形式で本書では9つの定理と10の小定理を掲げ、UNIXが何を重視し、何を犠牲にしてきたのかを明快に解説している。    最終章にはMS-DOSなどほかのOSの思想も紹介されている。UNIXの思想が他のOSとどう違うかをはっきり知ることになるだろう。UNIXの本質を理解するうえで、UNIX信者もUNIX初心者にとっても有用な1冊だ。(斎藤牧人)

【内容説明】

* Deals with powerful concepts in a simple way * Highlights important characteristics of Operating systems and other abstract entities in a new way * Explores the tenets of the UNIX operating system philosophyUnlike so many books that focus on how to use UNIX, The UNIX Philosophy concentrates on answering the questions: `Why use UNIX in the first place?'. Readers will discover the rationale and reasons for such concepts as file system organization, user interface and other system characteristics. In an informative, non-technical fashion, The UNIX Philosophy explores the general principles for applying the UNIX philosophy to software development. This book describes complex software design principles and addresses the importance of small programs, code and data portability, early prototyping, and open user interfaces. The UNIX Philosophy is a book to be read before tackling the highly technical texts on UNIX internals and programming. Written for both the computer layperson and the experienced programmer, this book explores the tenets of the UNIX operating system in detail, dealing with powerful concepts in a comprehensive, straightforward manner.
--このテキストは、






ペーパーバック
版に関連付けられています。

今更ですが Memcachedを使ってみる

ずっと業務系ばかりだったので、今更ですが Memcachedを使ってみることに。

とりあえずhttp://memcached.org/からソースを落としてインストール。

wget http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz
tar xzvf memcached-1.4.5.tar.gz
cd memcached-1.4.5
./configure 
./configureすると
onfigure: error: libevent is required.  You can get it from http://www.monkey.org/~provos/libevent/
とエラーになるので、指示通り http://www.monkey.org/~provos/libevent/からlibeventなるもののソースを落としてインストールしてみる。
wget http://www.monkey.org/~provos/libevent-2.0.12-stable.tar.gz
tar xzvf libevent-2.0.12-stable.tar.gz 
cd libevent-2.0.12-stable
./configure
make
sudo make install

無事にインストールされたようなので気を取り直してmemcacheのインストールにリトライ。

./configure
make
sudo make install

インストールは無事に済んだようなので
memcached
で実行しようとするとまたエラー。

memcached: error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such file or directory

実際には/usr/local/lib/にファイルは存在するのだけど、見つけてくれない。このページを参考に設定を変更。

再度

memcached -vv
で起動。 telnetで接続。
telnet localhost 11211

なんか適当にデータを登録してみる.

set myname 0 0 7  #最初の0は圧縮するかしないか / 次が有効期間(0だと無期限) / 登録するデータの総バイト数
satoyan
STORED
get myname
VALUE myname 0 7
satoyan
END
とりあえず動きました。

次はこれをRubyから扱ってみることにしよう。

Windows Vista or 7 の64bit版で PrintDialogが機能しない

ずーっと以前(.NET Framework2.0が最新だった頃)に作ったプログラムで、印刷ができないので直してくれ、との依頼があり、調査したところ、どうも64ビット版のWindowsの場合のみに発生する現象のよう。
で、スクリーンキャプしたものを印刷する機能で、キャプチャするのにWindowsAPIを使っていたから、その辺かな? でも、.NET Framework2.0からスクリーンキャプチャするAPIが追加されてるみたいだから(今頃知った)そいつに書き換えて試してみよう、ということで、やったのですが、うんともすんとも言わない。
どうも PintDialog.Show で止まってる、というか、何も起こらない(エラーになるわけでもない)。

windows 64bit PrintDialog でぐぐったら、

PrintDialog.UseEXDialog プロパティ

 というものがあり、MSDNのドキュメントをよむと、

AMD64 プロセッサの場合、このクラスは、UseEXDialog プロパティを true に設定しないと動作しない可能性があります。
とのこと。

なんだそれ? と思いながら早速該当プロパティにTrueをセットして、実行してみたら、あっさりOK。

1時間の試行錯誤が無駄だったようです。
 
MSDNを読んでもこのプロパティだけ説明が一切ないというのが凄い。 

MonoDevelop (C#) で Specを使ってテストを書いてみる

最近Javaを使う仕事をしていたので、何年かぶりにxUnitを使ったテストを書いていたのですが、やっぱり仕様をテストコード上に直接表現できるSpecの方が私は好きです。
で、ちょっと調べたらVisualStudioのアドインでSpecFlowなるものがあることを発見。で、ついでにMonoDevelop版もあることを発見。肝心のJavaの方は、なんかScalaを使ったものはあるようですが、まだまだjUnitが主流のようです。まあ、Javaの世界だからこれは仕方ない。

というわけで今回はMonoDevelopを使ってC#でSpecテストを書いてみた。

MonoDevelopの Tool -> Add-in Manager からSpecFlow Supportをインストール。
27)

次にSpecFlowのサイトから、というかGitHubからdllをダウンロード。
http://specflow.org/home.aspx
※Windows版はmsiインストーラ版が用意されていますが、それ以外の場合はZIPファイルをダウンロード&解凍して使う必要があります。

足し算をするクラスのテストをSpecFlowを使って書いてみる。
まず、NunitLibraryProjectをMySpecという名前で新規作成してみる。というのもSpecFlowは内部でNUnitを使っているので、デフォルトでNunitのライブラリを参照するように設定してくれるNunitLibraryProjectがちょっと楽だから。
23)

次にライブラリの参照でダウンロード&解凍したTechTalk.SpecFlow.dllを追加。
01)

次に、ファイルの新規作成で SpecFlowFeatureを追加。ここではCalculator.featureという名前で作ってみる。
32)

サンプルとしてこんな感じのモノが書かれている。

		Feature: Addition
			In order to avoid silly mistakes
			As a math idiot
			I want to be told the sum of two numbers

		@mytag
		Scenario: Add two numbers
			Given I have entered 50 into the calculator
			And I have entered 70 into the calculator
			When I press add
			Then the result should be 120 on the screen
	
先頭のFeature: の部分はタイトルとフリーテキストが入力可能で、まあ、こんな機能のテストをしますよー的なことを書いとけばいいんだと思います。
で、Scenarioってのがそれぞれのテストケースを書くところのよう。多分意味は
  • Given : 事前の条件的なこと(〜の場合に)
  • When : テスト対象の操作的なこと(〜をしたら)
  • Then : 結果(~になるはず)
てな感じだと思います。
この場合だと、50と70を入力して、addを押したら、結果は120を表示されるはず、てなテストになります。

とりあえずこの状態でテストを実行してみると結果はこんな感じになる。
44)

		Running MySpec.MySpec.MySpec.AdditionFeature.AddTwoNumbers ...
		Given I have entered 50 into the calculator
		-> No matching step definition found for the step. Use the following code to create one:
		    [Binding]
		    public class StepDefinitions
		    {
		        [Given(@"I have entered 50 into the calculator")]
		        public void GivenIHaveEntered50IntoTheCalculator()
		        {
		            ScenarioContext.Current.Pending();
		        }
		    }

		And I have entered 70 into the calculator
		-> No matching step definition found for the step. Use the following code to create one:
		    [Binding]
		    public class StepDefinitions
		    {
		        [Given(@"I have entered 70 into the calculator")]
		        public void GivenIHaveEntered70IntoTheCalculator()
		        {
		            ScenarioContext.Current.Pending();
		        }
		    }

		When I press add
		-> No matching step definition found for the step. Use the following code to create one:
		    [Binding]
		    public class StepDefinitions
		    {
		        [When(@"I press add")]
		        public void WhenIPressAdd()
		        {
		            ScenarioContext.Current.Pending();
		        }
		    }

		Then the result should be 120 on the screen
		-> No matching step definition found for the step. Use the following code to create one:
		    [Binding]
		    public class StepDefinitions
		    {
		        [Then(@"the result should be 120 on the screen")]
		        public void ThenTheResultShouldBe120OnTheScreen()
		        {
		            ScenarioContext.Current.Pending();
		        }
		    }
	
ステップ定義とかいうものが必要になってくるらしい。というわけでステップ定義とやらを追加してみる。

ファイルの新規作成で、SpecFlow Spec Definitionを選択して適当にCalculatorStepという名前で作ってみると、サンプルコード入りのファイルが作成されるので、とりあえずサンプルのメソッドは全て削除。するとこんな感じ。 58)

		using System;

		using TechTalk.SpecFlow;

		namespace MySpec
		{
			[Binding]
			public class CalculatorStep
			{
			}
		}
	

で、ここで上記テストの実行結果にある定義すべきメソッドをコピペして貼り付ける。するとこんな感じになる。

		using System;
		using TechTalk.SpecFlow;

		namespace MySpec
		{
			[Binding]
			public class CalculatorStep
			{
				[Given(@"I have entered 50 into the calculator")]
				public void GivenIHaveEntered50IntoTheCalculator ()
				{
					ScenarioContext.Current.Pending ();
				}

				[Given(@"I have entered 70 into the calculator")]
				public void GivenIHaveEntered70IntoTheCalculator ()
				{
					ScenarioContext.Current.Pending ();
				}

				[When(@"I press add")]
				public void WhenIPressAdd ()
				{
					ScenarioContext.Current.Pending ();
				}

				[Then(@"the result should be 120 on the screen")]
				public void ThenTheResultShouldBe120OnTheScreen ()
				{
					ScenarioContext.Current.Pending ();
				}
			}
		}

	
これでテストを実行するとさっきとは違ったメッセージになる。
		Running MySpec.MySpec.MySpec.AdditionFeature.AddTwoNumbers ...
		Given I have entered 50 into the calculator
		-> pending: CalculatorStep.GivenIHaveEntered50IntoTheCalculator()
		And I have entered 70 into the calculator
		-> skipped because of previous errors
		When I press add
		-> skipped because of previous errors
		Then the result should be 120 on the screen
		-> skipped because of previous errors
	

まだ実際に動くCalculatorクラスなんてものはないけど、このシナリオ通りにテストコードを書いてみる。この場合、まだ物がないので、完全に呼び出す側の立場でメソッド名とかを考えられる、、、。

		using System;
		using NUnit.Framework;
		using TechTalk.SpecFlow;

		namespace MySpec
		{
			[Binding]
			public class CalculatorStep
			{

				Calculator target = new Calculator();

				[Given(@"I have entered 50 into the calculator")]
				public void GivenIHaveEntered50IntoTheCalculator ()
				{
					target.Enter (50);
				}

				[Given(@"I have entered 70 into the calculator")]
				public void GivenIHaveEntered70IntoTheCalculator ()
				{
					target.Enter (70);
				}

				[When(@"I press add")]
				public void WhenIPressAdd ()
				{
					target.PressAdd ();
				}

				[Then(@"the result should be 120 on the screen")]
				public void ThenTheResultShouldBe120OnTheScreen ()
				{
					Assert.AreEqual (120, target.Result);
				}
			}
		}
	
この段階では当たり前だけどテストはおろか、コンパイルも通らない。

じゃあ、実際に動くクラス Calculatorを作る。
Add New Projectで、新規プロジェクトを追加。名前は適当にCalcとしてみる。
01)

で、このプロジェクトにCalculatorクラスを追加。
59)

で、必要なメソッドを、プロパティ等のスケルトンを実装すると、こんな感じ。

		using System;
		namespace Calc
		{
			public class Calculator
			{
				public int Result { get; set; }

				public Calculator ()
				{
				}

				public void Enter (int number)
				{
				}

				public void PressAdd ()
				{ 
				}
			}
		}
	

次に、テストプロジェクトの方で、このクラスを参照できるようにライブラリ参照の設定でプロジェクト参照を設定&using句も追加。
07)
18)

		using System;
		using NUnit.Framework;
		using TechTalk.SpecFlow;
		using Calc;
	
これでテストを実行してみると、結果はちゃんと失敗する。
39)

で、今度はテストがちゃんと通るようにCalculatorクラスのメソッドの中身を実装する。とりあえずこんな感じで実装てみる。

		using System;
		using System.Collections.Generic;

		namespace Calc
		{
			public class Calculator
			{

				List _numbers = new List(2);
				public int Result { get; set; }

				public Calculator ()
				{
				}

				public void Enter (int number)
				{
					_numbers.Add (number);
				}

				public void PressAdd ()
				{ 
					_numbers.ForEach( number => this.Result += number);			
				}
			}
		}
	
これで、再度テストを実行してみる。
48)
ちゃんとグリーンで成功!

でもこの感じだと、Enter(50), Enter(70)とかそれぞれにステップ定義の方にメソッドがあって、じゃあ、80を入力した場合のテストを書く場合は、こんな感じで都度メソッドを追加していくのか?、という疑問が、、、。

		//いちいちパラメータ毎にメソッドが必要?
		[Given(@"I have entered 80 into the calculator")]
		public void GivenIHaveEntered80IntoTheCalculator ()
		{
			target.Enter (80);
		}
	
実はその辺もちゃんと用意されていて、正規表現を使ってパラメータ化できたりします。で、featureとステップ定義をこんな感じで書き換えてみる。
feature:
		Feature: Addition
			In order to avoid silly mistakes
			As a math idiot
			I want to be told the sum of two numbers

		@mytag
		Scenario: Add two numbers
			Given I have entered "50" into the calculator
			And I have entered "70" into the calculator
			When I press add
			Then the result should be "120" on the screen
	
ステップ定義:
		using System;
		using NUnit.Framework;
		using TechTalk.SpecFlow;
		using Calc;

		namespace MySpec
		{
			[Binding]
			public class CalculatorStep
			{

				Calculator target = new Calculator();

				//[Given(@"I have entered 50 into the calculator")]
				[Given(@"I have entered ""(\d+)"" into the calculator")]
				public void GivenIHaveEnteredNumberIntoTheCalculator (int number)
				{
					target.Enter (number);
				}

		//		[Given(@"I have entered 70 into the calculator")]
		//		public void GivenIHaveEntered70IntoTheCalculator ()
		//		{
		//			target.Enter (70);
		//		}

				[When(@"I press add")]
				public void WhenIPressAdd ()
				{
					target.PressAdd ();
				}

				//[Then(@"the result should be 120 on the screen")]
				[Then(@"the result should be ""(\d+)"" on the screen")]
				public void ThenTheResultShouldBeOnTheScreen (int result)
				{
					Assert.AreEqual (result, target.Result);
				}
			}
		}
	
featureの方はパラメータ化したい値をダブルクオーテーションで囲みます。
ステップ定義の方は各メソッドのAttributeの中でパラメータ化した部分を正規表現パターンで置き換えます。ここではintが渡されるので数値以外受け付けないようにしてある。

やってみた感じ、やっぱりxUnitよりはSpecの方がいいですね。
また、結果をHTMLで表示したり、ということもできたりするようです。詳しくは下記のサイトとかガイドをご参照ください。
http://specflow.org/home.aspx
http://github.com/downloads/techtalk/SpecFlow/SpecFlow%20Guide.pdf

Javaで複数パターンの日付文字列を厳密にパースする。

Javaで日付文字列をパースする処理が必要になりました。 org.apache.commons.lang.time.DateUtilsを使えば複数のパターンを配列で指定できるそうなので、それを利用してやってみる。
ていうか、ロケールに合わせて yyyy/MM/dd とか yyyy.MM.dd とか yyyy-MM-dd とか、一般的に使用されそうなやつはデフォルトで対応しろよ、とか思うのですが、そこはJavaなのでハナから期待はしていません。

で、下記のようなコードを書いてみました。 区切り文字が /(スラッシュ), .(ドット)、-(ハイフン)という、かなり普通に使用されるであろうパターンです。

	import java.text.ParseException;
	import java.util.Date;
	import org.apache.commons.lang.time.DateUtils;

	public class Main {
	    public static void main(String[] args) {

	        String[] patterns = new String[] {"yyyy-MM-dd", "yyyy.MM.dd", "yyyy/MM/dd"};

	        String[] dateStringList = new String[] {"1973-05-30", "1973.5.30", "1973/5/30", "1972/16/60"};

	        for (String dateString : dateStringList) {
	            try {
	                Date d = DateUtils.parseDate(dateString, patterns);
	                System.out.printf("String=%s, Date=%s\n", dateString, d);
	            } catch (ParseException e) {
	                // TODO Auto-generated catch block
	                e.printStackTrace();
	            }
	        }
	    }
	}
	
で、結果は
53)

う〜ん、、、 1972/16/60 というありえない日付を 1973/05/30
とちゃんと解釈してくれています。ていうか、エラーにしろよ、とか思うのですが、まあ、この言語ではもうこういうのが当たり前なんだと、あきらめモード、、、。

でも、DateUtils.parseDateStrictly を使うと厳密に解釈してくれるそう。
で、やってみると結果は

05)

まあ、これが普通だと思うんだけど、、、。

ちなみにC#では、何もしなくても期待通り、、、。
43)

まあ、Rubyでも
55)

C#やRubyがお利口さんなだけです

超簡単にHTMLテーブルでエクセルのオートフィルタみたいなのを実現するライブラリ

仕事でHTMLテーブルでエクセルのオートフィルタのようなものを使いたい、という要望があり、多分探せばそんなスクリプトがあるんじゃないかと思い探したところやっぱりありました。
http://www.javascripttoolbox.com/lib/table/index.php

これは、tableやth要素にclass属性でtable-autofilterとかtable-filterableとしてあげるだけで, ソートやらフィルタリングやらページングやら(いずれもクライアント側で処理)を簡単に装備できてしまうという超スグレモノです。

早速やってみる。
まずソースをダウンロードして、とりあえず table.js という名前で保存してインクルード。JavaScriptに関してはこれだけで、自分でなにか書く必要とかはありません。

	<%= javascript_include_tag "table.js" %>	

次に、フィルタリングしたいテーブルのclass属性に class="table-autofilter" を追加して、フィルタしたい列のthに class="table-filterable"を指定。
こんな感じでしょうか、、。

 

<table class="table-autofilter">
  <thead>
    <th class="table-filterable">文字列</th>	 

※テーブルヘッダは thead, データ行は tbodyに書かないとダメなようです。JavaScriptソース見たらそうなってました、、、。

適当にデータを作って、ページを表示してみる。

22)
35)
 00)
06)

素晴らしい!

MonoDevelop(Mac版)でスクリプトエラー

先日MonoDevelopをアップデートして、プログラムを実行しようとcomand + R を押したら、こんなエラーが出た。
01)


普通にビルドしてターミナルから
mono 実行ファイル 
ってやれば問題なく動くので、単にIDEの問題なのかと思い、設定とか見てたら、どうも プロジェクトのオプションで

Run on external console 

を無効にすればいいらしい。
23)


このオプションがいつからあったのかも知らないし、なぜデフォルトで有効になっているのかはわかりませんが、とにかく外せば今まで通りにcommand + R で即実行することができる。
そもそも外部コンソールってここでは何を指してるんだろう?
IDE内部のものではなくターミナルとかが使えるってことなのかな? よくわかりません。


MonoDevelopでapp.config

.netアプリのアプリケーションの設定ファイルはデフォルトでは
実行ファイル名.config 
という名前になるのですが、通常はVisualStudioではApp.configというファイルを編集して、ビルドするとは出力先にリネームされて、hoge.exe.config みたいな感じにしてくれます。

で、MonoDevelop(マック版)ではどうやるんだろう?

ファイルの新規作成ダイアログにそれっぽいものがあります。

30)

とりあえず名前をApp.config にして適当に設定をかいてビルドしてみましたが、出力先には実行ファイルしか生成されず、、、、。

IDEでファイルのプロパティを見てみると、それっぽい設定を発見。
55)

BuildAction: ApplicationDefinition
Copty to output directory: Always copy | Copy if newer

にして、もう一度ビルドすると、ちゃんと
実行ファイル名.config というファイルが生成されて無事に設定も読み込めました。
 

mono C# で gmail送信

XmlWebService(って今時言わないのかな)を叩いてその結果を見て正常なのかエラーなのかを携帯にメールで通知したくなったので、仕事ではRubyとかJavaばっかりなので、C#で作ってみました。
cronで動かすので、当然monoのC#です。
static void sendMail (string message, string subject)
{
		var smtp = new SmtpClient ("smtp.gmail.com", 587);
		smtp.EnableSsl = true;

		//ユーザー名は@gmail.comは含めない
		smtp.Credentials = new NetworkCredential ("gmailAccount", "password");
		ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
		
		var msg = new MailMessage ();
		msg.Body = message;
		msg.Subject = subject;
		msg.To.Add ("hoge@hoge"); 
		msg.From = new MailAddress ("hoge2@hoge");
		smtp.Send (msg);

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
これがないと、
System.Net.Mail.SmtpException: Message could not be sent. ---> System.IO.IOException: The authentication or decryption has failed. ---> System.InvalidOperationException: SSL authentication error: RemoteCertificateNotAvailable, RemoteCertificateChainErrors
と怒られてしまいます。
本当はエラーとかチェックするんでしょうけど、とりあえずここでは、trueを返して問題なしですよー、としてます。
これについては、
http://www.atmarkit.co.jp/fdotnet/dotnettips/867sslavoidverify/sslavoidverify.html
が分かりやすく解説してあります。

Windows TortoiseSVN、VisualStudioのankhsvnとかで svn+ssh

先日、SVNリポジトリの場所を変更して、httpやめてsvn+sshにしたのですが、Windowsの場合を考えてなかった、、、。
TortoiseSVNは一応入れてあるし、VisualStudio使う場合はSVN統合アプリみたいなやつでankhsvnというのも使っているのですが、これらで svn+ssh はどうやるんだろう?
いろいろ調べたら、puttyでエージェント立てて、環境変数セット云々、、、。

なんか結構面倒なことが書いてあったのですが、要はTortoiseSVNのネットワークの設定に ssh_client という項目があるのでそこにSSHするプログラムを指定すればいいだけのような感じ。

自分の場合は Windowsの場合は、puttyではなくcygwin & poderosaなので、sshプログラムは c:¥cgwin¥bin¥ssh.exe が使えそう。
で、パスワードなしの鍵ファイルを作って、そいつを引数で指定してやれば、いちいちssh agentみたいのを起動する必要もないなぁ、ということでそれでやってみることに。

キャプチャ

puttyの場合は、
PLINK.EXE -i 鍵ファイル名
これで、TortoiseSVN, ankhsvnともに無事にリポジトリにアクセスできました。
鍵ファイルとか用意してなかったりすると、もっと面倒でしょうけど、既にそういう運用している場合はあっさり完了。
いちいち黒い小窓が出てくるのは仕方ない。接続終わったら消えるからいいんだけど、、、。


livedoor プロフィール
QRコード
QRコード
  • ライブドアブログ