The go-start webframework

The go-start webframework

A high level webframework for Go
#golang

F26b047fe2705816b840cf529bd8fc54?s=128

Erik Unger

March 22, 2012
Tweet

Transcript

  1. go-start A high level web-framework for Go Donnerstag, 22. März

    12
  2. Goals • Create a high level web-framework for Go, like

    Django for Python or Rails for Ruby • Be Go-ish • Don’t make stuff more complicated than it has to be • Convention over configuration • Easy setup and deployment (will be improved with Go v1.0) Donnerstag, 22. März 12
  3. Current status • In development for 10 months • Work

    sponsored by STARTeurope • Used in production for http://startuplive.in • Still Go v0.6, upgrade to v1.0 very soon • GoClipse + gb, but will move to Sublime Text 2 and go install with Go v1.0 Donnerstag, 22. März 12
  4. What’s in it? • MVC • Prefer Go syntax to

    template languages • HTML5 Boilerplate out of the box • MongoDB • All batteries included Donnerstag, 22. März 12
  5. All batteries included • HTML5 Boilerplate • jQuery • web.go

    • mustache.go (will be replaced by Go v1.0 template system) • mgo / bson • goconf Donnerstag, 22. März 12
  6. Views Donnerstag, 22. März 12

  7. Philosophy • Why learn another template language if you can

    use Go syntax? • DOM tree has a 1:1 Go object representation on the server • Every element has an ID to enable sync of DOM tree and server view representation Donnerstag, 22. März 12
  8. Higher level abstractions •view.List •view.Table •view.Form •view.ModelView •view.If •view.Menu •view.Video

    •view.Page Donnerstag, 22. März 12
  9. HTML Tags / Shortcuts •H1, H2, ... •P •Pre •A,

    A_blank •Img •Br •B, I, Q •Em, Strong •Ul, Ol Donnerstag, 22. März 12
  10. So how does a view look like? Donnerstag, 22. März

    12
  11. view := Views{ ! NewDiv("myclass", ! ! H1("Example HTML structure"),

    ! ! P("This is a paragraph"), ! ! P( ! ! ! HTML("Some unescaped HTML:<br/>"), ! ! ! Printf("The number of the beast: %d", 666), ! ! ! Escape("Will be escaped: 666 < 999"), ! ! ), ! ! A_blank("http://go-lang.org", "A very simple link"), ! ), ! Hr(), ! Pre("! <- pre formated text, followed by a list:"), ! Ul("red", "green", "blue"), ! &Template{ ! ! Filename: "mytemplate.html", ! ! GetContext: func(ctx *Context) (interface{}, os.Error) { ! ! ! return map[string]string{"Key": "Value"}, nil ! ! }, ! }, } Donnerstag, 22. März 12
  12. Yes, templates are supported Donnerstag, 22. März 12

  13. But I ended up using them only for the HTML5

    Boilerplate Page template Donnerstag, 22. März 12
  14. Dynamic Views Donnerstag, 22. März 12

  15. view := NewDynamicView( ! func(context *Context) (view View, err os.Error)

    { ! ! var names []string ! ! i := models.Users.Sort("Name.First").Sort("Name.Last").Iterator(); ! ! for doc := i.Next(); doc != nil; doc = i.Next() { ! ! ! names = append(names, doc.(*models.User).Name.String()) ! ! } ! ! if i.Err() != nil { ! ! ! return nil, i.Err() ! ! }! ! ! ! ! return &List{! // List = higher level abstraction, Ul() = shortcut ! ! ! Class: "my-ol", ! ! ! Ordered: true, ! ! ! Model: EscapeStringsListModel(names), ! ! }, nil ! }, ) Donnerstag, 22. März 12
  16. view := &ModelView{ ! GetModelIterator: func(context *Context) model.Iterator { !

    ! return models.Users.Sort("Name.First").Sort("Name.Last").Iterator() ! }, ! GetModelView: func(model interface{}, context *Context) (view View, err os.Error) { ! ! user := model.(*models.User) ! ! return PrintfEscape("%s, ", user.Name), nil ! }, } Donnerstag, 22. März 12
  17. HTML Pages Donnerstag, 22. März 12

  18. Homepage := &Page{ ! OnPreRender: func(page *Page, context *Context) (err

    os.Error) { ! ! context.Data = &PerPageData{...} // Set global page data at request context ! }, ! WriteTitle: func(context *Context, writer io.Writer) (err os.Error) { ! ! writer.Write([]byte(context.Data.(*PerPageData).DynamicTitle)) ! ! return nil ! }, ! CSS: HomepageCSS, ! WriteHeader: RSS("go-start.org RSS Feed", &RssFeed) ! WriteScripts: PageWriters( ! ! Config.Page.DefaultWriteScripts, ! ! JQuery, // jQuery/UI is built-in ! ! JQueryUI, ! ! JQueryUIAutocompleteFromURL(".select-username", IndirectURL(&API_Usernames), 2), ! ! GoogleAnalytics(GoogleAnalyticsID), // Google Analytics is built-in ! ) ! Content: Views{}, } Donnerstag, 22. März 12
  19. URL structure Donnerstag, 22. März 12

  20. Admin_Auth := NewBasicAuth("go-start.org", "admin", "password123") func Paths() *ViewPath { !

    return &ViewPath{View: Homepage, Sub: []ViewPath{ // / ! ! {Name: "style.css", View: HomepageCSS}, // /style.css ! ! {Name: "feed", View: RssFeed}, // /feed/ ! ! {Name: "admin", View: Admin, Auth: Admin_Auth, Sub: []ViewPath{ // /admin/ ! ! ! {Name: "user", Args: 1, View: Admin_User, Auth: Admin_Auth},// /admin/user/<ID>/ ! ! }}, ! ! {Name: "api", Sub: []ViewPath{ // 404 because no view defined ! ! ! {Name: "users.json", View: API_Usernames}, // /api/users.json ! ! }}, ! } } Donnerstag, 22. März 12
  21. Running the server Donnerstag, 22. März 12

  22. view.Init("go-start.org", CookieSecret, "pkg/myproject", "pkg/ gostart") view.Config.RedirectSubdomains = []string{"www"} view.Config.Page.DefaultMetaViewport =

    "width=960px" view.RunConfigFile(Paths(), "run.config") Donnerstag, 22. März 12
  23. Models Donnerstag, 22. März 12

  24. Models • Models are Go structs (marshaling via reflection) •

    Meta information for validation and display is added via tags • Forms and DB share the same model and validation mechanism • MongoDB is the default database Donnerstag, 22. März 12
  25. FormModel Donnerstag, 22. März 12

  26. type SignupFormModel struct { ! Email model.Email `gostart:"required"` ! Password1

    model.Password `gostart:"required|label=Password|minlen=6"` ! Password2 model.Password `gostart:"label=Repeat password"` } func (self *SignupFormModel) Validate(metaData model.MetaData) []*model.ValidationError { ! if self.Password1 != self.Password2 { ! ! return model.NewValidationErrors(os.NewError("Passwords don't match"), metaData) ! } ! return model.NoValidationErrors } form := &Form{ ! ButtonText: "Signup", ! FormID: "user_signup", ! GetModel: func(form *Form, context *Context) (interface{}, os.Error) { ! ! return &SignupFormModel{}, nil ! }, ! OnSubmit: func(form *Form, formModel interface{}, context *Context) (err os.Error) { ! ! m := formModel.(*SignupFormModel) ! ! // ... create user in db and send confirmation email ... ! ! return err ! }, } Donnerstag, 22. März 12
  27. mongo.Document Donnerstag, 22. März 12

  28. type ExampleDoc struct { ! mongo.DocumentBase `bson:",inline"` // Give it

    a Mongo ID ! Person mongo.Ref `gostart:"to=people"` // Mongo ID ref to a document in "people" collection ! LongerText model.Text `gostart:"rows=5|cols=80|maxlen=400"` ! Integer model.Int `gostart:"min=1|max=100"` ! Email model.Email // Normalization + special treament in forms ! PhoneNumber model.Phone // Normalization + special treament in forms ! Password model.Password // Hashed + special treament in forms ! SubDoc struct { ! ! Day model.Date ! ! RealFloat model.Float `gostart:"valid" ! ! Drinks []mongo.Choice `gostart:"options=Beer,Wine,Water"` ! } } Donnerstag, 22. März 12
  29. mongo.Collection Donnerstag, 22. März 12

  30. var ExampleDocs *mongo.Collection = mongo.NewCollection("exampledocs", (*ExampleDoc)(nil)) Donnerstag, 22. März 12

  31. mongo.Query Donnerstag, 22. März 12

  32. i := models.Users.Filter("Name.Last", "Smith").Sort("Name.First").Iterator(); for doc := i.Next(); doc !=

    nil; doc = i.Next() { ! user := doc.(*models.User) ! // ... } // Err() returns any error after Next() returned nil: if i.Err() != nil { ! panic(i.Err()) } Donnerstag, 22. März 12
  33. Create, modify, save a mongo.Document Donnerstag, 22. März 12

  34. user := models.Users.NewDocument().(*models.User) user.Name.First.Set("Erik") user.Name.Last.Set("Unger") doc, err := models.Groups.Filter(“Name”, “testgroup”).One()

    group := doc.(*models.Group) user.Group.Set(group) // sets a mongo.Ref to the group ! err := user.Save() Donnerstag, 22. März 12
  35. Additional packages • Email (message creation missing in standard mail

    package + Google Mail defaults) • Gravatar • RSS parsing • Amiando event management (used by http://startuplive.in) Donnerstag, 22. März 12
  36. Where to get it • https://github.com/ungerik/go-start/ • http://go-start.org/pkg/go-start/ Donnerstag, 22.

    März 12
  37. One more thing ;-) Donnerstag, 22. März 12

  38. Donnerstag, 22. März 12

  39. Donnerstag, 22. März 12

  40. We are hiring! STARTeurope Donnerstag, 22. März 12

  41. Go+Javascript Pioneers (Vienna) Donnerstag, 22. März 12

  42. The End Questions? • erik.unger@starteurope.at • Twitter: @ungerik • Skype:

    ungerik Donnerstag, 22. März 12