OS X Yosemite における LSOpenURLsWithRole エラーの解決
これまで自分はアプリケーションを起動したりファイルを開いたりするのにopen
コマンドを多用していたのですが、OS X Yosemiteにアップグレードしてから
LSOpenURLsWithRole() failed for the application /Applications/TextEdit.app with error -10810 for the file /some/file
というエラーが起こって困っていました。
このエラーはTmux上でopen
コマンドを使用した場合にのみ起こり、通常では問題なく使えていました。
open
コマンドじゃなくてアプリケーションなどでこのエラーが出ている人でも、tmux上で起動した時だけ上記エラーが出るようなら下の解決策が有効かもしれません。
ReattachToUserNamespaceのインストール
結論から言うと、ReattachToUserNamespaceというアプリケーションをインストールして、Tmuxのウィンドウ作成時にはそのコマンドを噛ませたシェルを起動するように変更すると問題がなくなりました。
恥ずかしながら自分は全く知らなかったのですが、このアプリケーションはTmux上でコピーしたものをMac OS Xのクリップボードにもコピーするための結構有名なものだそうです。
だからあんまりこのエラーで詰まっている人が見つからなかったのかな...
インストールはHomebrewでできます。とてもべんり。
$ brew install reattach-to-user-namespace
.tmux.confの変更
Tmuxのウィンドウ作成時に起動するアプリケーションは.tmux.conf
内でdefault-command
オプションを設定することで変更できます。
set-option -g default-command "which reattach-to-user-namespace > /dev/null && reattach-to-user-namespace -l $SHELL || $SHELL"
default-command
オプションはデフォルトでは空になっており、からの場合はログインシェルがデフォルトコマンドになります。
今回はreattach-to-user-namespace
コマンドがない場合でも正常に起動できるよう、which
コマンドの結果によってシェルの起動にReattachToUserNamespaceを噛ませるかどうかを変更するようにしました。
.tmux.conf
の変更が反映されない場合
ずっと前にもこれでハマってすっかり忘れていたのですが、.tmux.conf
の変更を反映させるためにはTmuxのプロセスを一度全て終了させる必要があるため、デタッチしたセッションがある場合には一度すべて殺さなければいけません。
Tmuxセッションが残っているかどうかはps
コマンドで確認します。
$ ps aux | grep taux
自分は以上でopen
コマンド使用時にエラーが起こることはなくなりました。
ついでに Mac OS X の tmux でクリップボードを使えるようにした(pbcopyとか) - 戦場のプログラマー とか tmuxとMacのクリップボードを共有する(copy-mode, vim) - Qiita を見てクリップボード共有の設定もして、今では穏やかにTmuxを使いまくっています。
Mac OS XのためのHaskell開発環境
はじめに
2012年ごろにとてもHaskellが流行り、たくさんの入門記事が書かれたものの、最近は関数型言語の物珍しさもあまりなくなり、むしろScalaやらC++11やらPythonの形に関数型の手法が定着しつつあるように感じます。
そんな中で出されたO'REILLY本「Haskellによる並列・並行プログラミング」を読み始めたわけですが、サンプルを動かそうとすると、これまでcabal install
でホイホイしてきたせいで多くのパッケージの依存関係が壊れていてイライラする。
というわけでHaskellの恐らく今最もスタンダードな環境構築を一からやり直したので、知っておいた方がいい情報と合わせてメモしてみました。 OSはMac OS X Mavericks (10.9.4)です。
GHC・Cabalのインストール
Haskellコンパイラの定番GHCとパッケージ管理ツールCabalのインストールです。Mac OSなので素直にHomebrewでインストールしました。Linux環境でもapptitudeやyumで簡単にインストールできるはず。
$ brew install ghc cabal-install
結構時間がかかるかと思いますが、特にエラーなくインストール出来ました。
なにか怒られてもエラーを読んだりbrew doctor
してみたりすればすぐに出来るかと思います。
ちなみにインストールできたバージョンは + GHC version 7.8.3 + Cabal library 1.20.0.2 + cabal-install 1.20.0.3 でした。
また、インストールしたcabal
コマンドを使うために.bash_profile
などお使いのシェルの起動時スクリプトで$HOME/.cabal/bin
を環境変数PATH
に追加しておくと楽でしょう。
GHCの使い方
GHCはghc
コマンドでソースコードのコンパイルが出来る他、ghci
コマンドで対話的な実行環境として実行できます。
主にghci
コマンドは後述するcabal repl
越しに関数の動き方の確認などに使用することとなるでしょう。
また、ghcコマンドにはGCCと同じように多彩なオプションが用意されています。 詳細はGHCのマニュアルを見ればわかりますが、以下に便利なオプションをいくつか書いておきます。
-o
$ ghc -o hoge Hoge.hs
基本ですが、一応。出力する実行ファイルの名前を指定します。 指定しない場合の出力ファイル名はmain関数を含むソースファイルのサフィックスを除いたものとなります。
-On
最適化オプションです。0~2の3段階があり、0は最適化なし、2は可能な限り最適化。指定しない場合は1となります。
-S
$ ghc -S Hoge.hs
...お楽しみオプションです。 ソースコードのコンパイルをアセンブルする前の状態(=アセンブリファイルを生成した状態)で止めます。 これによってGHCがどんなアセンブリを生成しているのか知ることができますが、ちょっとしたコードでもかなりの行数となるため、アセンブリコードからプログラムの性能を予測する、というのは現実的ではないかと思います。
ちなみに、GCCと同じように-c
オプションでリンク前までコンパイルすることもできますが、GHCでは渡されたソースコードにmain関数が含まれていなかった場合は自動的にオブジェクトの生成まででストップするため、断片コードのコンパイル時に明示的に-c
を付ける必要はありません。
cabalの使い方
Cabalを使うとRubyのBundlerのようにパッケージを管理することができます。 以下によく使用するコマンドを列挙しておきます。 なお、各コマンドのさらなる詳細は
$ cabal <コマンド名> --help
とするとみることができます。
init
$ cabal init
とすると対話的に自分の作成するプロジェクトのための.cabal
ファイルを作成することができます。
configure, build
$ cabal configure $ cabal build
とするとカレントディレクトリのプロジェクトを.cabal
ファイルに基づいてビルドすることができます。
Makefileよりも最適化された形があるのはとても嬉しいですね。
update
$ cabal update
とすると最新のパッケージリストを取得できます。パッケージのインストールを行う前に実行します。
list
$ cabal list <文字列>
とするとbrew search
のように<文字列>
にマッチするcabalでインストール可能なパッケージの一覧が取得できます。
info
$ cabal info <パッケージ名>
とするとbrew info
のように<パッケージ名>
で指定したパッケージの詳細を表示します。
sandbox
$ cabal sandbox init
とするとbundle install
の--path
オプションのようにカレントディレクトリ以下でパッケージを個別に管理します(かつては同様の目的でcabal-dev
パッケージが使用されていました)。
sandboxを指定せずにcabal install
をしてしまうと、プロジェクトごとに使用するパッケージのバージョンを管理できなくなるため、ひどいことになります。
パッケージのインストール前には必ずプロジェクト用のディレクトリを作成し、cabal sandbox init
を叩くようにしましょう。
install
$ cabal install <パッケージ名>
とすると<パッケージ名>
で指定したパッケージをインストールします。
前述のsandboxを使用していると、sandboxで指定されたディレクトリ(デフォルトでは./.cabal-sandbox
)以下にパッケージがインストールされます。
-jn
などとすると-jn
のn
で指定した数のジョブを同時に実行してくれるため、コアをフルに使ってインストールすることができます。
exec
$ cabal exec <コマンド名> -- <オプション>
とすると<コマンド名>
で指定したsandbox内にインストールしたパッケージの実行ファイル(デフォルトでは./cabal-sandbox/bin
以下にあるもの)を<オプション>
付きで実行できます。
これもRubyのBundlerと似た機能ですね。
repl
$ cabal repl
とすると、カレントディレクトリのsandboxにインストールしたパッケージと.cabal
ファイルで指定されているmain関数のあるファイルをロードしたghci
を起動することができます。
これはパッケージで提供されている関数などの挙動を確認する上で一番手っ取り早く、便利な方法になります。
ghc-pkgの使い方
ghc-pkg
コマンドでインストール済みのパッケージに対する操作を行うことができます。
こちらも以下によく使うものを列挙しておきます。
list
$ ghc-pkg list
とすると、グローバルスコープのインストール済みパッケージを一覧できます。
この時、ghcのインストール場所のpackage.conf.d
と$HOME/.cabal
以下のpackage.conf.d
の2つに分かれてパッケージリストが表示されますが、前者がシステム用のパッケージ、後者がユーザのインストールしたパッケージのようです。
なお、Cabalを用いて
$ cabal exec ghc-pkg list
とするとsandboxにインストールしたパッケージも一覧することができます。
check
$ ghc-pkg check
とすると、依存関係の壊れたパッケージを一覧することができます。
field
$ ghc-pkg field <パッケージ名> <フィールド名>
とすると、<パッケージ名>
に指定したパッケージの.conf
ファイルの<フィールド名>
に指定したフィールドの値を表示できます。
これを用いて
$ ghc-pkg field <パッケージ名> exposed-modules
とすれば、指定したパッケージで提供されるモジュールの一覧を表示できます。
unregister
$ ghc-pkg unregister <パッケージ名>
とすると、指定したパッケージをアンインストールすることができます。
おわりに
これらを知っておくだけで、かなりHaskellでの開発が楽しくなるかと思います。
というかたのしい。
参考サイト
HaskellのYesodでWebアプリ開発入門 (4)
はじめに
HaskellでYesodを使ってWebアプリケーションを作ってみている際のメモ記事です。
前回はビューを構成するためのウィジェットについて学び、CSSライブラリのバージョンアップを行いました。
Hamletを用いたレイアウト
今回はいよいよ、Hamletファイルを編集していきます。
なお、templates/layout/wrapper.hamlet
のほとんどを占めているIE対策コードやGoogle Analyticsのコードについてはここでは触れません。
サイトの用途に依っては重要ではありますが、それぞれの環境によって自分の思うように整形したりIE対策をどこまでやるか考えればいいかと思います(ここのサンプルコードではIE9以降のブラウザのみへの対応を考えています)。
さっそく、templates/layout/application.hamlet
からはサンプルコードのために書かれていたコードを取り除いて標準的なHTML5のテンプレート的な内容に書き換え、templates/layout/wrapper.hamlet
からは不要なコードを削除して単純化を行いました。
なお、ナビゲーションバーの内容がHomeとContactだけでは寂しいので、2回目で行なったのと同じ手順でAboutとHelpのハンドラとビューも追加しました。 この追加の仕方は、完全にrailstutorial.orgのパクリですね。
templates/layout/application.hamlet
$newline never <div #main> <header> <h1> <a href=@{HomeR}>Sample App <nav> <ul> <li> <a href=@{AboutR}>About <li> <a href=@{HelpR}>Help <li> <a href=@{ContactR}>Contact ^{widget} <footer> <p>© 2014 demmy_s
templates/layout/wrapper.hamlet
$newline never $doctype 5 <html lang="en"> <head> <meta charset="UTF-8"> <title>#{pageTitle pc} | Yesod Sample <meta name="description" content="Sample Application of Yesod"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> ^{pageHead pc} $maybe analytics <- extraAnalytics $ appExtra $ settings master <script> if(!window.location.href.match(/localhost/)){ window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']]; (function() { \ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; \ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; \ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); } \<!--[if lt IE 9]> \<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> \<![endif]--> <body> ^{pageBody pc}
とりあえず編集してページを見ると、嬉しい事にしっかりとルーティングがなされた汚い(CSSのない)ページを楽しむことができます。
なお、FOOTERタグ内のコピーライトについて、Scaffoldで生成されたものではconfig/settings.yml
で記述したものを表示するようになっていたのですが、通常それほど書き換える需要もないかと思いますので、単にベタ書きに変更しました。
不要になったextraCopyright
などが気になる場合はSettings.hs
の68行目以降を以下のように編集し、config/settings.yml
からcopyright
の行を取り除いてしまえばいいでしょう。
data Extra = Extra { extraAnalytics :: Maybe Text -- ^ Google Analytics } deriving Show parseExtra :: DefaultEnv -> Object -> Parser Extra parseExtra _ o = Extra <$> o .:? "analytics"
また、Foundation.hs
のdefaultLayout
内にあったmmsg <- getMessage
という記述もサンプルアプリケーション用の記述を消したので必要無くなり、コンパイル時にWarningとして出てしまうので、消しておくといいでしょう。
サンプルの方では前回Hamletの構文として紹介しなかったものが続々と出てきましたので、順に説明しておこうかと思います。
糖衣構文
まず、それぞれの1行目とwrappter.hamlet
の2行目はただの糖衣構文です。
$newline never
とすると、最終的に生成するHTMLから改行とインデントを除き、ファイルサイズを小さくしてくれるので、基本的には書いておくといいかと思います。
$doctype 5
は容易に想像がつく通り、HTML5のDOCTYPE宣言を挿入してくれるだけの構文です。
なお、DOCTYPE宣言は古いYesodのバージョンでは!!!
と書くだけで挿入されていましたが、わかりづらいために現在では推奨されていません。
また、BODYタグ直下のDIVタグで用いている#main
という表記はid="main"
の糖衣構文です。Hamletでは同じようにクラスの指定も.main
のように行うことができます。
エスケープ記号
Hamletテンプレートファイル内でスペース以外を含まない行頭に\
を記述した場合にはその行はエスケープされ、\
の後からスペースを含まない行末までに記述した内容がそのまま最終的なHTMLに含められます。
また、上記のサンプルでは使用されていませんが、行末に#
を記述することで、その#
以前に記述されたスペースをエスケープされたとしてそのまま最終的なHTMLに含ませることができます。
これらの含まれたコードは少し複雑にも思えますが、コメントや他のサービスから提供される埋め込み用コードをシンプルな記述で含ませられるのは選択肢として悪くない気がします。
内挿
続いて、全体に散りばめられている#
や^
、@
で始まり{ }
が続いている構文は内挿(Interpolation)です。これまでにも少し触れましたが、Haskellのコードを型安全に埋め込むための構文であり、{ }
の前の記号は埋め込まれる値の型を特定しています。
以下に記号と内挿できる値の対応関係を書いておきます。
記号 | 内挿できる型 |
---|---|
# |
ToHtml クラスのインスタンスとなっている型 |
^ |
Hamletテンプレートを表す型 |
@ |
config/routes などで作成されたURLの型 |
なお、@{ }
で内挿されるURLの文字列はドメイン名までを含む完全なURLとなります。
ひとまずHamletの説明は以上です。 次は、CSSを出力するテンプレートを用いてナビゲーションバーとフッターのデザインをしてみます。
Luciusを用いたデザイン
LuciusはCSSの拡張としてYesodで用意されているCSSテンプレートエンジンの1つですが、構文はほとんどRoRでのSassやLessと同じようなものとなっていますので、特にとっつきにくさもなく導入できるかと思います。
今回もHamletの時と同様にまずサンプルプログラムのために書いたLuciusファイルを紹介したいと思います。
Foundation.hs
の中でのtemplates/layout/application.hamlet
の展開は2回目の記事で紹介したwidgetFile
関数を使用して行われているので、これを利用するために、編集するLuciusファイルはtemplates/layout/application.lucius
という名前で保存します。
/* * Global */ @defaultColor: #222; @inverseColor: #fff; html { color: #{defaultColor}; font-size: 100%; height: 100%; } body { height: 100%; } h1,p,ul { margin: 0; } h1 { font-weight: normal; } a { color: #{defaultColor}; text-decoration: none; } ul { list-style: none; padding-left: 0; } /* * Layout */ @sidePadding: 3%; @headerBackColor: #222; @buttonColor: #bbb; @buttonActiveColor: #fff; @footerLineColor: #ccc; #main { width: 100%; margin: 0 auto; padding-bottom: 3em; } #main>header { display: table; width: 100%; padding: 0.5em 0; background-color: #{headerBackColor}; >* { display: table-cell; vertical-align: middle; } >h1 { font-size: 1.3em; width: 10em; padding-left: #{sidePadding}; >a { color: #{buttonActiveColor}; } } >nav { padding-right: #{sidePadding}; >ul { display: table; table-layout: fixed; float: right; >li { display: table-cell; font-size: 1em; vertical-align: middle; text-align: center; width: 5em; >a { color: #{buttonColor}; } >a:hover { color: #{buttonActiveColor}; } } >li.active>a { color: #{buttonActiveColor}; } } } } #main>section { margin: #{sidePadding}; } #main>footer { text-align: right; padding: 0.7em 1.5% 0 1.5%; margin: 3em 1.5% 0 1.5%; border-top: 1px solid #{footerLineColor}; } /* * Components */ @normalBackColor: #ccc; @redBackColor: #c0392b; @greenBackColor: #27ae60; @blueBackColor: #2980b9; .red { color: #{inverseColor}; background-color: #{redBackColor} !important; } .green { color: #{inverseColor}; background-color: #{greenBackColor} !important; } .blue { color: #{inverseColor}; background-color: #{blueBackColor} !important; } .button { display: inline-block; padding: 0.7em 1em; border-radius: 5px; background-color: #{normalBackColor}; }
Lucius内ではHamletと同様に#{valiable}
で変数を展開できるだけでなく、@valiable: value;
という構文によって変数を定義することができます。もちろん、url( )
による指定では@{ }
という形の内挿を記述することで型安全なURLを埋め込むことができます。
また、SassやLessと同じように{ }
を入れ子にすることで、子要素へのスタイルの適用を記述することができます。
なお、LuciusMixin
テンプレートを利用することでミックスインを簡単に記述することもできるのですが、それについてはまた出てきた際に説明します。
このLuciusファイルの適用でナビゲーションバーとフッターがいい感じにデザインされましたが、まだトップページがHello, Wold!
だけの悲しい状態ですので、またまたrailstutorial.orgのデザインを真似てtemplates/home/index.hamlet
とtemplates/home/index.lucius
も編集してみました。
<section> <h1>Welcome to the Yesod Sample <p>This is the home page for the Yesod sample application. <a href="#" .button .blue>Sign up now!
@topBackColor: #eee; #main>section { background-color: #{topBackColor}; text-align: center; padding: 3em; border-radius: 10px; >h1 { font-weight: bold; font-size: 2.5em; margin-bottom: 0.2em; } >p { font-size: 1.2em; } >a { margin-top: 1em; } }
おもいっきりパクリではありますが、なかなか良い感じになりました。
それでは、デザインでモチベーションを保ちつつ、残りの構文についても説明してしまいたいと思います。
Cassiusの構文
CassiusはLuciusの構文をよりHaskellらしくしたもので、スコープを表現するために{ }
ではなくインデントを用い、行の終りを表現するために;
ではなく改行を用います。
他の部分はLuciusと全く同様になっており、実際内部的にもLuciusと同じパーサが用いられているだけです。
原理主義的な発想ではCassiusを使用するべきなのかもしれませんが、Luciusの方が既存のCSSもそのまま一緒に記述できるため、ここでは一貫してLuciusのみを使用していきます。
Juliusの構文
Juliusは単純に、Hamletと同様の内挿を行えるようにしたJavaScriptの拡張です。 URLの記述や変数によるハンドラからの値の受け渡しができるというだけですが、本格的なアプリケーションを構築していく上では相当助けとなる機能でしょう。
おわりに
今回はYesodのフロントエンドを支えるShakespearen TemplateのDSL構文を順に見ていきました。
これで、とりあえず静的なWebサイトであれば自由に作れるくらいにはYesodがわかってきたかと思います。
次の内容についてはまだ考えていませんが、そろそろ本格的にYesodアプリケーションで遊ぶための環境をつくりたいので、本番環境の用意と結合テストの書き方などを見ていこうかと考えています。
HaskellのYesodでWebアプリ開発入門 (3)
前回の記事
HaskellでYesodを使ってWebアプリケーションを作ってみている際のメモ記事です。
ずいぶんと間が開いてしまいましたが、前回はディレクトリ構造のチェックとトップページ・コンタクトページへのルートの追加を行いました。
ウィジェット
今回は前回作ったトップページとコンタクトページをそれなりの見た目に整形していこうと思います。 ただ、DSLの構文を見る前に、Yesodでのビューの扱い方について少し知っておく必要がありました。 Yesodでは他のフレームワークとは少し違う、ウィジェットと呼ばれる概念を使ってページの構成を行なっています。
ウィジェットはページテンプレートで扱うコンテンツの柔軟性を向上し、テンプレートファイルを複数に分割したり、例外的なビューを作るたびにテンプレートファイル内に分岐を追加するようなことを防いでくれる、シンプルですが非常に有用な概念です。
ウィジェットではページのコンテンツを以下の7種類に分類して保持します。
- ページタイトル
- 外部スタイルシート
- 外部JavaScript
- スタイルシートファイル
- JavaScriptファイル
- HEADタグ内のコンテンツ
- BODYタグ内のコンテンツ
Hamletなどで作成したページコンテンツはtoWidget
関数を用いるだけでウィジェットに変換することができ、その際にちゃんとCassiusやLuciusのデータはSTYLEタグで囲まれ、JuliusのデータはSCRIPTタグで囲まれます。
toWidget
関数ではデフォルトで、CassiusやLuciusのデータはHEADタグ内のコンテンツ、HamletやJuliusのデータはBODYタグ内のコンテンツとしてウィジェットに変換しますが、代わりに以下の関数を用いることで、変換する種類を特定できます。
関数 | 変換する種類 |
---|---|
setTitle | ページタイトル |
addStylesheetRemote | 外部スタイルシート |
addScriptRemote | 外部JavaScript |
addStylesheet | スタイルシートファイル |
addScript | JavaScriptファイル |
toWidgetHead | HEADタグ内のコンテンツ |
toWidgetBody | Bodyタグ内のコンテンツ |
ちなみに、「外部〜」と「〜ファイル」は、指定するURLが外部の絶対URLであるかプロジェクト内のルートであるか、というだけの違いです。
それで、これをどう使うか、ですが、ここで前回さらっと流してしまったdefaultLayout
関数もう一度見てみます。
(コメントを省略しています)
defaultLayout widget = do master <- getYesod mmsg <- getMessage pc <- widgetToPageContent $ do $(combineStylesheets 'StaticR [ css_normalize_css , css_bootstrap_css ]) $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
なお、この中のwidgetToPageContent
は大体以下の様な定義の関数です。
widgetToPageContent :: Widget -> Handler (PageContent url) data PageContent url = PageContent { pageTitle :: Html , pageHead :: HtmlUrl url , pageBody :: HtmlUrl url }
(Yesod Web Frameword Book - Using Widgetsより引用)
そして、長過ぎるので貼り付けることはしませんが、templates/default-layout-wrapper.hamlet
では<title>#{pageTitle pc}
, ^{pageHead pc}
, ^{pageBody pc}
という行があります。
つまり、defaultLayout
関数はwidgetToPageContent
関数に全てのウィジェットを生成するdo構文の式を適用して、ページのコンテンツとして埋め込める形にウィジェット達を整形してpc
という変数へ束縛し、その整形結果を埋め込むよう目論んだtemplates/default-layout-wrapper.hamlet
を変数pc
のスコープ内でwithUrlRenderer
関数に適用することで、その式が評価された時にはバラバラに追加されたウィジェット達がレイアウトの中に整然と並ぶよう仕向けているのです。
まだ上の7つの関数の使い所はわかりづらいので、defaultLayout
関数の呼び出し元も確認してみます。
前回書いたHandler/Home.hs
と、補足としてtemplates/default-layout.hamlet
も以下に貼り付けました。
{-# LANGUAGE OverloadedStrings #-} module Handler.Home where import Import getHomeR :: Handler Html getHomeR = defaultLayout $(widgetFile "home/index")
$maybe msg <- mmsg <div #message>#{msg} ^{widget}
defaultLayout
関数に適用されている$(widgetFile "home/index")
という式はdefaultLayout
関数内でwidget
という変数に束縛され、それがdefaultLayout
関数内のdo構文の中で展開されているtemplates/default-layout.hamlet
の中に埋め込まれています。
つまり、defaultLayout
関数の引数として先の関数達をつなげたdo構文を渡せば、それが最終的なHTMLにもうまく反映される、ということです。実際にHandler/Home.hs
とHandler/Contact.hs
を編集してみます。
{-# LANGUAGE OverloadedStrings #-} module Handler.Home where import Import getHomeR :: Handler Html getHomeR = defaultLayout $ do setTitle "Home | Yesod Sample" $(widgetFile "home/index")
{-# LANGUAGE OverloadedStrings #-} module Handler.Contact where import Import getContactR :: Handler Html getContactR = defaultLayout $ do setTitle "Contact | Yesod Sample" $(widgetFile "contact/index")
ちゃんとタイトルが付いたかと思います。
もちろんこれらのことはRoRなどでもできないことはありませんが、遅延評価とモナドの力がなければこれほど自然には書けず、多くの暗黙的ルールをドキュメントで学ばないと使いこなせない代物になってしまっていたのではないでしょうか。
Hamletの構文
YesodではHTMLのテンプレート言語としてHamletを用いているので、まずはその構文をについて知る必要があります。 細かいことはYesod Web Framework BookのShakespearean Templatesの項に書いありますが、Hamletの特徴としては大体以下のような点があるように思いました。
- インデントでタグのスコープを見るために閉じタグが要らない
- 型安全にHaskellの値を埋め込むことができる
- 呼び出されたスコープを引き継ぐ(引数で渡したりすることなく、束縛されている値や関数を参照できる)
- 手続き的なディレクティブで分岐やループ、パターンマッチを扱える
1つめはそのままですが、2つめの埋め込める値は文字列や数字だけでなく、ToHtml
クラスのインスタンスとなっている全ての型の値となります。
ToHtml
クラスの型はtoHtml
関数とfromHtml
関数でHtml
型と行き来できる必要があります。
また、3つめはこれまで見てきたコードからも分かる通り、テンプレートファイル内のスコープはそのテンプレートが展開される関数のスコープに属することを表します。 RoRでは呼び出したコントローラを表現するクラスがスコープとなっていたので、クラス変数の汚染があり、特定のアクションのための変数を他のアクションから分離できませんでした。 それを、そもそもHaskellではそういったスコープが存在しないので、普通にしていると引数を使う必要が出てきますが、TemplateHaskellでHamletファイルを評価する関数を呼び出したスコープのクロージャにすることでいいとこ取りをした、ということですかね。
4つめが非常に煩雑なのですが、Hamletでは分岐やループ、パターンマッチといった構文を必要とする動的なコンテンツの挿入に$
記号から始まる特殊なディレクティブを用います。詳細についてはYesod Web Framework BookのHamlet Syntaxの項に網羅されているので、ここでは今後出てきた都度に紹介する形にしようかと思います。
また、HamletではHTMLをそのまま書くよりもIDやクラス、その他の属性値を簡単に設定できるようになっていますが、これらも今後出てきた際に都度紹介します。
レイアウトファイルの移動
さて、説明が長くなってしまいましたが、Hamletを用いたコーディングの方法がだいたいわかったので、主にレイアウトファイルをいじって、前回作成したトップページとコンタクトページをまあまあまともなページにしてみたいと思います。
まず、ディレクトリ階層が浅いと後々不便になるかと思うので、レイアウト用のHamletファイルをlayout
ディレクトリ以下に移動し、名前も分かりやすいものに変更します。
$ cd templates $ mkdir layout $ mv default-layout.hamlet layout/application.hamlet $ mv default-layout-wrapper.hamlet layout/wrapper.hamlet
この変更にともなって、Foundation.hs
の中のdefaultLayout
関数で指定しているファイル名を変更する必要があります。
pc <- widgetToPageContent $ do $(combineStylesheets 'StaticR [ css_normalize_css , css_bootstrap_css ]) $(widgetFile "layout/application") giveUrlRenderer $(hamletFile "templates/layout/wrapper.hamlet")
ところで、ファイルを編集するとyesod devel
のプロセスがカタカタと動いてリビルドした上でエラーがあったら提示してくれるというのは、RoRでGuardを使ってテスト駆動開発をしている時と同じくらいの快適さと安心感ですね。
まだテストコードは1行も書いていないというのに...。こんなんだったら、ユニットテストなどほとんど必要なくなるように思えます。
Normalize.cssのバージョンアップとBootstrap.jsの不要な箇所の削除
次に、ずっと放置していましたがこの際なので、Normalize.cssを最新版にバージョンアップします。ついでにBootstrap.jsもバージョンアップしようかと思いましたが、個人的にこれが入るとCSSが格段に書きづらくなるため、ひとまずBootstrap.jsは便利なGlyphicon部分だけ残して、後は削除してしまうこととしました。本記事を描いた時点でNormalize.cssはバージョン3.0.1、Bootstrap.jsはバージョン3.2.0でした。
単に各サイトからファイルをダウンロードして、Bootstrap.jsはコピーライト表示とGlyphiconに関する部分だけを抜き出したファイルを作成し、必要なファイルをstatic
ディレクトリ以下に配置します。
筆者のstatic
ディレクトリ内は以下の様になりました。
static ├── combined │ └── rMJ_CwK2.css ├── css │ ├── glyphicons.css │ └── normalize.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── tmp ├── autogen-SWBGXCgb.js └── autogen-vozS04Wr.css 3 directories, 5 files
これに伴ってFoundation.hs
のdefaultLayout
関数の中身を再び多少変更します。
pc <- widgetToPageContent $ do $(combineStylesheets 'StaticR [ css_normalize_css , css_glyphicons_css ]) $(widgetFile "layout/application") giveUrlRenderer $(hamletFile "templates/layout/wrapper.hamlet")
このcombineStylesheets
およびcombineScripts
関数はSettings/StaticFiles.hs
で定義されています。
-- | This generates easy references to files in the static directory at compile time, -- giving you compile-time verification that referenced files exist. -- Warning: any files added to your static directory during run-time can't be -- accessed this way. You'll have to use their FilePath or URL to access them. $(staticFiles Settings.staticDir) combineSettings :: CombineSettings combineSettings = def -- The following two functions can be used to combine multiple CSS or JS files -- at compile time to decrease the number of http requests. -- Sample usage (inside a Widget): -- -- > $(combineStylesheets 'StaticR [style1_css, style2_css]) combineStylesheets :: Name -> [Route Static] -> Q Exp combineStylesheets = combineStylesheets' development combineSettings combineScripts :: Name -> [Route Static] -> Q Exp combineScripts = combineScripts' development combineSettings
また、その上のコメントに書かれている通り、css_normalize_css
やcss_glyiphicon_css
はSettings/StaticFiles.hs
をコンパイルした際に定義される、静的ファイルのパスを表現する変数です。
yesod devel
のプロセスを起動しっぱなしにしている場合、static
ディレクトリ以下の変更だけではSettings/StaticFiles.hs
はリビルドされないため、Vimでファイルを開いて:w
で保存するなどしないと、上の変更はエラーになります。
なお、確実にリビルドさせるにはcabal build
コマンドをプロジェクトルートで走らせます。
おわりに
HamletファイルとCassiusファイルを編集してそこそこ使い物になりそうなレイアウトを作るところまで行くつもりでしたが、思いの外説明が長くなってしまったのでここで一度記事を切ろうかと思います。 次回はレイアウトをして、静的なWebページとして公開できるくらいまでアプリケーションの完成度を高めます。
HaskellのYesodでWebアプリ開発入門 (2)
前回の記事
HaskellでYesodを使ってWebアプリケーションを作ってみている際のメモ記事です。
前回はインストールと初回起動までやりました。
Yesodのディレクトリ構造
前回は何も考えずに起動しただけでしたので、次はお決まりの流れでScaffoldのディレクトリ構造の確認をしたいと思います。
現在プロジェクトディレクトリに含まれるファイルは以下の様な感じですね。Homebrewでインストールしたtree
コマンドの結果からdist
ディレクトリを省略したものです。
sample ├── Application.hs ├── Foundation.hs ├── Handler │ └── Home.hs ├── Import.hs ├── Model.hs ├── Settings │ ├── Development.hs │ └── StaticFiles.hs ├── Settings.hs ├── app │ ├── DevelMain.hs │ └── main.hs ├── config │ ├── client_session_key.aes │ ├── favicon.ico │ ├── keter.yaml │ ├── models │ ├── mysql.yml │ ├── postgresql.yml │ ├── robots.txt │ ├── routes │ └── settings.yml ├── deploy │ └── Procfile ├── devel.hs ├── dist │ ├─ ... │ ... ├── messages │ └── en.msg ├── static │ ├── combined │ │ └── rMJ_CwK2.css │ ├── css │ │ ├── bootstrap.css │ │ └── normalize.css │ └── img │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png ├── templates │ ├── default-layout-wrapper.hamlet │ ├── default-layout.hamlet │ ├── homepage.hamlet │ ├── homepage.julius │ └── homepage.lucius ├── tests │ ├── HomeTest.hs │ ├── TestImport.hs │ └── main.hs └── yesod-devel │ ├── arargs.txt │ └── ghcargs.txt └── yesod-sample.cabal 23 directories, 148 files
RoRをある程度触ったことのある人ならなんとなく想像がつくかと思いますが、ざっくりと分かる範囲でファイルの役割を書き出してみます。 ファイルの中身を見ながら読んでいただけるとなんとなくわかっていただけるかと。
(以下の表には想像も含まれていますので、正しいことが分かり次第更新します)
ディレクトリやファイル | 役割 |
---|---|
Application.hs | app/main.hsから呼び出されるアプリケーションの本体を作る関数makeApplication が定義されている |
Foundation.hs | Application.hsでインスタンスが作成されるアプリケーションの本体が定義されている |
Handler/ | いわゆるコントローラの働きをするHandlerの定義ファイルが置かれるディレクトリ。中のファイルの詳細は後で書くので省略 |
Import.hs | プロジェクト内のすべてのモジュールで読み込まれるYesodなどのモジュールをimportしたモジュール |
Model.hs, config/models | O/R mapperであるPersistentで使用されるモデル関連ファイル |
Settings/Development.hs | Development環境用を区別するための関数が定義されている |
Settings/StaticFiles.hs | 静的ファイルの読み出しのための関数を定義したファイル |
Settings.hs | いろいろな設定が書かれているファイル。静的ファイルのURL設定やHTMLファイルなどの構文解析に関する情報が書かれている |
app/DevelMain.hs | ghciでアプリケーションの環境を使うためのメイン・エントリ・ポイント |
app/main.hs | 通常のアプリケーションのメイン・エントリ・ポイント |
config/client_session_key.aes | コンパイル時に自動生成されるユーザのクッキー情報を暗号化するためのファイル |
config/favicon.ico, config/robots.txt | 普通のファビコンやらrobots.txtやら |
config/keter.yaml, deploy/Procfile | デプロイに関するファイル。後者にはHerokuへデプロイするための情報が書かれている |
config/mysql.yml, config/postgresql.yml | 前回も紹介したデータベース設定に関するファイル |
config/routes | URLとコントローラの対応関係を記述するファイル |
config/settings.yml | ルートURLやポートなど、アプリケーション全体に関する情報が記述されたファイル |
dist/build/sample/sample | アプリケーションの実行ファイル。本番環境ではこの実行ファイルのプロセスからデーモンプロセスを作る |
devel.hs | デバッグ環境のアプリケーションのメイン・エントリ・ポイント |
massages/ | i18n(国際化対応)のための辞書ファイルを置くディレクトリ |
sample.cabal | 前回紹介したcabalのための設定ファイル |
static/ | CSS・JavaScriptライブラリや画像などの静的ファイルを置くディレクトリ。デフォルトではBootstrapのバージョン2.3.2(古い!)のCSSと画像ファイル、Normallize.cssのバージョン2.1.2(古い!)とそれらCSSをつなげたCSSファイルが含まれている |
templates | 動的な部分を含む(テンプレートを利用する)HTMLやJavaScript、CSSファイルを置くディレクトリ。Yesodでは標準でHTMLにHamlet、JavaScriptにJulius、CSSにLuciusかCassiusという言語を使うようになっている |
tests/ | テストコードを置くディレクトリ。Ruby並に自然な形でテストコードが書かれていてワクワクする |
yesod-devel/ | デバッグ環境で実行する際の設定を記したファイル、かな |
思っていたよりも見通しはスッキリ。 Haskellの構文の柔軟さのお陰で全体的にファイルの可読性が高く、性能を追求したりし始めるまでは案外簡単に開発出来てしまうのでは? という期待が膨らみます。
ghci
表のDevelMain.hs
の説明にちょろっと書きましたが、Yesodではアプリケーション実行時と同じ環境でghciを利用するための設定が追加されています。
DevelMain.hs
の冒頭に説明がありますが、ghciを
$ cabal repl --ghc-options="-O0 -fobject-code"
として実行すると、アプリケーション使用時にロードされるパッケージを全てロードした状態でHaskellの対話環境を使えます。
とてつもなく便利ですね。
TemplateHaskell
ファイルを見ていくと、少ししかHaskellを学んだことがない人は「おややっ??」となったのではないでしょうか。
きっとこれを編集すればいいんじゃないかな、というHandler/Home.hs
にも以下の様な見慣れない(というかjQueryみたいな)式があります。
$(widgetFile "homepage")
これはTemplateHaskellというHaskellにおけるマクロのようなもので、こいつを使うとLispのquoteとeval, funcallでやるようなことが書けてしまうというすぐれものです。
widgetFile
はYesodの関数ではなく、Scaffoldで生成されたSettings.hs
の中に含まれており、型を確認することができます。
widgetFile :: String -> Q Exp widgetFile = (if development then widgetFileReload else widgetFileNoReload) widgetFileSettings
つまり、先の式はwidgetFile
関数が"homepage"
というString
を元にQuoteモナド(Lispのquoteされた式みたいなもの)をつくり、それを$()
で評価した結果を表していたわけですね。
Yesod公式のScaffoldに関するエントリによると、widgetFile
はtemplates
ディレクトリ以下にある適用した文字列を名前として持つHaml, Julius, Lucius, Cassiusファイルをパースして1つのウィジェット(アプリケーション内でのレスポンスの表現?)にまとめてくれる関数のようです。
この関数はFoundation.hs
内のApp
インスタンスの定義にあるdefaultLayout
関数でも使われています。
defaultLayout widget = do master <- getYesod mmsg <- getMessage -- We break up the default layout into two components: -- default-layout is the contents of the body tag, and -- default-layout-wrapper is the entire page. Since the final -- value passed to hamletToRepHtml cannot be a widget, this allows -- you to use normal widget features in default-layout. pc <- widgetToPageContent $ do $(combineStylesheets 'StaticR [ css_normalize_css , css_bootstrap_css ]) $(widgetFile "default-layout") giveUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
コメントにも書かれていますが、templates/default-layout-wrapper.hamlet
にページ全体のレイアウト、templates/default-layout.hamlet
にBODYのレイアウトが記述されているようです。
ここまで見てから再度Handler/Home.hs
のgetHomeR
関数の全体を見ると
getHomeR :: Handler Html getHomeR = do (formWidget, formEnctype) <- generateFormPost sampleForm let submission = Nothing :: Maybe (FileInfo, Text) handlerName = "getHomeR" :: Text defaultLayout $ do aDomId <- newIdent setTitle "Welcome To Yesod!" $(widgetFile "homepage")
ざっくり、default-layout-wrapper.hamlet
, default-layout.hamlet
, homepage.hamlet
, homepage.julius
, homepage.lucius
をくっつけて、widgetFile
関数を呼ぶまでに用意したformWidget
やらformEnctype
やらsubmission
やらやらが展開されて、レスポンスとして返されてるんだろうなあ、というくらいは分かるようになりました。
それではいよいよ、ここらへんをいじって静的ファイルを自分なりに作り替えてみたいと思います。
ちなみに、Template Haskellの簡単な説明ならruiccさんの記事がわかりやすかったです。
プロジェクトのGit管理
ところで、プロジェクトの生成の際に.gitignore
ファイルも一緒に作成されるので、編集を始める前にプロジェクトのディレクトリをGitで管理しておくと、この後にScaffoldのコマンドで行なった変更点をgit diff
で見れたり、簡単に変更をやめたりできるので便利です。
ただ、もしプロジェクトをオープンソースで管理したりするのであれば、パスワードが含まれるconfig/mysql.yml
やconfig/postgresql.yml
はデフォルトでは.gitignore
に含まれていないので追加して、本番環境へデプロイする際に別個で送信するべきかと思います。
(Capistranoを使うのはなんだかちょっとなあ、っていう感じなんですけどね...)
Vimのシンタックスハイライト
編集を始める前に準備をもう一点。 テンプレート用の言語達をハイライトしてくれるVimプラグインをインストールしておきます。
幸い、pbrisbinさんのhtml-template-syntaxというレポジトリのプラグインを追加すれば、簡単にテンプレートファイル達をシンタックスハイライトすることができました。
Warningの修正
yesod init
でダウンロードされるscaffoldではYesodの最新版に対応していないらしく、最初からいくつかのWarningがでてしまっているので、それを修正します。
まずはModel.hs
内でimport Prelude
と書かれていますが、Preludeはデフォルトでimportされるので消去します。
次に、Foundation.hs
内で使用されているgiveUrlRenderer
がdeprecatedとなっているので、代わりにwithUrlRenderer
を使用します。
これで鬱陶しいWarningが消えたはずです。 なんで修正されないんだろう...
テンプレートファイルの書き換え
まずは単に、Scaffoldで用意されているHomeハンドラを編集して、Webのトップページらしくしてみます。
せっかく作ってくれた(といってもGithubからコピーしてきているだけだが)Homeハンドラやらやらなので少し躊躇しましたが、サンプルプログラムもそんなに面白く無いのでスパッと全てなくしちゃいます。
Homeハンドラに関する記述があるのはconfig/routes
, Handler/Home.hs
, templates/homepage.*
, sample.cabal
, Application.hs
の5箇所のファイルですが、後者2つはimportなどの宣言を行っているだけなので、前の3箇所だけを変更します。
ひとまず、config/routes
とHandler/Home.hs
は以下のように編集しました。
/static StaticR Static getStatic /auth AuthR Auth getAuth /favicon.ico FaviconR GET /robots.txt RobotsR GET / HomeR GET
{-# LANGUAGE OverloadedStrings #-} module Handler.Home where import Import getHomeR :: Handler Html getHomeR = defaultLayout $(widgetFile "home/index")
とりあえず表示するためにtemplates/home/index.hamlet
という名前でHello, World!
だけのファイルをつくります。
<h1>Hello, World!
これらの変更を行ってからyesod devel
を叩けばlocalhost:3000でHello, World!の文字が見られると思います。
ルーティングはRoRみたいにStaticPagesとかいう名前のハンドラを作ってその中のメソッドでURLによってGETの結果を変える、という風にしようかとも考えましたが、それは別にRESTでもなんでもないし、widgetFile
関数でうまくテンプレートファイル達をくっつけてくれることを考えたらトップページと概要ページ、コンタクトページとかで1つづつディレクトリを作るほうがいいように思えたのでこうしてみました。
よくよく考えて見れば、よくやるHTMLはviewとかのディレクトリに置いてCSSとJavaScriptはassetsとかに置くという区別は、DSLを使うなら同じような階層構造を持つディレクトリが増殖するだけであまり意味ない気がするのですが、どうなんでしょうかね。VimでHTMLとCSSを並べて開きたい時とかも面倒なだけだし、Assets precompileするとしても拡張子で判断がつく上にむしろwidgetFile
の形式のほうが依存関係はわかりやすいし...
ちなみに、サンプルプログラムのHandlerの一番上で宣言されていたTupleSections
というエクステンションは(, True)
を\x -> (x, True)
として解釈してくれるようなものらしいです。これはsampleForm
関数で使用していただけなので消しても問題ありませんが、OverloadedStrings
はダブルクオーテーションの文字列をByteString
として扱ってくれ、性能改善になるので一応残しておきました。
Handlerの追加
早くテンプレートファイル達をいじってHamletやらやらと戯れたいところですが、さすがに見れるファイルが1つだけだとヘッダも作りようがないので、コンタクトページだけ追加しておきます。
Handlerを追加するには、Handler
以下にモジュールを作るだけでなく、sample.cabal
の依存関係に追加したモジュールを加えて、Application.hs
で追加したモジュールをインポートした上で、config/route
でルーティングの設定をする必要があります。地味に面倒臭いですね。
というわけで、Scaffoldを使いました。
$ yesod add-handler Name of route (without trailing R): Contact Enter route pattern (ex: /entry/#EntryId): /contact Enter space-separated list of methods (ex: GET POST): GET $
作成したHandlerを見ると
module Handler.Contact where import Import getContactR :: Handler Html getContactR = error "Not yet implemented: getContactR"
と、ただエラーを投げる関数になっていますので、これをHomeハンドラと同じように編集し、とりあえずのHamletファイルも追加しておきます。
{-# LANGUAGE OverloadedStrings #-} module Handler.Contact where import Import getContactR :: Handler Html getContactR = defaultLayout $(widgetFile "contact/index")
templates/contact/index.hamlet
<h1>Contact
Homeの変更からデバッグ環境のサーバを起動したままでいれば、変更が勝手に反映されてlocalhost:3000/contactでコンタクトページを表示できます。
Play! frameworkのActivatorなんかよりよっぽど高速でいいですね。
おわりに
だんだんそのユーザとしてはYesodの全体像が見えてきて楽しくなってきました。
本当はテンプレートファイルを綺麗に編集するところまで書こうと思っていたのですが、無意味に長くなってきてしまったのでそれらは次回に回します。
HaskellのYesodでWebアプリ開発入門 (1)
この記事について
ふと思い立ってHaskellでWebアプリ開発をしたいなと思ったところ、乗り遅れている感は否めないものの、Yesodというフレームワークが一番有名らしいので触ってみることにした。
この記事は、環境構築などの手順を忘れてしまうと勿体無いので、踏んだ手順をメモしておくためのものです。
なので、一応将来的にはEsqueletoも使ってちゃんとしたTDDでRESTfulなWebアプリにしたいと思っていますが、気分が変われば途中で書くのをやめるかもしれないので、入門記事としては期待できないかと思います。
※ 2014年9月13日に Mac OS XのためのHaskell開発環境 - プログラミング芸術論 という記事を書いた関係で、使用バージョンや導入方法の情報を更新しました。
環境
当分は手元のMacBookを使うので、
- Mac OS X Mavericks (10.9.4)
- GHC version 7.8.3
- Yesod version 1.2.12.8
- Cabal library version 1.20.0.3
- cabal-install version 1.20.0.2
な環境でのお話です。
ちなみに、筆者のスペックは
- O'RaillyのReal World Haskellをある程度まで読んだが、もう内容忘れた
- Webアプリケーション開発はRuby on Rails Tutorialを読んで、アプリケーションを作ってみたり商用プログラミングも少しだけ必要に迫られてやったことがある
という程度なので、解説はほぼできないも同然です。
開発環境の構築
GHCを始めとするHaskellの開発環境、cabalなどの使い方については Mac OS XのためのHaskell開発環境 - プログラミング芸術論 にまとめてありますので、そちらをご覧ください。
Yesodのインストール
まず、Yesodをグローバルにインストールします。
もともとはプロジェクトのsandboxを作成してからYesodもローカルのインストールを行うだけにしていたのですが、それだとYesodの実行ファイルへのパスが通らずにcabal exec yesod devel
が動かなかったため、Yesodだけはグローバルにインストールするようにしました。
このcabal exec yesod devel
にパスが通らない件はYesodのGithubレポジトリのissue Cannot use yesod-bin from within the cabal sandbox · Issue #737 · yesodweb/yesod · GitHub でも議論されていますが、この記事を書いている現在では有効な解決策は見つかっていないようなので、プロジェクト内で使うために計2回Yesodの長時間コンパイルを味わうこととなりますが、グローバルにインストールするやり方が一番問題なく済むかと思います。
$ cabal update $ cabal install yesod-bin --max-backjumps=-1 --reorder-goals -j
reorder-goals
は他のパッケージの依存バージョンなどの関係でインストールするパッケージのバージョンがあわない場合に頑張ってくれるオプションで、--max-backjumps
に-1
を指定することでその試行回数を無制限に設定します。
たくさんのパッケージをインストールするので、驚くほど時間が掛かります。
ちなみに私の環境では上記cabal install
実行時にhaskell-platformによってインストールされていたalexとhappyを使おうとしてバージョンが古くてインストールがコケてしまったので、別個にcabal install alex happy -j
としてやる必要がありました。
プロジェクトの作成
新しいプロジェクトはyesod init
で作成でき、対話式でプロジェクト名と使用するデータベースソフトを尋ねられます。
$ 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: yesod-sample 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? mysql 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 It's highly recommended to follow the quick start guide for installing Yesod: http://www.yesodweb.com/page/quickstart If your system is already configured correctly, please run: cd yesod-sample && cabal install -j --enable-tests --max-backjumps=-1 --reorder-goals && yesod devel
なんか人出てきた...
今回はyesod-sampleという名前のプロジェクトにして、使用するDBMSはMySQLを選びました。
依存パッケージのインストール
アプリケーションを起動するには、依存パッケージ(再びYesodの全体を含む...)のインストールとプロジェクト全体のコンパイルおよびインストールをする必要があります。
yesod init
の最後に出てきたコマンドではグローバルにプロジェクトをインストールしてしまっていますが、プロジェクトの依存パッケージをグローバルにインストールしてしまうと他のHaskellプロジェクトに影響が出るので、cabalのsandbox機能を有効化してからYesodをインストールします。
$ cd yesod-sample $ cabal sandbox init $ cabal install -j --enable-tests --max-backjumps=-1 --reorder-goals
上のコマンドを叩くだけで自動的にプロジェクト全体のコンパイルが行われた上で、プロジェクト全体が他のcabalでインストールするライブラリと同じようにインストールされた状態になります。
--enable-tests
はテストのための依存パッケージもインストールするオプションです。
yesod-sample.cabal
を見ると依存パッケージやコンパイル時に使用されるオプションなどがデフォルトでどう設定されているかわかります。
ちなみに、build-depends
の記述にあるpersistent
っていうのが、Yesodを使う際に現状最もポピュラーなO/R mapperみたいですが、MySQLだけの構文などには弱いみたいなので、ゆくゆくはその拡張のEsqueletoを使ってみたいなと思っています。
で、いよいよサーバの起動ですが、今回はデータベースソフトにMySQLを指定してしまったので、先にその設定を行う必要があります。
データベースの設定とアプリケーションの起動
データベースの情報はconfig/mysql.yml
にあり、RoRのconfig/database.yml
とほぼ同じなので、特に迷うことはないかと思います。とりあえずDevelopment環境とTest環境のデータベース設定をしっかりとやって、MySQLでユーザとデータベースの作成を行なっておけばOKでしょう。
ちなみに、データベースの設定ファイルはYAML形式なので、パスワードが数字だけだったり空だったりする場合にはダブルクオーテーションで囲む必要があります。
設定が完了したら、以下のコマンドでアプリケーションを起動します。
$ yesod devel
自動的にソースコードがビルドされ、Scaffoldに含まれているデータベースマイグレーションが行われた後、アプリケーションサーバが起動します。なぜか筆者の環境ではマイグレーションを含む一回目の起動の際にDevel application launched
の表示がされなかったのですが、問題なく起動していました。
localhost:3000にアクセスしてScaffoldのホームページが表示されていれば、起動までのプロセスは完了です。
おわりに
無事環境構築とScaffoldのアプリケーションを起動することができました。
なお、プロジェクトを削除する際には依存パッケージも全てプロジェクトディレクトリ以下に含まれているので、単にディレクトリを削除するだけで済みます。
$ rm -r yesod-sample
次回はScaffoldで生成されたファイルの確認とハンドラの追加までです。
tmuxのバージョンアップには要注意
tmux 1.9a
久しぶりにbrew outdated
してみたらtmuxの新しいバージョンが利用可能になっていたので、インストールした。
protocol version mismatch
すると、protocol version mismatch (client 8, server 7)
という見慣れないメッセージが出てセッションを回復できない。どうやら、バージョンアップしたことでセッションの保存形式か何かが変わったようで、セッションを再読み込みできなくなってしまったみたいだ...
vimいっぱい開いてたのでスワップファイルの処理に朝からイライラしてしまった。
結局
というわけで、tmuxのバージョンアップをする時には注意しましょう、というだけの話でした。
結局tmux kill-server
も効かなかったので、単にps aux | grep tmux
でtmuxのPIDを探してkill
で済ませました。