Mithril＋golang Gin を試す

mithril.jsで簡単なアプリを作ってみる。
##利用するFW
- mithril.js　https://lhorie.github.io/mithril/
- Gin(golang) https://gin-gonic.github.io/gin/
- genmai(goのdbアクセスライブラリ) https://github.com/naoina/genmai
- golang用sqliteドライバ https://github.com/mattn/go-sqlite3
- HotReload用 https://github.com/skelterjohn/rerun

##アプリ説明
- mithrilのチュートリアルにあったTodoがベース
- SPAじゃなくて、動的な画面をmithrilで作るだけ
- サーバ側はGin(golang)+genmai(orm)

##コード
- jsonとDBアクセスで使う構造体 $GOPATH/src/app/model/todo.go

```go:todo.go
package model

type Todo struct {
	Id int64 `db:"pk" json:"id"`
	Description string `json:"description"`
	Done bool `json:"done"`
}

```

- dbアクセス $GOPATH/src/app/db/db.go

```go:db.go
package db
import (
	_ "github.com/mattn/go-sqlite3"
	"github.com/naoina/genmai"
	"app/model"
	"github.com/gin-gonic/gin"
	"fmt"
)


var DB *genmai.DB

func InitDB() {

	var err error
	DB, err = genmai.New(&genmai.SQLite3Dialect{}, ":memory:")
	if err != nil {
		panic(err)
	}

	if err := DB.CreateTable(&model.Todo{}); err != nil {
		panic(err)
	}

	initData := []model.Todo{
		{1, "なんかやる", false},
		{2, "なんかやる2", true},
	}
	if _,err=DB.Insert(&initData);err != nil{
		panic(err)
	}


}

/**
トランザクション制御のミドルウェア
 */
func TransactionHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println("END TRANSACTION ROLLBACK")
				DB.Rollback()
				panic(err)
			} else {

				DB.Commit()
				fmt.Println("END TRANSACTION COMMIT")
			}
		}()

		fmt.Println("START TRANSACTION")
		if err := DB.Begin(); err != nil {
			panic(err)
		}

		c.Next()



	}
}

```

- main+ルーティング $GOPATH/src/app/main/app.go

```go:app.go
package main
import (
	"github.com/gin-gonic/gin"
	"app/db"
	"net/http"
	"app/model"
)

func main(){

	db.InitDB()
	r := gin.Default()
	r.Static("/static","static")
	r.LoadHTMLGlob("templates/*")

	r.Use(db.TransactionHandler())


	r.GET("/",Index)
	r.GET("/todo",GetTodo)
	r.POST("/todo",AddTodo)

	r.Run(":9000")
}

func Index(c *gin.Context){

	c.HTML(http.StatusOK,"index.html",nil)
}

func GetTodo(c *gin.Context){
	db := db.DB

	var todos []model.Todo
	if err := db.Select(&todos,); err != nil {
		panic(err)
	}
	c.JSON(http.StatusOK,todos)
}

func AddTodo(c *gin.Context){

        var todo model.Todo
        if c.BindJSON(&todo) == nil {

		db := db.DB

		if num, err := db.Update(&todo); err != nil {
			panic(err)

		}else if num == 0 {
			
			if _, err := db.Insert(&todo); err != nil {
				panic(err)
			}
		}

		c.JSON(http.StatusOK, todo)

	}else {
		c.JSON(http.StatusBadRequest, nil)
	}

}

```

- インデックスページ（htmlテンプレート）
$GOPATH/templates/index.html

```html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/js/mithril.min.js" type="text/javascript" charset="utf-8"></script>
    <title>Mithril TODO</title>
</head>
<body>
<div id="$contents"></div>

<script type="text/javascript">
    //todo component
    var todo = {};
    //model
    todo.Todo = function (data) {
        this.description = m.prop(data.description);
        this.done = m.prop(data.done);
    };

    todo.TodoList = Array;

    //define the view-model
    todo.vm = (function () {
        var vm = {}
        vm.init = function () {
            //a running list of todos
            vm.list = new todo.TodoList();

            //a slot to store the name of a new todo before it is created
            vm.description = m.prop("");

            //init
            m.request({method: "GET", url: "/todo"}).then(function (todoList) {

                todoList.forEach(function (v) {
                    vm.list.push(new todo.Todo({description: v.description, done: v.done}));
                });

            });

            vm.add = function () {
                if (vm.description()) {

                    m.request({
                        method: "POST",
                        url: "/todo",
                        data: {id:t.id,description: vm.description()}
                    }).then(function (t) {

                        vm.list.push(new todo.Todo({id:t.id,description: t.description}));

                        vm.description("");

                    },function(error){
                        console.log("error ");
                    });
                }
            };
            vm.update = function (task) {

                m.request({
                    method: "POST",
                    url: "/todo",
                    data: {id: task.id(),
                           description:task.description(),
                           done:task.done()}
                }).then(function () {
                    console.log("updated");
                }, function (error) {
                    console.log("error ");
                });
            };
          
        }
        return vm
    }())

    todo.controller = function () {
        todo.vm.init()
    }

    todo.view = function () {
        return m("div", [
            m("input", {onchange: m.withAttr("value", todo.vm.description), value: todo.vm.description(),}),
            m("button", {onclick: todo.vm.add}, "Add"),
            m("table", [
                todo.vm.list.map(function (task, index) {
                    return m("tr", [
                        m("td", [
                            m("input[type=checkbox]",
                               {
                               onclick:function (e) {
                                    task.done(e.target.checked)
                                    todo.vm.update(task)
                                }
                               ,checked: task.done()
                               })
                        ]),
                        m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
                    ])
                })
            ])
        ]);
    };
    m.mount($contents, {controller: todo.controller, view: todo.view});
</script>
</body>
</html>

```
（ちと修正。下記を参考にdocument.getElementByIdをidのみで指定してみた。知らんかった。。。
http://qiita.com/LightSpeedC/items/a2c967928f9cc13e0ebc）

- mithrilの置き場
$GOPATH/static/mithril.js

##実行
```
rerun app/main
```