go-start - A high level webframework for Go

go-start - A high level webframework for Go

GTUG Vienna 27.3.2012 presentation

go get github.com/ungerik/go-start

F26b047fe2705816b840cf529bd8fc54?s=128

Erik Unger

March 28, 2012
Tweet

Transcript

  1. go-start A high level web-framework for Go Mittwoch, 28. 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 Mittwoch, 28. März 12
  3. Current status • In development for 10 months • Work

    sponsored by STARTeurope • Used in production for http://startuplive.in • Go v1.0 Mittwoch, 28. 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 Mittwoch, 28. März 12
  5. All batteries included • HTML5 Boilerplate • jQuery • External

    dependencies fetched automatically: go get -u github.com/ungerik/go-start Mittwoch, 28. März 12
  6. Views Mittwoch, 28. 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 Mittwoch, 28. März 12
  8. Higher level abstractions •view.List •view.Table •view.Form •view.ModelView •view.If •view.Menu •view.Video

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

    A_blank •Img •Br •B, I, Q •Em, Strong •Ul, Ol Mittwoch, 28. März 12
  10. So how does a view look like? Mittwoch, 28. 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{}, error) { ! ! ! return map[string]string{"Key": "Value"}, nil ! ! }, ! }, } Mittwoch, 28. März 12
  12. Yes, templates are supported Mittwoch, 28. März 12

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

    Boilerplate Page template Mittwoch, 28. März 12
  14. Dynamic Views Mittwoch, 28. März 12

  15. view := NewDynamicView( ! func(context *Context) (view View, err 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 ! }, ) Mittwoch, 28. 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 error) { ! ! user := model.(*models.User) ! ! return PrintfEscape("%s, ", user.Name), nil ! }, } Mittwoch, 28. März 12
  17. HTML Pages Mittwoch, 28. März 12

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

    error) { ! ! context.Data = &PerPageData{...} // Set global page data at request context ! }, ! WriteTitle: func(context *Context, writer io.Writer) (err 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{}, } Mittwoch, 28. März 12
  19. URL structure Mittwoch, 28. 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 ! ! }}, ! } } Mittwoch, 28. März 12
  21. Running the server Mittwoch, 28. 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") Mittwoch, 28. März 12
  23. Models Mittwoch, 28. 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 Mittwoch, 28. März 12
  25. FormModel Mittwoch, 28. 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{}, error) { ! ! return &SignupFormModel{}, nil ! }, ! OnSubmit: func(form *Form, formModel interface{}, context *Context) (err error) { ! ! m := formModel.(*SignupFormModel) ! ! // ... create user in db and send confirmation email ... ! ! return err ! }, } Mittwoch, 28. März 12
  27. mongo.Document Mittwoch, 28. 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"` ! } } Mittwoch, 28. März 12
  29. mongo.Collection Mittwoch, 28. März 12

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

  31. mongo.Query Mittwoch, 28. 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()) } Mittwoch, 28. März 12
  33. Create, modify, save a mongo.Document Mittwoch, 28. 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() Mittwoch, 28. 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) Mittwoch, 28. März 12
  36. Where to get it • go get -u github.com/ungerik/go-start •

    Documentation: http://go-start.org/ Mittwoch, 28. März 12
  37. One more thing ;-) Mittwoch, 28. März 12

  38. Mittwoch, 28. März 12

  39. Mittwoch, 28. März 12

  40. We are hiring! STARTeurope Mittwoch, 28. März 12

  41. Go+Javascript Pioneers (Vienna) Mittwoch, 28. März 12

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

    ungerik Mittwoch, 28. März 12