Skip to content

Instantly share code, notes, and snippets.

@Jxck
Last active August 29, 2015 13:57
Show Gist options
  • Select an option

  • Save Jxck/9551539 to your computer and use it in GitHub Desktop.

Select an option

Save Jxck/9551539 to your computer and use it in GitHub Desktop.

Revisions

  1. Jxck revised this gist Mar 16, 2014. 2 changed files with 82 additions and 45 deletions.
    51 changes: 36 additions & 15 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -160,7 +160,7 @@ Task{} はインスタンスの実体を返し、

    ```go
    func main() {
    // new 的なこと
    // new 的なこと
    task := &Task{
    Id: 1,
    Detail: "buy the milk",
    @@ -174,7 +174,7 @@ func main() {

    ```go
    func main() {
    // bool のゼロ値は false なので Done は省略可
    // bool のゼロ値は false なので Done は省略可
    task := &Task{
    Id: 1,
    Detail: "buy the milk",
    @@ -317,13 +317,13 @@ func main() {

    ```go
    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    // heredoc
    // heredoc
    html := `
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    fmt.Fprintf(w, html)
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="%s">tasks</a></h1>
    `
    fmt.Fprintf(w, html, "/tasks")
    }

    func main() {
    @@ -366,7 +366,7 @@ func TasksHandler(w http.ResponseWriter, r *http.Request) {
    tasks.Add(NewTask(1, "buy the milk"))
    tasks.Add(NewTask(2, "eat the pie"))

    // こういうのがあっても大丈夫
    // こういうのがあっても大丈夫
    tasks.Add(NewTask(3, "<b>go to the bank</b>"))

    html := `
    @@ -380,7 +380,7 @@ func TasksHandler(w http.ResponseWriter, r *http.Request) {
    </ul>
    `

    // 第二引数がエラーを返すイディオム
    // 第二引数がエラーを返すイディオム
    TasksList, err := template.New("tasklist").Parse(html)
    if err != nil {
    log.Fatal(err)
    @@ -420,9 +420,9 @@ func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    }

    func main() {
    // snip
    // snip
    http.HandleFunc("/tasks", TasksHandler(tasks))
    // snip
    // snip
    }
    ```

    @@ -436,7 +436,7 @@ Tasks を [encoding/json](http://golang.org/pkg/encoding/json/) を用いてシ
    // b, err := json.Marshal(tasks) // インデントなし
    b, err := json.MarshalIndent(tasks, "", " ") // プレフィックス、インデントあり
    if err != nil {
    log.Fatal(err)
    log.Fatal(err)
    }
    log.Println(string(b)) // 文字列化
    ```
    @@ -478,6 +478,27 @@ type Tasks struct {
    送信は fmt.Fprint() で良いでしょう。
    Content-Type とか細かいことは気にしない!

    ## おまけ

    当日時間があったので Slice の内部構造について解説し、
    それを利用して Tasks.Del() を実装した。

    ```go
    func (tasks *Tasks) Del(id int) {
    for i, task := range tasks.Tasks {
    if task.Id == id {
    copy(tasks.Tasks[i:], tasks.Tasks[i+1:])
    tasks.Tasks[len(tasks.Tasks)-1] = nil
    tasks.Tasks = tasks.Tasks[:len(tasks.Tasks)-1]
    break
    }
    }
    }
    ```

    詳細は、この辺を参照してほしい。
    https://code.google.com/p/go-wiki/wiki/SliceTricks


    ## 終わり

    @@ -486,7 +507,7 @@ Content-Type とか細かいことは気にしない!
    話せなかったこと

    - interface
    - make(), new()
    - new()
    - channel, goroutine
    - module 分割
    - その他たくさん
    @@ -535,7 +556,7 @@ Content-Type とか細かいことは気にしない!

    ```go
    func (task *Task) Edit(detail string) {
    task.Detail = detail
    ttask.Detail = detail
    }
    ```

    76 changes: 46 additions & 30 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -12,23 +12,6 @@ func init() {
    log.SetFlags(log.Lshortfile)
    }

    type Tasks struct {
    Owner string `json:"owner"`
    Tasks []*Task `json:"tasks"`
    }

    func NewTasks(owner string) *Tasks {
    tasks := &Tasks{
    Owner: owner,
    Tasks: []*Task{},
    }
    return tasks
    }

    func (tasks *Tasks) Add(task *Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }

    type Task struct {
    Id int `json:"id"`
    Detail string `json:"detail"`
    @@ -39,36 +22,67 @@ func NewTask(id int, detail string) *Task {
    task := &Task{
    Id: id,
    Detail: detail,
    Done: false,
    }
    return task
    }

    func (task *Task) Finish() { // 引数,戻り値無し
    func (task *Task) Finish() {
    task.Done = true
    }

    func (task *Task) Edit(detail string) { // 引数あり,戻り値無し
    task.Detail = detail
    }

    func (task Task) String() string { // 引数なし,戻り値あり
    func (task *Task) String() string {
    var status string
    if task.Done {
    status = "done"
    } else {
    status = "not yet"
    }
    return fmt.Sprintf("%d) %s (%s)", task.Id, task.Detail, status)

    str := fmt.Sprintf(
    "%d) %s (%s)", task.Id, task.Detail, status)
    return str
    }

    func (task *Task) Edit(detail string) {
    task.Detail = detail
    }

    type Tasks struct {
    Owner string `json:"owner"`
    Tasks []*Task `json:"tasks"`
    }

    func NewTasks(owner string) *Tasks {
    tasks := &Tasks{
    Owner: owner,
    Tasks: make([]*Task, 0, 16),
    }
    return tasks
    }

    func (tasks *Tasks) Add(task *Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }

    func (tasks *Tasks) Del(id int) {
    for i, task := range tasks.Tasks {
    if task.Id == id {
    copy(tasks.Tasks[i:], tasks.Tasks[i+1:])
    tasks.Tasks[len(tasks.Tasks)-1] = nil
    tasks.Tasks = tasks.Tasks[:len(tasks.Tasks)-1]
    break
    }
    }
    }

    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    // heredoc
    html := `
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    <h1>here is your <a href="%s">tasks</a></h1>
    `
    fmt.Fprintf(w, html)
    fmt.Fprintf(w, html, "/tasks")
    }

    func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    @@ -78,14 +92,16 @@ func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    <h1>{{.Owner}}'s tasks</h1>
    <ul>
    {{range .Tasks}}
    <li>{{.}}</li>
    <li>{{.}}</li>
    {{end}}
    </ul>
    `

    TasksList, err := template.New("tasklist").Parse(html)
    if err != nil {
    log.Fatal(err)
    }

    return func(w http.ResponseWriter, r *http.Request) {
    TasksList.Execute(w, tasks)
    }
    @@ -97,15 +113,15 @@ func TasksJSONHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request)
    log.Fatal(err)
    }
    return func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, string(b))
    fmt.Fprintf(w, string(b))
    }
    }

    func main() {
    tasks := NewTasks("Jxck")
    tasks.Add(NewTask(1, "buy the milk"))
    tasks.Add(NewTask(2, "eat the pie"))
    tasks.Add(NewTask(3, "go to the bank"))
    tasks.Add(NewTask(3, "<b>go to the bank</b>"))

    http.HandleFunc("/", IndexHandler)
    http.HandleFunc("/tasks", TasksHandler(tasks))
  2. Jxck revised this gist Mar 15, 2014. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -302,7 +302,7 @@ func main() {
    tasks.Add(NewTask(2, "eat the pie"))
    tasks.Add(NewTask(3, "go to the bank"))

    log.Println(tasks.Owner)
    log.Println(tasks.Owner)
    for i, task := range tasks.Tasks {
    log.Printf("%d %s", i, task)
    }
    @@ -545,7 +545,6 @@ func (task *Task) Edit(detail string) {
    func NewTasks(owner string) *Tasks {
    tasks := &Tasks{
    Owner: owner,
    Tasks: []*Task{},
    }
    return tasks
    }
  3. Jxck revised this gist Mar 15, 2014. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -78,7 +78,7 @@ $ godoc fmt
    $ godoc -http=":4000" &
    ```

    http://localhost:4000 で起動される。
    [http://localhost:4000](http://localhost:4000) で起動される。


    ## Print Debug
    @@ -339,7 +339,7 @@ fmt.Fprintf は、第一引数に Writer を受け、第二引数をそこに書

    ## 課題3

    TasksHandler を作成し、とりあえず main でやっていたことを移して、 http://localhost:3000/tasks でタスクをリスト表示する。
    TasksHandler を作成し、とりあえず main でやっていたことを移して、 [http://localhost:3000/tasks](http://localhost:3000/tasks) でタスクをリスト表示する。

    実装は、 fmt.Fprintf を用いて ResponseWriter にガンガン書き込む感じで良い。

    @@ -473,7 +473,7 @@ type Tasks struct {

    ## 課題4

    http://localhost:3000/tasks.json でタスクを JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。
    [http://localhost:3000/tasks.json](http://localhost:3000/tasks.json) でタスクを JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。

    送信は fmt.Fprint() で良いでしょう。
    Content-Type とか細かいことは気にしない!
  4. Jxck revised this gist Mar 15, 2014. 2 changed files with 41 additions and 22 deletions.
    51 changes: 34 additions & 17 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,20 @@
    # Go Kyoto (Go勉強会 そうだ京都、行こう)

    ## About

    ### 実施概要

    - 2014/3/15 (土) 14:00
    - http://www.zusaar.com/event/4367004


    ### 前提

    - 多言語で基本的なプログラミング知識(Web 系?) がある。
    - Go のインストール、開発環境(エディタとか)の準備済み。
    - http://go-tour-jp.appspot.com/ を一通り(24, 48, 60, 69, 70 は飛ばしてよし)


    ## タスク管理ツール

    - タスク管理ツール的なものを作りながら、 Go のひと通りの機能を触る。
    @@ -8,7 +23,7 @@
    - 標準モジュールでできることのみ。
    - 環境構築は終わっている前提。
    - 1 ファイルで完結させる構成(main.go のみ)
    - 時間がないので並行系の話はなくなく削る
    - 時間がないので色々省略している部分は最後にリンクを
    - 時間があまったらテストの話。


    @@ -63,14 +78,14 @@ $ godoc fmt
    $ godoc -http=":4000" &
    ```

    [http://localhost:4000] で起動される。
    http://localhost:4000 で起動される。


    ## Print Debug

    fmt.Print() が stdout, log.Print() が stderr に出力。
    init() は初期化処理が記述でき、 log.SetFlags() でフォーマットが変えられる。
    フォーマットは [http://golang.org/pkg/log/#pkg-constants] 参照
    フォーマットは http://golang.org/pkg/log/#pkg-constants 参照

    ```go
    import (
    @@ -208,7 +223,7 @@ func (task *Task) Finish() { // 引数,戻り値無し

    ## 課題1

    Task.Detail を引数で受け取った detail でまるっと上書きする Edit メソッドを実装しなさい。
    Task.Detail を引数で受け取った detail string でまるっと上書きする Edit メソッドを実装しなさい。

    ## IF

    @@ -296,17 +311,18 @@ func main() {

    ## HTTP

    タスク内容を net/http で公開するサーバを作成。
    タスク内容を [net/http](http://golang.org/pkg/net/http) で公開するサーバを作成。
    まず index ページとしてリンクのみを表示するハンドラを作成。
    ハンドラを登録したサーバを :3000 で起動。
    ハンドラを登録したサーバを port 3000 で起動。

    ```go
    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    // heredoc
    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    html := `
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    fmt.Fprintf(w, html)
    }

    @@ -323,7 +339,7 @@ fmt.Fprintf は、第一引数に Writer を受け、第二引数をそこに書

    ## 課題3

    TasksHandler を作成し、とりあえず main でやっていたことを移して、 [http://localhost:3000/tasks] でタスクをリスト表示する。
    TasksHandler を作成し、とりあえず main でやっていたことを移して、 http://localhost:3000/tasks でタスクをリスト表示する。

    実装は、 fmt.Fprintf を用いて ResponseWriter にガンガン書き込む感じで良い。

    @@ -353,7 +369,8 @@ func TasksHandler(w http.ResponseWriter, r *http.Request) {
    // こういうのがあっても大丈夫
    tasks.Add(NewTask(3, "<b>go to the bank</b>"))

    html := `<!DOCTYPE html>
    html := `
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>{{.Owner}}'s tasks</h1>
    <ul>
    @@ -456,7 +473,7 @@ type Tasks struct {

    ## 課題4

    [http://localhost:3000/tasks.json] でタスクを JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。
    http://localhost:3000/tasks.json でタスクを JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。

    送信は fmt.Fprint() で良いでしょう。
    Content-Type とか細かいことは気にしない!
    @@ -479,10 +496,10 @@ Content-Type とか細かいことは気にしない!

    ### Documents

    - 言語仕様: [http://golang.org/ref/spec]
    - Effective Go: [http://golang.org/doc/effective_go.html]
    - パッケージドキュメント: [http://golang.org/pkg]
    - 公式ブログ(細かい機能解説などがある): [http://blog.golang.org/]
    - 言語仕様: http://golang.org/ref/spec
    - Effective Go: http://golang.org/doc/effective_go.html
    - パッケージドキュメント: http://golang.org/pkg
    - 公式ブログ(細かい機能解説などがある): http://blog.golang.org/


    ### Resources
    12 changes: 7 additions & 5 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -63,15 +63,17 @@ func (task Task) String() string { // 引数なし,戻り値あり
    }

    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    html := `
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    fmt.Fprintf(w, html)
    }

    func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    html := `<!DOCTYPE html>
    html := `
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>{{.Owner}}'s tasks</h1>
    <ul>
  5. Jxck revised this gist Mar 15, 2014. 1 changed file with 106 additions and 74 deletions.
    180 changes: 106 additions & 74 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -5,13 +5,18 @@
    - タスク管理ツール的なものを作りながら、 Go のひと通りの機能を触る。
    - Go をひと通り触るのが目的なので、ツール自体の機能とかはこだわらない。
    - 他の言語での経験がある方が対象なので、プログラミング言語の基本的な話は省略。
    - 標準モジュールでできることしかやらない
    - 標準モジュールでできることのみ
    - 環境構築は終わっている前提。
    - 1 ファイルで完結させる構成(main.go のみ)
    - 時間がないので並行系の話はなくなく削ります。
    - 時間がないので並行系の話はなくなく削る。
    - 時間があまったらテストの話。


    # Hands On

    ハンズオン形式で進め、途中で課題をいくつか実施。


    ## main

    main() がいわゆる main 文。
    @@ -22,38 +27,58 @@ package main

    import (
    "fmt"
    )
    )

    func main() {
    fmt.Println("Go Kyoto!")
    }
    ```

    フォーマット

    ```sh
    $ go fmt main.go
    ```

    実行

    ```sh
    $ go run main.go
    Go Kyoto!
    ```

    フォーマッタは、コミットフックやエディタで保存時にやるのを推奨。

    ## Document

    電波がなくても、標準モジュールと自分が書いたモジュールのドキュメントはローカルで見られる。

    ```sh
    $ go build main.go
    $ ./main
    Go Kyoto!
    $ godoc fmt
    ```

    サーバも内蔵されている。

    ```sh
    $ godoc -http=":4000" &
    ```

    ## プリントデバッグ
    [http://localhost:4000] で起動される。


    Print は、 fmt が stdout, log が stderr に出力。
    init() は初期化処理が記述でき、 log.SetFlags でフォーマットが変えられる。
    ## Print Debug

    fmt.Print() が stdout, log.Print() が stderr に出力。
    init() は初期化処理が記述でき、 log.SetFlags() でフォーマットが変えられる。
    フォーマットは [http://golang.org/pkg/log/#pkg-constants] 参照

    ```go
    import (
    "fmt"
    "log"
    )

    func init() {
    func init() { // 初期化処理
    // 日付ではなく行番号になるのでおすすめ
    log.SetFlags(log.Lshortfile)
    }
    @@ -66,7 +91,7 @@ func main() {
    }
    ```

    ## 変数定義
    ## Variables

    定義は変数名が先、型が後。

    @@ -90,7 +115,7 @@ func main() {
    }
    ```

    暗黙的宣言
    暗黙的型宣言

    ```go
    func main() {
    @@ -103,7 +128,7 @@ func main() {

    ## Struct

    構造体。オブジェクトのようなもの。
    構造体。(オブジェクト的なもの)
    メンバは、大文字が Public、小文字が Private

    ```go
    @@ -130,10 +155,23 @@ func main() {
    }
    ```

    インスタンス生成時に、値を明示的に指定しなかったら暗黙的に各型に応じた初期値が使用される。これを [ゼロ値](http://golang.org/ref/spec#The_zero_value) という。

    ```go
    func main() {
    // bool のゼロ値は false なので Done は省略可
    task := &Task{
    Id: 1,
    Detail: "buy the milk",
    }
    log.Printf("%+v", task) // 見やすい
    }
    ```

    ## Function

    Task を New する関数は NewTask という関数名にするのが通例。
    (いわゆるコンストラクタの位置付け)
    (コンストラクタ相当)

    ```go
    func NewTask(id int, detail string) *Task {
    @@ -153,10 +191,10 @@ func main() {

    ## Method

    struct にはメソッドがつけられる
    struct にはメソッドが定義できる
    レシーバが、実体のものとポインタのもので定義方法が違う。

    String() は、表示などで文字列化するときに使われる Stringer というインタフェースのメソッド。(toString() 相当)
    String() は、表示などで文字列化するときに使われる Stringer というインタフェースのメソッド。(toString() とか相当)

    ```go
    func (task Task) String() string { // 引数なし,戻り値あり
    @@ -174,11 +212,11 @@ Task.Detail を引数で受け取った detail でまるっと上書きする Ed

    ## IF

    中括弧などいらない
    中括弧はいらない
    三項演算子はない。

    ```go
    func (task Task) String() string { // 引数なし,戻り値あり
    func (task Task) String() string {
    var status string
    if task.Done {
    status = "done"
    @@ -191,8 +229,9 @@ func (task Task) String() string { // 引数なし,戻り値あり

    ## Slice

    配列(固定長)とは別で、可変長な配列だと思えば良い。
    追加の append は、結果を返すことに注意。
    可変長な配列だと思えば良い。
    追加には組み込み関数の append() を使用する。
    (追加結果を返すことに注意)

    ```Go
    func main() {
    @@ -206,54 +245,28 @@ func main() {
    ```

    実際は、固定長配列のビューとなっている。
    今回は make() を用いた初期化は省略。詳細は http://blog.golang.org/go-maps-in-action を参照のこと。
    固定長な配列は別にある。
    今回は make() を用いた初期化は省略。
    詳細は http://blog.golang.org/go-maps-in-action を参照のこと。

    ## For

    繰り返しを表現する方法は for のみ。
    range はイテレータの役割を果たし、インデックスと要素を返す。

    ```go
    for i, task := range tasks {
    log.Printf("%d %s", i, task)
    }

    // 宣言して使わない変数は許されないので
    // 使わないなら _ で宣言する
    for _, task := range tasks {
    log.Printf("%s", task)
    }
    ```

    ## Tasks

    タスクをまとめる struct を定義してみる。
    繰り返しを表現する方法は for のみ。While などない。
    range を用いてイテレーションが可能、インデックスと要素を返す。
    (イテレータ相当)

    ```go
    type Tasks struct {
    Owner string
    Tasks []*Task
    file string
    for i, task := range tasks {
    log.Printf("%d %s", i, task)
    }

    func NewTasks(owner string) *Tasks {
    file := owner + ".json"
    tasks := &Tasks{
    Owner: owner,
    Tasks: make([]*Task, 0),
    file: file,
    }
    return tasks
    // 宣言して使わない変数は許されないので
    // 使わないなら _ で宣言する
    for _, task := range tasks {
    log.Printf("%s", task)
    }
    ```

    Tasks は *Task の空 slice で初期化している。
    slice のメモリ確保は make() 関数を用いて行うことができ、
    引数は length で今回は 0 にしている。

    詳細はこちらを参照のこと。 [http://golang.org/ref/spec#Making_slices_maps_and_channels]


    ## 課題2

    Tasks を以下のように定義し、 NewTasks 関数と Add メソッドを実装しなさい。
    @@ -303,13 +316,14 @@ func main() {
    }
    ```

    http.ResponseWriter は Writer というインタフェースを実装しており、書き込み可能なオブジェクト。
    [http.ResponseWriter](http://golang.org/pkg/net/http/#ResponseWriter) は Writer というインタフェースを実装しており、書き込み可能なオブジェクト。
    fmt.Fprintf は、第一引数に Writer を受け、第二引数をそこに書き込む。

    今回は使用していないが、 [*http.Request](http://golang.org/pkg/net/http/#Request) の中にはリクエストに関する情報が全て入っている。

    ## 課題3

    TasksHandler を作成し、とりあえず main でやっていたことを移して、 http://localhost:3000/tasks でタスクをリスト表示する。
    TasksHandler を作成し、とりあえず main でやっていたことを移して、 [http://localhost:3000/tasks] でタスクをリスト表示する。

    実装は、 fmt.Fprintf を用いて ResponseWriter にガンガン書き込む感じで良い。

    @@ -328,8 +342,7 @@ TasksHandler を作成し、とりあえず main でやっていたことを移

    ## Template

    標準で test/template と html/template があり、インタフェースは基本的に同じだが、 html/template の方はエスケープ処理などを標準で行ってくれるため今回はこちらを使う。

    標準で [text/template](http://golang.org/pkg/text/template/)[html/template](http://golang.org/pkg/html/template/) があり、インタフェースは基本的に同じだが、 html/template の方はエスケープ処理などを標準で行ってくれるため Web 用途ではこちらを使う。

    ```go
    func TasksHandler(w http.ResponseWriter, r *http.Request) {
    @@ -359,14 +372,15 @@ func TasksHandler(w http.ResponseWriter, r *http.Request) {
    }
    ```

    Go の関数は多値を返せる、 err の処理はイディオム
    実際はこれを処理したり、関数から返したりするが今回は log.Fatal はプロセスを落としている
    Go の関数は多値を返せる、 error が発生する場合は、引数の最後に返すのがイディオム
    実際はこれを処理したり、呼び出し元に返したりするが今回は log.Fatal() でプロセスを落としている
    (今回は扱わないが、 panic/recover というエラーの制御方法もある http://blog.golang.org/defer-panic-and-recover)


    ## Closure

    tasks を受け取り http.Handler を返す関数にし、 main から tasks を渡せるようにする。
    クロージャを用いて関数を返す関数にする。(Go では関数は First Class)
    tasks を受け取り [http.Handler](http://golang.org/pkg/net/http/#Handler) を返す関数にし、 main() から tasks を渡せるようにする。

    ```go
    func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    @@ -397,9 +411,9 @@ func main() {

    ## JSON

    Tasks を encoding/json を用いてシリアライズする。
    基本的には Marshal か MarshalIndent に渡すだけ。
    戻り値は []byte というバイトスライスなので、文字列にする場合は変換が必要。
    Tasks を [encoding/json](http://golang.org/pkg/encoding/json/) を用いてシリアライズする。
    基本的には Marshal() か MarshalIndent() に渡すだけ。
    戻り値は []byte というバイトのスライスなので、文字列にする場合は変換が必要。

    ```go
    // b, err := json.Marshal(tasks) // インデントなし
    @@ -435,22 +449,40 @@ type Task struct {

    ```go
    type Tasks struct {
    Owner string `json:"owner"`
    Owner string `json:"owner"`
    Tasks []*Task `json:"tasks"`
    }
    ```

    ## 課題4

    http://localhost:3000/tasks.json JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。
    [http://localhost:3000/tasks.json] でタスクを JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。

    送信は fmt.Fprint で良いでしょう。
    content-type とか細かいことは気にしない!
    送信は fmt.Fprint() で良いでしょう。
    Content-Type とか細かいことは気にしない!


    ## 終わり

    時間が余ったら、ファイル保存を行う。
    時間が余ったら、 tasks のモジュール分けとテストを書きます。

    話せなかったこと

    - interface
    - make(), new()
    - channel, goroutine
    - module 分割
    - その他たくさん


    # 参考資料

    ### Documents

    - 言語仕様: [http://golang.org/ref/spec]
    - Effective Go: [http://golang.org/doc/effective_go.html]
    - パッケージドキュメント: [http://golang.org/pkg]
    - 公式ブログ(細かい機能解説などがある): [http://blog.golang.org/]


    ### Resources
    @@ -480,7 +512,7 @@ content-type とか細かいことは気にしない!
    - etc


    ## 回答
    ## 解答例

    ### 課題1

  6. Jxck revised this gist Mar 15, 2014. 1 changed file with 0 additions and 27 deletions.
    27 changes: 0 additions & 27 deletions omake
    Original file line number Diff line number Diff line change
    @@ -1,27 +0,0 @@
    package main

    func (tasks *Tasks) WriteFile() error {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    return err
    }

    err = ioutil.WriteFile(tasks.File, b, os.ModePerm)
    if err != nil {
    return err
    }
    return nil
    }

    func (tasks *Tasks) ReadFile() error {
    b, err := ioutil.ReadFile(tasks.File)
    if err != nil {
    return err
    }

    err = json.Unmarshal(b, tasks)
    if err != nil {
    return err
    }
    return nil
    }
  7. Jxck revised this gist Mar 14, 2014. 1 changed file with 30 additions and 7 deletions.
    37 changes: 30 additions & 7 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,6 @@
    - 1 ファイルで完結させる構成(main.go のみ)
    - 時間がないので並行系の話はなくなく削ります。


    # Hands On

    ## main
    @@ -31,13 +30,13 @@ func main() {
    ```


    ```
    ```sh
    $ go run main.go
    Go Kyoto!
    ```


    ```
    ```sh
    $ go build main.go
    $ ./main
    Go Kyoto!
    @@ -119,7 +118,7 @@ type Task struct {
    Task{} はインスタンスの実体を返し、
    &Task{} はポインタを返す。

    ```
    ```go
    func main() {
    // new 的なこと
    task := &Task{
    @@ -178,7 +177,7 @@ Task.Detail を引数で受け取った detail でまるっと上書きする Ed
    中括弧などいらない。
    三項演算子はない。

    ```
    ```go
    func (task Task) String() string { // 引数なし,戻り値あり
    var status string
    if task.Done {
    @@ -230,7 +229,7 @@ range はイテレータの役割を果たし、インデックスと要素を

    タスクをまとめる struct を定義してみる。

    ```
    ```go
    type Tasks struct {
    Owner string
    Tasks []*Task
    @@ -483,9 +482,33 @@ content-type とか細かいことは気にしない!

    ## 回答

    ### 課題3
    ### 課題1

    ```go
    func (task *Task) Edit(detail string) {
    task.Detail = detail
    }
    ```

    ### 課題2

    ```go
    func NewTasks(owner string) *Tasks {
    tasks := &Tasks{
    Owner: owner,
    Tasks: []*Task{},
    }
    return tasks
    }

    func (tasks *Tasks) Add(task *Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }
    ```

    ### 課題3

    ```go
    func TasksHandler(w http.ResponseWriter, r *http.Request) {
    tasks := NewTasks("Jxck")
    tasks.Add(NewTask(1, "buy the milk"))
  8. Jxck revised this gist Mar 14, 2014. 2 changed files with 0 additions and 25 deletions.
    25 changes: 0 additions & 25 deletions Jxck.json
    Original file line number Diff line number Diff line change
    @@ -1,25 +0,0 @@
    {
    "owner": "Jxck",
    "tasks": [
    {
    "id": 1,
    "detail": "buy the zombie",
    "done": false
    },
    {
    "id": 2,
    "detail": "eat the television",
    "done": false
    },
    {
    "id": 3,
    "detail": "send mail to King",
    "done": false
    },
    {
    "id": 4,
    "detail": "go to the moon",
    "done": false
    }
    ]
    }
    File renamed without changes.
  9. Jxck revised this gist Mar 14, 2014. 3 changed files with 604 additions and 71 deletions.
    523 changes: 522 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,522 @@
    # Go Kyoto (Go勉強会 そうだ京都、行こう)
    # Go Kyoto (Go勉強会 そうだ京都、行こう)

    ## タスク管理ツール

    - タスク管理ツール的なものを作りながら、 Go のひと通りの機能を触る。
    - Go をひと通り触るのが目的なので、ツール自体の機能とかはこだわらない。
    - 他の言語での経験がある方が対象なので、プログラミング言語の基本的な話は省略。
    - 標準モジュールでできることしかやらない。
    - 環境構築は終わっている前提。
    - 1 ファイルで完結させる構成(main.go のみ)
    - 時間がないので並行系の話はなくなく削ります。


    # Hands On

    ## main

    main() がいわゆる main 文。
    main() は main パッケージにのみ作成可能。

    ```go
    package main

    import (
    "fmt"
    )

    func main() {
    fmt.Println("Go Kyoto!")
    }
    ```


    ```
    $ go run main.go
    Go Kyoto!
    ```


    ```
    $ go build main.go
    $ ./main
    Go Kyoto!
    ```

    ## プリントデバッグ

    Print は、 fmt が stdout, log が stderr に出力。
    init() は初期化処理が記述でき、 log.SetFlags でフォーマットが変えられる。

    ```go
    import (
    "fmt"
    "log"
    )

    func init() {
    // 日付ではなく行番号になるのでおすすめ
    log.SetFlags(log.Lshortfile)
    }

    func main() {
    fmt.Println("Go Kyoto!")
    fmt.Printf("%v", "Go Kyoto!\n")
    log.Println("Go Kyoto!")
    log.Printf("%v", "Go Kyoto!\n")
    }
    ```

    ## 変数定義

    定義は変数名が先、型が後。

    ```go
    func main() {
    var id int = 1
    var detail string = "buy the milk"
    var done bool = false
    log.Println(id, detail, done)
    }
    ```

    ```go
    func main() {
    var (
    id int = 1
    detail string = "buy the milk"
    done bool = false
    )
    log.Println(id, detail, done)
    }
    ```

    暗黙的宣言

    ```go
    func main() {
    id := 1
    detail := "buy the milk"
    done := false
    log.Println(id, detail, done)
    }
    ```

    ## Struct

    構造体。オブジェクトのようなもの。
    メンバは、大文字が Public、小文字が Private

    ```go
    type Task struct {
    Id int
    Detail string
    Done bool
    }
    ```

    インスタンスの生成。
    Task{} はインスタンスの実体を返し、
    &Task{} はポインタを返す。

    ```
    func main() {
    // new 的なこと
    task := &Task{
    Id: 1,
    Detail: "buy the milk",
    Done: false,
    }
    log.Printf("%+v", task) // 見やすい
    }
    ```

    ## Function

    Task を New する関数は NewTask という関数名にするのが通例。
    (いわゆるコンストラクタの位置付け)

    ```go
    func NewTask(id int, detail string) *Task {
    task := &Task{
    Id: id,
    Detail: detail,
    Done: false,
    }
    return task
    }

    func main() {
    task := NewTask(1, "buy the milk")
    log.Printf("%+v", task)
    }
    ```

    ## Method

    struct にはメソッドがつけられる。
    レシーバが、実体のものとポインタのもので定義方法が違う。

    String() は、表示などで文字列化するときに使われる Stringer というインタフェースのメソッド。(toString() 相当)

    ```go
    func (task Task) String() string { // 引数なし,戻り値あり
    return fmt.Sprintf("%d) %s (%t)", task.Id, task.Detail, task.Done)
    }

    func (task *Task) Finish() { // 引数,戻り値無し
    task.Done = true
    }
    ```

    ## 課題1

    Task.Detail を引数で受け取った detail でまるっと上書きする Edit メソッドを実装しなさい。

    ## IF

    中括弧などいらない。
    三項演算子はない。

    ```
    func (task Task) String() string { // 引数なし,戻り値あり
    var status string
    if task.Done {
    status = "done"
    } else {
    status = "not yet"
    }
    return fmt.Sprintf("%d) %s (%s)", task.Id, task.Detail, status)
    }
    ```

    ## Slice

    配列(固定長)とは別で、可変長な配列だと思えば良い。
    追加の append は、結果を返すことに注意。

    ```Go
    func main() {
    tasks := []*Task{
    NewTask(1, "buy the milk"),
    NewTask(2, "eat the pie"),
    }
    tasks = append(tasks, NewTask(3, "go to the bank"))
    log.Printf("%s", tasks)
    }
    ```

    実際は、固定長配列のビューとなっている。
    今回は make() を用いた初期化は省略。詳細は http://blog.golang.org/go-maps-in-action を参照のこと。

    ## For

    繰り返しを表現する方法は for のみ。
    range はイテレータの役割を果たし、インデックスと要素を返す。

    ```go
    for i, task := range tasks {
    log.Printf("%d %s", i, task)
    }

    // 宣言して使わない変数は許されないので
    // 使わないなら _ で宣言する
    for _, task := range tasks {
    log.Printf("%s", task)
    }
    ```

    ## Tasks

    タスクをまとめる struct を定義してみる。

    ```
    type Tasks struct {
    Owner string
    Tasks []*Task
    file string
    }
    func NewTasks(owner string) *Tasks {
    file := owner + ".json"
    tasks := &Tasks{
    Owner: owner,
    Tasks: make([]*Task, 0),
    file: file,
    }
    return tasks
    }
    ```

    Tasks は *Task の空 slice で初期化している。
    slice のメモリ確保は make() 関数を用いて行うことができ、
    引数は length で今回は 0 にしている。

    詳細はこちらを参照のこと。 [http://golang.org/ref/spec#Making_slices_maps_and_channels]


    ## 課題2

    Tasks を以下のように定義し、 NewTasks 関数と Add メソッドを実装しなさい。

    ```go
    type Tasks struct {
    Owner string
    Tasks []*Task // ここは空のスライスで初期化
    }
    ```

    動作例

    ```go
    func main() {
    tasks := NewTasks("Jxck")
    tasks.Add(NewTask(1, "buy the milk"))
    tasks.Add(NewTask(2, "eat the pie"))
    tasks.Add(NewTask(3, "go to the bank"))

    log.Println(tasks.Owner)
    for i, task := range tasks.Tasks {
    log.Printf("%d %s", i, task)
    }
    }
    ```

    ## HTTP

    タスク内容を net/http で公開するサーバを作成。
    まず index ページとしてリンクのみを表示するハンドラを作成。
    ハンドラを登録したサーバを :3000 で起動。

    ```go
    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    // heredoc
    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    fmt.Fprintf(w, html)
    }

    func main() {
    http.HandleFunc("/", IndexHandler)
    http.ListenAndServe(":3000", nil)
    }
    ```

    http.ResponseWriter は Writer というインタフェースを実装しており、書き込み可能なオブジェクト。
    fmt.Fprintf は、第一引数に Writer を受け、第二引数をそこに書き込む。


    ## 課題3

    TasksHandler を作成し、とりあえず main でやっていたことを移して、 http://localhost:3000/tasks でタスクをリスト表示する。

    実装は、 fmt.Fprintf を用いて ResponseWriter にガンガン書き込む感じで良い。

    動作例(html)

    ```html
    <!DOCTYPE html>
    <title>tasks</title>
    <h1>Jxck's tasks</h1>
    <ul>
    <li>1) buy the milk (not yet)</li>
    <li>2) eat the pie (not yet)</li>
    <li>3) go to the bank (not yet)</li>
    </ul>
    ```

    ## Template

    標準で test/template と html/template があり、インタフェースは基本的に同じだが、 html/template の方はエスケープ処理などを標準で行ってくれるため今回はこちらを使う。


    ```go
    func TasksHandler(w http.ResponseWriter, r *http.Request) {
    tasks := NewTasks("Jxck")
    tasks.Add(NewTask(1, "buy the milk"))
    tasks.Add(NewTask(2, "eat the pie"))

    // こういうのがあっても大丈夫
    tasks.Add(NewTask(3, "<b>go to the bank</b>"))

    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>{{.Owner}}'s tasks</h1>
    <ul>
    {{range .Tasks}}
    <li>{{.}}</li>
    {{end}}
    </ul>
    `

    // 第二引数がエラーを返すイディオム
    TasksList, err := template.New("tasklist").Parse(html)
    if err != nil {
    log.Fatal(err)
    }
    TasksList.Execute(w, tasks)
    }
    ```

    Go の関数は多値を返せる、 err の処理はイディオム。
    実際はこれを処理したり、関数から返したりするが今回は log.Fatal はプロセスを落としている。
    (今回は扱わないが、 panic/recover というエラーの制御方法もある http://blog.golang.org/defer-panic-and-recover)


    ## Closure

    tasks を受け取り http.Handler を返す関数にし、 main から tasks を渡せるようにする。

    ```go
    func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>{{.Owner}}'s tasks</h1>
    <ul>
    {{range .Tasks}}
    <li>{{.}}</li>
    {{end}}
    </ul>
    `
    TasksList, err := template.New("tasklist").Parse(html)
    if err != nil {
    log.Fatal(err)
    }
    return func(w http.ResponseWriter, r *http.Request) {
    TasksList.Execute(w, tasks)
    }
    }

    func main() {
    // snip
    http.HandleFunc("/tasks", TasksHandler(tasks))
    // snip
    }
    ```

    ## JSON

    Tasks を encoding/json を用いてシリアライズする。
    基本的には Marshal か MarshalIndent に渡すだけ。
    戻り値は []byte というバイトスライスなので、文字列にする場合は変換が必要。

    ```go
    // b, err := json.Marshal(tasks) // インデントなし
    b, err := json.MarshalIndent(tasks, "", " ") // プレフィックス、インデントあり
    if err != nil {
    log.Fatal(err)
    }
    log.Println(string(b)) // 文字列化
    ```

    ```json
    {
    "Owner": "Jxck",
    "Tasks": [
    {
    "id": 1,
    "detail": "buy the milk",
    "done": false
    }
    ]
    }
    ```

    対応する key 名を変更したい場合はタグで指定する。

    ```go
    type Task struct {
    Id int `json:"id"`
    Detail string `json:"detail"`
    Done bool `json:"done"`
    }
    ```

    ```go
    type Tasks struct {
    Owner string `json:"owner"`
    Tasks []*Task `json:"tasks"`
    }
    ```

    ## 課題4

    http://localhost:3000/tasks.json で JSON で取得できる API を作成するため TasksJSONHandler を作成し、サーバに登録しなさい。

    送信は fmt.Fprint で良いでしょう。
    content-type とか細かいことは気にしない!


    ## 終わり

    時間が余ったら、ファイル保存を行う。


    ### Resources

    - [Go言語でWebAppの開発に必要なN個のこと](http://qiita.com/tenntenn/items/b8b27e32c28f7569f41a)
    - [Go言語の初心者が見ると幸せになれる場所](http://qiita.com/tenntenn/items/0e33a4959250d1a55045)


    ### WAF

    - [goweb](https://github.com/stretchr/goweb)
    - [martini](http://martini.codegangsta.io/)
    - [revel](http://robfig.github.io/revel/)


    ### 事例

    [事例集](https://code.google.com/p/go-wiki/wiki/GoUsers) からピックアップ

    - [docker](https://www.docker.io/)
    - [packer](http://www.packer.io/)
    - [vitess](https://github.com/youtube/vitess)
    - [bitly](http://word.bitly.com/post/33232969144/nsq)
    - [github](http://techno-weenie.net/2013/11/2/key-value-logs-in-go/)
    - [cloudflare](http://blog.cloudflare.com/go-at-cloudflare)
    - [heroku](https://github.com/heroku/hk)
    - etc


    ## 回答

    ### 課題3

    ```
    func TasksHandler(w http.ResponseWriter, r *http.Request) {
    tasks := NewTasks("Jxck")
    tasks.Add(NewTask(1, "buy the milk"))
    tasks.Add(NewTask(2, "eat the pie"))
    tasks.Add(NewTask(3, "go to the bank"))
    fmt.Fprintf(w, "<!DOCTYPE html><title>tasks</title>")
    fmt.Fprintf(w, "<h1>%s's tasks</h1>", tasks.Owner)
    fmt.Fprintf(w, "<ul>")
    for _, task := range tasks.Tasks {
    fmt.Fprintf(w, "<li>%s</li>", task)
    }
    fmt.Fprintf(w, "</ul>")
    }
    ```

    ## 課題4

    ```go
    func TasksJSONHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    log.Fatal(err)
    }
    return func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, string(b))
    }
    }
    ```

    ## License

    The MIT License (MIT)
    Copyright (c) 2013 Jxck
    27 changes: 27 additions & 0 deletions file
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    package main

    func (tasks *Tasks) WriteFile() error {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    return err
    }

    err = ioutil.WriteFile(tasks.File, b, os.ModePerm)
    if err != nil {
    return err
    }
    return nil
    }

    func (tasks *Tasks) ReadFile() error {
    b, err := ioutil.ReadFile(tasks.File)
    if err != nil {
    return err
    }

    err = json.Unmarshal(b, tasks)
    if err != nil {
    return err
    }
    return nil
    }
    125 changes: 55 additions & 70 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -4,124 +4,109 @@ import (
    "encoding/json"
    "fmt"
    "html/template"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    )

    func init() {
    log.SetFlags(log.Lshortfile)
    }

    type Tasks struct {
    Owner string `json:"owner"`
    Tasks []Task `json:"tasks"`
    File string `json:"-"`
    Owner string `json:"owner"`
    Tasks []*Task `json:"tasks"`
    }

    func NewTasks(owner string) *Tasks {
    file := owner + ".json"
    tasks := &Tasks{
    Owner: owner,
    File: file,
    Tasks: []*Task{},
    }
    return tasks
    }

    func (tasks *Tasks) Add(task Task) {
    func (tasks *Tasks) Add(task *Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }

    func (tasks *Tasks) WriteFile() error {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    return err
    }

    err = ioutil.WriteFile(tasks.File, b, os.ModePerm)
    if err != nil {
    return err
    }
    return nil
    }

    func (tasks *Tasks) ReadFile() error {
    b, err := ioutil.ReadFile(tasks.File)
    if err != nil {
    return err
    }

    err = json.Unmarshal(b, tasks)
    if err != nil {
    return err
    }
    return nil
    }

    type Task struct {
    Id int `json:"id"`
    Detail string `json:"detail"`
    Done bool `json:"done"`
    }

    func (task *Task) Finish() {
    task.Done = false
    func NewTask(id int, detail string) *Task {
    task := &Task{
    Id: id,
    Detail: detail,
    Done: false,
    }
    return task
    }

    func (task *Task) Edit(detail string) {
    func (task *Task) Finish() { // 引数,戻り値無し
    task.Done = true
    }

    func (task *Task) Edit(detail string) { // 引数あり,戻り値無し
    task.Detail = detail
    }

    func (task Task) String() string {
    return fmt.Sprintf("%d) %s", task.Id, task.Detail)
    func (task Task) String() string { // 引数なし,戻り値あり
    var status string
    if task.Done {
    status = "done"
    } else {
    status = "not yet"
    }
    return fmt.Sprintf("%d) %s (%s)", task.Id, task.Detail, status)
    }

    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "<html><a href=\"/tasks\">tasks</a></html>")
    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>here is your <a href="/tasks">tasks</a></h1>
    `
    fmt.Fprintf(w, html)
    }

    func TasksHandler(w http.ResponseWriter, r *http.Request) {
    tasks := NewTasks("Jxck")

    err := tasks.ReadFile()
    if err != nil {
    log.Fatal(err)
    }

    htmltemplate := `<html>
    func TasksHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    html := `<!DOCTYPE html>
    <title>tasks</title>
    <h1>Owner {{.Owner}}
    <h1>{{.Owner}}'s tasks</h1>
    <ul>
    {{range .Tasks}}
    {{range .Tasks}}
    <li>{{.}}</li>
    {{end}}
    {{end}}
    </ul>
    </html>`

    t := template.New("t")
    template.Must(t.Parse(htmltemplate))
    t.Execute(w, tasks)
    `
    TasksList, err := template.New("tasklist").Parse(html)
    if err != nil {
    log.Fatal(err)
    }
    return func(w http.ResponseWriter, r *http.Request) {
    TasksList.Execute(w, tasks)
    }
    }

    func main() {
    tasks := NewTasks("Jxck")

    err := tasks.ReadFile()
    func TasksJSONHandler(tasks *Tasks) func(w http.ResponseWriter, r *http.Request) {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    log.Fatal(err)
    }

    for _, task := range tasks.Tasks {
    fmt.Println(task)
    return func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, string(b))
    }
    }

    func main() {
    tasks := NewTasks("Jxck")
    tasks.Add(NewTask(1, "buy the milk"))
    tasks.Add(NewTask(2, "eat the pie"))
    tasks.Add(NewTask(3, "go to the bank"))

    http.HandleFunc("/", IndexHandler)
    http.HandleFunc("/tasks", TasksHandler)
    http.HandleFunc("/tasks", TasksHandler(tasks))
    http.HandleFunc("/tasks.json", TasksJSONHandler(tasks))
    http.ListenAndServe(":3000", nil)

    err = tasks.WriteFile()
    if err != nil {
    log.Fatal(err)
    }
    }
  10. Jxck revised this gist Mar 14, 2014. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # Go Kyoto (Go勉強会 そうだ京都、行こう)
  11. Jxck created this gist Mar 14, 2014.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # Go Kyoto (Go勉強会 そうだ京都、行こう)
  12. Jxck revised this gist Mar 14, 2014. 1 changed file with 33 additions and 0 deletions.
    33 changes: 33 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -3,8 +3,10 @@ package main
    import (
    "encoding/json"
    "fmt"
    "html/template"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    )

    @@ -75,6 +77,33 @@ func (task Task) String() string {
    return fmt.Sprintf("%d) %s", task.Id, task.Detail)
    }

    func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "<html><a href=\"/tasks\">tasks</a></html>")
    }

    func TasksHandler(w http.ResponseWriter, r *http.Request) {
    tasks := NewTasks("Jxck")

    err := tasks.ReadFile()
    if err != nil {
    log.Fatal(err)
    }

    htmltemplate := `<html>
    <title>tasks</title>
    <h1>Owner {{.Owner}}
    <ul>
    {{range .Tasks}}
    <li>{{.}}</li>
    {{end}}
    </ul>
    </html>`

    t := template.New("t")
    template.Must(t.Parse(htmltemplate))
    t.Execute(w, tasks)
    }

    func main() {
    tasks := NewTasks("Jxck")

    @@ -87,6 +116,10 @@ func main() {
    fmt.Println(task)
    }

    http.HandleFunc("/", IndexHandler)
    http.HandleFunc("/tasks", TasksHandler)
    http.ListenAndServe(":3000", nil)

    err = tasks.WriteFile()
    if err != nil {
    log.Fatal(err)
  13. Jxck revised this gist Mar 13, 2014. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -37,8 +37,6 @@ func (tasks *Tasks) WriteFile() error {
    return err
    }

    log.Println(string(b))

    err = ioutil.WriteFile(tasks.File, b, os.ModePerm)
    if err != nil {
    return err
  14. Jxck revised this gist Mar 13, 2014. 2 changed files with 3 additions and 2 deletions.
    File renamed without changes.
    5 changes: 3 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -18,7 +18,8 @@ type Tasks struct {
    File string `json:"-"`
    }

    func NewTasks(owner, file string) *Tasks {
    func NewTasks(owner string) *Tasks {
    file := owner + ".json"
    tasks := &Tasks{
    Owner: owner,
    File: file,
    @@ -77,7 +78,7 @@ func (task Task) String() string {
    }

    func main() {
    tasks := NewTasks("Jxck", "tasks.json")
    tasks := NewTasks("Jxck")

    err := tasks.ReadFile()
    if err != nil {
  15. Jxck revised this gist Mar 13, 2014. 1 changed file with 21 additions and 11 deletions.
    32 changes: 21 additions & 11 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -15,38 +15,47 @@ func init() {
    type Tasks struct {
    Owner string `json:"owner"`
    Tasks []Task `json:"tasks"`
    File string `json:"-"`
    }

    func NewTasks(owner, file string) *Tasks {
    tasks := &Tasks{
    Owner: owner,
    File: file,
    }
    return tasks
    }

    func (tasks *Tasks) Add(task Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }

    func (tasks *Tasks) WriteFile(filename string) error {
    func (tasks *Tasks) WriteFile() error {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    return err
    }

    log.Println(string(b))

    err = ioutil.WriteFile(filename, b, os.ModePerm)
    err = ioutil.WriteFile(tasks.File, b, os.ModePerm)
    if err != nil {
    return err
    }
    return nil
    }

    func ReadTasks(filename string) (tasks Tasks, Error error) {
    b, err := ioutil.ReadFile(filename)
    func (tasks *Tasks) ReadFile() error {
    b, err := ioutil.ReadFile(tasks.File)
    if err != nil {
    return tasks, err
    return err
    }

    err = json.Unmarshal(b, &tasks)
    err = json.Unmarshal(b, tasks)
    if err != nil {
    return tasks, err
    return err
    }
    return tasks, nil
    return nil
    }

    type Task struct {
    @@ -68,8 +77,9 @@ func (task Task) String() string {
    }

    func main() {
    const FILENAME = "tasks.json"
    tasks, err := ReadTasks(FILENAME)
    tasks := NewTasks("Jxck", "tasks.json")

    err := tasks.ReadFile()
    if err != nil {
    log.Fatal(err)
    }
    @@ -78,7 +88,7 @@ func main() {
    fmt.Println(task)
    }

    err = tasks.WriteFile(FILENAME)
    err = tasks.WriteFile()
    if err != nil {
    log.Fatal(err)
    }
  16. Jxck revised this gist Mar 13, 2014. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -8,8 +8,6 @@ import (
    "os"
    )

    const FILENAME = "tasks.json"

    func init() {
    log.SetFlags(log.Lshortfile)
    }
    @@ -70,6 +68,7 @@ func (task Task) String() string {
    }

    func main() {
    const FILENAME = "tasks.json"
    tasks, err := ReadTasks(FILENAME)
    if err != nil {
    log.Fatal(err)
  17. Jxck revised this gist Mar 13, 2014. 1 changed file with 30 additions and 15 deletions.
    45 changes: 30 additions & 15 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -23,6 +23,34 @@ func (tasks *Tasks) Add(task Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }

    func (tasks *Tasks) WriteFile(filename string) error {
    b, err := json.MarshalIndent(tasks, "", " ")
    if err != nil {
    return err
    }

    log.Println(string(b))

    err = ioutil.WriteFile(filename, b, os.ModePerm)
    if err != nil {
    return err
    }
    return nil
    }

    func ReadTasks(filename string) (tasks Tasks, Error error) {
    b, err := ioutil.ReadFile(filename)
    if err != nil {
    return tasks, err
    }

    err = json.Unmarshal(b, &tasks)
    if err != nil {
    return tasks, err
    }
    return tasks, nil
    }

    type Task struct {
    Id int `json:"id"`
    Detail string `json:"detail"`
    @@ -42,13 +70,7 @@ func (task Task) String() string {
    }

    func main() {
    b, err := ioutil.ReadFile(FILENAME)
    if err != nil {
    log.Fatal(err)
    }

    var tasks Tasks
    err = json.Unmarshal(b, &tasks)
    tasks, err := ReadTasks(FILENAME)
    if err != nil {
    log.Fatal(err)
    }
    @@ -57,14 +79,7 @@ func main() {
    fmt.Println(task)
    }

    b, err = json.MarshalIndent(tasks, "", " ")
    if err != nil {
    log.Fatal(err)
    }

    log.Println(string(b))

    err = ioutil.WriteFile(FILENAME, b, os.ModePerm)
    err = tasks.WriteFile(FILENAME)
    if err != nil {
    log.Fatal(err)
    }
  18. Jxck revised this gist Mar 11, 2014. 2 changed files with 26 additions and 3 deletions.
    19 changes: 16 additions & 3 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -2,11 +2,14 @@ package main

    import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    )

    const FILENAME = "tasks.json"

    func init() {
    log.SetFlags(log.Lshortfile)
    }
    @@ -26,7 +29,17 @@ type Task struct {
    Done bool `json:"done"`
    }

    const FILENAME = "tasks.json"
    func (task *Task) Finish() {
    task.Done = false
    }

    func (task *Task) Edit(detail string) {
    task.Detail = detail
    }

    func (task Task) String() string {
    return fmt.Sprintf("%d) %s", task.Id, task.Detail)
    }

    func main() {
    b, err := ioutil.ReadFile(FILENAME)
    @@ -40,8 +53,8 @@ func main() {
    log.Fatal(err)
    }

    for i, task := range tasks.Tasks {
    log.Printf("%d %#v\n", i, task)
    for _, task := range tasks.Tasks {
    fmt.Println(task)
    }

    b, err = json.MarshalIndent(tasks, "", " ")
    10 changes: 10 additions & 0 deletions tasks.json
    Original file line number Diff line number Diff line change
    @@ -10,6 +10,16 @@
    "id": 2,
    "detail": "eat the television",
    "done": false
    },
    {
    "id": 3,
    "detail": "send mail to King",
    "done": false
    },
    {
    "id": 4,
    "detail": "go to the moon",
    "done": false
    }
    ]
    }
  19. Jxck revised this gist Mar 10, 2014. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -26,10 +26,6 @@ type Task struct {
    Done bool `json:"done"`
    }

    // func (task *Task) Done() {
    // task.done = true
    // }

    const FILENAME = "tasks.json"

    func main() {
  20. Jxck revised this gist Mar 10, 2014. 2 changed files with 8 additions and 9 deletions.
    11 changes: 5 additions & 6 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -22,13 +22,13 @@ func (tasks *Tasks) Add(task Task) {

    type Task struct {
    Id int `json:"id"`
    Detail string `json"detail"`
    done bool
    Detail string `json:"detail"`
    Done bool `json:"done"`
    }

    func (task *Task) Done() {
    task.done = true
    }
    // func (task *Task) Done() {
    // task.done = true
    // }

    const FILENAME = "tasks.json"

    @@ -45,7 +45,6 @@ func main() {
    }

    for i, task := range tasks.Tasks {
    task.Done()
    log.Printf("%d %#v\n", i, task)
    }

    6 changes: 3 additions & 3 deletions tasks.json
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    {
    "owner" : "Jxck",
    "tasks" : [
    "owner": "Jxck",
    "tasks": [
    {
    "id": 1,
    "detail": "buy the zombie",
    @@ -12,4 +12,4 @@
    "done": false
    }
    ]
    }
    }
  21. Jxck revised this gist Mar 10, 2014. 1 changed file with 16 additions and 1 deletion.
    17 changes: 16 additions & 1 deletion main.go
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,7 @@ import (
    "encoding/json"
    "io/ioutil"
    "log"
    "os"
    )

    func init() {
    @@ -29,8 +30,10 @@ func (task *Task) Done() {
    task.done = true
    }

    const FILENAME = "tasks.json"

    func main() {
    b, err := ioutil.ReadFile("tasks.json")
    b, err := ioutil.ReadFile(FILENAME)
    if err != nil {
    log.Fatal(err)
    }
    @@ -45,4 +48,16 @@ func main() {
    task.Done()
    log.Printf("%d %#v\n", i, task)
    }

    b, err = json.MarshalIndent(tasks, "", " ")
    if err != nil {
    log.Fatal(err)
    }

    log.Println(string(b))

    err = ioutil.WriteFile(FILENAME, b, os.ModePerm)
    if err != nil {
    log.Fatal(err)
    }
    }
  22. Jxck revised this gist Mar 10, 2014. 1 changed file with 7 additions and 3 deletions.
    10 changes: 7 additions & 3 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -15,14 +15,18 @@ type Tasks struct {
    Tasks []Task `json:"tasks"`
    }

    func (tasks *Tasks) Add(task Task) {
    tasks.Tasks = append(tasks.Tasks, task)
    }

    type Task struct {
    Id int `json:"id"`
    Detail string `json"detail"`
    done bool
    }

    func (t *Task) Done() {
    t.done = true
    func (task *Task) Done() {
    task.done = true
    }

    func main() {
    @@ -39,6 +43,6 @@ func main() {

    for i, task := range tasks.Tasks {
    task.Done()
    log.Println(i, task)
    log.Printf("%d %#v\n", i, task)
    }
    }
  23. Jxck created this gist Mar 9, 2014.
    44 changes: 44 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    package main

    import (
    "encoding/json"
    "io/ioutil"
    "log"
    )

    func init() {
    log.SetFlags(log.Lshortfile)
    }

    type Tasks struct {
    Owner string `json:"owner"`
    Tasks []Task `json:"tasks"`
    }

    type Task struct {
    Id int `json:"id"`
    Detail string `json"detail"`
    done bool
    }

    func (t *Task) Done() {
    t.done = true
    }

    func main() {
    b, err := ioutil.ReadFile("tasks.json")
    if err != nil {
    log.Fatal(err)
    }

    var tasks Tasks
    err = json.Unmarshal(b, &tasks)
    if err != nil {
    log.Fatal(err)
    }

    for i, task := range tasks.Tasks {
    task.Done()
    log.Println(i, task)
    }
    }
    15 changes: 15 additions & 0 deletions tasks.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    {
    "owner" : "Jxck",
    "tasks" : [
    {
    "id": 1,
    "detail": "buy the zombie",
    "done": false
    },
    {
    "id": 2,
    "detail": "eat the television",
    "done": false
    }
    ]
    }