2014年9月29日月曜日

行指向ファイル処理フレームワーク

業務でファイルを一行ずつ読み込んで処理するプログラムをコーディングしているのですが、提供された設計書のフローチャートに従って実装を進めていたら、脳みそがぐちゃぐちゃにかき回されるようないらだちを覚えました。
"ファイルを一行ずつ読み込んで処理する"という要件は、あちこちで需要がありそうなので、モデルを考えてみました。 Process、FileFinder、FileHandler、LineHandlerの4つのクラスで構成します。

2014年9月22日月曜日

VB.NETを使い始めて戸惑ったこと3つ

最近VB.NETを使っているのですが、いくつか戸惑ったことがあったので紹介します。

Forループの終了条件
処理を10回繰り返そうとして次のようにFor文を記述します。
For i = 0 To 10
    Console.WriteLine(i)
Next
実行すると、11回実行されます。Toもループ変数の範囲に含むんですね。

charリテラル
VB.NETでは、charリテラルを次のように記述します。
Dim c As Char = "A"c
""c…。

抽象クラスの宣言
VB.NETでは、抽象クラスを次のように記述します。
Public MustInherit Class AbstractClass

End Class
MustInheritは「継承しないと使えないよ」という意味でしょうか。

2014年9月15日月曜日

JScriptからInternetExplorerを操作してYahoo!JAPANを検索する

JScriptからIEを操作し、Yahoo!JAPANで"ほどよくしっかり"を検索させてみました。











ちゃんと、このブログが先頭に表示されました。
JScriptからIEを操作できるということは、標準入力と標準出力を使ってIEを操作できるということで、諸々の自動化が捗るかもしれないということです。興味深い。

以下、コードです。

2014年9月8日月曜日

.NETで改行を含む文字列を組み立てるならStringWriterがおすすめ

プログラムの実行時に文字列を組み立てるなら、StringBuilderクラスを使うのが定石です。しかし、改行で区切った文字列を組み立てようとすると、どうにも見苦しくなってしまいます。
var sb = new StringBuilder()
.Append("select *").Append(Environment.NewLine)
.Append("  from Table1").Append(Environment.NewLine)
.Append(" where Column1 = @Value1").Append(Environment.NewLine);


そんなときはStringWriterクラスを使うのがお勧めです。
var sw = new StringWriter();
sw.WriteLine("select *");
sw.WriteLine("  from Table1");
sw.WriteLine(" where Column1 = @Value1");

コードがスッキリ見やすくなりました。

参照
StringWriterクラス
StringBuilderクラス

2014年9月1日月曜日

ADO.NETのData ProviderでRDBへの依存をカプセル化するにはDbProviderFactoryを使う

これまでに経験したシステム開発では、DBを決めずに開発が始まることはなかったので、必要になる場面はなかなかないと思いますが、DbProviderFactoryを使えばRDBに依存しないコードが書けます。
次に例を示します。
    class Class1
    {
        private readonly DbProviderFactory factory;
        private readonly string connectionString;

        public Class1(DbProviderFactory factory, string connectionString)
        {
            this.factory = factory;
            this.connectionString = connectionString;
        }

        public List<object> Method1()
        {
            using (var conn = factory.CreateConnection())
            using (var command = factory.CreateCommand())
            {
                conn.ConnectionString = connectionString;
                conn.Open();

                command.Connection = conn;
                command.CommandText = "select * from Table1 where ID = @ID";

                var param = factory.CreateParameter();
                param.ParameterName = "ID";
                param.Value = "ID1";
                command.Parameters.Add(param);

                var reader = command.ExecuteReader();

                var result = new List<object>();
                while (reader.Read())
                {
                    var arr = new object[reader.FieldCount];
                    reader.GetValues(arr);
                    result.Add(arr);
                }

                return result;
            }
        }

実際にはSQLもRDBに合わせてコーディングしないといけないので、DbProviderFactoryだけでは不十分と思います。

2014年8月25日月曜日

ASP.NET開発を始めるにあたって参考にした記事3つ

ASP.NETアプリケーション開発に取り組んで1ヶ月になりました。
開発も終盤にさしかかってきたので、忘れないうちに開発スタート時に参考にした記事を3つ紹介します。

@IT プログラミングASP.NET
ASP.NETについて、基本から解説してあります。(連載なので、正確には1記事ではないかも)
全体を把握するのにおすすめです。特に重要だと感じたキーワードを挙げておきます。
  • ポストバック
  • ビューステート
  • データバインド

ASP.NET Page Life Cycle Overview
ASP.NETプログラミングの特徴のひとつに、イベントドリブン方式で実装することがあります。この特徴を活用するには、イベントの順番を把握しなければなりません。この記事ではそれが紹介されています。
今回のプロジェクトで主に使用したイベントと、用途を紹介します。
  • Page_Init…ページやコントロールのフィールドを初期化する。オブジェクトをセッションから取得するなど。
  • Page_Load…コントロールの初期化をする。ドロップダウンリストの項目をセットするなど。
  • Button_Click…ユーザーの入力値を受け取る。入力値をもとに業務ロジックを呼び出して結果を受け取る。
  • Page_PreRender…業務ロジックの結果をコントロールへ反映する。

ASP.NET Application Life Cycle Overview for IIS 7.0
上で紹介したライフサイクルよりも、大きなサイクルについての記事です。アプリケーションやセッションの初期処理を組み込もうとするときに参考になります。

これまで食わず嫌いでASP.NETには手をつけてこなかったのですが、実際に開発をしてみるとすんなり進められました。今後は、ASP.NET案件にも積極的に取り組んでいこうと思います。

2014年8月18日月曜日

Javaでクラス図を書いてみた

Javaでクラス図(1クラスだけ!)を書いてみたの続きになります。
クラス同士の関連を出力できるようにしました。

 
上の図が出力に使用したプログラムを出力したものです。
ざっくりとしたイメージを把握する程度の要求は満たせるかな、という感じです。

2014年8月11日月曜日

ASP.NETでJavaScriptからポストバックを発生させる方法

ASP.NETアプリケーションをコーディングしていて、JavaScriptからポストバックしたいというケースがあります。例えば、検索結果の一覧をテーブルで表示しており、任意のセルをクリックした時に詳細を表示する画面へ遷移するケースなどです。

こういう状況に対応できるのが、次のクラスとインターフェイスです。
まず、JavaScriptのイベントハンドラに対して、ClientScriptManager.GetPostBackEventReferenceで取得した文字列を呼び出すように実装します。このとき、引数として渡した文字列が、次で実装するポストバックイベントハンドラの引数になります。ClientScriptManagerオブジェクトは、Page.ClientScriptから取得できます。
class MyPage : Page {

     protected void Page_PreRender(object sender, EventArgs e) {
         var args = "xxx"; // 例えば、詳細情報を表示するためのプライマリキー
         var js = ClientScript.GetPostBackEventReference(this, args);
         Table1.Rows[0].Cells[0].Attributes.Add("onclick", js);
     }

}

次に、ポストバックを受けるクラスが、IPostBackEventHandler.RaisePostBackEventを実装します。
// 上のクラスへIPostBackEventHandlerを実装
class MyPage : Page, IPostBackEventHandler { 
    ...
    public void RaisePostBackEvent(string eventArgs) {
        // 例えばセッションへeventArgsをセットして詳細ページへ遷移する
        Session.Add("key", eventArgs);
        var detail = ResolveUrl("~/MyDetailPage.aspx");
        Server.Transfer(detail);
    }
}
以上でJavaScriptからのポストバックが機能します。私が最初に思いついたのは、CSSで非表示にしたボタンをJavaScriptからクリックするという方法でした。しかし、こちらのほうが何倍もスマートだと思います。
ClientScriptManagerには、他にも様々なメソッドがあります。まだまだ便利な機能がありそうです。

参考:
ClientScriptManager
IPostBackEventHandler

2014年8月4日月曜日

ASP.NETアプリケーションの起動時にWeb.configファイルを読み込むには

ASP.NETアプリケーションの起動時に設定ファイルを読み込み、システムの初期設定をしたいというケースがあると重います。

設定ファイルを読み込むための、WebConfigurationManager.OpenWebConfigurationメソッドには、引数として設定ファイルの仮想パスが必要です。仮想パスの取得方法として、ASP.NET Web プロジェクトの仮想パスに手順が記載されていますが、アプリケーションの起動時には適用できません。なぜなら、紹介されている方法ではHttpRequestクラスを使用するのですが、アプリケーション起動時には、HttpRequestオブジェクトが使える状態にないからです。

そこで、代わりに使用するのが、HttpRuntimeクラスです。このクラスの、AppDomainVirtualPathプロパティから、アプリケーションの仮想パスを取得できるので、あとはファイル名を結合するだけです。結合には、VirtualPathUtilityクラスを使うとベターでしょう。
var path = VirtualPathUtility.Combine(HttpRuntime.AppDomainVirtualPath,
                                      "Web.config");
var conf = WebConfigurationManager.OpenWebConfiguration(path);

仮想パスをいい加減に扱うと、サーバーへインストールしたら動かなくなることがあります。HttpRuntimeクラスを使い、環境の変化に強いコードを書きましょう。

2014年7月28日月曜日

Javaでクラス図(1クラスだけ!)を書いてみた

グラフを書くためのライブラリの一つに、Graphvizというものがあります。
調べてみたところ、クラス図を書くこともできるようなので、Javaのリフレクションを使ってクラス図を書いてみました。

まだ単体のクラスを出力できるだけですが、関連を出力できるようになるとなかなかおもしろそうです。

参照
Graphviz
UML Diagrams Using Graphviz DOT

2014年7月21日月曜日

C#でアプリケーション設定ファイルを拡張する方法

.NETには標準で設定ファイルを読み込むためのAPIが公開されています。
そのままでも単純なキー=値形式の定義できます。
カスタマイズするには、主に次の3つのクラスを使用します。
  • ConfigurationSectionクラス
  • ConfigurationElementクラス
  • ConfigurationElementCollectionクラス
ConfigurationSectionクラス
カスタマイズした設定項目のルートになるクラスです。
    class NetworkConfigurationSection : ConfigurationSection
    {
        [ConfigurationProperty("LocalHost")]
        public NetworkConfigElement LocalHost
        {
            get { return base["LocalHost"] as NetworkConfigElement; }
            set { base["LocalHost"] = value; }
        }

        [ConfigurationProperty("Networks")]
        public NetworkConfigElementCollection Networks
        {
            get { return base["Networks"] as NetworkConfigElementCollection; }
        }
    }


ConfigurationElementクラス
設定値をもつXMLエレメントに対応するクラスです。
    class NetworkConfigElement : ConfigurationElement
    {
        [ConfigurationProperty("IPAddress")]
        public string IPAddress
        {
            get { return base["IPAddress"] as string; }
            set { base["IPAddress"] = value; }
        }
    }


ConfigurationElementCollectionクラス
複数のConfigurationElementをまとめるクラスです。
    class NetworkConfigElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new NetworkConfigElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            NetworkConfigElement elm = element as NetworkConfigElement;
            if (null == elm) return null;
            return elm.IPAddress;
        }
    }



2014年7月14日月曜日

ちょっとしたツールを作るのに便利なPythonのcmdモジュールを使うための3ステップ

シンプルなコマンドラインツールが必要なとき、Pythonのcmdモジュールが有力な選択肢になります。ツールを実装するために必要な3ステップを紹介します。
  1. cmd.Cmdクラスを継承したクラスを作る
  2. do_XXXメソッドを定義する
  3. cmdloopメソッドを呼ぶ
cmd.Cmdクラスを継承したクラスを作る
特に言うべきことはありません。普通に継承するだけです。
class HelloCmd(cmd.Cmd):
...

do_XXXメソッドを定義する
メソッド名の"XXX"がコマンドになります。
パラメータを受け取るためのargパラメータがポイントです。
    def do_hello(self, arg):
        print 'hello, ' + str(arg)

cmdloopメソッドを呼ぶ
クラスをインスタンス化し、cmdloopを呼ぶとREPLがスタートします。
終了するにはCtrl-Cで落とします。
>>> HelloCmd().cmdloop()
(Cmd) hello Taro
Hello, Taro
(Cmd) Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python2.7/cmd.py", line 130, in cmdloop
    line = raw_input(self.prompt)
KeyboardInterrupt

以下Tips。必須ではありませんが、知っておくと便利です。

2014年7月7日月曜日

Google GuiceのBest Practicesを訳してみた - Guiceがインスタンス化するクラスのコンストラクタはできるだけ隠蔽する

Guiceがインスタンス化するクラスのコンストラクタはできるだけ隠蔽する
次のシンプルなインターフェイスについて考える。
public interface DataReader {
 
  Data readData(DataSource dataSource);
}

一般的には、次のようなpublicクラスで実装することだろう。
public class DatabaseDataReader implements DataReader {
  
   private final ConnectionManager connectionManager;

   @Inject
   public DatabaseDataReader(
      ConnectionManager connectionManager) {
     this.connectionManager = connectionManager;
   }

   @Override
   public Data readData(DataSource dataSource) {
      // ... read data from the database
      return Data.of(readInData, someMetaData);
   }
}

ちょっと見ただけでは、このコードには何も問題がないようにみえる。不幸なことに、

よく知られたことだが、コンストラクタをpublicにするといいことは何もない。publicコンストラクタは、ソースとともに誤った使用方法を広めてしまう。誤った使用方法には次のようなデメリットがある。
  • リファクタリングが困難になる
  • インターフェースによる抽象化を破壊する
  • ソースとの結びつきを強める
何よりも悪いのは、コンストラクタを直接使用すると、Guiceによるインスタンス化を避けることになる。

正すためには、単純に実装クラスとコンストラクタの両方の可視性を制限すればよい。通常はパッケージプライベートを選択すればよく、次のような効果が得られる。
  • クラスを同じパッケージ内のModuleでバインドする
  • クラスを対象とする単体テストは、直接インスタンス化する
publicと@Injectはエルフとドワーフのようなものだと覚えておくとよい。彼らは協力することもできるが、独立して存在しているのだ。

参照
Keep constructors on Guice-instantiated classes as hidden as possible.

2014年6月30日月曜日

Google GuiceのBest Practicesを訳してみた - Moduleでは条件分岐を避けよ

Moduleでは条件分岐を避けよ
Moduleに変化する部品をもたせることと、異なった環境で異なった動作をするように定義するのは推奨されない。

public class FooModule {
  private final String fooServer;

  public FooModule() {
    this(null);
  }

  public FooModule(@Nullable String fooServer) {
    this.fooServer = fooServer;
  }

  @Override protected void configure() {
    if (fooServer != null) {
      bind(String.class).annotatedWith(named("fooServer")).toInstance(fooServer);
      bind(FooService.class).to(RemoteFooService.class);
    } else {
      bind(FooService.class).to(InMemoryFooService.class);
    }
  }
}
条件分岐自身にはそれほど問題はない。しかし、設定がテストされていない時に問題は現れる。この例では、InMemoryFooServiceは開発環境で使用され、RemoteFooServiceは本番環境で使用される。しかし、この特定の状況をテストしていないと、RemoteFooServiceが統合されたアプリケーションと動作するかは保証できない。

このことからわかるのは、アプリケーションの設定項目は最小限にすべきだということだ。開発環境と本番環境を別々のModuleへ分割すると、本番コードの全てがテストされているのを証明するのはより容易になる。この例では、FooModuleをRemoteFooModuleとInMemoryFooModuleへ分割する。これはまた、製品版のクラスがテストコードに依存しなくなる効果もある。

参照
Avoid conditional logic in modules

2014年6月23日月曜日

Google GuiceのBest Practicesを訳してみた - Providerでの入出力に注意せよ

Providerの入出力に注意せよ
Providerは便利だが、以下の機能を欠いている。
  • Providerはチェック例外を宣言しない。特定のエラーからの復帰しなければいけないコードを書いているのなら、TransactionRolledbackExceptionはcatchできない。ProvisionExceptionにより、一般的な生成エラーから復旧でき、その原因を列挙することもできる。しかし、それらの原因を指定することはできない。
  • Providerはタイムアウトをサポートしない。
  • Providerはリトライ戦略を定義しない。値が有効でないとき、何度もget()を呼ぶと、何度も失敗することになるだろう。
ThrowingProvidersは、ExceptionをthrowするProviderを実装するGuice拡張だ。それはエラーのスコープを定義でき、それにより、リクエストやセッションで一度だけエラーが発生する。

参照
Be careful about I/O in Providers 

2014年6月16日月曜日

Google GuiceのBest Practicesを訳してみた - Moduleは高速で副作用がないほうがよい

Moduleは高速で副作用がないほうがよい
Guiceのモジュールは、設定にXMLファイルではなく、Javaコードを使用する。Javaは親しみやすく、IDEの機能を活用でき、リファクタリングにも対応できる。

しかし、Javaの力はコストももたらす。つまり、Moduleでやりすぎてしまうのだ。GuiceのModuleでは、データベースへ接続し、HTTPサーバーを起動することもできる。ダメだ!Moduleで困難な仕事をすると、問題が発生する。

  • Moduleは起動するが、終了しない ― データベース接続を開いたとき、その接続を閉じるフックはない
  • Moduleはテストされるべき ― 実行時にデータベースへ接続すると、単体テストが難しくなる
  • Moduleはオーバーライドできる ― GuiceのModuleはオーバーライドをサポートしており、製品版とテスト版や軽量版と置換えができる。製品版の機能がモジュール実行の一部として実装されたとき、そのようなオーバーライドは効果がない。
Module自身で実行するよりも、適切な抽象レベルのインターフェイスを定義する。アプリケーションでは、このようなインターフェイスを使用できる。
public interface Service {
  /**
   * Starts the service. This method blocks until the service has completely started.
   */
  void start() throws Exception;

  /**
   * Stops the service. This method blocks until the service has completely shut down.
   */
  void stop();
}
Injectorを生成した後でServiceを開始し、アプリケーションの起動を完了する。また、アプリケーションが停止したときに、リソースを解放するシャットダウンフックを追加する。
  public static void main(String[] args) throws Exception {
    Injector injector = Guice.createInjector(
        new DatabaseModule(),
        new WebserverModule(),
        ...
    );

    Service databaseConnectionPool = injector.getInstance(
        Key.get(Service.class, DatabaseService.class));
    databaseConnectionPool.start();
    addShutdownHook(databaseConnectionPool);

    Service webserver = injector.getInstance(
        Key.get(Service.class, WebserverService.class));
    webserver.start();
    addShutdownHook(webserver);
  }

参照
Modules should be fast and side-effect free

2014年6月9日月曜日

Google GuiceのBest Practicesを訳してみた - @Nullableを使用せよ

@Nullableを使用せよ
NullPointerExceptionをコードから追い出すために、null参照についてよく知らなければならない。次のシンプルなルールに従うことで、成功を収めている。

すべてのパラメータは、特に示されない限り、nullではない。

Guava: Google Core Libraries for JavaJSR-305にはnullをコントロールするためのシンプルなAPIがある。Preconditions.checkNotNullはnull参照を発見したら直ちに終了するために使用でき、@Nullableはパラメータがnullを許可することを示すために使用できる。
import static com.google.common.base.Preconditions.checkNotNull;
import static javax.annotation.Nullable;

public class Person {
    ...

    public Person(String firstName, String lastName, @Nullable Phone phone) {
        this.firstName = checkNotNull(firstName, "firstName");
        this.lastName = checkNotNull(lastName, "lastName");
        this.phone = phone;
    }

Guiceはデフォルトでnullを拒絶する。nullの注入を拒否し、代わりにProvisionExceptionをスローして失敗する。もしnullを許可したいのであれば、フィールドやパラメータへ@Nullableを付けられる。Guiceはedu.umd.cs.findbugs.annotations.Nullableのようなあらゆる@Nullableに対応している。

参照
Use @Nullable

2014年6月6日金曜日

Google GuiceのBest Practicesを訳してみた - 静的状態を避けよ

静的状態を避けよ
静的状態とテスタビリティは敵対している。テストは高速で、副作用がないほうがよい。しかし、定数以外が静的フィールドに保持されると、管理に苦痛をもたらす。テストによってモックが作られた、静的なシングルトンを安全に処分するのは難しい。さらに、他のテストにも悪影響をおよぼす。
requestStaticInjection()は松葉杖である。Guiceは、静的に構成されたアプリケーションをDIに対応したスタイルに簡単に移行できるように、このAPIを追加した。新しく開発されるアプリケーションは、このメソッドを使用するべきではない。
静的状態は悪いが、静的であること自体には問題はない。静的クラスはOKであり(むしろ好まれる)、純粋な関数(ソートや数学)にとっては、静的であることが相応しい。

参照
Avoid static state

関連
Google GuiceのBest Practicesを訳してみた - 可変性を最小化せよ
Google GuiceのBest Practicesを訳してみた - 直接の依存性のみ注入せよ
Google GuiceのBest Practicesを訳してみた - 循環する依存関係を解決する

2014年6月2日月曜日

Google GuiceのBest Practicesを訳してみた - 循環する依存関係を解決する

循環する依存関係を解決する
Store、Boss、Clerkを含むいくつかのクラスがあるアプリケーションについて考える。
public class Store {
        private final Boss boss;
        //...

        @Inject public Store(Boss boss) {
                this.boss = boss;
                //...
        }

        public void incomingCustomer(Customer customer) {...}
        public Customer getNextCustomer() {...}
}

public class Boss {
        private final Clerk Clerk;
        @Inject public Boss(Clerk Clerk) {
                this.Clerk = Clerk;
        }
}

public class Clerk {
        // Nothing interesting here
}

今のところ、依存関係は全く問題ない。Bossを使用してStoreをインスタンス化し、Clerkを使用してBossをインスタンス化する。ところが、販売のためにClerkにCustomerを取得させようとすると、Storeへの参照が必要になる。
public class Store {
        private final Boss boss;
        //...

        @Inject public Store(Boss boss) {
                this.boss = boss;
                //...
        }
        public void incomingCustomer(Customer customer) {...}
        public Customer getNextCustomer() {...}
}

public class Boss {
        private final Clerk clerk;
        @Inject public Boss(Clerk clerk) {
                this.clerk = clerk;
        }
}

public class Clerk {
        private final Store shop;
        @Inject Clerk(Store shop) {
                this.shop = shop;
        }

        void doSale() {
                Customer sucker = shop.getNextCustomer();
                //...
        }
}

これはClerk -> Store -> Boss -> Clerkという循環をもたらす。Clerkをインスタンス化しようとするとStoreがインスタンス化され、StoreはBossのインスタンスが必要であり、BossはClerkのインスタンスが必要なのだ。
この循環を解決するための方法がいくつかある。

循環を断ち切る(推奨)
しばしば循環は不十分な責務の分割を反映している。そのような循環を断ち切るには、分割したクラスへ依存を抽出する。
この例では、来店する顧客を管理する機能をCustomerLineというクラスに抽出でき、ClerkとStoreへ注入できる。
public class Store {
        private final Boss boss;
        private final CustomerLine line;
        //...

        @Inject public Store(Boss boss, CustomerLine line) {
                this.boss = boss; 
                this.line = line;
                //...
        }

        public void incomingCustomer(Customer customer) { line.add(customer); } 
}

public class Clerk {
        private final CustomerLine line;

        @Inject Clerk(CustomerLine line) {
                this.line = line;
        }

        void doSale() {
                Customer sucker = line.getNextCustomer();
                //...
        }
}

StoreとClerkはどちらもCustomerLineへ依存しているが、循環は存在しない(StoreとClerkの両方が同じインスタンスを参照するときも)。このことはまた、テントで特売セールをするとき、店員が自動車を販売できることも意味する。ただ単に他のCustomerLineを注入すれば良い。

Providerを使用して循環を解決する
Providerを注入することで、依存関係へ継ぎ目を追加できる。ClerkはまだStoreに依存しているが、必要になるまでShopを参照することはない。
public class Clerk {
        private final Provider shopProvider;
        @Inject Clerk(Provider shopProvider) {
                this.shopProvider = shopProvider;
        }

        void doSale() {
                Customer sucker = shopProvider.get().getNextCustomer();
                //...
        }
}

StoreがSingletonや他の再利用するスコープを指定しない時、shopProvider.get()は新たなShopをインスタンス化し、Bossをインスタンス化し、Clerkをインスタンス化することに注意が必要だ。

ファクトリメソッドを使用して2つのオブジェクトを結びつける
依存するクラス同士がより密接に結びついているときは、上記のメソッドでは解決できない。View/Presenterパターンを使用していると、このような状況に出くわす。
public class FooPresenter {
        @Inject public FooPresenter(FooView view) {
                //...
        }

        public void doSomething() {
                view.doSomethingCool();
        }
}

public class FooView {
        @Inject public FooView(FooPresenter presenter) {
                //...
        }

        public void userDidSomething() {
                presenter.theyDidSomething();
        }
        //...
}

どちらのオブジェクトも相手のオブジェクトを必要としている。このような状況とうまくやるために、AssistedInjectが使える。
public class FooPresenter {
        privat final FooView view;
        @Inject public FooPresenter(FooView.Factory viewMaker) {
                view = viewMaker.create(this);
        }

        public void doSomething() {
        //...
                view.doSomethingCool();
        }
}

public class FooView {
        @Inject public FooView(@Assisted FooPresenter presenter) {...}

        public void userDidSomething() {
                presenter.theyDidSomething();
        }

        public static interface Factory {
                FooView create(FooPresenter presenter)
        }
}

そのような状況は、Guiceを使用してビジネスモデルを表現しようとするときに出くわす。そのモデルには、異なる関係を反映した循環があるかもしれない。そのような状況にも、AssistedInjectは効果的だ。

参照
Resolving Cyclic Dependencies

関連
Google GuiceのBest Practicesを訳してみた - 可変性を最小化せよ
Google GuiceのBest Practicesを訳してみた - 直接の依存性のみ注入せよ

2014年5月29日木曜日

Google GuiceのBest Practicesを訳してみた - 直接の依存性のみ注入せよ

直接の依存性のみ注入せよ
他のオブジェクトを取得するために注入することを避けよ。例えば、Accountを取得するためにCustomerを注入してはいけない。
public class ShowBudgets {
    private final Account account;

    @Inject
    ShowBudgets(Customer customer) {
        account = customer.getPurchasingAccount();
    }

代わりに、依存するクラスを直接注入せよ。これはテストを簡単にする。テストはテスト自身とCustomerにだけ注意すればよい。@Providesを付けたメソッドを使い、CustomerからAccountをバインドする。
public class CustomerModule extends AbstractModule {
    @Override public void configure() {
        ...
    }

    @Provides
    Account providePurchasingAccount(Customer customer) {
        return customer.getPurchasingAccount();
    }

依存性を直接注入することで、コードはシンプルになる。
public class ShowBudgets {
    private final Account account;

    @Inject
    ShowBudgets(Account account) {
        this.account = account;
    }

2014年5月26日月曜日

Google GuiceのBest Practicesを訳してみた - 可変性を最小化せよ

可変性を最小化せよ
可能であればいつでも、不変なオブジェクトを生成するようにコンストラクターインジェクションを使用すること。不変なオブジェクトはシンプルで、共有でき、再利用できる。このパターンにしたがって注入可能なクラスを定義する。
public class RealPaymentService implements PaymentService {

    private final PaymentQueue paymentQueue;
    private final Notifier notifier;

    @Inject
    RealPaymentService(
        PaymentQueye paymentQueue,
        Notifier notifier) {
        this.paymentQueue = paymentQueue;
        this.notifier = notifier;
    }

    ...

このクラスの全てのフィールドはfinalであり、@Injectが付けられたコンストラクタで初期化されている。Effective Javaでは不変性の他の利点について述べられている。

メソッドインジェクションとフィールドインジェクション
コンストラクターインジェクションにはいくつかの制限がある。

  • 注入されるコンストラクタはオプションにならない。
  • オブジェクトがGuiceによって生成されない限り、使用されない。これは一部のフレームワークとの関係を断つ。
  • サブクラスはsuper()を全ての依存するクラスとともに呼ばなければならない。これはコンストラクターインジェクションを扱いにくくする。親クラスに変更があったときは特に。
Guiceによって生成されないオブジェクトを初期化するなら、メソッドインジェクションがもっとも便利だ。AssistedInjectやMultibinderのような拡張は、バインドされたオブジェクトを初期化するために、メソッドインジェクションを使用する。

フィールドインジェクションは、コンパクトに記述できる。故に、例を示すときやスライドで頻繁に目にする。それはカプセル化もできていないし、テストもしにくい。finalなフィールドへ注入してはいけない。注入された値が全てのスレッドで参照できることを、JVMは保証していない。

2014年5月19日月曜日

Lispでバケツソート

日経ソフトウェアの2014年6月号で、バケツソートが紹介されていました。
興味深かったので、Lispで実装してみました。

ソース
(defun backet-sort (v backet-count)
  "バケツソート"
  (let ((backet (array-to-backet v backet-count)))
    (backet-to-array backet (length v))))

(defun array-to-backet (arr backet-count)
  "配列からバケツを構成する"
  (let ((backet (make-array backet-count :initial-element 0)))
    (dotimes (i (length arr) backet)
      (let ((n (aref arr i)))
        (setf (aref backet n) (1+ (aref backet n)))))))

(defun backet-to-array (backet array-length)
  "バケツからソート済みの配列を構成する"
  (let ((arr (make-array array-length)))
    (loop with arr-index = 0
          for i below (length backet)
          when (< 0 (aref backet i))
          do (loop repeat (aref backet i)
                   do (setf (aref arr arr-index) i)
                      (setq arr-index (1+ arr-index))))
    arr))


(defun main ()
  "動作確認のための関数"
  (let ((l (loop with state = (make-random-state t)
                 repeat 10
                 collect (random 10 state))))
    (print (sort (make-array (length l) :initial-contents l) #'<))
    (print (backet-sort (make-array (length l) :initial-contents l) 10))))

(main)

なんとなく汚い感じがするのは、きっと自分の腕が未熟だからでしょう。

2014年5月12日月曜日

JavaScriptでは"false"はtrueとして扱われる

タイトルだけでは「そんなバカな」と思われるかもしれませんが、要はtruthy/falsyの話です。

JavaScriptでのTruthy/Falsy
仕様は、ECMAScript Language Specification - 9.2 ToBooleanにあります。これによれば、falseとして扱われるのは次の6つです。

  1. undefined
  2. null
  3. false
  4. 0
  5. NaN
  6. "" (空文字列)
Boolean関数を使って確認してみました。


"false"がtrueとして扱われることが確認できました。

問題になるケース
問題になるケースとしては、サーバーサイドの言語からJavaScriptへ、type="hidden"のinputのvalueへfalseをセットする場合が考えられます。サーバーでセットされた値を使用して、次のようなif文を書くと問題が発生します。

// <input id="someElement" type="hidden" value="false" />のとき
if (document.getElementById('someElement').value) {
    // このブロックが実行される
    ...
}

回避するには、明示的に"true"と比較するのがいいでしょう。

// <input id="someElement" type="hidden" value="false" />のとき
if ("true" === document.getElementById('someElement').value) {
    // このブロックは実行されない
    ...
}

JavaScriptの型は独特のゆるさがあるので、ついついチェックがあまくなりがちです。しかし、今自分がどんな型を扱おうとしているのか把握していないと、思わぬところでハマってしまいます。という記事でした。

おまけ
「Boolean型に変換するAPIはないのか」と思って調べてみましたが、次のような結果に。


参考
ECMAScript Language Specification

2014年5月5日月曜日

unittest.mockの簡単な紹介

単体テストに欠かせないツールのひとつに、モックがあります。
Pythonにも3.3からモックモジュールが追加されました。
unittest.mockといいます。今回はこのモジュールの簡単な使い方を紹介します。

モックが必要になる場面として、システム日付の取得があります。
例えば、当日が閏日かどうか判定する関数を考えてみましょう。
なにも考えずに関数を実装すると、次のようになると思います。
def is_leap():
    d = datetime.date.today()
    if 2 == d.month and 29 == d.day:
        print(str(d) + ' is LEAP')
        return True
    else:
        print(str(d) + ' is NOT LEAP')
        return False

このままではOSの時計を設定しないと試験ができないので、
日付をパラメータとして渡すようにします。
def is_leap(d):
    if 2 == d.month and 29 == d.day:
        print(str(d) + ' is LEAP')
        return True
    else:
        print(str(d) + ' is NOT LEAP')
        return False

日付を注入できるようにしたことで、テストしやすくなりました。
例えば、このようにします。
assert True == is_leap(datetime.date(2012, 2, 29))
assert False == is_leap(datetime.date(2012, 3, 1))

unittest.mockを使うと、次のような感じになります。
d = unittest.mock.MagicMock()
d.month = 2
d.day = 29
assert True == is_leap(d)

モックを使わないほうが単純です。こういう場合はdateオブジェクトを使ったほうがいいでしょう。
さて、現実には全てのコードを自由に変更できるとは限りません。
たとえそんな場合でも、unittest.mock.patchを呼ぶことで、対応できる場合があります。
is_leap関数を修正せずに次のようにします。
with unittest.mock.patch('datetime.date') as dt:
    dt.today().month = 2
    dt.today().day = 29
    assert True == is_leap()

datetime.date.today()メソッドが返すオブジェクトをモックオブジェクトと置き換え、
属性を設定することでテストをパスしています。
確かにこの方法でも試験はできますが、日付をパラメータ化するほうがベターだと思います。

他にも、unittest.mockにはメソッドの呼び出しを記録する機能もあります。
詳しいことはオフィシャルのドキュメントを参照してください。

参考:

2014年4月28日月曜日

xyzzyでブックマーク機能

Emacsのブックマーク機能で紹介したEmacsのブックマーク機能が便利だったので、xyzzyでも使いたくなりました。
Lispの勉強も兼ねて、自分で実装してみました。
プログラムは記事の最後に載せます。

機能
実装済みの機能は以下のとおりです。

  • 現在表示しているバッファをブックマークとして登録する
  • ブックマークの一覧を表示する
  • ブックマークの一覧からファイルを開く
  • ブックマークを削除する
使用方法
まず、プログラムを読み込みます。専用のパッケージを定義したので、合わせてuse-packageします。

(require "bookmark")
(use-package "bookmark")

次に、キーバインドの設定をします。今のところ公開している関数は次の2つです。
  1. bookmark-add-current-buffer: 表示しているバッファをブックマークする
  2. bookmark-list-bookmarks: ブックマークの一覧を表示する
(global-set-key '(#\C-c #\a #\b) 'bookmark-add-current-buffer)
(global-set-key '(#\C-c #\l #\b) 'bookmark-list-bookmarks)
bookmark-add-current-bufferを呼ぶと、現在のバッファをブックマークします。その際、ブックマーク名を入力できます。
bookmark-list-bookmarksを呼ぶと、ブックマーク一覧を表示します。一覧からファイルの表示と、ブックマークの削除ができます。ブックマークしたファイルの表示は[f]キーを押下し、ブックマークの削除は[d]キーです。

ブックマーク一覧を実装するときは、xyzzy付属のbuf-menu.lが参考になりました。
よろしければ使ってみてください。

2014年4月21日月曜日

Emacs: 検索/置換チュートリアル


Google+で流れてきたEmacsのチュートリアルを日本語に訳してみました。

Emacs: 検索/置換チュートリアル

このページでは、Emacsの検索/置換機能を紹介します。
大文字と小文字を区別するかどうかについて紹介します。
正規表現にマッチした文字列を大文字や小文字に変換する方法について紹介します。

検索/置換コマンド
最も便利な検索/置換コマンドについて紹介します。
これらは、メニューのEdit > Replaceにあります。
コマンド名 キーバインド 対象 目的
query-replace M-% 有効なリージョン
カーソルから後方
対話的な検索/置換
query-replace-regexp C-M-% 有効なリージョン
カーソルから後方
正規表現による対話的な検索/置換
dired-do-query-replace-regexp diredでQ マークしたファイル 複数ファイルに対する対話的な検索/置換

例:query-replaceを呼び、検索文字列と置換文字列を入力します。

コマンドが確認を求めた時の一般的なコマンドは以下のとおりです。
  • y - 置換を実行する
  • n - スキップする
  • ! - これ以降確認なしで置換する
  • C-g - キャンセル(置換を元に戻すにはundoを呼ぶ)
dired-do-query-replace-regexpについては、Interactively Find/Replace String Patterns on Multiple Filesを参照してください。

一括置換
Emacsにはreplace-stringとreplace-regexpコマンドもあります。
それらはquery-replaceとquery-replace-regexpの対話的でないバージョンです。
一度の実行で確認せずにすべて置換します。
練習段階では、これらはさほど便利ではありません。
対話的なバージョンを使用し、!を入力すれば一括で置換できます。

デフォルトの大小文字区別: 自動
デフォルトでは、検索文字に大文字を含むとき、自動的に大小文字を区別して検索します。そうでなければ、大証文字を区別せずに検索します。
デフォルトでは、置換後の文字列の大小文字検索にマッチした文字列に依存します。
例えば、検索文字列が「here」で置換文字列が「dragon」のときについて考えます。
Emacsは「here」「Here」「HERE」のいずれかを検索します。
そして、「here」を「dragon」で置換し、「Here」を「Dragon」で置換し、「HERE」を「DRAGON」で置換します。

入力したとおりの文字列で置換したいときは、case-replace変数へnilをセットします。set-variableを使用します。

自動大小文字区別のON/OFF
検索と置換の両方で入力した通りの大小文字を使用したいときはtoggle-case-fold-searchを呼ぶか、メニューのOptions > Case-Insensitive Searchを使用します。
キーやエイリアスを割り当てることもできます。(Emacs: How to Define Keys) (Emacs: Defining Alias to Increase Productivity)

正規表現にマッチした文字列の大小文字を強制的に置換する
検索に正規表現を使用しており、大文字や小文字に置換したいとき、「\,(upcase \1)」や「\,(downcase \1)」を使用できます。
例えば、次のようなテキストについて考えます。
<p>once upon a time ...</p>
<p>there is a dragon who lived in ...</p>
<p>princess tana is still witing ...</p> 
すべてのパラグラフを大文字で開始したいとき、<p>に続く一文字をキャプチャする「<p>\{[a-z]\}」のようなパターンを使います。

キャプチャした文字を大文字に置換するために、置換文字列に「<p>\,(upcase \1)」を使用します。「\,」は続く文字列がlisp式であることを示します。「(upcase \1)」はlisp式です。「upcase」はlisp関数で、「\1」は1番目のキャプチャ文字列です。

「\,」を使用したより複雑な置換については、Regex Replace with a Function in Emacs Lispを参照してください。

参考
Emacs: Find/Replace Tutorial

2014年4月14日月曜日

シェルスクリプト初心者の私がまず覚えたこと9つ

今年の4月にシェルスクリプトを書く機会がありました。今までシェルを使った経験は多少あったものの、シェルスクリプトを書くのは初めてでした。シェルスクリプトを書くにあたり、まず抑えたことを紹介します。

  1. 1行目のおまじない
  2. 変数の使い方
  3. 関数の使い方
  4. 制御文
  5. バッククォート
  6. 終了ステータス
  7. 設定ファイル
  8. 文字列操作
  9. 文字列のエスケープ
それぞれについて以下に記載します。

2014年4月7日月曜日

GZIP圧縮に対応したHTTPクライアントを作る

前回の記事で予告したとおり、今回はC#でGZIP圧縮に対応したHTTPクライアントを作ります。
ポイントは3つあります。

  1. GZipStreamを使って入力データを圧縮する
  2. HttpClientHandlerのAutomaticDecompressionプロパティを設定する
  3. HTTPリクエストヘッダのContent-Encodingをgzipに設定する

GZipStreamを使って入力データを圧縮する
圧縮用にMemoryStreamをインスタンス化し、GZipStreamでラップします。
usingをネストしているのはGZipStreamをCloseするタイミングで圧縮が実行されるからです。
byte[] data = null;

using (var memory = new MemoryStream()) {
  using (var gzip = new GZipStream(memory, CompressionMode.Compress)) {
    int input = 0;
    byte[] buffer = new byte[1];

    while (-1 != (input = Console.Read())) {
      buffer[0] = Convert.ToByte(input);
      gzip.Write(buffer, 0, buffer.Length);
    }
  }
  data = memory.ToArray();
}

HttpClientHandlerのAutomaticDecompressionプロパティを設定する
リクエストボディの圧縮は実装する必要がありますが、レスポンスの解凍は.NET Frameworkが対応しています。
var clientHandler = new HttpClientHandler();
clientHandler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

HTTPリクエストヘッダのContent-Encodingをgzipに設定する
コンテンツがgzip圧縮されていることを示すため、Content-Encodingをgzipに設定します。
using(var client = new HttpClient(clientHandler))
using(var content = new ByteArrayContent(data))
{
  content.Headers.ContentEncoding.Add("gzip");
  using(var message = client.PostAsync(url, content).Result)
  {
    Console.WriteLine(message.Content.ReadAsStringAsync().Result);
  }
}

一般的に、HTTPリクエストが圧縮を必要とするほど大きくなることはありません。
全ての画面データを毎回やりとりするような設計であれば、効果があるかもしれません。

プログラム全体を以下に記載します。

2014年3月31日月曜日

FilterでHTTP通信のGZIP圧縮に対応する

HTTP/1.1では仕様に電文の圧縮が盛り込まれています。
今回はサーブレットフィルタでGZIP圧縮に対応してみたいと思います。

必要なクラス
圧縮されたHTTPリクエストの解凍と、HTTPレスポンスの圧縮を実現するには、次の5つのクラスが必要です。

  1. GZIPFilter - Filterを実装します。HTTPヘッダを確認し、圧縮/解凍をするかどうか判断します。
  2. GZIPRequest - HttpServletRequestWrapperを継承します。GZIPServletInputStreamを返します。
  3. GZIPServletInputStream - HTTPリクエストを解凍するInputStreamです。
  4. GZIPResponse - HttpServletResponseWrapperを継承します。GZIPServletOutputStreamを返します。
  5. GZIPServletOutputStream - HTTPレスポンスを圧縮するOutputStreamです。
ほか、リクエストをそのままレスポンスするEchoServletを準備します。

2014年3月24日月曜日

PythonでEvernoteへメールを送る - 添付ファイル追加

PythonでEvernoteへメールを送る」で載せたプログラムへ、ファイル添付機能を追加しました。
合わせてクラス設計を見直しました。

# -*- coding: utf-8 -*-

# ---- CONFIGURATIONS ----
EVERNOTE_MAIL_ADDRESS = 'evernote_mail_address'
FROM_ADDRESS = 'from_address'
SMTP_HOST = 'smtp_host'
SMTP_PORT = 25
# ---- CONFIGURATIONS ----

import datetime
import mimetypes
import optparse
import os.path
import smtplib
import sys

from email import encoders
from email.header import Header
from email.mime.audio import MIMEAudio
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

class EverMailClient(object):
 def __init__(self, smtp):
  assert isinstance(smtp, smtplib.SMTP)
  self._smtp = smtp
  
 def send(self, mail):
  assert isinstance(mail, EverMail)
  msg = mail.as_MIME()
  mail_from = msg['From']
  mail_to = msg['To']
  self._smtp.sendmail(mail_from, mail_to, msg.as_string())


class EverMail(object):
 def __init__(self, title, from_address, to_address, body, attached_files):
  assert isinstance(title, EverMailTitle)
  assert isinstance(body, EverMailBody)
  self._title = title
  self._from_address = from_address
  self._to_address = to_address
  self._body = body
  self._attached_files = attached_files
  
 def as_MIME(self):
  msg = MIMEMultipart()
  msg['Subject'] = Header(str(self._title), 'utf-8')
  msg['From'] = self._from_address
  msg['To'] = self._to_address
  msg.attach(self._body.as_MIME())
  for attached_file in self._attached_files:
   msg.attach(attached_file.as_MIME())
  return msg


class EverMailTitle(object):
 def __init__(self, title, notebook, tags):
  self._title = title if title else str(datetime.datetime.now())
  self._notebook = notebook if notebook else ''
  self._tags = tags if tags else []
  
 def __str__(self):
  result = self._title
  if 0 < len(self._notebook):
   result += ' @' + self._notebook
  for tag in self._tags:
   result += ' #' + tag
  return result


class EverMailBody(object):
 def __init__(self, stdin):
  assert hasattr(stdin, 'read')
  self._stdin = stdin
  
 def as_MIME(self):
  body = sys.stdin.read()
  msg = MIMEText(bytes(body))
  msg.set_charset('utf-8')
  return msg

  
class AttachedFile(object):
 def __init__(self, path):
  self._path = path
  
 def as_MIME(self):
  ctype, encoding = mimetypes.guess_type(path)
  if ctype is None or encoding is not None:
   ctype = 'application/octet-stream'
  maintype, subtype = ctype.split('/', 1)
  msg = None
  with open(self._path, 'rb') as f:
   if maintype == 'text':
    msg = MIMEText(f.read(), _subtype=subtype)
   elif maintype == 'image':
    msg = MIMEImage(f.read(), _subtype=subtype)
   elif maintype == 'audio':
    msg = MIMEAudio(f.read(), _subtype=subtype)
   else:
    msg = MIMEBase(maintype, subtype)
    msg.set_payload(f.read())
    encoders.encode_base64(msg)
  file_name = os.path.basename(self._path) 
  msg.add_header('Content-Disposition', 'attachment', filename=file_name)
  return msg


class EverMailOptionParser(optparse.OptionParser):
 def __init__(self):
  optparse.OptionParser.__init__(self)
  self.add_option('--notebook', '-n', action='store')
  self.add_option('--tag', '-t', action='append')

    
def create_smtp_client():
 smtp = smtplib.SMTP(SMTP_HOST, SMTP_PORT)
 smtp.set_debuglevel(1)
 return smtp
 
 
if __name__ == '__main__':
 option_parser = EverMailOptionParser()
 (options, args) = option_parser.parse_args()
 note_title = args[0] if args and 0 < len(args) else ''
 title = EverMailTitle(note_title,
                       options.notebook,
                       options.tag)
 body = EverMailBody(sys.stdin)
 attached_files = []
 if 2 <= len(args):
  attached_files = [AttachedFile(path) for path in args[1]]
 mail = EverMail(title,
                 FROM_ADDRESS,
                 EVERNOTE_MAIL_ADDRESS,
                 body,
                 attached_files)
 
 smtp = None
 try:
  smtp = create_smtp_client()
  client = EverMailClient(smtp)
  client.send(mail)
 finally:
  if smtp: smtp.close()

複数ファイルを添付するとうまくいかないところが問題です。
また時間のあるときに対応したいと思います。

2014年3月21日金曜日

Emacsのブックマーク機能

Google+で流れてきたEmacsのチュートリアルを日本語に訳してみました。

Emacsのブックマーク機能を使う

このページはEmacsに搭載されたブックマーク機能のチュートリアルです。
Emacsのブックマーク機能は、ブラウザのそれによく似ています。
Emacsのブックマークは、しばしば必要になるファイルを簡単に開けるようにします。

ブックマークを使う
ファイルをブックマークへ追加する
追加したいファイルを開いてください。
続いて、bookmark-setを呼ぶ[C-x r m]と、名前を入力するプロンプトが表示されます。

ブックマークファイルを開く
ブックマークを開くには、bookmark-bmenu-listを呼びます[C-x r l]。
ブックマーク一覧が表示されます。
この一覧でEnterを入力するかクリックすれば、ファイルを開きます。
また、oを入力すれば分割されたウィンドウでファイルを開きます。

ブックマークを保存する
ブックマークを保存するには、bookmark-saveを呼びます(ブックマーク一覧でsを入力する)。
もし保存しなければ、新たに追加されたブックマークは現在のセッションでのみ有効です。
つまり、Emacsを再起動すると表示されなくなってしまいます。

ブックマークを削除または名前を変更する
ブックマーク一覧を開いた状態で、Dでマークすればブックマークを削除します。
また、rを入力すればブックマークの名前を変更します。

ブックマークのコマンドとショートカットを忘れないようにするには
ブックマークコマンドは、メニューのEdit->Bookmarksにあります。
コマンドやショートカットを忘れてしまったときは、メニューを見てください。
メニューはEmacsのコマンドを覚えるための偉大なる道です。

また、ブックマークコマンドはすべてbookmark-で始まります。
本当に覚えなければいけないのは、bookmark-bmenu-listだけです。
いったんブックマーク一覧を開いてしまえば、describe-mode[F1 m]を呼ぶことでコマンドとショートカットの一覧を見ることができます。

起動時にブックマークを表示する
起動時にブックマークを表示するには、次のelispを初期化ファイルへ追加してください。
(setq inhibit-splash-screen t)
(require 'bookmark)
(bookmark-bmenu-list)
(switch-to-buffer "*Bookmark List*")

ブックマークファイルの場所
emacs 24.xでは、ブックマークファイルは~/.emacs.d/bookmarksにあります。emacs 23.xでは、~/.emacs.bmkにあります。
デフォルトの場所は、変数bookmark-default-fileによって制御されます。describe-variable[F1 v]を呼び、見てみてください。
デフォルトの場所は、次のように設定できます。
(setq bookmark-default-file  (concat user-emacs-directory "bookmarks"))
ブックマークファイルの読み込みは、次のようにします。
(bookmark-load bookmark-default-file t)

ファイルを早く開くほかの方法
最近開いたファイルを開くのも便利な機能です。参考:Emacs: Open File Fast: recentf-mode
ブックマークの1つの問題は、ファイルを直接開くキーがないことです。しかし、Emacs Lisp: Hotkeys to Open File Fastで実現できます。

参考
Emacs: Using Bookmark Feature
Emacsのブックマーク機能

2014年3月17日月曜日

PythonでEvernoteへメールを送る

Evernoteへメールを送ることでノートを追加できるのはご存知かと思います。
最近、古いネットブックをUSBブートのLinuxで使用しています。ネットブック上で作成したファイルをEvernoteへアップロードしたいと思うことがあるのですが、ブラウザから操作するのは厳しいと感じています。
Pythonからメールでファイルを送るようにすればいいのではないかと思い、標準入力から読み込んだ内容をEvernoteへ送るプログラムを書きました。

プログラム
# -*- coding: utf-8 -*-
# ever_mail.py

# ---- CONFIGURATIONS ----
EVERNOTE_MAIL_ADDRESS = 'evernote_mail_address'
FROM_ADDRESS = 'from_address'
SMTP_HOST = 'smtp._host'
SMTP_PORT = 25
DEBUG = True
# ---- CONFIGURATIONS ----

import datetime
import email.header
import optparse
import smtplib
import sys

from email.mime.text import MIMEText

class EverMailClient(object):
    def __init__(self, smtp):
        assert isinstance(smtp, smtplib.SMTP)
        self._smtp = smtp

    def send(self, msg):
        assert isinstance(msg, MIMEText)
        mail_from = msg['From']
        mail_to = msg['To']
        self._smtp.sendmail(mail_from, mail_to, msg.as_string())


class EverMailTitle(object):
    def __init__(self, title, notebook, tags):
        self._title = title if title else str(datetime.datetime.now)
        self._notebook = notebook if notebook else ''
        self._tags = tags if tags else []

    def __str__(self):
        result = self._title
        if 0 < len(self._notebook):
            result += ' @' + self._notebook
        for tag in self._tags:
            result += ' #' + tag
        return result


class EverMailOptionParser(optparse.OptionParser):
    def __init__(self):
        optparse.OptionParser.__init__(self)
        self.add_option('--notebook', '-n', action='store')
        self.add_option('--tag', '-t', action='append')


def create_smtp_client():
    smtp = smtplib.SMTP(SMTP_HOST, SMTP_PORT)
    if DEBUG:
        smtp.set_debuglevel(1)
    return smtp

def create_message_from_stdin(title):
    assert isinstance(title, EverMailTitle)
    body = sys.stdin.read()
    msg = MIMEText(bytes(body))
    msg['Subject'] = email.header.Header(str(title), 'utf-8')
    msg['From'] = FROM_ADDRESS
    msg['To'] = EVERNOTE_MAIL_ADDRESS
    msg.set_charset('utf-8')
    return msg


if __name__ == '__main__':
    option_parser = EverMailOptionParser()
    (options, args) = option_parser.parse_args()
    note_title = args[0] if args and 0 < len(args) else ''
    title = EverMailTitle(note_title,
                          options.notebook,
                          options.tag)
    msg = create_message_from_stdin(title)
    smtp = None
    try:
        smtp = create_smtp_client()
        client = EverMailClient(smtp)
        client.send(msg)
    finally:
        if smtp: smtp.close()


使用例
$ cat your_important_file | python ever_mail.py TITLE -nNOTEBOOK -tTAG -tTAG

工夫したところは、optparseモジュールを使ってオプションの解析を実装したところです。
暇があれば、画像の添付なども対応したいと思います。

参考
15.5. optparse — コマンドラインオプション解析器
Eメールを送信するだけでEvernoteに簡単送信!

2014年3月10日月曜日

DIに1年間はまってみて気づいたこと

2013年は、個人的にDIを推進しました。
1年間活動してみた結果、気づいたことをまとめておきます。

DIコンテナは使いませんでした。自作のファクトリクラスでインスタンス化するようにしました。理由は2つあり、1つはDIコンテナを組み込む権限がプロジェクト内で与えられていなかったから、もう1つは自分の担当範囲で使用するだけなので、シンプルな仕組みで十分だったからです。

気づいたこと
クラスを設計するときの考え方が変わりました。新しいクラスを追加するとき、「そのクラスが機能を実現するにはどんなクラスが必要か」ということを考えるようになりました。こうすることで、クラスが果たすべき責務が明確になり、クラス設計が改善されました。

反面、注意すべきことも見えてきました。「依存する側」と「依存される側」という観点でクラスをどんどん分割していきますが、やり過ぎると何もしないクラスができてしまいます。望ましい設計について、「2回測って1回で切る」とどこかで読んだことがあります。自分なりに解釈すると、1回目は機能をどんどん分割していく設計、2回目は無駄なクラスを統合していく設計とするとうまくいくような気がしました。

自作のファクトリにも問題点が見えてきました。小さいアプリケーション(2画面/5人日)だったのでファクトリクラスを1つしか用意しなかったのですが、巨大なクラスになりかけていました。ファクトリを分割し、ファクトリ同士のDIも必要になると思いました。ただ、そこまで複雑になるようであれば、DIコンテナの採用を検討するべきだと思います。

2014年3月3日月曜日

Pythonで単体テストをするには

pythonでunittestをするには」にて、
しかし、複数のテストファイルを作成した後でまとめてテストを実行する手段がないようなので、以下のようなコードを書いてみました。
という記載がありますが、Python 2.6にてまとめてテストを実行する手段が追加されましたので、勝手にフォローさせていただきます。動作確認にはPython 3.3を使用しました。

テストプログラム
テストプログラムのディレクトリ構成とプログラムは以下のとおりです。
test
├test1.py
├test2.py
└testsub
 ├__init__.py
 └testsub1.py

test/test1.py
# -*- coding: utf-8 -*-

import unittest

class TestCase1(unittest.TestCase):
    def test_success(self):
        pass

    def test_fail(self):
        self.fail()

class TestCase2(unittest.TestCase):
    def test_success(self):
        pass

    def test_fail(self):
        self.fail()

suite = unittest.TestSuite()
suite.addTest(TestCase1('test_success'))
suite.addTest(TestCase2('test_success'))


test/test2.py
# -*- coding: utf-8 -*-

import unittest

class TestCase1(unittest.TestCase):
    def test_success(self):
        pass

    def test_fail(self):
        self.fail()



test/testsub/__init__.py
空のファイルです。testsubをモジュールとして認識させるために必要です。

test/testsub/testsub1.py
# -*- coding: utf-8 -*-

import unittest

class TestCase1(unittest.TestCase):
    def test_success(self):
        pass

    def test_fail(self):
        self.fail()



フォルダ配下のテストをすべて実行する
pythonの-mオプションのパラメータとしてunittestを渡します。詳細な出力を得るために、-vオプションも追加します。
~/py/test $ python3 -m unittest -v
test_fail (test1.TestCase1) ... FAIL
test_success (test1.TestCase1) ... ok
test_fail (test1.TestCase2) ... FAIL
test_success (test1.TestCase2) ... ok
test_fail (test2.TestCase1) ... FAIL
test_success (test2.TestCase1) ... ok
test_fail (testsub.testsub1.SubTestCase1) ... FAIL
test_success (testsub.testsub1.SubTestCase1) ... ok

======================================================================
FAIL: test_fail (test1.TestCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/cygdrive/c/home/myamamo/py/test/test1.py", line 10, in test_fail
    self.fail()
AssertionError: None

======================================================================
FAIL: test_fail (test1.TestCase2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/cygdrive/c/home/myamamo/py/test/test1.py", line 17, in test_fail
    self.fail()
AssertionError: None

======================================================================
FAIL: test_fail (test2.TestCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/cygdrive/c/home/myamamo/py/test/test2.py", line 10, in test_fail
    self.fail()
AssertionError: None

======================================================================
FAIL: test_fail (testsub.testsub1.SubTestCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/cygdrive/c/home/myamamo/py/test/testsub/testsub1.py", line 10, in test_fail
    self.fail()
AssertionError: None

----------------------------------------------------------------------
Ran 8 tests in 0.004s

FAILED (failures=4)


モジュールを指定してテストする
上のオプションに加え、モジュール名を追加するとモジュールのテストになります。
~/py/test $ python3 -m unittest -v test1
test_fail (test1.TestCase1) ... FAIL
test_success (test1.TestCase1) ... ok
test_fail (test1.TestCase2) ... FAIL
test_success (test1.TestCase2) ... ok

======================================================================
FAIL: test_fail (test1.TestCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test1.py", line 10, in test_fail
    self.fail()
AssertionError: None

======================================================================
FAIL: test_fail (test1.TestCase2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test1.py", line 17, in test_fail
    self.fail()
AssertionError: None

----------------------------------------------------------------------
Ran 4 tests in 0.002s

FAILED (failures=2)


クラスを指定してテストする
さらにクラス名を追加します。
~/py/test $ python3 -m unittest -v test1.TestCase1
test_fail (test1.TestCase1) ... FAIL
test_success (test1.TestCase1) ... ok

======================================================================
FAIL: test_fail (test1.TestCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test1.py", line 10, in test_fail
    self.fail()
AssertionError: None

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)


テストメソッドを指定してテストする
さらにメソッド名を追加します。
~/py/test $ python3 -m unittest -v test1.TestCase1.test_fail
test_fail (test1.TestCase1) ... FAIL

======================================================================
FAIL: test_fail (test1.TestCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test1.py", line 10, in test_fail
    self.fail()
AssertionError: None

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)


テストスイートを指定してテストする
テストスイートを指定することもできます。要領はクラスやメソッドを指定する時と同様です。
~/py/test $ python3 -m unittest -v test1.suite
test_success (test1.TestCase1) ... ok
test_success (test1.TestCase2) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK



一括して実行できるだけでなく、任意のテストメソッドを選択することもできるので、テンポよく開発が進められると思いました。

2014年2月24日月曜日

lispでiniファイルからalistをconsする

lispでiniファイルからalistをconsするプログラムを書きました。

サンプルのiniファイルはこのようになっています。
[Section1]
Key1=Value1
Key2=Value2

[Section2]
Key1=Value1

[Section3]


プログラム本体は以下のとおりです。
;; inifile.l
(defun read-lines (input)
  (loop as line = (read-line input nil nil)
        while line
        collect line))

(defun sectionp (line)
  (and (stringp line)
       (<= 2 (length line))
       (eq #\[ (char line 0))
       (eq #\] (char line (1- (length line))))))

(defun keyvaluep (line)
  (and (stringp line)
       (<= 3 (length line))
       (find #\= line)
       (< 0 (position #\= line))))

(defun strip-section (line)
  (if (not (sectionp line))
      line
    (subseq line 1 (1- (length line)))))

(defun split-keyvalue (line)
  (if (not (keyvaluep line))
      line
    (list (subseq line 0 (position #\= line))
          (subseq line (1+ (position #\= line))))))

(defun convert-to-alist (alist line)
  (cond ((sectionp line)
         (cons (list (strip-section line)) alist))
        ((keyvaluep line)
         (cons (append (car alist) (list (split-keyvalue line))) (cdr alist)))
        (t alist)))

(defun ini-assoc (section key alist)
  (car (last
        (assoc key
               (cdr (assoc section alist :test 'string-equal))
               :test 'string-equal))))


;; 以下実行部
(setf ini-alist (reduce 'convert-to-alist (read-lines *standard-input*)
                        :initial-value '()))
(format t "~A~%" ini-alist)
(format t "~A ~A = ~A~%" "Section2" "Key1"
        (ini-assoc "Section2" "Key1" ini-alist))
(format t "~A ~A = ~A~%" "SECTION1" "KEY2"
        (ini-assoc "SECTION1" "KEY2" ini-alist))


実行すると、以下のように出力します。
$ cat sample.ini | clisp inifile.l
((Section3) (Section2 (Key1 Value1)) (Section1 (Key1 Value1) (Key2 Value2)))
Section2 Key1 = Value1
SECTION1 KEY2 = Value2


工夫したところは、関数"ini-assoc"でstring-equalを使用し、大文字と小文字を区別し内容にしたところです。
alistで返すことが目標だったので、パフォーマンスはよくありません。
今までの書き方であれば、DictionaryやMapを更新しまくるプログラムを書いてました。reduceに渡す関数を実装するにあたり、「新しいリストを返す」という発想に辿り着くまで時間がかかりました。

2014年2月17日月曜日

IronPythonで.NETモジュールをテストする

MSDN Magazineの記事を読んでいたら、タイトルのような記事を見つけました。
どれどれ、と読んでみるとせっかくIronPythonを使用しているのに、unittestモジュールを使用していないではありませんか。
もしかしたら当時のIronPythonには、まだunittestモジュールが含まれていなかったのかもしれません。
unittestモジュールを使用すれば、もっと簡単にテストが書けると思われるので、試してみます。
unittestモジュールの詳しい説明は割愛します。公式のドキュメントなどを参照してください。

テストコード
unittestモジュールを使用して書きなおしたテストが以下になります。
元の記事と同様、3番目のテストケースはスキップするようにしました。
# -*- coding: shift-jis -*-

import clr
import unittest

clr.AddReferenceToFileAndPath('Class1.dll')
from TwoCardPokerLib import Hand

class HandTest(unittest.TestCase):
    def test_royal_flush_wins_pair_aces(self):
        h1 = Hand('Ac', 'Kc')
        h2 = Hand('Ad', 'As')
        self.assertEquals(1, h1.Compare(h2))

    def test_straight_flush_diamonds_eq_straight_flush_hearts(self):
        h1 = Hand('Td', '9d')
        h2 = Hand('Th', '9h')
        self.assertEquals(0, h1.Compare(h2))

    @unittest.skip('')
    def test_straight_loses_ace_high(self):
        h1 = Hand('Ah', '2c')
        h2 = Hand('As', '9c')
        self.assertEquals(1, h1.Compare(h2))

    def test_flush_9_6_high_loses_flush_9_7(self):
        h1 = Hand('9h', '6h')
        h2 = Hand('9d', '7d')
        self.assertEquals(-1, h1.Compare(h2))

    def test_king_high_loses_ace_high(self):
        h1 = Hand('Kc', 'Jh')
        h2 = Hand('As', '5d')
        self.assertEquals(-1, h1.Compare(h2))


テスト結果
テストの実行結果は以下のようになります。
C:\>ipy.exe -m unittest -v testhand
test_flush_9_6_high_loses_flush_9_7 (testhand.HandTest) ... FAIL
test_king_high_loses_ace_high (testhand.HandTest) ... ok
test_royal_flush_wins_pair_aces (testhand.HandTest) ... ok
test_straight_flush_diamonds_eq_straight_flush_hearts (testhand.HandTest) ... ok
test_straight_loses_ace_high (testhand.HandTest) ... skipped ''

======================================================================
FAIL: test_flush_9_6_high_loses_flush_9_7 (testhand.HandTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\testhand.py", line 29, in test_flush_9_6_high_loses_flush_9_7
    self.assertEquals(-1, h1.Compare(h2))
AssertionError: -1 != 0

----------------------------------------------------------------------
Ran 5 tests in 0.164s

FAILED (failures=1, skipped=1)

元記事との比較
元記事と比較して、テストランナーを実装していない分、工数を減らす効果はあったと思います。
反面、テストケースの追加については、元記事ではテキストファイルに1行追加するだけなのに対して、unittestを使用した場合はメソッドの定義が必要になっています。サードパーティ製のユニットテストフレームワークであるpytestはパラメータ化されたテストにも対応しているようなので、検討する価値があると思いました。

参考

2014年2月10日月曜日

Traceを使用してログを出力するときの設定例(Filter)

ログに要求される機能の1つとして、レベルによるフィルタリングがあります。
Traceを使用したログ出力にも、同様の機能があります。
具体的には、<filter>タグを使用します。

設定例
<configuration>
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <clear />

        <add name="file"
             type="System.Diagnostics.TextWriterTraceListener"
             initializeData="TEXT-WRITER.log">
            <!-- Warning以上を出力 -->
            <filter type="System.Diagnostics.EventTypeFilter"
                    initializeData="Warning" />
        </add>

      </listeners>
    </trace>
  </system.diagnostics>

</configuration>

出力例
TraceTest Warning: 0 : TraceWarning
TraceTest Error: 0 : TraceError

Informationレベルのログが出力されなくなりました。

参考
<filter> Element for <add> for <listeners> for <trace>

関連記事
Traceを使用してログを出力するときの設定例(TraceListener)
Traceを使用してログを出力するときの設定例(TraceOutputOptions)

2014年2月3日月曜日

Traceを使用してログを出力するときの設定例(TraceOutputOptions)

Traceを使用してログを出力するときの設定例(TraceListener)でTraceListenerの設定方法を書きました。
しかし、あれだけでは最低限のメッセージしか表示されません。
この記事では、日時などを表示する方法について書きます。

メッセージ以外を表示するには、<listeners>の<add>のtraceOutputOptions属性を使用します。



<configuration>

  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <clear />

        <!-- テキストファイル -->
        <add name="file"
             type="System.Diagnostics.TextWriterTraceListener"
             initializeData="TEXT-WRITER.log"
             traceOutputOptions="Callstack, DateTime, LogicalOperationStack, ProcessId, ThreadId, Timestamp" />

      </listeners>
    </trace>
  </system.diagnostics>

</configuration>

この属性を設定した時の出力例は、以下のようになります。

TraceTest.exe Information: 0 : TraceInformation
    ProcessId=5700
    LogicalOperationStack=
    ThreadId=1
    DateTime=2014-02-02T12:47:28.3398027Z
    Timestamp=6032477769
    Callstack=   場所 System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
   場所 System.Environment.get_StackTrace()
   場所 System.Diagnostics.TraceEventCache.get_Callstack()
   場所 System.Diagnostics.TraceListener.WriteFooter(TraceEventCache eventCache)
   場所 System.Diagnostics.TraceListener.TraceEvent(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, String message)
   場所 System.Diagnostics.TraceInternal.TraceEvent(TraceEventType eventType, Int32 id, String format, Object[] args)
   場所 TraceTest.TraceTest.Main(String[] args)

属性の詳しい説明については、TraceOutputOptions Valuesを参照してください。

2014年1月27日月曜日

Lispでxmlの実体参照変換

当ブログでは、プログラムをハイライト表示するときにSyntaxHighlighterを使用しています。
XMLを表示するときは、実体参照へ変換しなければいけません。
「普段使用しているエディタから変換できれば楽ちんだよね」と思い、xyzzy Lispで変換プログラムを書いてみました。

プログラム
; convert-xml
(defparameter *conversion-alist*
  '((#\< . "&lt;")
    (#\> . "&gt;")
    (#\& . "&amp;")
    (#\" . "&quot;")
    (#\' . "&apos;")))

(defun convert-xml ()
  (interactive "*")
  (let ((input-buffer (window-buffer (selected-window))))
    (with-output-to-temp-buffer ("*Converted*")
      (with-open-stream (s (make-buffer-stream input-buffer))
        (loop 
          (let ((c (read-char s nil)))
            (if c (format t "~A" (convert-char c *conversion-alist*))
              (return nil))))))))

(defun convert-char (c alist)
  (let ((conversion (assoc c alist)))
    (if conversion (cdr conversion)
      c)))

実行すると現在のバッファを読み込み、一時バッファへ変換後のxmlを表示します。
工夫したところは、変換の定義をalistにしたところです。

参考

2014年1月20日月曜日

Traceを使用してログを出力するときの設定例(TraceListener)

.Netでログを出力するときの選択肢の1つとして、System.Diagnostics.Traceクラスを使用する方法があります。
この時のアプリケーション構成ファイルの記述例を記載します。
テストプログラムとして、次のプログラムを使用します。

namespace TraceTest 
{
  using System.Diagnostics;
  
  public class TraceTest {
    
    public static void Main(string[] args) {
      Trace.TraceInformation("TraceInformation");
      Trace.TraceWarning("TraceWarning");
      Trace.TraceError("TraceError");
    }
  }
}

次にTraceListenerの設定例を記載します。

<configuration>

  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <clear />

        <!-- 標準出力 -->
        <add name="console"
             type="System.Diagnostics.ConsoleTraceListener" />

        <!-- 区切りリスト -->
        <add name="delimited-list"
             type="System.Diagnostics.DelimitedListTraceListener"
             initializeData="DELIMITED-LIST.log" />

        <!-- イベントログ -->
        <add name="eventlog"
             type="System.Diagnostics.EventLogTraceListener"
             initializeData="Application" />

        <!-- テキストファイル -->
        <add name="file"
             type="System.Diagnostics.TextWriterTraceListener"
             initializeData="TEXT-WRITER.log" />

        <!-- XML形式 -->
        <add name="xml-writer"
             type="System.Diagnostics.XmlWriterTraceListener"
             initializeData="XML-WRITER.log" />

      </listeners>
    </trace>
  </system.diagnostics>

</configuration>
参考
<trace> の <listeners> の <add> 要素

2014年1月13日月曜日

FormでShowing/Hidingイベントを実装

System.Windows.Forms.Formにありそうでないイベントの一つに、表示前/非表示前イベントがあります。
なければ作っちゃえということで、SetVisibleCoreをオーバーライドして実装してみました。
処理の流れは以下のようになります。

  1. 引数と現在のVisibleを比較し、等しければイベントを発生させずにSetVisibleCoreを呼んで終了する。
  2. 引数がtrueのとき、Showingイベントを発生させる
  3. 引数がfalseのとき、Hidingイベントを発生させる
  4. 2,3の結果、CancelEventArgs.Cancelがtrueのとき、引数を逆にしてSetVisibleCoreを呼び、終了する。
  5. SetVisibleCoreを呼び、終了する。

なお、注意点として、SetVisibleCoreでShow/Hideメソッドを呼ばないこと、Visibleプロパティを更新しないことがあります。これらに違反すると、StackOverflowExceptionが発生してしまいます。

サンプル


参考

Form.SetVisibleCore メソッド