goos 本を Mac で写経してたらはまったので解決した

goos 本の写経を Mac でやってると WindowLicker のキーボードに関するところではまるようです.

ここでも解決方法が示されていますが,WindowLicker を自前ビルドする必要があって面倒です.

少しだけましな方法が WindowLicker のソースを見ていたら発見できました.

WindowLicker がキーボード レイアウトを決定するときに System.getProperty していたのでテストのセットアップで明示的に指定するようにしました. 具体的には AuctionSniperEndToEndTest クラスに @BeforeClass を指定したセットアップ メソッドを用意するだけで OK です. 中身はこんな感じ.

public class AuctionSniperEndToEndTest {
    ...

    @BeforeClass
    public static void setupKeyboardLayout() {
        System.setProperty("com.objogate.wl.keyboard", "US");
    }

    ...
}

goos 本は,コードが変更した部分しか示されていなかったり,すっとばされていたりで,ただ読むだけだと大変です. なので読むときはぜひ写経しながら読むのがいいんじゃないかと思います.

github にあるコードは特に気にすることもなく実行できました.

Cucumber と Scala でテストを書いてみた

Cucumber を使ってみたい衝動に駆られて sbt で Cucumber のテストを実行するのを試してみた.

環境

Scala 2.10 だと違うところとかあるみたい. なので Cucumber-JVM, xsbt-cucumber-plugin の github とかをみて適宜修正する必要がある.

xsbt-cucumber-plugin の使い方

xsbt-cucumber-plugin を使ってテスト実行するのは,

% sbt cucumber

と実行する.

構成

関係するファイルのディレクトリ構成は以下:

$(SBT_ROOT)/
  build.sbt
  project/
    build.properties
    build.sbt
  src/
    test/
      resources/
        Addition.feature  <- Cucumber のテスト
      scala/
        Addition.scala    <- Addition.feature の実装
    main/
      scala/
        Calculator.scala  <- Production コード

それぞれのファイルはこんな感じになった.

$(SBT_ROOT)/build.sbt:

name := "project-name"

version := "1.0"

scalaVersion := "2.9.2"

libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test"

seq(cucumberSettings : _*)

$(SBT_ROOT)/project/build.sbt:

resolvers += "Templemore Repository" at "http://templemore.co.uk/repo"

addSbtPlugin("templemore" % "sbt-cucumber-plugin" % "0.7.2")

$(SBT_ROOT)/project/build.properties:

sbt.version=0.12.0

$(SBT_ROOT)/src/test/resources/Addtion.feature:

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

  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

$(SBT_ROOT)/src/test/scala/Addition.scala:

import cucumber.runtime.{ScalaDsl, EN}
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
import scala.collection.mutable.MutableList

class Addition extends ScalaDsl with EN with ShouldMatchers {
  val numbers = MutableList.empty[Int]
  var result = 0

  Given ("""^I have entered (\d+) into the calculator$""") { (arg0: Int) =>
    numbers += arg0
  }

  When ("""^I press add$""") { () =>
    result = Calculator.add(numbers(0))(numbers(1))
  }

  Then("""^the result should be (\d+) on the screen$""") { (arg0: Int) =>
    assert(result === arg0)
  }
}

$(SBT_ROOT)/src/main/scala/Calculator.scala:

object Calculator {

  def add(a: Int)(b: Int) = a + b

}

スニペット

Addition.feature だけがある状態でテストを実行すると,スニペットが得られる.

% sbt
> cucumber
[info] Running cucumber…
[info] Feature: Addition
[info] In order to avoid silly mistakes
[info] As a math idiot
[info] I want to be told the sum of two numbers
[info] 
[info] Scenario: Add two numbers # Addition.feature:6
[info] Given I have entered 50 into the calculator
[info] And I have entered 70 into the calculator
[info] When I press add
[info] Then the result should be 120 on the screen
[info] 
[info] 
[info] You can implement missing steps with the snippets below:
[info] 
[info] Given("""^I have entered (\d+) into the calculator$"""){ (arg0:Int) =>
[info] //// Express the Regexp above with the code you wish you had
[info] throw new PendingException()
[info] }
[info] When("""^I press add$"""){ () =>
[info] //// Express the Regexp above with the code you wish you had
[info] throw new PendingException()
[info] }
[info] Then("""^the result should be (\d+) on the screen$"""){ (arg0:Int) =>
[info] //// Express the Regexp above with the code you wish you had
[info] throw new PendingException()
[info] }
[success] Total time: 1 s, completed 2013/02/02 12:34:26

その他

今回は Basic Configuration しかやらなかったけど sbt test で実行できるようにとか sbt cucumber 以外で実行する方法とかもある.

感想

Scala で書くのは辛いという印象をもった. Cucumber で各部分は手続き的な内容になりがちだと思うので別な言語で書いた方がいいのかも知れない.

GetDateFormat with Calendar in Win32 API

この記事は LL/ML Advent Calendar #LLAdventJP の 19 日目です.

ある日ケバブを食べにいったら登録されてしまいました.
世の中何を信じればいいのかわかりません.

Win32 での日付のフォーマット

Windows のネイティブ コードでも日付のフォーマットをしたいと思い作ってみました.

Win32 での日付のフォーマットの現状

Windows のネイティブ コードで日付のフォーマットを行う場合どんな方法があるかというと、

といった方法があります.

しかし,これらの方法はどれもカレンダーを指定できるような形になっていません.

ATL/MFC の CTime::Format は wcsftime や VarFormatDateTime を呼んでいます.C 言語の標準ライブラリの wcsftime は和暦に対応していませんし,最終的には NLS の GetDateFormat を使っています.では肝心の NLS の GetDateFormat / GetDateFormatEx はというとカレンダーの指定ではなく,ユーザーの設定に依存したフォーマットを行うようになっています.また GetCalendarDateFormatEx という期待通りのことをやってくれそうな関数が Vista で追加されていますが,すでに Deprecated です.

結局ロケールやカレンダーを指定した任意の日付のフォーマットはできないのが現状です。

コードとか

github に登録しました.まだ完成してません.

https://github.com/kei10in/GetDateFormatEx

実装

ポイントは,

  • GetCalendarInfoEx を使って各種翻訳の必要なリソースを取得する.
  • グレゴリオ暦以外の計算は自前でやる必要がある.

というところかなと思います.

GetCalendarInfoEx で各種リソースの取得

日付のフォーマットを実装しようと思って一番困るのはリソースかなと思います.アラビア語やタイ語なんて全くわかりません.

マイクロソフト ランゲージポータルで必要な翻訳を集める方法がないわけではないですがなかなかつらいと思います.

そんなときは GetCalendarInfo / GetCalendarInfoEx の出番です.この関数を使うとロケールとカレンダーに対応したカレンダーに関する情報を取得することができます.引数 CalType でどんな情報を取得するかを指定することができます.どんな情報があるかは Calendar Type Information にまとまっています.

例えば lpLocaleName に "ja-JP", Calendar に CAL_GREGORIAN, CalType に CAL_SERASTRING を指定した場合は "西暦" が返ってきます.CalType に CAL_JAPAN を指定すると "平成" が返ってきます."昭和"・"大正"・"明治" といった文字列も取得したい場合は EnumCalendarInfoExEx を使います.EnumCalendarInfoExEx でも GetCalendarInfoEx 同様,CAL_SERASTRING を指定します.

GetCalendarInfoEx は文字列をひとつだけ取得できましたが,EnumCalendarInfoExEx はひとつの Calendar Type Information で複数の文字列が得られる場合に使用する関数です.CAL_SERASTRING の他には CAL_IYEAROFFSETRANGE が複数の値を返します.

グレゴリオ暦以外の計算

日付のフォーマットをする場合グレゴリオ暦から和暦やヒジュラ暦へ変換する必要があります.

しかし,先ほどの GetCaldnerInfoEx, EnumCalendarInfoExEx を使って変換することができません.

例えば CAL_IYEAROFFSETRANGE で西暦に対する平成や昭和のオフセット値を得ることができますが,何月何日からかといった情報は GetCalendarInfoEx や EnumCalendarInfoEx では取得できません.また和暦のオフセットは西暦から減算するのですがタイの仏暦の場合は西暦に加算する必要があり,個別に実装する必要があります.ヒジュラ暦に関しては全く別な計算が必要です.

感想

ただ作るだけだろうと,軽い気持ちではじめてみたら結構つらかったです.

和暦の計算とかをやってくれる関数くらいあるだろー,とか思ってたら自分で実装する必要がありました.

Era Handling for the Japanese Calendar (Windows) にはレジストリに情報がある旨書いてありますが,Windows 7 以降の話です.XP サポートしないといけない間に元号変わったら大変なことになりまね.

他にも XP サポートのためには LocaleName ではなくて LCID を使わないといけないとか色々やらねばならぬことがあります.

package.el を使って必要なパッケージを自動インストールする

最近のビルド ツールは使ってるライブラリの取得とかを全部やってくれて素敵!!ということで Emacs で使うパッケージも、使っているものは勝手に取得してくれるようにしてみました。

何に使うの?

家では Windows と Mac OS X、会社では Windows と環境がいろいろあって全部を同じ開発環境にするのは結構面倒です。なので EmacsZsh の設定ファイルは github で管理しています。

ここで問題になってくるのが Emacs などで使っている外部のパッケージです。
外部のパッケージを管理する方法としては次の方法が考えられると思います。

  1. 毎回手動でインストールする。
  2. 他の設定ファイルと同様に一緒にバージョン管理する。
  3. 足りていないものは自動でインストールされるようにする。

1. はとてもつもなく大変ですね。これまでは 2. でやってきました。大体は 2. で十分だと思います。ですがどうにもカッコ悪いです。

そこで、Emacs 24 から標準搭載された package.el に対応しているパッケージについては 3. の自動インストールができるようにしてみました。

package.el についてはすでに色々な方が書かれているのでそちらを参考にしてください。

やり方

以下のコードを Emacs の設定ファイルに書いてください。

「どのパッケージをインストールするか」だけは手動で設定する必要があります。

(require 'cl)

(defvar installing-package-list
  '(
    ;; ここに使っているパッケージを書く。
    undo-tree
    igrep
    grep-a-lot
    auto-complete
    goto-chg
    google-c-style
    ))

(let ((not-installed (loop for x in installing-package-list
                            when (not (package-installed-p x))
                            collect x)))
  (when not-installed
    (package-refresh-contents)
    (dolist (pkg not-installed)
        (package-install pkg))))

これを設定すると、installing-package-list に書かれているパッケージのうち、インストールされていないものを Emacs の起動時に自動でインストールしてくれるようになります。

おわりに

ちょっと家でがんばってみたんですが、うちの会社では Emacs が http proxy を超えてくれなくてこの設定が使えてないです。

package.el 自体は http proxy を使っている環境でも使えるようなので、うちの会社だけの問題だと思いますが。

Cygwin で Python 3.2.3 をビルドする

Python 3.2.3 がリリースされたので Cygwin でビルドしてみました.

前回Python 3.2.2 だったのと,curses や sqlite3 といったモジュールのビルドはできていなかったので,今回はこれらのモジュールのビルドまでやってみました.

Cygwin パッケージのインストール

まず最初に Python のモジュールをビルドするのに必要なパッケージをインストールします.

自分が追加でインストールしたのはこの 5 つです.他にも必要なものがあるかもしれません.

  • libsqlite3-devel
  • libcurses-devel
  • openssl-devel
  • libgdbm-devel
  • readline

ソースの修正

そのままビルドすると libpython3.2m.dll.a を make するビルド ターゲットがないと言われるので,パッチを当てます.

パッチは gist においたのでそれを当ててください.

% patch -p0 < Python-3.2.3_cygwin_build.patch

このパッチは curses, sqlite3 モジュールをビルドするのに必要な修正も入っています.

ビルド

最後ににビルドとインストールをします.

% ./configure
% make
% make install

IPython を設定してみた

IPython が Python 3.1 以降に対応していたのでインストールと設定をしてみた.

やったのは

  • インストール.
  • プロンプトの設定.
  • ipython 起動時に実行されるスクリプトの設定.

インストール

IPython は pip でインストールした.

% pip-3.2 install ipython
% pip-3.2 install readline

readline を入れていなかったせいでプロンプトが崩れていたので readline を追加でインストールした.

Windows の場合は readline ではなくて pyreadline をインストールするみたい.

IPython の設定 - profile の作成

IPython の設定は profile という形でディレクトリ単位で管理されてる.
コマンドで profile を作ることができるので,とりあえずコマンドで作る.

% ipython3 profile create

とやると ~/.config/ipython/profile_default/ が作られる.
この場所が気に入らない場合は ~/.ipython/profile_default/ であれば特別な設定をしなくても読み込まれる.

IPython の設定 - プロンプトの設定

IPython のプロンプトは profile のディレクトリにある ipython_config.py に記述できた.
肝心のプロンプトの設定はこんな感じ.以外にも改行が使えてハッピー.

# Input prompt.  '\#' will be transformed to the prompt number
c.PromptManager.in_template = '{color.LightBlue}\\w\n{color.LightCyan}>>> '

一行目にカレント ワーキング ディレクトリのフルパス,二行目に ">>> " を表示するようにした.
バックスラッシュのあとに文字を指定するとその文字に従った内容に展開される.
IPython のドキュメントには書いてないっぽいので色々と試してみた.
だいたいこんな感じ.ドキュメントには載ってないので変更されるかもしれないから注意してね.

\h
hostname -s の結果
\H
hostname の結果
\t
時間
\u
ユーザー名
\w
カレント ワーキング ディレクトリのフルパス
\W
カレント ワーキング ディレクトリのディレクトリ名

IPython の設定 - スタートアップ スクリプト

Python Shell で言うところの ${PYTHONSTARTUP}.

IPython では profile のディレクトリにある startup ディレクトリに .py か .ipy ファイルを置くと読み込まれる.

今のところよく使うモジュールがインポートされてれば十分なので import しか書いてない.

import sys
import os
import io
import itertools
import functools
import re

おわりに

IPython の設定ファイルの形式が変わったらしいけど,新しい方式について書かれた記事が少なかったのでまとめてみた.

実際の内容は github にあるのでそちらを見てください.