2016年7月17日日曜日

[haskell][yesod] stack対応版Yesod tutorial

HaskellのwebサービスフレームワークにYesodというフレームワークがあります。Yesodに触れたことのない開発者向けに書かれたチュートリアルの一つにYesod tutorialがあり、手順に沿っていくだけで簡単なwebサービスを動作させることができ、Yesodで何ができるかを簡単に理解できるようになっています。
ただ残念なことに、このYesod tutorialの記載は内容が古く、stackを利用した現行の手順とマッチしなくなっています。stackに対応している最新環境(Yesod 1.4.x)における順があると役に立つと思い、書き起こしてみました。

  1. Before the real start(はじめに)
    1. Install(インストール手順)
    2. stackをインストールする。以下のサイトが参考になります。
    3. Initialize(初期化)
    4. オリジナルのチュートリアルではyesod initの実行する手順が記載されていますが、最新版のYesodでは以下のようにstack newを利用するよう指示されます。
      // original
      % yesod init
      yesod: The init command has been removed. Please use 'stack new' instead
      
      stack対応版では、以下のようにテンプレートを指定してプロジェクトを生成します。これでオリジナル版でプロジェクト名にyosogを、利用DBにsqliteを指定したのと同じ状態になっています。
      // stack support
      % stack new my-project yesod-sqlite
      
      次にyosogプロジェクトをビルドします。オリジナルの手順は以下のようになっています。
      // original
      % cd yosog
      % cabal sandbox init
      % cabal install --enable-tests . yesod-platform yesod-bin --max-backjumps=-1 --reorder-goals
      % yesod devel
      
      これに対応する、stack版での手順は以下になります。
      // stack support
      % cd my-project
      % stack build yesod-bin cabal-install --install-ghc
      % stack build
      % stack exec -- yesod devel
      
      あとはブラウザを起動して http://localhost:3000/ にアクセスすればscaffolding siteの画面が表示されます。ただ、環境によっては"getAddrInfo: does not exist"というエラーが表示され、scaffolding siteに繋がらない現象があるので、そのときにはこちらの情報を参考に対処してください。
    5. Configure git
    6. 「この作業は必須ではありませんが、gitを使うことはよい習慣です」だそうです
      % git init .
      % git add .
      % git commit -a -m "Initial yesod commit"
      
    7. A few words before we start
    8. my-project以下のディレクトリ構成の概要:
      config/routesURLとコードのマッピング設定ファイル
      Handler/URLにマッピングされたコード(ハンドラ)を格納
      templates/HTMLファイル、js, cssテンプレートファイルを格納
      config/modelsデータモデル(DBスキーマ)設定ファイル
  2. Echo
  3. さてEchoサーバーの実装です。最初の手順としてオリジナルサイトには以下のコマンド実行が記載されています。
    // original
    $ yesod add-handler
    
    stack対応版ではyesodコマンドを直に実行することはできません。かならず"stack exec"を介する必要があります。具体的には以下のコマンドを実行すればOKです。
    // stack support
    % stack exec -- 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
    
    上記手順でEchoハンドラを登録することができます。そして本来ならばこの状態で何も編集を加えなくてもビルドができるはずなのですが、以下のエラーが発生してしまいました・・・。
    % stack exec -- yesod devel
    Yesod devel server. Type 'quit' to quit
    Application can be accessed at:
    
    http://localhost:3000
    https://localhost:3443
    If you wish to test https capabilities, you should set the following variable:
      export APPROOT=https://localhost:3443
    
    Warning: The package list for 'hackage.haskell.org' is 146.2 days old.
    Run 'cabal update' to get the latest list of available packages.
    Resolving dependencies...
    Configuring my-project-0.0.0...
    ghc: unable to load package `my-project-0.0.0'
    ghc: C:\work_haskell\yesodweb\scaffolding\my-project\dist\build\HSmy-project-0.0.0-5AgQdK8FuSe8tlY0YoDpHN.o: unknown symbol `myprozu5AgQdK8FuSe8tlY0YoDpHN_HandlerziEcho_getEchoR_closure'
    
    確認したところ、add-hanlderコマンドによってmy-project.cabalに追加された、Handler.Echoの追加場所が正しくなく、リンクエラーになっている模様。
    --- a/yesodweb/scaffolding/my-project/my-project.cabal
    +++ b/yesodweb/scaffolding/my-project/my-project.cabal
    @@ -101,6 +101,7 @@ test-suite test
         other-modules:     Handler.CommentSpec
                            Handler.CommonSpec
                            Handler.HomeSpec
    +                       Handler.Echo
                            TestImport
         hs-source-dirs:    test
         ghc-options:       -Wall
    
    自動追加された上記の状態ではダメで、以下の場所に移動する必要がある。add-handlerの不具合により、.cabalファイル内の"library"欄に追加する項目が"test suite"欄に追加されてしまうのが原因です。
    --- a/yesodweb/scaffolding/my-project/my-project.cabal
    +++ b/yesodweb/scaffolding/my-project/my-project.cabal
    @@ -23,6 +23,7 @@ library
                          Handler.Common
                          Handler.Home
                          Handler.Comment
    +                     Handler.Echo
     
         if flag(dev) || flag(library-only)
             cpp-options:   -DDEVELOPMENT
    
    これでコンパイルが無事に通り、scafolding siteを起動できる状態になります。tutorialに沿ってブラウザから以下のURLにアクセスしてみます。
    現時点ではまだハンドラの実装が空のままなので、以下のエラーが返されるのが期待値になります。
    いよいよ、Echoハンドラの実装です。 エディタでHandler/Echo.hsを開いてみてください。以下のようになっているはずです(前述のNot yet implementedエラーはこのコードによるものです)。
    module Handler.Echo2 where
    
    import Import
    
    getEcho2R :: String -> Handler Html
    getEcho2R string = error "Not yet implemented: getEcho2R"
    
    Handler/Echo.hsを以下の実装に変えることでEchoが動作する状態になります。
    module Handler.Echo where
    
    import Import
    
    getEchoR :: String -> Handler Html
    getEchoR theText = defaultLayout [whamlet|<h1>#{theText}|]
    
    試しにブラウザから以下のURLにアクセスしてみましょう!
    以下の通り"foo"がエコーバックされれば成功です。

オリジナルのチュートリアルではEchoサーバーの実装後、以下の手順が案内されています。yesodコマンド実行時にstack execを適用することで問題なく進められると思います。
    2. Echo
    2.1. Bulletproof?
    2.2. Cleaning up
    2.2.1. Data.Text
    2.2.2. Use templates
    3. Mirror
    4. A Blog
一通りの手順を実装したコードをgithubの以下のサイトにコミットしています。必要に応じてこちらも参照してみてください。

環境:

  • The Glorious Glasgow Haskell Compilation System, version 7.10.3
  • yesod-bin version: 1.4.17.1

参考:


[haskell][yesod] stack exec -- yesod devel で devel.hs: getAddrInfo: does not existというエラーになる問題の対処方法

Widnwos環境での現象:

windows上でyesodのscafolding siteをセットアップし、さあ起動!ブラウザから接続確認してOKとなるはずが、なぜか「The application isn't built」という表示が出てしまいました。

このときターミナルには以下のようなログが出力されていました。
% stack exec -- yesod devel
Yesod devel server. Type 'quit' to quit
Application can be accessed at:

http://localhost:3000
https://localhost:3443
If you wish to test https capabilities, you should set the following variable:
  export APPROOT=https://localhost:3443

Warning: The package list for 'hackage.haskell.org' is 146.0 days old.
Run 'cabal update' to get the latest list of available packages.
Resolving dependencies...
Configuring my-project-0.0.0...
Rebuilding application... (using cabal)
Starting development server...
Starting devel application
Devel application launched: http://localhost:3000
devel.hs: getAddrInfo: does not exist (error 11001)
receiveloop: failed (No error)

ブラウザには「ビルドができてない!」と表示されていますが、ビルド自体は成功しています。以下のディレクトリにバイナリが生成されています。
% find . -name "*.exe"
./.stack-work/dist/2672c1f3/build/my-project/my-project.exe
./.stack-work/dist/2672c1f3/build/test/test.exe

PCによってはうまく動くこともあり原因を切り分けて調査したところ、HOST環境変数の有無で挙動が変わることがわかりました。ここから先は推測ですが、HOST環境変数に設定されている名前でIPアドレス解決を試みてエラーとなっているような気がします。
以下の手順のいずれかでHOST環境変数を空にして、scaffolding siteを再起動すると正常にアクセスできるようになります。
  • cmd.exe
  • set HOST=
    
  • tcsh
  • unsetenv HOST
    
  • bash
  • export HOST=
    

mac環境の現象:

macでも同様の問題が発生します。mac上のログは以下のようになります。やはりHOST環境変数を無効にすることでブラウザからアクセスできるようになりました。
% stack exec -- yesod devel
Yesod devel server. Type 'quit' to quit
Application can be accessed at:

http://localhost:3000
https://localhost:3443
If you wish to test https capabilities, you should set the following variable:
  export APPROOT=https://localhost:3443

Warning: The package list for 'hackage.haskell.org' is 42.1 days old.
Run 'cabal update' to get the latest list of available packages.
Resolving dependencies...
Configuring my-project-0.0.0...
Rebuilding application... (using cabal)
Starting development server...
Starting devel application
Devel application launched: http://localhost:3000
devel.hs: getAddrInfo: does not exist (nodename nor servname provided, or not known)

参考:

2016年7月12日火曜日

[haskell][yesod] stackのnewコマンドで指定できるyesod関連templateの説明

現状、stackで指定できるyesod関連のtemplatesには以下のものがあります。どのtemplateに何が用意されているのか、知りたかったのですがどこにも説明されていないようなので、調べてまとめてみました。
% stack templates | grep yesod
yesod-hello-world (←現時点では削除されています)
yesod-minimal
yesod-mongo
yesod-mysql
yesod-postgres
yesod-postgres-fay
yesod-simple
yesod-sqlite

以下、各テンプレートの説明です。後に出てくるテンプレートほど内容が複雑になっています。テンプレートを指定して新しいプロジェクトを生成する場合は以下のコマンドを実行します。
% stack new プロジェクト名 yesod-???

yesod-hello-world

  • 最もシンプルなテンプレート
  • app.hs内のコードで"/"に対応するHomeハンドラだけが登録されている。
  • configurationファイルなどは一切なし。
  • Home
    • "Hello World"を表示するだけ
(2016/12/03 追記:githubの情報によるとこのテンプレートは削除されたようです。)


yesod-minimal

  • 次にシンプルなテンプレート。
  • 以下のroutesファイルによって、Home, Addの2つのハンドラが登録されている。
    • routes
    • /              HomeR GET
      /add/#Int/#Int AddR  GET
      
  • Home, Addハンドラの実装は以下の通り。
  • Home
    • 5+7の通常(HTML形式)リンクと、JSON形式のリンクを表示
  • Add
    • 5+7の結果を出力(通常はHTML形式でレスポンスを返す)
      • accept=application/jsonの場合のみJSON形式でレスポンスを返す

yesod-simple

  • 各DB用のテンプレートのベースになるテンプレート。
  • HTML, javascript, CSSの動的生成、リンク切れ検知など、DB接続と認証機能を除いて一通りの機能を確認できる。
  • 以下のフォルダ構成が生成される
    • app
      • 通常起動、devel起動用のエントリ関数
      • 通常起動は、引数で設定ファイル(yaml)を指定可能。
        % yesod-simple config/settings.yml
        
        以下のコマンドでdevel起動。
        % stack exec -- yesod devel
        
    • config
      • routesファイル(ハンドラリスト)
      • /static StaticR Static appStatic
        
        /favicon.ico FaviconR GET
        /robots.txt RobotsR GET
        
        / HomeR GET POST
        
        /comments CommentR POST
        
        
      • 設定ファイル
    • static
      • 静的ファイル置き場。デフォルトではcss, fontが配置される。
    • templates
      • テンプレートファイル置き場。Haskellコードを埋め込むことができる。
      • *.hamlet: HTML
      • *.julius: javascript
      • *.lucius: CSS
    • test
      • テストコード置き場。
      • 以下のコマンドでテスト実行。
        % stack test
        
  • 以下はコードが格納されるディレクトリ
    • Handler
      • 以下のHome, Common, Commentハンドラが生成されている。
      • Home.hs
        • ホーム画面定義。
      • Common.hs
        • FaviconR(favicon.ico)、RobotsR(robots.txt)への参照を定義。
        • これらはFoundation.hsから参照される。
      • Comment.hs
        • DB接続がないため単にエラーを表示するだけ。
    • Import
      • Import宣言まとめ。
    • Settings
      • staticディレクトリ内のファイル参照を記載しておき、コンパイル時にリンク切れのチェックを行う。

yesod-mongo/mysql/postgres/sqlite

  • 各DB用のconnectionコードと認証機能が追加されたテンプレート。
    • それぞれ、MongoDB, MySQL, PostgreSQL, SQLiteと接続するためのコードが自動生成されます。
  • yesod-simpleとの違いは下記の通り
    • config
      • route
      • 下記の通り"/auth"が追加されています。
        /static StaticR Static appStatic
        /auth   AuthR   Auth   getAuth
        
        /favicon.ico FaviconR GET
        /robots.txt RobotsR GET
        
        / HomeR GET POST
        
        /comments CommentR POST
        
      • setting.yml, test-setting.yml
        • DB接続のためのパラメタ追加。
    • Handler
      • Comment.hs
        • クライアントから送信されたコメントをDBに格納・参照する処理が定義されている。
        • 認証済みの場合はユーザー情報も合わせて格納。

yesod-postgres-fay

  • PostgreSQL+Fayを利用するためのテンプレート環境。
  • 調査が追いついていないのですが、FayではHaskellの仕様のサブセットがサポートされていて、Haskellのコードをjavascriptにコンパイルしてくれるとこのこと。

参考: