Yahoo!Storeを作った、Lisp界の貴公子ポール・グレアムさんが書いた、真のハッカーとはといったエッセイ集。
話題なのと、会社のイケメンワカモノエンジニアくんが貸してくださったので読んだ。
結構長いのだけど、ポールさんが如何に先見の明を持っていたのかが伺えて、どのエッセイも面白かった。
人気のある本は、やっぱり面白いんだな。
不死となった人類最後の、死ぬことのできる男「ニモ・ノーバディー」
その半生を本にしようとする記者のインタビュー内容から、彼の人生を垣間見ることのできる映画。
しかし、どの話もチグハグで、記者としては混乱しっぱなし。
離婚する両親のどちらについていったのか × 3人の女性の内誰と結婚するか などなど、ニモの選択肢は多岐に渡り、選ばなかった選択の世界を交互に見せられていく。
実は、このニモという男(と不死となった人類が暮らす世界)は、実は離婚した両親にどちらについていくのかという選択肢を迫られた子供(ニモ)の想像の世界。
混乱しがちなのだけど、割りとハッキリ世界が別れているのでそんなことは無いのも映画の出来として良い。
前回の予告どおり、echoのサンプルプロジェクトのcrudモジュールについて、ソースファイルの分割をしてみる。
なお、普段からgolangを書いている人にとってはつまらない内容かもしれない。
けれども、なかなかどうしてimport何かではまったりして楽しかった。
こんな感じにしてみた
$ tree crud ├── handler │ ├── create.go │ ├── delete.go │ ├── list.go │ └── update.go ├── model │ └── user.go └── router.go
server.go は router だけをまとめて、 router.go とした。
package main
import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"./handler"
)
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Routes
e.GET("/users", handler.ListUser)
e.GET("/users/:id", handler.GetUser)
e.POST("/users", handler.CreateUser)
e.PUT("/users/:id", handler.UpdateUser)
e.DELETE("/users/:id", handler.DeleteUser)
// Start server
e.Logger.Fatal(e.Start(":1324"))
}
modelディレクトリはその名の通り、モデルを格納している。
package model
type (
User struct {
ID int `json:"id"`
Name string `json:"name"`
}
)
var (
Users = map[int]*User{}
Seq = 1
)
var (
Users = map[int]*User{}
Seq = 1
) は永続層で、本来DBに置き換わる領域。
handler という名前はサンプルから取った名前。ビジネスロジックを格納する領域としており、 Service とした方が良いかもしれない。
それぞれCRUDの役割を持つロジックを定義している。
$ tree crud ├── handler │ ├── create.go │ ├── delete.go │ ├── list.go │ └── update.go
package handler
import (
"net/http"
"github.com/labstack/echo"
"../model"
)
func CreateUser(c echo.Context) (err error) {
u := &model.User{
ID : model.Seq,
}
if err := c.Bind(u); err != nil {
return err
}
model.Users[u.ID] = u
model.Seq++
return c.JSON(http.StatusCreated, u)
}
router.go の e.POST("/users", handler.CreateUser) にて下記の様なPOST リクエストを受け取り、データを格納する。
curl -v POST -H "Content-Type: application/json" "http://localhost:1324/users" -d '{
"id" : 1,
"name" : "Anthony Kiedis"
}'
modelは ./model というようにimportする。
GOPATH配下の場合は絶対パスを指定するのがお作法らしいが、今回の場合はGOPATH配下に構築はしていないので、相対パスで書いてしまっている。
ID : model.Seq, の末尾に , が必要な理由が分からなかった…(削除するとコンパイルエラー)
package handler
import (
"net/http"
"strconv"
"github.com/labstack/echo"
"../model"
)
func UpdateUser(c echo.Context) (err error) {
u := new(model.User)
if err := c.Bind(u); err != nil {
return err
}
id, _ := strconv.Atoi(c.Param("id"))
model.Users[id].Name = u.Name
return c.JSON(http.StatusOK, model.Users[id])
}
router.go の e.PUT("/users/:id", handler.UpdateUser) にて下記の様なPUT リクエストを受け取り、データを更新する。
curl -v -X PUT -H "Content-Type: application/json" "http://localhost:1324/users/1" -d '{
"name" : "Anthony Kiedis at Red Hot Chili Peppers"
}'
Golang/echoとは直接関係ないのだけど、Elasticsearchをcurlで触っていた時はコンテントタイプは不要だった。
しかし、今回 -H "Content-Type: application/json" をちゃんと指定しないと、正しくJSONパラメータとして渡せなかった。
まあコレが普通なのだけど、イマイチ・・・。
package handler
import (
"net/http"
"strconv"
"github.com/labstack/echo"
"../model"
)
func GetUser(c echo.Context) (err error) {
id, _ := strconv.Atoi(c.Param("id"))
return c.JSON(http.StatusOK, model.Users[id])
}
func ListUser(c echo.Context) (err error) {
return c.JSON(http.StatusOK, model.Users)
}
router.go の e.GET("/users", handler.ListUser) や e.GET("/users/:id", handler.GetUser) にて、下記の様なGET リクエストを受け取り、データを取得する。
curl -i -v GET "http://localhost:1324/users" curl -i -v GET "http://localhost:1324/users/1"
package handler
import (
"log"
"net/http"
"strconv"
"github.com/labstack/echo"
"../model"
)
func DeleteUser(c echo.Context) (err error) {
log.Println(model.Users)
id, _ := strconv.Atoi(c.Param("id"))
delete(model.Users, id)
log.Println(model.Users)
return c.NoContent(http.StatusNoContent)
}
router.go の e.DELETE("/users/:id", handler.DeleteUser) にて、下記の様なDELETE リクエストを受け取り、データを削除する。
curl -i -v -X DELETE "http://localhost:1324/users/1"
まあこんな所でしょうか。
あとは
当たりの優先順位でやっていきたい。