読者です 読者をやめる 読者になる 読者になる

M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

Yesod Framework の PathPieces を使ってみる

f:id:m12i:20130915013134p:plain

Developing Web Applications with Haskell and Yesod”をちまちまと読みすすめてYesod Frameworkについて勉強している。

その中でPathPiecesの挙動について解説されている内容がいささか中途半端だったので、実際に試してみた。

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
import  Yesod
import  Data.Text (Text)
import qualified Data.Text as T

{-
アプリのデータ型としてPathPieceStudyを定義。
Template Haskell(TH)でPathPieceStudyをYesod化してもらう。
QuasiQuoter(QQ)“parceRoutes”を使ってサイトのリソース(パス)を定義。
リソース定義の際にPathPiecesの指定をするが、
“*”で可変長のピースを定義する場合TextではなくTextsとしている点に注意。
-}
data PathPieceStudy = PathPieceStudy

mkYesod "PathPieceStudy" [parseRoutes|
/	HomeR	GET
/show/#Text	ShowR	GET
/show_directly/#Text	ShowDirectlyR	GET
/show_list/*Texts	ShowListR	GET
/factorial/#Integer	FactorialR	GET
|]

instance Yesod PathPieceStudy

{-
それぞれのパスへのアクセスを処理するハンドラを定義。
getHomeR については @{…}記法でリソースに対してパラメータを渡す方法に注意。
こうすることでYesod側で自動生成しているリソースのデータ・コンストラクタに
引数を与えているっぽい。
-}
getHomeR :: Handler Html
getHomeR = defaultLayout $ do
        [whamlet|
        <h1>Home
        <ul>
                <li>
                        <a href=@{ShowR (head foos)}>show/foo
                <li>
                        <a href=@{ShowR "こんにちは"}>show/こんにちは
                <li>
                        <a href=@{ShowDirectlyR "こんにちは"}>show_directly/こんにちは
                <li>
                        <a href=@{ShowListR (take 3 foos)}>show_list/foo/bar/baz
                <li>
                        <a href=@{FactorialR 5}>factorial/5
                <li>
                        <a href=@{FactorialR 10}>factorial/10
                |]
        where 
                foos = map T.pack ["foo", "bar", "baz", "..."]

{-
PathPieces付きで定義されたリソースに対応するハンドラは、
PathPiecesとして定義されたパラメータを受け取る型定義を持つことになる。
ShowRリソースに対応するgetShowRハンドラの型はText -> Handler Htmlであり、
getShowListRリソースに対応するget getShowListRハンドラの型は
[Text] -> Handler Htmlとなっている。
-}
getShowR :: Text -> Handler Html
getShowR x = defaultLayout [whamlet|
        <h1>show x = #{show x}
        <p>#{loremIpsum1000}
        <a href=@{HomeR}>return to home.
        |]

getShowDirectlyR :: Text -> Handler Html
getShowDirectlyR x = defaultLayout [whamlet|
        <h1>x = #{x}
        <p>#{loremIpsum1000}
        <a href=@{HomeR}>return to home.
        |]

getShowListR :: [Text] -> Handler Html
getShowListR xs = defaultLayout [whamlet|
        <h1>show xs = #{show xs}
        <p>#{loremIpsum1000}
        <a href=@{HomeR}>return to home.
        |]

{-
ところでgetFactorialRハンドラは0や1は受け取るけれど、-1は受け取らない。
数字の前に文字列があるとFactorialRの定義(/factorial/#Integer)
にマッチしないらしい。
加えて、ブラウザから「/factorial/0.1」や「/factorial/2.4」、
「/factorial/100,000」といったパスでアクセスした場合には、
getFactorialRが呼び出されるけれど、ドットやカンマ以降の英数字は単に無視される。
-}
getFactorialR :: Integer -> Handler Html
getFactorialR n = defaultLayout [whamlet|
        <h1>factorial n = #{result n}
        <p>#{loremIpsum1000}
        <a href=@{HomeR}>return to home.
        |]
        where
                factorial n = if n <= 1 then 1 else n * factorial (n - 1)
                result n = if n < 10 then show (factorial n)  else "too big!"

{-
何となく寂しいページになるのが嫌だったのでLorem ipsumも定義…。
-}	
loremIpsum1000 :: Text
loremIpsum1000 = "Lorem ipsum dolor sit amet, consectetur..."

{-
Warpサーバを立ち上げるmainを定義。
-}
main :: IO ()
main = warp 3000 PathPieceStudy