Thursday, September 4, 2014

Example of Beegae - Part 3

Now we're finally getting to the code. This app maintains a list of names, so naturally I made a struct called "Name". Here is models/model.go:

  // model
package models

import (
    "appengine"
    "appengine/datastore"
)

type Name struct {
    FirstName, LastName string
}

const COLLECTION = "name"

func (this Name) String() string {
    return this.FirstName + " " + this.LastName
}

func (name Name) GetMatchCount(c appengine.Context) (count int, err error) {
    //    c := appengine.NewContext(rq)
    q := datastore.NewQuery(COLLECTION).Filter("FirstName =", name.FirstName).
        Filter("LastName =", name.LastName)
    var results []Name
    _, err = q.GetAll(c, &results)
    if err != nil {
        count = -1
    } else {
        count = len(results)
    }
    return
}

func (this Name) Add(c appengine.Context) error {
    key := datastore.NewIncompleteKey(c, COLLECTION, nil)
    _, err := datastore.Put(c, key, &this)
    return err
}

func GetAllNames(c appengine.Context) (names []Name, err error) {
    q := datastore.NewQuery(COLLECTION)
    _, err = q.GetAll(c, &names)
    return
}

As you can see, I'm using Datastore. The COLLECTION constant sets the data type string. The GetMatchCount function queries the database for names matching the incoming name, then calculates the number of matches and returns the total. The GetAll API function retrieves all of the documents matching the query. NewIncompleteKey generates a document key. Lastly, the String function tells Go how to convert a Name to a string. We will see how that is used soon. The Contexts determine which database gets accessed. 

Now we'll get to the controllers. Here is controllers/default.go:

package controllers
import (
    "fmt"
    "github.com/astaxie/beegae"
    "log"
    "models"
)

type MainController struct {
    beegae.Controller
}

func (this *MainController) Get() {
    this.Data["Title"] = "Beegae Example #1"
    this.TplNames = "form.tpl"
}

type VerifyController MainController

func (this *VerifyController) Post() {
    context := this.AppEngineCtx
    first := this.GetString("first")
    last := this.GetString("last")
    name := models.Name{first, last}
    count, err := name.GetMatchCount(context)
    message := fmt.Sprintf("%s %d", name, count)
    if err != nil {
        message = err.Error()
    }
    if count == 0 {
        if err := name.Add(context); err != nil {
            message = err.Error()
        }
    }
    this.Data["Title"] = "Beegae Example #1"
    this.Data["Message"] = message
    this.TplNames = "verify.tpl"
}

type ListController struct {
    beegae.Controller
}

func (this *ListController) Get() {
    context := this.AppEngineCtx
    //    var names []models.Name
    names, err := models.GetAllNames(context)
    if err != nil {
        log.Fatal(err.Error())\
    }
    this.Data["Title"] = "Beegae Example #1"
    this.Data["Names"] = names
    this.TplNames = "list.tpl"
}

GetString retrieves the form variable with the given name and casts it as a string. As you can see, the Sprintf function refers to a variable of type "Name" as a string. models.String tells Go how to implicitly cast that structure. In Beegae, constructors are actually types, which are used as receivers for the appropriate REST function. 
The views are pretty much trivial except for list.tpl. Here that is:

  
<!DOCTYPE html>

<html>
      <head>
        <title>{{.Title}}</title>
        <link rel="stylesheet" href="/static/css/style.css" type="text/css" />

    </head>
    <body>
        <h3>Updated List</h3>
        <ul>
            {{range $v := .Names}}
                <li>{{$v}}</li>
            {{end}}
        </ul>
    </body>
</html>        

The "range" keyword loops through the list of names and assigns each entry in turn to $v. As you can see, the data is treated like a string. The routing is fairly simple:

package routers
import (
    "beegaenames/controllers"
    "github.com/astaxie/beegae"
)

func init() {
    beegae.Router("/", &controllers.MainController{})
    beegae.Router("/verify", &controllers.VerifyController{})
    beegae.Router("/list", &controllers.ListController{})
}

That is routers/myrouter.go. Each Router has a URL and a variable. As you saw in earlier code, those variables are usually structs that contain a member of type beegae.Controller. The last thing we'll look at is how I deployed this app.

No comments:

Post a Comment