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

the industrial

都内で働くITエンジニアの日記

ハッカーと画家

program book

ハッカーと画家 コンピュータ時代の創造者たち

Yahoo!Storeを作った、Lisp界の貴公子ポール・グレアムさんが書いた、真のハッカーとはといったエッセイ集。

話題なのと、会社のイケメンワカモノエンジニアくんが貸してくださったので読んだ。

結構長いのだけど、ポールさんが如何に先見の明を持っていたのかが伺えて、どのエッセイも面白かった。

人気のある本は、やっぱり面白いんだな。

サボタージュ

movies

サボタージュ(字幕版)

シュワちゃん主演のサスペンス映画。

かなり骨太なサスペンスで、誰が犯人なのかとか色々疑っちゃうのだけど、それをいろんな意味で裏切ってきたのはビックリ。

若干のゴア表現あり。

ミスターノーバディ

movies

ミスター・ノーバディ(字幕版)

不死となった人類最後の、死ぬことのできる男「ニモ・ノーバディー」

その半生を本にしようとする記者のインタビュー内容から、彼の人生を垣間見ることのできる映画。

しかし、どの話もチグハグで、記者としては混乱しっぱなし。

離婚する両親のどちらについていったのか × 3人の女性の内誰と結婚するか などなど、ニモの選択肢は多岐に渡り、選ばなかった選択の世界を交互に見せられていく。

 

実は、このニモという男(と不死となった人類が暮らす世界)は、実は離婚した両親にどちらについていくのかという選択肢を迫られた子供(ニモ)の想像の世界。

 混乱しがちなのだけど、割りとハッキリ世界が別れているのでそんなことは無いのも映画の出来として良い。

 

ヘルボーイ/ゴールデン・アーミー

movies

ヘルボーイ/ゴールデン・アーミー (字幕版)

1作目をかなり前に観て、意外と面白かった覚えが。 今作(といってもかなり前の映画だけど)もなかなか面白かった。

スクール オブ ロック

movies

スクール・オブ・ロック (字幕版)

 

前から見たかった映画。

そんな映画もプライムビデオなら無料で観れちゃうのスゴイ。

お金も人気もないギタリストが、嘘をついて小学校の先生に。

子どもたちの才能を見抜き、学校に内緒でロックバンドを組んでしまうお話。

ラストは気持ちよく終わった。

LOFT

movies

ロフト -完全なる嘘-(字幕版)

 

正直あまりおもしろくなかった。

特にどんでん返し系にもかかわらず、あまりどんでんしていない感じ。

ただ、オランダの映画だそうで、オランダ語の響きがとても耳に心地よい。

その2 ファイル分割してみる - golang製Web Framework 「echo」事始め

work program golang

続き

前回の予告どおり、echoのサンプルプロジェクトのcrudモジュールについて、ソースファイルの分割をしてみる。

なお、普段からgolangを書いている人にとってはつまらない内容かもしれない。

けれども、なかなかどうしてimport何かではまったりして楽しかった。

ディレクトリ構成

こんな感じにしてみた

$ tree
crud
├── handler
│   ├── create.go
│   ├── delete.go
│   ├── list.go
│   └── update.go
├── model
│   └── user.go
└── router.go

分割

server.go から router.go へ

server.gorouter だけをまとめて、 router.go とした。

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ディレクト

modelディレクトリはその名の通り、モデルを格納している。

model/user.go
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ディレクト

handler という名前はサンプルから取った名前。ビジネスロジックを格納する領域としており、 Service とした方が良いかもしれない。

それぞれCRUDの役割を持つロジックを定義している。

$ tree
crud
├── handler
│   ├── create.go
│   ├── delete.go
│   ├── list.go
│   └── update.go

create.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.goe.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, の末尾に , が必要な理由が分からなかった…(削除するとコンパイルエラー)

update.go

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.goe.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パラメータとして渡せなかった。

まあコレが普通なのだけど、イマイチ・・・。

list.go

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.goe.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"

delete.go

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.goe.DELETE("/users/:id", handler.DeleteUser) にて、下記の様なDELETE リクエストを受け取り、データを削除する。

curl -i -v -X DELETE "http://localhost:1324/users/1"

おわり

まあこんな所でしょうか。

あとは

  • DBへの永続化
  • エラーハンドリング
  • デプロイ

当たりの優先順位でやっていきたい。