Richard Clayton
Sr. Software Engineer
Next Level Security Systems
@richardclayton http://rclayton.silvrback.com
[1] http://redmonk.com/dberkholz/2014/03/18/go-the-emerging-language-of-cloud-infrastructure/
package main
import "fmt"
func runMeConcurrently(done chan bool) {
fmt.Println("Hello concurrently running function!")
done <- true
}
func main() {
fmt.Println("'Go'ing concurrent!")
doneSignal := make(chan bool)
go runMeConcurrently(doneSignal)
<- doneSignal
}
Sinatra-style web framework.
While probably the most popular because of its easy of use, it's been abandoned because it's not idiomatic Go.
Middleware toolkit similar to Connect on the Node.js platform.
"Use what you want, ignore the rest" philosophy.
Great for simple applications, but you have to provide the structure.
Middleware toolkit created by Martini's author.
Provides simple and idiomatic (to Go) ways of interacting with the net/http library.
Once again, you have to provide the structure.
All-in-one web application framework with it's own ORM.
Comprehensive like Rails. Great if you want structure. Lots of extra tooling to support testing and hot code reload.
Another all-in-one framework, but you bring your own ORM (BYOORM).
Not as comprehensive as Beego, but it's more flexible.
* Beego has become a lot more comprehensive since I've learned Go. I need to explore more before I can make a Beego vs. Revel recommendation.
File: <app-root>/conf/routes
GET /user/:id UserCtrl.Get
POST /user UserCtrl.Add
PUT /user/:id UserCtrl.Update
DELETE /user/:id UserCtrl.Delete
GET /users UserCtrl.List
File: <app-root>/app/models/user.go
package models
type User struct {
Id int64 `db:"id" json:"id"`
FirstName string `db:"first_name" json:"first_name"`
LastName string `db:"last_name" json:"last_name"`
Email string `db:"email" json:"email"`
}
File: <app-root>/app/models/user.go
var emailRegex = regexp.MustCompile("\\w*@\\w+[.]\\w+")
func (user *User) Validate(v *revel.Validation) {
v.Check(user.FirstName,
revel.ValidRequired(),
revel.ValidMaxSize(25))
v.Check(user.LastName,
revel.ValidRequired(),
revel.ValidMaxSize(25))
v.Check(user.Email,
revel.ValidMatch(emailRegex))
}
package controllers
import (
"github.com/coopernurse/gorp"
"database/sql"
"github.com/revel/revel"
)
var (
Dbm *gorp.DbMap
)
type GorpController struct {
*revel.Controller
Txn *gorp.Transaction
}
func (c *GorpController) Begin() revel.Result {
txn, err := Dbm.Begin()
if err != nil {
panic(err)
}
c.Txn = txn
return nil
}
func (c *GorpController) Commit() revel.Result {
if c.Txn == nil {
return nil
}
if err := c.Txn.Commit(); err != nil && err != sql.ErrTxDone {
panic(err)
}
c.Txn = nil
return nil
}
func (c *GorpController) Rollback() revel.Result {
if c.Txn == nil {
return nil
}
if err := c.Txn.Rollback(); err != nil && err != sql.ErrTxDone {
panic(err)
}
c.Txn = nil
return nil
}
File: <app-root>/app/controllers/gorp.go
package controllers
import "github.com/revel/revel"
func init(){
revel.InterceptMethod((*GorpController).Begin, revel.BEFORE)
revel.InterceptMethod((*GorpController).Commit, revel.AFTER)
revel.InterceptMethod((*GorpController).Rollback, revel.FINALLY)
}
File: <app-root>/app/controllers/init.go
[dev]
db.user = myapp
db.password = password
db.host = 192.168.24.42
db.port = 3306
db.name = myapp
File: <app-root>/conf/app.conf
func getParamString(param string, defaultValue string) string {
p, found := revel.Config.String(param)
if !found {
if defaultValue == "" {
revel.ERROR.Fatal("Cound not find parameter: " + param)
} else {
return defaultValue
}
}
return p
}
File: <app-root>/app/controllers/init.go
func getConnectionString() string {
host := getParamString("db.host", "")
port := getParamString("db.port", "3306")
user := getParamString("db.user", "")
pass := getParamString("db.password", "")
dbname := getParamString("db.name", "myapp")
protocol := getParamString("db.protocol", "tcp")
dbargs := getParamString("dbargs", " ")
if strings.Trim(dbargs, " ") != "" {
dbargs = "?" + dbargs
} else {
dbargs = ""
}
return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s%s",
user, pass, protocol, host, port, dbname, dbargs)
}
File: <app-root>/app/controllers/init.go
func defineUserTable(dbm *gorp.DbMap) {
// set "id" as primary key and autoincrement
t := dbm.AddTable(models.User{}).SetKeys(true, "id")
// e.g. VARCHAR(25)
t.ColMap("first_name").SetMaxSize(25)
t.ColMap("last_name").SetMaxSize(25)
}
File: <app-root>/app/controllers/init.go
var InitDb func() = func() {
connectionString := getConnectionString()
if db, err := sql.Open("mysql", connectionString); err != nil {
revel.ERROR.Fatal(err)
} else {
Dbm = &gorp.DbMap{
Db: db,
Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
}
// Defines the table for use by GORP
// This is a function we will create soon.
defineUserTable(Dbm)
if err := Dbm.CreateTablesIfNotExists(); err != nil {
revel.ERROR.Fatal(err)
}
}
File: <app-root>/app/controllers/init.go
func init() {
revel.OnAppStart(InitDb)
revel.InterceptMethod((*GorpController).Begin, revel.BEFORE)
revel.InterceptMethod((*GorpController).Commit, revel.AFTER)
revel.InterceptMethod((*GorpController).Rollback, revel.FINALLY)
}
File: <app-root>/app/controllers/init.go
package controllers
import (
"revel-demo/app/models"
"github.com/revel/revel"
"encoding/json"
)
type UserCtrl struct {
GorpController
}
File: <app-root>/app/controllers/user.go
func (c UserCtrl) parseUser() (models.User, error) {
user := models.User{}
err := json.NewDecoder(c.Request.Body).Decode(&user)
return user, err
}
File: <app-root>/app/controllers/user.go
func (c UserCtrl) Add() revel.Result {
if user, err := c.parseUser(); err != nil {
return c.RenderText("Unable to parse the User from JSON.")
} else {
// Validate the model
user.Validate(c.Validation)
if c.Validation.HasErrors() {
// Do something better here!
return c.RenderText("You have error with the User.")
} else {
if err := c.Txn.Insert(&user); err != nil {
return c.RenderText(
"Error inserting record into database!")
} else {
return c.RenderJson(user)
}
}
}
}
File: <app-root>/app/controllers/user.go
func (c UserCtrl) Get(id int64) revel.Result {
user := new(models.User)
err := c.Txn.SelectOne(user, `SELECT * FROM User WHERE id = ?`, id)
if err != nil {
return c.RenderText("Error. User probably doesn't exist.")
}
return c.RenderJson(user)
}
File: <app-root>/app/controllers/user.go
func (c UserCtrl) List() revel.Result {
lastId := parseIntOrDefault(c.Params.Get("lid"), -1)
limit := parseUintOrDefault(c.Params.Get("limit"), uint64(25))
users, err := c.Txn.Select(models.User{},
`SELECT * FROM User WHERE id > ? LIMIT ?`, lastId, limit)
if err != nil {
return c.RenderText("Error trying to get records from DB.")
}
return c.RenderJson(users)
}
File: <app-root>/app/controllers/user.go
func (c UserCtrl) Update(id int64) revel.Result {
user, err := c.parseUser()
if err != nil {
return c.RenderText("Unable to parse the User from JSON.")
}
// Ensure the Id is set.
user.Id = id
success, err := c.Txn.Update(&user)
if err != nil || success == 0 {
return c.RenderText("Unable to update user.")
}
return c.RenderText("Updated %v", id)
}
File: <app-root>/app/controllers/user.go
func (c UserCtrl) Delete(id int64) revel.Result {
success, err := c.Txn.Delete(&models.User{Id: id})
if err != nil || success == 0 {
return c.RenderText("Failed to remove User")
}
return c.RenderText("Deleted %v", id)
}
File: <app-root>/app/controllers/user.go