2013年12月21日土曜日

[haskell][yesod] Yesodのインストールとチュートリアルの実行手順のまとめ

HaskellのウェブアプリケーションフレームワークであるYesodに触れてみました。Yesodのチュートリアルを参考にして、自分のmac上にYesodをインストールし、サンプルサーバーをデプロイしたときの手順と、あとで調べたいと思った疑問点のメモを残しておきます。haskell platformがインストールされている環境を前提にしています。

Yesodのセットアップ手順:

  1. cabalでyesod関連のパッケージをインストール
    • コマンドラインから以下のコマンドを実行。20分くらい?かかります。
      • cabal install yesod-platform yesod-bin cabal-dev
  2. プロジェクトの生成
    • 以下のコマンドを実行
      • yesod init
        • プロジェクト名に"Yosog"を入力
        • DBは's' (sqlite) を選択
        
        % yesod init
        Welcome to the Yesod scaffolder.
        I'm going to be creating a skeleton Yesod project for you.
        
        What do you want to call your project? We'll use this for the cabal name.
        
        Project name: Yosog
        Yesod uses Persistent for its (you guessed it) persistence layer.
        This tool will build in either SQLite or PostgreSQL or MongoDB support for you.
        We recommend starting with SQLite: it has no dependencies.
        
            s      = sqlite
            p      = postgresql
            pf     = postgresql + Fay (experimental)
            mongo  = mongodb
            mysql  = MySQL
            simple = no database, no auth
            url    = Let me specify URL containing a site (advanced)
        
        So, what'll it be? s
        That's it! I'm creating your files now...
        
        ---------------------------------------
        
                             ___
                                    {-)   |\
                               [m,].-"-.   /
              [][__][__]         \(/\__/\)/
              [__][__][__][__]~~~~  |  |
              [][__][__][__][__][] /   |
              [__][__][__][__][__]| /| |
              [][__][__][__][__][]| || |  ~~~~
          ejm [__][__][__][__][__]__,__,  \__/
        
        
        ---------------------------------------
        
        The foundation for your web application has been built.
        
        
        There are a lot of resources to help you use Yesod.
        Start with the book: http://www.yesodweb.com/book
        Take part in the community: http://yesodweb.com/page/community
        
        
        Start your project:
        
           cd Yosog && cabal install && yesod devel
        
        or if you use cabal-dev:
        
           cd Yosog && cabal-dev install && yesod --dev devel
        
        
        
      • 生成されたYesogディレクトリに移動
        • cd Yosog
    • サーバーのテンプレートをセットアップ、ビルドして起動?こちらは30分以上かかりました。
      • cabal-dev install
      • yesod --dev devel
  3. サーバーへの接続確認
    • ブラウザで以下のURLにアクセス
      • http://localhost:3000/

これでサンプルサーバーが起動され、ブラウザからのアクセスに対して上記のようなレスポンスを返してくれる状態になりました。さらに、自前のハンドラを登録してエコーバックするweb serviceを動作させるには以下の作業を行います。

エコーバックハンドラの登録:

  1. ハンドラの追加
    • 以下のコマンドで空のハンドラが生成されます。
      • yesod add-handler
      
      % yesod add-handler
      Name of route (without trailing R): Echo
      Enter route pattern (ex: /entry/#EntryId): /echo/#String
      Enter space-separated list of methods (ex: GET POST): GET
      
      
      • Name of routeに"Echo"を指定
      • route patternに"/echo/#String"を指定
      • list of methodsに"GET"を指定
  2. echoハンドラの実装
    • add-handlerで生成されたHandler/Echo.hsを以下のように編集
      
      
      module Handler.Echo where
      
      import Import
      
      getEchoR :: String -> Handler Html
      getEchoR theText = defaultLayout [whamlet|<h1>#{theText}|]
      
      
  3. サーバーを起動
    • 以下のコマンドを実行する。このコマンドでビルドもされます。
      • yesod --dev devel
  4. ブラウザからechoサーバーのuriをたたく。
    • 以下のURLを指定してブラウザを開きます。
      • http://localhost:3000/echo/Hello%20Echo%20Server

これで上記のような画面が表示されます。

チュートリアルを一通りなぞった後の感想

最新のサーバーフレームワーク事情に精通している訳ではないのですが、ちょっと触った感覚としてはYesodが想像以上に多機能で本格的なフレームワークであることがわかりました。rubyのWEBrick的なプリミティブなものをイメージしていたのですが比較になりません。ざっとみただけでも以下のような機能、仕組みが用意されており「恐れ入りました」という感じです。
  • ビルド→サーバー起動がyesodコマンド一発
    • サーバー起動中もコードの変更を検知して自動的にビルド&デプロイ
  • ログ出力
  • Handlerの登録がadd-handler一発
    • 空のハンドラコード
    • ハンドラの登録
  • 各種DB対応
    • SQLite, PostgreSQL, MySQL, MongoDB
    • config/modelsにORマップ定義ができる
  • 認証・認可の仕組みも備わっている模様
    • OAuthのためのライブラリも存在
  • viewをtemplate(.hamlet)として分離できる
    • hamletの中でhaskellコードを書くことも可能
    • ループを書ける!
    • コードから参照しているhamletが存在しないと、ビルド時にエラー!
  • 自動生成されるhtmlをみていると各種ブラウザの振る舞いの違いも吸収してくれそう?
最後に(haskell超初心者の)自分にとって、よく理解できなかった手順の意味、チュートリアルの説明と調査結果をまとめておきます。
  • cabal-devってなんのためのパッケージ?
    • どうもcabalの代わりにcabal-devを利用することで、cabalでインストール済みのパッケージ群から独立して、個別のパッケージ群をインストールすることができるようです。これにより特定の環境で利用するパッケージのバージョンを固定し、cabalによるパッケージ更新の影響を受けなくできます(ここここのブログエントリ参考にしました)。
  • cabal-dev installは具体的には何をやっているの?
    • 前述の通りcabal-devを用いると既存のパッケージを参照しないように、必要なパッケージを全て0からダウンロード&インストールすることになります。これによりyesodでは30分という時間がかかるようです。
  • チュートリアル中に"It is a good practice to use Data.Text instead of String."とあったが、その心は?
    • hackageのData.Textのページを見ると、「時間的・空間的に効率の良いユニコードテキストの実装」「正規化、正規表現、非標準的なエンコーディング、ロケール等の便利な機能がそろっている」ということらしい。

github上のリポジトリでチュートリアルで利用されたコード類が公開されているようです。
終わり。

2013年9月18日水曜日

[debug][posix] sleep, usleepの知らないとはまる(かもしれない)恐ろしい仕様

POSIXのAPIであるsleep(), usleep()について、引数で指定した時間だけスリープしないで途中で起きてくることがある、という仕様はご存じでしょうか?
意外と知らずに使っている方が多いのでは、と思います。自分も知りませんでした。シグナルが発生すると引数で指定した時間を無視して関数を抜けてきます。

参考:


"The Open Group Base Specifications"のsleepのページにより詳しい説明が記載されていたので引用しておきます。
The sleep() function shall cause the calling thread to be suspended from execution until either the number of realtime seconds specified by the argument seconds has elapsed or a signal is delivered to the calling thread and its action is to invoke a signal-catching function or to terminate the process. The suspension time may be longer than requested due to the scheduling of other activity by the system.
If a SIGALRM signal is generated for the calling process during execution of sleep() and if the SIGALRM signal is being ignored or blocked from delivery, it is unspecified whether sleep() returns when the SIGALRM signal is scheduled. If the signal is being blocked, it is also unspecified whether it remains pending after sleep() returns or it is discarded.
If a SIGALRM signal is generated for the calling process during execution of sleep(), except as a result of a prior call to alarm(), and if the SIGALRM signal is not being ignored or blocked from delivery, it is unspecified whether that signal has any effect other than causing sleep() to return.
If a signal-catching function interrupts sleep() and examines or changes either the time a SIGALRM is scheduled to be generated, the action associated with the SIGALRM signal, or whether the SIGALRM signal is blocked from delivery, the results are unspecified.


この仕様を知ったきっかけですが、sqliteのsqlite3_busy_timeout()で3秒という時間を設定しているにもかかわらず、数百ミリ秒しかロックが解放されるのを待ってくれない、という不具合を追いかけていました。

このsleep()の仕様を知らず、バグの原因追及に思わぬ時間をとられました・・・。皆さんもsleep()の挙動がおかしい場合には、他のプロセス、スレッドから、シグナルが発射されていないかを疑ってみてください。


2013年9月14日土曜日

[sqlite] sqliteの内部テーブル

sqliteにはスキーマ、データベースファイル、テーブルのメタ情報を管理するための内部テーブルが用意されています。どんなテーブルが用意されていて、どのように活用できるかをまとめておきます。

テーブル一覧:
参考:"The SQLite Database File Format"の2.5 Storage Of The SQL Database Schema


sqlite_master

sqlite_masterテーブルは、データベースファイル毎に一つだけ生成されます。このテーブルの中には、該当データベースファイルの中に生成された、以下の4種類のオブジェクトの情報が格納される。
  • テーブル
  • インデックス
  • ビュー
  • トリガー

スキーマ:

CREATE TABLE sqlite_master(
  type text,
  name text,
  tbl_name text,
  rootpage integer,
  sql text
);
  • type
    • オブジェクトのタイプ。"table", "index", "trigger", "view"のいずれかが格納される。
  • name
    • オブジェクトの名前。typeが"table"もしくは"view"の場合はtbl_nameと一致する。
  • tbl_name
    • オブジェクトが所属するテーブルの名前。
  • rootpage
    • テーブル及びインデックスのroot b-tree pageの番号。ビュー、トリガー、バーチャルテーブルの場合は0もしくはNULLが格納される。
  • sql
    • オブジェクトを生成する際に実行されたSQL文(CREATE TABLE, CREATE INDEX, CREATE VIEW, CREATE TRIGGER等)が格納される。

このテーブルの利用例:

  SELECT name FROM sqlite_master
  WHERE type='table'
  ORDER BY name;
例えば上記のSQLを実行すると、DBファイル中のテーブルの一覧がテーブル名でソートした状態で出力される。


sqlite_temp_master

sqlite_temp_masterテーブルは、テンポラリデータベースにおいて、sqlite_masterテーブルの代わりに生成される。名前が異なる以外はsqlite_masterと仕様は同じ。


sqlite_sequence

sqlite_sequenceテーブルは、データベースファイル毎に一つだけ生成される。データベース内に存在しているテーブルのINTEGER PRIMARY KEYの最大値を保持する。この最大値はAUTOINCREMENTの動作で利用される。

スキーマ:
CREATE TABLE sqlite_sequence(name,seq);


sqlite_stat1

ANALYZEコマンドで生成される内部テーブル。テーブルやインデックスの補助的な情報が格納され、query plannerが効率的なクエリを見つけるために利用される。 アプリケーションからupdate, delete from, insert into, dropを実行できるが、createとalter tableは実行できない。

スキーマ:

CREATE TABLE sqlite_stat1(tbl,idx,stat);


sqlite_stat2

SQLiteのバージョンが3.6.18と3.7.8の間で、かつ、SQLITE_ENABLE_STAT2オプションでコンパイルされている場合にのみ利用される。インデックス中のキーの分散に関する補助的な情報が格納される。

スキーマ:

CREATE TABLE sqlite_stat2(tbl,idx,sampleno,sample);


sqlite_stat3

SQLiteのバージョンが3.7.9以降で、かつ、SQLITE_ENABLE_STAT3オプションでコンパイルされている場合にのみ生成・利用される。インデックス中のキーの分散に関する情報、および、query plannerがよりよいアルゴリズムを生成するための情報が格納される。

スキーマ:

CREATE TABLE sqlite_stat3(tbl,idx,nEq,nLt,nDLt,sample);


2013年7月5日金曜日

[windows] Windows上で(ショートカットでない)シンボリックリンク作る方法

Windows上でもシンボリックリンクが使えたら・・・、と考えたことはないでしょうか。

自分の場合、複数(バージョン)の開発環境を切り替えて使う目的でシンボリックリンクが欲しいとずっと思っていました。例えば、Javaの開発環境であればC:\JavaからC:\Program Files\Java\jre7にシンボリックリンクを張っておき、C:\Java以下のbinやlibにパス・クラスパスを設定しておきます。こうしておけば、JDK7からJDK6に環境を切り替えたいときには、C:\Javaのリンク先をC:\Program Files\Java\jre6に変更するだけでOK!という算段です。

前置きが長くなりましたが、mklinkというコマンドを利用すれば、Windows上でもシンボリックリンクを張ることができます。ショートカットではディレクトリ階層を辿れませんがmklinkであればそれが可能になります。

C:\> mklink /?
シンボリック リンクを作成します。

MKLINK [[/D] | [/H] | [/J]] リンク ターゲット

        /D          ディレクトリのシンボリック リンクを作成します。既定では、
                    ファイルのシンボリック リンクが作成されます。
        /H          シンボリック リンクではなく、ハード リンクを作成します。
        /J          ディレクトリ ジャンクションを作成します。
        リンク      新しいシンボリック リンク名を指定します。
        ターゲット  新しいリンクが参照するパス (相対または絶対)
                    を指定します。

mklinkはdirやcopyと同じ内部コマンドです。exeが存在している訳ではありません。 また、cygwin上でも正しくリンクを解決してくれるので重宝しています。
#cygwin上のlnがmklink相当の動作をしてくれると嬉しいのですが残念ながらそうはなっていません。

ただ一点、気をつけないといけないのが、ディレクトリのリンクを作るときには"/D"オプションを付ける必要がある点。UNIXのlnはディレクトリもファイルも区別されませんが、mklinkでは引数を忘れるとディレクトリをたどれなくなります。

2013年5月16日木曜日

[win7] Windows7で設定したショートカットキーが効かない場合の対応方法

Windows7でショートカットにショートカットキーを設定する場合、ショートカットはスタートメニューの中に存在している必要があるようです。

Windows XPの頃には、デスクトップだろうがQuick Lunchだろうが、作ったショートカットでのプロパティを開いてショートカットキーの設定をしておけば、普通にショートカットキーでアプリケーション起動ができていました。
ですが、Windows7では仕組みが変わったようで、ショートカットキーが有効にならないことがあり困っていました。

ちょっと暇なときにどうすればショートカットキーが効く状態になるかと、調べていたところ、この情報を発見しました。原因と解決方法がバッチリ書かれていたわけではないのですが、どうもショートカットキーを設定したショートカットを作る場所がキモのようです。

ショートカットをスタートメニューの中に配置し、そのプロパティでショートカットキーを設定したところ、所望の結果が得られました!

2013年4月7日日曜日

[emacs] csharp-modeでrevert-bufferが効かなくなる不具合

emacsと、別ツールで同じファイルを開いていて相互で編集を行うような場合、emacsの外の変更を取り込むときに、'revert-buffer'を利用しているのですが、いつのタイミングからか、以下のメッセージが表示され正常に再読込ができない状態になっていました。
'Symbol's value as variable is void: flymake-mode'
数日間、ガマンして使っていたのですがやっぱり不便。 google先生に問い合わせたところ、以下のページがヒットし復旧することができました。
https://code.google.com/p/csharpmode/issues/detail?id=8 
バグ入りのcsharp-modeを入れてしまったのが原因で、上記のサイトに記載されている修正を反映したところ、revert-bufferが復活しました。
(let ((is-flymake-enabled
     (and (fboundp 'flymake-mode)
        flymake-mode)))
"fboudp"を"boudp"に修正する必要があるそうです。 emacs lispは知識不足で問題解析ができるレベルに達していないので精進が必要です。

2013年3月21日木曜日

[c++] if文でポインタがNULLかどうかの判定をするコードのc++言語上の解釈について

ポインタが実体を指しているか、それともNULLか、という判定をして処理を分岐することがよくあります。
if (ptr != NULL) {
         // do some operation.
}

よく見るのは上記のようなコードです。 ですが、NULLとの比較で分岐するのでなく、NULLが0であることを期待した以下のようなコードを見かけることがあります。
if (!ptr) {
     // do some operation.
}
この書き方は問題がありますが、ここではその議論は脇に置いておいて、ptr がポインタではなく、オブジェクトの実体だった場合に何が起こるのか?
という疑問に勝手に応えたいと思います。
class SampleClass {
public:
 SampleClass(int _a) :
 a(_a) {
 }
private:
 int a;
};

int main(int argc, char** argv)
{
 SampleClass s0(0);
 if (s0) {
  printf("s0: true\n");
 } else {
  printf("s0: false\n");
 }
 return 0;
}
上記のコードをコンパイルするとエラーになります。
hoge.cpp:13:8: error: could not convert ‘n0’ to ‘bool’
まあそうですよね。 ここからやっと本題に入りますが、上記のif文をエラーでなくSampleClassの内部状態に応じて、分岐させることが可能です。 具体的にどうすればよいか?答えは「キャスト演算子をオーバーロードする」ことです。
class SampleClass {
public:
 SampleClass(int _a) :
 a(_a) {
 }
 operator bool () {
  // false: if a is 0. true: if a is not 0.
  return a != 0;
 }
private:
 int a;
};
SampleClass を上記のように定義して、bool型へのキャスト演算子を定義するとコンパイルができるようになり、メンバ変数の"a"の値に応じて処理が分岐されるようになります。
この例ではbool型へのキャスト演算子を定義しましたが、int型やvoid*型へのキャスト演算子の定義でも所望の動作をさせることができます。これは暗黙的型変換によってintもしくはvoid*がboolに変換された上でif文で評価されるためです。 実際、stlのiosやbasic_iosではboolではなく、void*へのキャスト演算子が定義されているようです。

2013年1月16日水曜日

[windows] windows7セットアップメモ

最近、windows7をセットアップすることが多いため、自分の好みの環境をセットアップ手順をメモとして残しておきます(今更ですが・・・)。

windows本体の設定

  • タスクバーとスタートメニューの設定
    • プロパティの「タスクバー」タブ
      • 「小さいアイコンを使う」にチェック
      • 「タスクバーのボタン」:タスクバーがいっぱいの場合に結合
      • 「aeroプレビューを利用してデスクトップをプレビューする」をアンチェック
    • プロパティの「スタートニュー」タブのカスタマイズ
      • 「お気に入りメニュー」にチェック
      • 「ファイル名を指定して実行」コマンドにチェック
      • 「システム管理ツール」で「全てのぷろぐらむメニューに表示する」を選択
      • 「最近使った項目」にチェック
      • 「大きいアイコンを使用する」をアンチェック
    • Windows 7 で「クイック起動」を表示させる方法
  • エクスプローラーの設定
    • 「ゴミ箱にファイルを移動しないで、削除と同時にファイルを消去する」を選択
    • 「整理」→「フォルダーと検索のオプション」の設定
      • 全般タブ
        • 自動的に現在のフォルダーまで展開するにチェック
      • 表示タブ
        • 「タイトルバーにファイルのパス名を表示する」にチェック
        • 「ドライブ文字を表示する」にチェック
        • 「空のドライブはコンピューターフォルダーに表示しない」をアンチェック
        • 「登録されている拡張子は表示しない」をアンチェック
        • 「保護されている拡張子は表示しない」をアンチェック
    • 「ライブラリ」に表示する項目を整理する
  • コントロールパネル


インストールすべきアプリ

  • google chrome
    • ブックマーク、設定が一瞬で自分の環境になるのがうれしい。
  • KeySwap
    • 以下の3項目の設定で幸せになれる。
      • 「caps lock」キーと「左ctrl(1D)」キーの入れ替え
      • 「変換」キーと「全角/半角(29)」キーの入れ替え
      • 「全角/半角」キーと「Esc(01)」キーの入れ替え
  • 秀丸
  • Atok
    • 「入力・変換」タブ
      • 入力補助
        • 「テンキーからの入力を必ず半角にする」にチェック
        • 「キー操作でのカナロックON/OFFを無効にする」にチェック
        • 「スペースキーで入力する空白文字」を「常に半角」へ
      • 表示
        • 「カーソル位置に入力モードを表示」を「しない」に
  • WinSCP
  • PuTTY
  • cygwin
    • 項目多量になったので、別章に分けます。

cygwinの設定&パッケージ

  • 追加選択するパッケージ
    • Archive
      • unzip
      • zip
    • Database
      • sqlite3
    • Devel
      • autoconf
      • automake
      • bashdb
      • binutils
      • ctags
      • dmalloc
      • doxygen
      • gcc
      • gdb
      • git
      • jlint
      • make
      • mercurial
      • ocaml
      • patchutils
      • subversion
    • Editors
      • emacs
      • emacs-X11
      • vim
    • Net
      • curl
      • inetutils  (telnet)
      • openssh
      • openssl
      • ping
      • wget
    • Ruby
      • ruby
    • Shells
      • rxvt
      • tcsh
      • xterm
    • Utils
      • gnupg
      • patch
      • git
    • Web
      • wget
      • wput
    • X11
      • xhost
      • xmodmap
      • xinit
      • xorg-server
  • 環境変数
    • HOME
      • ホームディレクトリを切り替えられる
    • SHELL
      • "/bin/tcsh"とすることでデフォルトのシェルが切り替わる
  • その他
    • /etc/passwd
      • ユーザー名を変えるとき
      • ユーザーのホームディレクトリを変える
    • ~/.ssh/
      • ssh-agentのために秘密鍵セットアップ(※group/otherの権限は削除)
    • ping.exeに管理者権限を設定
      • インストールしただけだと以下のエラーが表示されます
      • ping: socket: Operation not permitted

2013年1月11日金曜日

[windows][cygwin] windows7 上で ping が "ping: socket: Operation not permitted" になる問題の対処

windows7 上に cygwin の ping をインストールして実行すると、以下のようなエラーが出ます。

% ping hogehost
ping: socket: Operation not permitted

試行錯誤の末、ping.exeを右クリックしてプロパティダイアログを表示し、「互換性」タブの「管理者としてこのプログラムを実行する」にチェックを入れることで、pingが正常動作するようになりました。





google先生に伺ったところ、どうもファイアウォールの設定で管理者にしかICMPv4の送受信権限が与えられていないようです。と、歯切れが悪いのは、ここに載っていた情報に従って設定したにもかかわらず、状況が改善しなかったためです。

# もし正しいよりスマートな設定方法をご存じの方、是非お知らせください。m(_ _)m


(以下、2013/05/16追記)
ping.exe自体に管理者権限を設定してもエラーになる環境が多く悩んでいたのですが、cygwinのMLにこのような過去ログを発見しました。
これによると、ping.exeを起動するターミナル(mintty)側に予め管理者権限を設定しておく必要がある、ということです。

私はXを使っているので試しにrun.exeに管理者権限を与えておくと、X上で起動した任意のターミナルがその権限を引き継いでくれて、ping.exeも正しく動作することが分かりました!


この設定をしておくとping.exe自体には管理者権限を設定する必要はありません。



(以下、2016/09/29追記)
ショートカットの詳細設定で「管理者権限で実行」にチェックを入れておくと、管理者権限でXWinを起動できます。ですが、起動時に毎回「ユーザーアカウント制御」ダイアログが表示されて「この不明な発行元からのアプリがデバイスに変更を加えることを許可しますか?」、[はい」ボタンを押す必要があり、非常に面倒・・・。
このダイアログをスキップできないかと思ってネット上の情報を漁ってみたところ、UAC全体を無効にすることなく、XWinの起動時だけダイアログ表示をスキップするうってつけの方法が見つかりました。
タスクスケジューラにタスクを設定することでダイアログ表示を回避できます。以下のサイトを参考に設定してください。


(2016/10/16追記)
上記タスクスケジューラからアプリを起動すると、プロセスの優先度が「通常以下」となり、動作が遅くなる問題がありました。以下の手順で「通常」優先度でタスクを起動するとよいです。