RSS

日々のメモ書き

Debian Developerが綴るメモ

Yesod でアプリを作る 7

次にコメント機能を追加する。template/post.hamletを編集。

<h2>Comments
$if null comments 
  <p> There are no comments in this post.
$else
  $forall Entity commentId comment <- comments
    <div>
      Commenter: #{commentCommenter comment} 
      (#{show $ commentCreatedAt comment})
      <p>
        #{commentBody comment}
    <hr>

<form method=post action=@{CommentNewR postId} enctype=#{enctype}>
    ^{commentWidget}
    <div>
        <input type=submit value="Post New Comment">

次に config/route にコメントを作成する部分を追加

/blog/#PostId/comment/new       CommentNewR     POST

config/model にコメントのモデルを作成

Comment
  commenter Text
  body Textarea 
  createdAt UTCTime default=CURRENT_TIMESTAMP
  postId PostId
  deriving

Handler/Blog.hs

getPostDeleteR :: PostId -> Handler RepHtml
getPostDeleteR postId = do
  runDB $ do
    _post <- get404 postId
    delete postId
    deleteWhere [ CommentPostId ==. postId ]
  redirect $ BlogR

deleteWhere ... の部分で元記事が削除されたら関係するコメントも削除するような処理をしている。

commentForm :: PostId -> Form Comment
commentForm postId = renderDivs $ Comment
  <$> areq textField     "Commenter" Nothing
  <*> areq textareaField "Body"      Nothing
  <*> aformM (liftIO getCurrentTime)
  <*> pure postId

フォームの部分で理解できない部分があってpure PostIdがはたしてこれでいいのかわかっていない。モデルをそのまま流用するフォームにしないほうがいいのかもしれない。

postCommentNewR :: PostId -> Handler RepHtml
postCommentNewR postId = do
  _post <- runDB $ get404 postId
  ((res, commentWidget), enctype) <- runFormPost $ commentForm postId
  case res of
    FormSuccess comment -> do
      commentId <- runDB $ insert comment
      setMessage $ toHtml $ (commentCommenter comment)
      redirect $ PostViewR postId
    _ -> do
      setMessage "add correct comment"
      redirect $ PostViewR postId

ここは普通にコメントを追加する。

getPostviewRに投稿フォームとコメントの一覧を表示させる部分を追加。

getPostViewR :: PostId -> Handler RepHtml
getPostViewR postId = do
  post <- runDB $ get404 postId
  comments <- runDB $ selectList [CommentPostId ==. postId] [Asc CommentId]
  (commentWidget, enctype) <- generateFormPost $ commentForm postId
  defaultLayout $ do
    setTitle $ toHtml $ postTitle post
    $(widgetFile "post")

このような具合で一通りできた。

Yesod でアプリを作る 6

CRUDのうちUpdateとDeleteの実装をする。config/routeに追加する。

/blog/post/edit/#PostId         PostEditR       GET POST
/blog/post/delete/#PostId       PostDeleteR     GET

template/blog.hamlet にリンクを追加する。

    <table border="1">
      <tr><th>Name</th><th>Title</th><th>tim
estamp</th><th>Action</th>
      $forall Entity postId post <- posts
        <tr>
            <td> #{postName post}
            <td> #{postTitle post}
            <td> #{show $ postCreatedAt post}
            <td>
                <a href=@{PostViewR postId} > Show
                <a href=@{PostEditR postId} > Edit
                <a href=@{PostDeleteR postId} > Delete

次に Handler/Blog.hs に次を追加する

getPostEditR :: PostId -> Handler RepHtml
getPostEditR postId = do
  post <- runDB $ get404 postId
  (postWidget, enctype) <- generateFormPost $ postForm $ Just post
  defaultLayout $ do
    $(widgetFile "post_new")

ここでポイントなのがpostForm $ Just post の部分。postFormの部分を見るとフォームの各要素を取り出して埋めている。新規作成のときには postForm Nothing が呼ばれるので全て空っぽになる。

postPostEditR :: PostId -> Handler RepHtml
postPostEditR postId = do
  ((res, postWidget), enctype) <- runFormPost $ postForm Nothing
  case res of
       FormSuccess post -> do 
         runDB $ do 
           _post <- get404 postId
           update postId [ PostName    =. postName post
                         , PostTitle   =. postTitle post
                         , PostContent =. postContent post]
         setMessage $ toHtml $ (postTitle post) <> "updated"
         redirect $ PostViewR postId
       _ -> defaultLayout $ do
         setTitle "Please corrrect your entry form"
         $(widgetFile "post_new")

get404を使って存在しない記事に対して更新をかけてないかチェックをする。そのあとupdateを使ってデータを更新。

         
getPostDeleteR :: PostId -> Handler RepHtml
getPostDeleteR postId = do
  runDB $ do
    _post <- get404 postId
    delete postId
  redirect $ BlogR

やはり削除の前にget404でチェック。これで編集と削除ができるようになる。

Yesod でアプリを作る 5

次に個々の記事を表示させる(View)。先程と同じように config/route の設定から始める。

/blog/#PostId                   PostViewR       GET

次に Handler/Blog.hsgetPostViewR を定義する。

getPostViewR :: PostId -> Handler RepHtml
getPostViewR postId = do
  post <- runDB $ get404 postId
  defaultLayout $ do
    setTitle $ toHtml $ postTitle post
    $(widgetFile "post")

そしてテンプレートである template/post.hamlet の作成。

<h1> #{postTitle post}
<div> #{postContent post}
<p> #{show $ postCreatedAt post}

<ul>
  <li>
    <a href=@{BlogR}>Blog Top

最後に個々の記事へのリンクを作成するために template/blog.hamlet を編集する。

<h1>Posts (/blog)</h1>
$if null posts
    <p> There are no articles in the blog
$else
    <table border="1">
      <tr><th>Name</th><th>Title</th><th>tim
estamp</th><th>Action</th>
      $forall Entity postId post <- posts
        <tr>
            <td> #{postName post}
            <td> #{postTitle post}
            <td> #{show $ postCreatedAt post}
            <td>
                <a href=@{PostViewR postId} > Show

<a href=@{PostNewR} >Post New

これで個々の記事の表示ができるようになった。

Yesod でアプリを作る 4

CRUDやVENDと呼ばれる機能は順次実装していくが最初に新しく記事を投稿する機能を実装する。

config/routeに次のように行を追加する。

/blog/post/new                  PostNewR        GET POST

yesodが次のようなエラーを出力することに注意。

[9 of 9] Compiling Application      ( Application.hs, dist/build/Application.o )

Application.hs:28:1: Not in scope: `getPostNewR'

Application.hs:28:1: Not in scope: `postPostNewR'
Build failure, pausing...

getPostNewRとpostPostNewRが実装されてないことによるエラーである。これを実装していく。Handler/Blog.hsに次の関数を追加する。

import Data.Monoid
import Data.Time
-- (snip)
postForm :: Maybe Post -> Form Post
postForm p = renderDivs $ Post 
  <$> areq textField "Name"  (postName  <$> p)
  <*> areq textField "Title" (postTitle <$> p)
  <*> areq nicHtmlField "Content" (postContent <$> p)
  <*> aformM (liftIO getCurrentTime)

getPostNewR :: Handler RepHtml
getPostNewR = do
  (postWidget, enctype) <- generateFormPost $ postForm Nothing
  defaultLayout $ do
    $(widgetFile "post_new")

postPostNewR :: Handler RepHtml
postPostNewR = do
    ((res,postWidget),enctype) <- runFormPost $ postForm Nothing
    case res of 
         FormSuccess post -> do 
            postId <- runDB $ insert post
            setMessage $ toHtml $ (postTitle post) <> " created"
            redirect $ BlogR 
         _ -> defaultLayout $ do
                setTitle "Please correct your entry form"
                $(widgetFile "post_new")

template/post_new.hamlet を作成する。

<form method=post enctype=#{enctype}>
    ^{postWidget}
    <div>
        <input type=submit value="Post New Post">

template/blog.hamlet に記事の投稿画面へのリンクを追加する。

...
...
<a href=@{PostNewR} >Post New

となる。ここまでできたら記事の投稿ができるようになる。 http://localhost:3000/blog に戻って Post New のリンクを辿って新しい記事を作成する。作成すると記事の一覧に追加されていくのを確認する。一覧だけでは寂しいので次は記事の中身を閲覧する(View)機能を追加する。

Yesod でアプリを作る 3

次にBlogのモデルを作成。config/routeに以下を追加する。

Post
  name Text
  title Text
  content Html
  createdAt UTCTime default=CURRENT_TIMESTAMP
  deriving 

ただし、これだけではUTCTimeが定義されてないというエラーになるので次の作業が必要となる。

  1. blog.cabal の build-depends に time を追加する。
  2. Model.hsにimport Data.Timeを追加する。

サーバーの出すログを見ると自動的にPostのモデルが作成されたことが確認できる。

次にURLルーティングを定義する。config/routeを開いて以下を追加する。

/blog                           BlogR           GET

するとyesodのサーバーが自動的にファイルをコンパイルして次のようなエラーを出す。

Rebuilding application...
Forcing recompile for ./Foundation.hs because of config/routes
Forcing recompile for ./Foundation.hs because of messages/en.msg
Forcing recompile for ./Foundation.hs because of templates/default-layout-wrapper.hamlet
Forcing recompile for ./Foundation.hs because of templates/default-layout.hamlet
Building blog-0.0.0...
Preprocessing library blog-0.0.0...
[5 of 8] Compiling Foundation       ( Foundation.hs, dist/build/Foundation.o )
[6 of 8] Compiling Import           ( Import.hs, dist/build/Import.o )
[7 of 8] Compiling Handler.Home     ( Handler/Home.hs, dist/build/Handler/Home.o )
[8 of 8] Compiling Application      ( Application.hs, dist/build/Application.o )

Application.hs:27:1: Not in scope: `getBlogR'
Build failure, pausing...
Rebuilding application...
Building blog-0.0.0...
Preprocessing library blog-0.0.0...
[8 of 8] Compiling Application      ( Application.hs, dist/build/Application.o )

Application.hs:27:1: Not in scope: `getBlogR'
Build failure, pausing...

getBlogRというハンドラが定義されてないことによりエラーが起きている。このようなエラーチェックが行われるため定義忘れなどのミスが発生しない。次にハンドラの定義を行う。Handler/Blog.hsを作成して次のように入力する。

module Handler.Blog where

import Import

import Yesod.Form.Nic (YesodNic, nicHtmlField)
instance YesodNic App

getBlogR :: Handler RepHtml
getBlogR = do
  posts <- runDB $ selectList [] [Desc PostTitle]
  defaultLayout $ do
    setTitle "Blog Index"
    $(widgetFile "blog")
  1. blog.cabalのexposed-moduleにHandler.Blogを追加する
  2. Application.hsに import Handler.Blog を追加する

それが終わったあとの状態ではテンプレートが定義されてないということでエラーが起きている。次にテンプレートを作成する。templates/blog.hamleを次のように作成する。

<h1>Posts (/blog/)</h1> $if null posts <p> There are no articles in the blog $else <table border="1"> <tr><th>Name</th><th>Title</th><th>timestamp</th><th>Action</th> $forall Entity postId post <- posts <tr> <td> #{postName post} <td> #{postTitle post} <td> #{show $ postCreatedAt post}

これで http://localhost:3000/blog にアクセスするとblogのトップが表示される。データがないので何も表示されない。次にblogの記事を投稿する部分を作成する。

Yesod でアプリを作る 2

ここではscaffoldをつかってプロジェクトを作成する。名前は自分の名前を入れてプロジェクト名はblogとする。データベースにはsqliteを使う。

$ yesod init 
Welcome to the Yesod scaffolder.
I'm going to be creating a skeleton Yesod project for you.

What is your name? We're going to put this in the cabal and LICENSE files.

Your name: takaki
Welcome takaki.
What do you want to call your project? We'll use this for the cabal name.

Project name: blog
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
    mongo = mongodb
    mysql = MySQL (experimental)

So, what'll it be? s
That's it! I'm creating your files now...
Generating deploy/Procfile
Generating config/sqlite.yml
Generating config/settings.yml
Generating main.hs
Generating devel.hs
Generating blog.cabal
Generating .ghci
Generating LICENSE
Generating Foundation.hs
Generating Import.hs
Generating Application.hs
Generating Handler/Home.hs
Generating Model.hs
Generating Settings.hs
Generating Settings/StaticFiles.hs
Generating Settings/Development.hs
Generating static/css/bootstrap.css
Generating templates/default-layout.hamlet
Generating templates/default-layout-wrapper.hamlet
Generating templates/normalize.lucius
Generating templates/homepage.hamlet
Generating config/routes
Generating templates/homepage.lucius
Generating templates/homepage.julius
Generating config/models
Generating messages/en.msg
Generating tests/main.hs
Generating tests/HomeTest.hs

---------------------------------------

                     ___
                            {-)   |\
                       [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 blog && cabal install && yesod devel

or if you use cabal-dev:

   cd blog && cabal-dev install && yesod --dev devel

と実行する。そして次のように実行する。

$ cd blog && cabal install && yesod devel
Resolving dependencies...
Configuring blog-0.0.0...
Building blog-0.0.0...
Preprocessing library blog-0.0.0...
[1 of 8] Compiling Settings.Development ( Settings/Development.hs, dist/build/Settings/Development.o )
[2 of 8] Compiling Model            ( Model.hs, dist/build/Model.o )
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package HUnit-1.2.4.2 ... linking ... done.
Loading package array-0.4.0.0 ... linking ... done.
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package text-0.11.2.0 ... linking ... done.
Loading package attoparsec-0.10.1.1 ... linking ... done.
Loading package blaze-builder-0.3.1.0 ... linking ... done.
Loading package blaze-html-0.4.3.1 ... linking ... done.
Loading package hashable-1.1.2.3 ... linking ... done.
Loading package case-insensitive-0.4.0.1 ... linking ... done.
Loading package extensible-exceptions-0.1.1.4 ... linking ... done.
Loading package old-locale-1.0.0.4 ... linking ... done.
Loading package time-1.4 ... linking ... done.
Loading package random-1.0.1.1 ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
Loading package QuickCheck-2.4.2 ... linking ... done.
Loading package unix-2.5.1.0 ... linking ... done.
Loading package ansi-terminal-0.5.5 ... linking ... done.
Loading package filepath-1.3.0.0 ... linking ... done.
Loading package old-time-1.1.0.0 ... linking ... done.
Loading package directory-1.1.0.2 ... linking ... done.
Loading package process-1.1.0.1 ... linking ... done.
Loading package Cabal-1.14.0 ... linking ... done.
Loading package binary-0.5.1.0 ... linking ... done.
Loading package bin-package-db-0.0.0.0 ... linking ... done.
Loading package hoopl-3.8.7.3 ... linking ... done.
Loading package hpc-0.5.1.1 ... linking ... done.
Loading package ghc-7.4.1 ... linking ... done.
Loading package silently-1.1.4 ... linking ... done.
Loading package transformers-0.3.0.0 ... linking ... done.
Loading package hspec-1.1.0 ... linking ... done.
Loading package base-unicode-symbols-0.2.2.3 ... linking ... done.
Loading package transformers-base-0.4.1 ... linking ... done.
Loading package monad-control-0.3.1.3 ... linking ... done.
Loading package lifted-base-0.1.1 ... linking ... done.
Loading package resourcet-0.3.2.1 ... linking ... done.
Loading package semigroups-0.8.3.2 ... linking ... done.
Loading package void-0.5.5.1 ... linking ... done.
Loading package conduit-0.4.2 ... linking ... done.
Loading package system-filepath-0.4.6 ... linking ... done.
Loading package system-fileio-0.3.8 ... linking ... done.
Loading package filesystem-conduit-0.4.0 ... linking ... done.
Loading package blaze-builder-conduit-0.4.0.2 ... linking ... done.
Loading package tagstream-conduit-0.3.2 ... linking ... done.
Loading package attoparsec-conduit-0.4.0.1 ... linking ... done.
Loading package dlist-0.5 ... linking ... done.
Loading package data-default-0.4.0 ... linking ... done.
Loading package failure-0.2.0.1 ... linking ... done.
Loading package xml-types-0.3.1 ... linking ... done.
Loading package xml-conduit-0.7.0.2 ... linking ... done.
Loading package html-conduit-0.0.1 ... linking ... done.
Loading package http-types-0.6.11 ... linking ... done.
Loading package hxt-charproperties-9.1.1 ... linking ... done.
Loading package mtl-2.1.1 ... linking ... done.
Loading package parsec-3.1.2 ... linking ... done.
Loading package hxt-regex-xmlschema-9.0.4 ... linking ... done.
Loading package hxt-unicode-9.0.2 ... linking ... done.
Loading package network-2.3.0.13 ... linking ... done.
Loading package hxt-9.2.2 ... linking ... done.
Loading package syb-0.3.6.1 ... linking ... done.
Loading package unordered-containers-0.2.1.0 ... linking ... done.
Loading package primitive-0.4.1 ... linking ... done.
Loading package vector-0.9.1 ... linking ... done.
Loading package aeson-0.6.0.2 ... linking ... done.
Loading package base64-bytestring-0.1.1.1 ... linking ... done.
Loading package path-pieces-0.1.0 ... linking ... done.
Loading package stm-2.3 ... linking ... done.
Loading package resource-pool-0.2.1.0 ... linking ... done.
Loading package pool-conduit-0.1.0.2 ... linking ... done.
Loading package persistent-0.9.0.4 ... linking ... done.
Loading package vault-0.2.0.0 ... linking ... done.
Loading package wai-1.2.0.2 ... linking ... done.
Loading package cookie-0.4.0 ... linking ... done.
Loading package wai-test-1.2.0.2 ... linking ... done.
Loading package xml2html-0.1.2.3 ... linking ... done.
Loading package yesod-test-0.2.0.6 ... linking ... done.
Loading package cereal-0.3.5.2 ... linking ... done.
Loading package entropy-0.2.1 ... linking ... done.
Loading package largeword-1.0.1 ... linking ... done.
Loading package tagged-0.4.2.1 ... linking ... done.
Loading package crypto-api-0.10.2 ... linking ... done.
Loading package crypto-conduit-0.3.2 ... linking ... done.
Loading package cryptohash-0.7.5 ... linking ... done.
Loading package file-embed-0.0.4.4 ... linking ... done.
Loading package unix-compat-0.3.0.1 ... linking ... done.
Loading package http-date-0.0.2 ... linking ... done.
Loading package wai-app-static-1.2.0.3 ... linking ... done.
Loading package cpu-0.1.1 ... linking ... done.
Loading package crypto-pubkey-types-0.1.1 ... linking ... done.
Loading package cryptocipher-0.3.5 ... linking ... done.
Loading package cprng-aes-0.2.3 ... linking ... done.
Loading package skein-0.1.0.7 ... linking ... done.
Loading package clientsession-0.7.5 ... linking ... done.
Loading package fast-logger-0.0.2 ... linking ... done.
Loading package shakespeare-1.0.0.2 ... linking ... done.
Loading package hamlet-1.0.1.3 ... linking ... done.
Loading package shakespeare-css-1.0.1.2 ... linking ... done.
Loading package shakespeare-i18n-1.0.0.2 ... linking ... done.
Loading package shakespeare-js-1.0.0.2 ... linking ... done.
Loading package zlib-0.5.3.3 ... linking ... done.
Loading package zlib-bindings-0.1.0.1 ... linking ... done.
Loading package zlib-conduit-0.4.0.1 ... linking ... done.
Loading package wai-extra-1.2.0.4 ... linking ... done.
Loading package byteorder-1.0.3 ... linking ... done.
Loading package wai-logger-0.1.4 ... linking ... done.
Loading package yesod-routes-1.0.1.2 ... linking ... done.
Loading package yesod-core-1.0.1.2 ... linking ... done.
Loading package yesod-static-1.0.0.2 ... linking ... done.
Loading package network-conduit-0.4.0.1 ... linking ... done.
Loading package simple-sendfile-0.2.3 ... linking ... done.
Loading package warp-1.2.1.1 ... linking ... done.
Loading package yaml-0.7.0.2 ... linking ... done.
Loading package yesod-default-1.0.1.1 ... linking ... done.
Loading package shakespeare-text-1.0.0.2 ... linking ... done.
Loading package SHA-1.5.0.1 ... linking ... done.
Loading package enumerator-0.4.19 ... linking ... done.
Loading package attoparsec-enumerator-0.3 ... linking ... done.
Loading package asn1-data-0.6.1.3 ... linking ... done.
Loading package pem-0.1.1 ... linking ... done.
Loading package certificate-1.2.3 ... linking ... done.
Loading package regex-base-0.93.2 ... linking ... done.
Loading package regex-posix-0.95.1 ... linking ... done.
Loading package regex-compat-0.95.1 ... linking ... done.
Loading package socks-0.4.1 ... linking ... done.
Loading package tls-0.9.5 ... linking ... done.
Loading package tls-extra-0.4.6 ... linking ... done.
Loading package utf8-string-0.3.7 ... linking ... done.
Loading package http-conduit-1.4.1.6 ... linking ... done.
Loading package tagsoup-0.12.6 ... linking ... done.
Loading package authenticate-1.2.1.1 ... linking ... done.
Loading package mime-mail-0.4.1.1 ... linking ... done.
Loading package persistent-template-0.9.0.2 ... linking ... done.
Loading package pureMD5-2.1.0.3 ... linking ... done.
Loading package pwstore-fast-2.2 ... linking ... done.
Loading package ranges-0.2.4 ... linking ... done.
Loading package email-validate-0.2.8 ... linking ... done.
Loading package css-text-0.1.1 ... linking ... done.
Loading package xss-sanitize-0.3.2 ... linking ... done.
Loading package yesod-persistent-1.0.0.1 ... linking ... done.
Loading package yesod-form-1.0.0.4 ... linking ... done.
Loading package safe-0.3.3 ... linking ... done.
Loading package yesod-json-1.0.0.1 ... linking ... done.
Loading package yesod-auth-1.0.2.1 ... linking ... done.
Loading package yesod-1.0.1.6 ... linking ... done.
Loading package persistent-sqlite-0.9.0.2 ... linking ... done.
Loading package utf8-light-0.4.0.1 ... linking ... done.
Loading package language-javascript-0.5.4 ... linking ... done.
Loading package hjsmin-0.1.1 ... linking ... done.
[3 of 8] Compiling Settings         ( Settings.hs, dist/build/Settings.o )
[4 of 8] Compiling Settings.StaticFiles ( Settings/StaticFiles.hs, dist/build/Settings/StaticFiles.o )
[5 of 8] Compiling Foundation       ( Foundation.hs, dist/build/Foundation.o )
[6 of 8] Compiling Import           ( Import.hs, dist/build/Import.o )
[7 of 8] Compiling Handler.Home     ( Handler/Home.hs, dist/build/Handler/Home.o )
[8 of 8] Compiling Application      ( Application.hs, dist/build/Application.o )
Registering blog-0.0.0...
Preprocessing executable 'blog' for blog-0.0.0...
[1 of 1] Compiling Main             ( dist/../main.hs, dist/build/blog/blog-tmp/Main.o )
Linking dist/build/blog/blog ...
Installing library in /home/takaki/.cabal/lib/blog-0.0.0/ghc-7.4.1
Installing executable(s) in /home/takaki/.cabal/bin
Registering blog-0.0.0...
Yesod devel server. Press ENTER to quit
Resolving dependencies...
Configuring blog-0.0.0...
Rebuilding application...
Building blog-0.0.0...
Preprocessing library blog-0.0.0...
[1 of 8] Compiling Settings.Development ( Settings/Development.hs, dist/build/Settings/Development.o )
[2 of 8] Compiling Model            ( Model.hs, dist/build/Model.o )
[3 of 8] Compiling Settings         ( Settings.hs, dist/build/Settings.o )
[4 of 8] Compiling Settings.StaticFiles ( Settings/StaticFiles.hs, dist/build/Settings/StaticFiles.o )
[5 of 8] Compiling Foundation       ( Foundation.hs, dist/build/Foundation.o )
[6 of 8] Compiling Import           ( Import.hs, dist/build/Import.o )
[7 of 8] Compiling Handler.Home     ( Handler/Home.hs, dist/build/Handler/Home.o )
[8 of 8] Compiling Application      ( Application.hs, dist/build/Application.o )
Registering blog-0.0.0...
Starting development server: runghc -package-confdist/package.conf.inplace devel.hs
Starting devel application
Devel application launched: http://localhost:3000
Migrating: CREATE TABLE "user"("id" INTEGER PRIMARY KEY,"ident" VARCHAR NOT NULL,"password" VARCHAR NULL,CONSTRAINT "unique_user" UNIQUE ("ident"))
Migrating: CREATE TABLE "email"("id" INTEGER PRIMARY KEY,"email" VARCHAR NOT NULL,"user" INTEGER NULL REFERENCES "user","verkey" VARCHAR NULL,CONSTRAINT "unique_email" UNIQUE ("email"))

ここまで実行して http://localhost:3000/ にアクセスすると yesod の初期のトップページが表示される

Yesod でアプリを作る 1

Yesodを覚えるのにチュートリアルだけやっていてもしょうがないので何かしらアプリケーションを作ってみることにする。定番のBlogを作ってみることにした。仕様を考えるのもめんどくさいので(どうせ大層なものでもないし) 後述のRoRのチュートリアルにある Blog を移植することにした。しかしタグの実装のところがうまく行ってない。複雑なフォームの処理の仕方がまだよくわかっていないためである。

完成はしていないのだが作成してみての感想。Haskellの文法を利用してエラーチェックが行われることがかなり強力。テンプレートのリンク切れもURLのルーティングのハンドラの未実装もコンパイル時に静的に検出される。設定だけしておいて中身がないという状態をランタイムでエラーが検出されることがないのはかなり効率がいい。設定ファイルを記述したあとエラーが出なくなるまで実装を続ければいいという意識でやっていける。

苦労するのは資料があまりにもないこと。オライリーから出た本のHTML版が唯一まとまった資料。しかしscaffoldを使った流儀で説明されていない(scaffold自体はある)。また細かいことを調べたくてもリファレンス的な資料がない。サンプルになるようなソースも見当らない。このあたりはそのうち解決していくんだろうけど…。

以下は参考資料

yesod の勉強

HaskellのWeb Frameworkであるyesodを勉強。やっと懸案事項が一つ片付いた。tutorialとbookのドキュメントを読む。tutorialはわかりやすいが、yesod bookのほうがちょっと面倒。何が面倒かと言うとSite Templateを使わず説明している。なんとなくリファレンスマニュアルぽくなっている。Site Templateの説明自体はあとで出てくるがSite Templateを使わず直にやる方法から説明されるとちょっととまどう。細かいことを調整するのには理解が必要なんだろうが。

yesod自体の基本的な仕組みはいろんなフレームワークを見てきたから大体理解できるものとなっている。Haskellの型を使って堅牢なシステムになっているのかなあという印象。あとは実際何かを組んでみて勉強あるのみ。

Parrot (PASM) を試す

Perl6のVMになるParrotで少しプログラムを組んでみた。一番低レベルのPASMを実験。少し書いたら力尽きた。やはりこんなものは人の手で書くのは大変だ。

  set I1, 10
  set I2, I1
  set S1, "\n"
  set S2, ", "
REDO:
  print I1
  print S2
  print I2
  print S1
  dec I1
  lt I1,1,END
  mul I2, I2, I1
  branch REDO
END:
  print I1
  print S2
  print I2
  print S1
  end

10の階乗を求めるサンプル。これをfact.pasmで保存して以下のように実行する。

$ parrot fact.pasm 
10, 10
9, 90
8, 720
7, 5040
6, 30240
5, 151200
4, 604800
3, 1814400
2, 3628800
1, 3628800
0, 3628800

まあ、大変だね

伊奈川 三日目

この日は帰る。5時に起きて、朝御飯を食べて、荷物をまとめて出発。荷物は当然ながらかなり軽い。天気は微妙に良くなるような気配がないでもないというところ。

水は大部引いていたので渡渉も支障はない。10分余りで入渓点に戻る。帰りはちょっと違う道をということで堰堤のそばを通らず山側に登っていく。頑張って斜面を登ったら林道に出たが…。

あとで航空写真などもみて確認するが終わったと思った草をかきわけ勧むと林道はまだ続いていたようだ。入渓しやすいかどうかはよくわからないが…。最後の堰堤より向こうには出られたのかもしれない。

そのあとは林道を歩くだけ。一回沢に下りて釣りを1時間余りしたがあとは真っ直ぐ駐車場に帰り無事下山。

今回の反省点はツェルトに関して。確かに軽いがやはり設営はかなり面倒。なんとか軽い自立テントはないものかねと思う。

< 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 >