Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Views Donnerstag, 22. März 12

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Higher level abstractions •view.List •view.Table •view.Form •view.ModelView •view.If •view.Menu •view.Video •view.Page Donnerstag, 22. März 12

Slide 9

Slide 9 text

HTML Tags / Shortcuts •H1, H2, ... •P •Pre •A, A_blank •Img •Br •B, I, Q •Em, Strong •Ul, Ol Donnerstag, 22. März 12

Slide 10

Slide 10 text

So how does a view look like? Donnerstag, 22. März 12

Slide 11

Slide 11 text

view := Views{ ! NewDiv("myclass", ! ! H1("Example HTML structure"), ! ! P("This is a paragraph"), ! ! P( ! ! ! HTML("Some unescaped HTML:
"), ! ! ! 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

Slide 12

Slide 12 text

Yes, templates are supported Donnerstag, 22. März 12

Slide 13

Slide 13 text

But I ended up using them only for the HTML5 Boilerplate Page template Donnerstag, 22. März 12

Slide 14

Slide 14 text

Dynamic Views Donnerstag, 22. März 12

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

HTML Pages Donnerstag, 22. März 12

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

URL structure Donnerstag, 22. März 12

Slide 20

Slide 20 text

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// ! ! }}, ! ! {Name: "api", Sub: []ViewPath{ // 404 because no view defined ! ! ! {Name: "users.json", View: API_Usernames}, // /api/users.json ! ! }}, ! } } Donnerstag, 22. März 12

Slide 21

Slide 21 text

Running the server Donnerstag, 22. März 12

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Models Donnerstag, 22. März 12

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

FormModel Donnerstag, 22. März 12

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

mongo.Document Donnerstag, 22. März 12

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

mongo.Collection Donnerstag, 22. März 12

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

mongo.Query Donnerstag, 22. März 12

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Create, modify, save a mongo.Document Donnerstag, 22. März 12

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Where to get it • https://github.com/ungerik/go-start/ • http://go-start.org/pkg/go-start/ Donnerstag, 22. März 12

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Donnerstag, 22. März 12

Slide 39

Slide 39 text

Donnerstag, 22. März 12

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

The End Questions? • [email protected] • Twitter: @ungerik • Skype: ungerik Donnerstag, 22. März 12