├── README.md ├── public ├── css │ └── main.css └── js │ └── main.js ├── server.go └── templates ├── error.tmpl ├── layout.tmpl ├── post.tmpl └── posts.tmpl /README.md: -------------------------------------------------------------------------------- 1 | sampleapp 2 | ========= 3 | 4 | Sample GoLang App with Martini, Render, Binding, and GORM with MySQL 5 | -------------------------------------------------------------------------------- /public/css/main.css: -------------------------------------------------------------------------------- 1 | h1 {font-size:100px} 2 | -------------------------------------------------------------------------------- /public/js/main.js: -------------------------------------------------------------------------------- 1 | //your main js file 2 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "database/sql" 6 | "github.com/coopernurse/gorp" 7 | "github.com/codegangsta/martini" 8 | "github.com/codegangsta/martini-contrib/render" 9 | "github.com/codegangsta/martini-contrib/binding" 10 | _ "github.com/go-sql-driver/mysql" 11 | "html/template" 12 | "log" 13 | "time" 14 | ) 15 | 16 | type Post struct { 17 | Id int64 `db:"post_id"` 18 | Created int64 19 | Title string `form:"Title"` 20 | Body string `form:"Body" binding:"required"` 21 | } 22 | 23 | func (bp Post) Validate(errors *binding.Errors, req *http.Request) { 24 | //custom validation 25 | if len(bp.Title) == 0 { 26 | errors.Fields["title"] = "Title cannot be empty" 27 | } 28 | } 29 | 30 | 31 | func main() { 32 | 33 | // initialize the DbMap 34 | dbmap := initDb() 35 | defer dbmap.Db.Close() 36 | 37 | // setup some of the database 38 | 39 | // delete any existing rows 40 | err := dbmap.TruncateTables() 41 | checkErr(err, "TruncateTables failed") 42 | 43 | // create two posts 44 | p1 := newPost("Post 1", "Lorem ipsum lorem ipsum") 45 | p2 := newPost("Post 2", "This is my second post") 46 | 47 | // insert rows 48 | err = dbmap.Insert(&p1, &p2) 49 | checkErr(err, "Insert failed") 50 | 51 | 52 | 53 | // lets start martini and the real code 54 | m := martini.Classic() 55 | 56 | m.Use(render.Renderer(render.Options{ 57 | Directory: "templates", 58 | Layout: "layout", 59 | Funcs: []template.FuncMap{ 60 | { 61 | "formatTime": func(args ...interface{}) string { 62 | t1 := time.Unix(args[0].(int64), 0) 63 | return t1.Format(time.Stamp) 64 | }, 65 | "unescaped": func(args ...interface{}) template.HTML { 66 | return template.HTML(args[0].(string)) 67 | }, 68 | }, 69 | }, 70 | })) 71 | 72 | m.Get("/", func(r render.Render) { 73 | //fetch all rows 74 | var posts []Post 75 | _, err = dbmap.Select(&posts, "select * from posts order by post_id") 76 | checkErr(err, "Select failed") 77 | 78 | newmap := map[string]interface{}{"metatitle": "this is my custom title", "posts": posts} 79 | 80 | r.HTML(200, "posts", newmap) 81 | }) 82 | 83 | m.Get("/:id", func(args martini.Params, r render.Render) { 84 | var post Post 85 | 86 | err = dbmap.SelectOne(&post, "select * from posts where post_id=?", args["id"]) 87 | 88 | //simple error check 89 | if err != nil { 90 | newmap := map[string]interface{}{"metatitle":"404 Error", "message":"This is not found"} 91 | r.HTML(404, "error", newmap) 92 | } else { 93 | newmap := map[string]interface{}{"metatitle": post.Title+" more custom", "post": post} 94 | r.HTML(200, "post", newmap) 95 | } 96 | }) 97 | 98 | //shows how to create with binding params 99 | m.Post("/", binding.Bind(Post{}), func(post Post, r render.Render) { 100 | 101 | p1 := newPost(post.Title, post.Body) 102 | 103 | log.Println(p1) 104 | 105 | err = dbmap.Insert(&p1) 106 | checkErr(err, "Insert failed") 107 | 108 | newmap := map[string]interface{}{"metatitle": "created post", "post": p1} 109 | r.HTML(200, "post", newmap) 110 | }) 111 | 112 | m.Run() 113 | 114 | } 115 | 116 | 117 | func newPost(title, body string) Post { 118 | return Post{ 119 | //Created: time.Now().UnixNano(), 120 | Created: time.Now().Unix(), 121 | Title: title, 122 | Body: body, 123 | } 124 | } 125 | 126 | func initDb() *gorp.DbMap { 127 | // connect to db using standard Go database/sql API 128 | // use whatever database/sql driver you wish 129 | 130 | //db, err := sql.Open("sqlite3", "/tmp/post_db.bin") 131 | db, err := sql.Open("mysql", "USERNAME:PASSWORD@unix(/var/run/mysqld/mysqld.sock)/sample") 132 | checkErr(err, "sql.Open failed") 133 | 134 | // construct a gorp DbMap 135 | // dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}} 136 | dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} 137 | 138 | // add a table, setting the table name to 'posts' and 139 | // specifying that the Id property is an auto incrementing PK 140 | dbmap.AddTableWithName(Post{}, "posts").SetKeys(true, "Id") 141 | 142 | // create the table. in a production system you'd generally 143 | // use a migration tool, or create the tables via scripts 144 | err = dbmap.CreateTablesIfNotExists() 145 | checkErr(err, "Create tables failed") 146 | 147 | return dbmap 148 | } 149 | 150 | func checkErr(err error, msg string) { 151 | if err != nil { 152 | log.Fatalln(msg, err) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /templates/error.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
Home
3 | 4 |

Error!

5 |

{{.message}}

6 | 7 | -------------------------------------------------------------------------------- /templates/layout.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{.metatitle}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | {{yield}} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /templates/post.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
Home
3 | 4 |

{{ .post.Title }}

5 |
{{ .post.Body | unescaped }}
6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/posts.tmpl: -------------------------------------------------------------------------------- 1 | 2 | {{range .posts}} 3 |

{{ .Title }}

4 | {{.Created | formatTime}} 5 |
{{ .Body }}
6 | {{end}} 7 | 8 | 9 |

Create a Post

10 |
11 |
12 |
13 | 14 |
15 | --------------------------------------------------------------------------------