ログ日記

作業ログと日記とメモ

PATH_INFOを見てテンプレートを読み込んで表示する


やっと少しだけ分かってきた。
IO a の型は普通の a型 に戻すことは出来ないけれど、do の中ならIOを取ることが出来るのか。


PATH_INFOから templates/ 以下のファイルを読み込むプログラム

--
-- pathinfo test
--
import System.Environment

contentType = "text/html"
templateDir = "templates"

main :: IO ()
main = do pathinfo <- pathInfo
          res      <- parseRequest pathinfo
          putStr (show res)

parseRequest :: String -> IO HTTPResponse
parseRequest p = do body <- template p
                    return (HTTPResponse contentType body)

template :: String -> IO String
template name = catch (readFile $ templatePath name)
                      (const $ return $ templatePath name ++ " not found.\n")

templatePath :: String -> String
templatePath name = templateDir ++ name ++ ".html"

pathInfo :: IO String
pathInfo = catch (getEnv "PATH_INFO") (const $ return "")
               -- エラーの場合は const (return Nothing) (IOError) となる

data HTTPResponse = HTTPResponse { header :: String, body :: String }
instance Show HTTPResponse where
    show = httpResponseToString

httpResponseToString :: HTTPResponse -> String
httpResponseToString (HTTPResponse h b) =
    concat [ "Content-Type: ", h, "\r\n",
             "Content-Length: ", show (length b), "\r\n",
             "\r\n",
             b ]

ここでは、テンプレートを読み込む関数は readFile を使っているので IO String 型になってしまう。
これを String 型に戻すことは出来ないが、

parseRequest p = do body <- template p

のようにすれば、body はString 型になる。
do の中で使う関数は引数がString型の関数でも問題ない。


という風に作っていくので合ってるのかなぁ。。。いまいち自信がない。



# 追記

template :: String -> IO String
template name = catch (readFile $ templatePath name)
                      (const $ return $ templatePath name ++ " not found.\n")

この部分は

template :: String -> IO String
template name = catch (readFile $ template)
                      (const $ return $ template ++ " not found.\n")
                where
                  template = templatePath name

こうするとHaskellっぽくなるのか。
templatePath関数もスコープを限定できるな・・。なるほど。なるほど。