Buffalo v0.12.0 Released!

Mark Bates
Buffalo — Rapid Web Development in Go
5 min readJun 13, 2018

--

Please read through the notes to see what is new, what has been improved, and most importantly, what might be breaking changes for existing applications.

In addition to what is listed here, I recommend you read the CHANGELOG for a complete list of what has changed in v0.12.0.

How to Upgrade

First you should upgrade the buffalo binary. This can be done from source using go get

$ go get -u -v github.com/gobuffalo/buffalo/buffalo

You can also download a pre-built binary at:
https://github.com/gobuffalo/buffalo/releases/tag/v0.12.0

You can run the following command to attept to upgrade your application from v0.11.x to v0.12.0.

$ buffalo update

Note: While we have done our best to make this update command work well, please understand that it might not get you to a complete upgrade depending on your application and its complexities, but it will get you pretty close.

If you already have a previous version of Buffalo installed, you can try the new buffalo upgradex plugin. We would love your feedback.

What’s New

Custom Servers

An often asked question on GitHub and Slack is: “How do I run a Buffalo application with SSL?”. The answer has been something along the lines of “Use Nginx or Caddy in front of it”.

Buffalo v0.12.0 introduces two new enhancements to solve this problem.

The first is that gobuffalo/buffalo.App#Serve now accepts a variadic list of gobuffalo/buffalo/servers.Server. The default, with no servers.Server passed in is to get a “default” HTTP server. This is the same HTTP server used in previous versions of Buffalo.

You can now control not only what type of server to run, but how many servers you would like to run.

The second enhancement is the new gobuffalo/buffalo/servers package and the gobuffalo/buffalo/servers.Server interface:

// Server allows for custom server implementations
type Server interface {
Shutdown(context.Context) error
Start(context.Context, http.Handler) error
SetAddr(string)
}

There are also a few wrapper functions to make it easier for you to write your own custom server implementations:

// Wrap converts a standard *http.Server to a buffalo.Server
func Wrap(s *http.Server) Server
// WrapTLS Server converts a standard *http.Server to a
// buffalo.Server but makes sure it is run with TLS.
func WrapTLS(s *http.Server, certFile string, keyFile string) Server
// WrapListener wraps an *http.Server and a net.Listener
func WrapListener(s *http.Server, l net.Listener) Server

Modular Locales Extractors

Buffalo provides basic i18n support since v0.8.1, and advanced support since v0.10.2. In v0.12.0 this features has seen a big upgrade.

There are many ways to find the right locale to display a page to an user. By default, Buffalo extracts the user locale from a cookie, a session, a HTTP header, then fallbacks to the default app language. Until now, if you wanted to customize this behavior, you had to provide a custom LanguageFinder:

// userLanguageFinder implements the `LanguageFinder` interface to provide the locales from the current user profile.
func userLanguageFinder(t *Translator, c buffalo.Context) []string {
langs := []string{}
// try to get the language from the current user
if u, valid := c.Value("currentUser").(models.User); valid {
langs = append(langs, u.Lang)
}
// The predefined extractors are erased, we need to reimplement
// them here
// [...]
return langs
}

With that approach, you had to override the whole behavior to customize it to your needs. That’s no longer the case in Buffalo v0.12.0! In this version, we introduced the concept of Modular Locales Extractors. Instead of rewriting the same thing to add a custom behavior, the i18n middleware now takes a slice of LanguageExtractor. Those extractors are small functions you can stack and order as you want. You don’t need to rewrite every extractor to add a custom one:

// userLanguageExtractor implements the `LanguageExtractor` 
// interface to provide the locales from the current user profile.
func userLanguageExtractor(o LanguageExtractorOptions,
c buffalo.Context) []string {
langs := []string{}
// try to get the language from the current user
if u, valid := c.Value("currentUser").(models.User); valid {
langs = append(langs, u.Lang)
}
// No need to reimplement the other extractors return langs
}

Later you can stack extractors to suit your application:

var T *i18n.Translator// Add our new extractor at the top of the extractors stack.
T.LanguageExtractors = []LanguageExtractor{
userLanguageExtractor,
i18n.CookieLanguageExtractor,
i18n.SessionLanguageExtractor,
i18n.HeaderLanguageExtractor,
}

New Runtime Package

A new package, gobuffalo/buffalo/runtime, is now available for use in applications.

With this package your applications, and built binaries, can gain access to their own version information.

// Build returns the information about the current build
// of the application. In development mode this will almost
// always run zero values for BuildInfo.
func Build() BuildInfo

In your applications you can use it like this:

bi := runtime.Build()
bi.Version // in development "" in a binary "<git-sha of build>"
bi.Time // in development time.Time{} in a binary <timestamp of build>

Webpack 4/Bootstrap 4

New Buffalo applications that are generated with webpack support now get Webpack 4.x, as well as a host of upgraded modules.

Bootstrap has also been upgraded support version 4.

Path Helpers for Redirects

Buffalo has had “path” helpers for a long time. Path helpers are those strange little functions that show up only in your templates that help you to build a path from your routing table:

<%= widgetPath({widget_id: widget.ID}) %>

In v0.12.0 you can now use these same helpers with redirects:

c.Redirect(302, "widgetPath()", render.Data{"widget_id": widget.ID})
// or
c.Redirect(302, "widgetPath()", map[string]interface{}{"widget_id": widget.ID})

Handle 405 Routing Errors

In previous releases errors around missing routes, 405, were not being handled by the Buffalo error handling system, but were, instead, being handled directly by the Gorilla mux router.

In v0.12.0 you can now handle 405 errors just like any other status code error.

a.ErrorHandlers[405] = func(status int, err error, c Context) error {
res := c.Response()
res.WriteHeader(status)
res.Write([]byte("my custom 405"))
return nil
}

Testing Suite Fixture Support

While support for testing fixtures in the gobuffalo/suite package was released a while ago, https://github.com/gobuffalo/suite#fixtures-test-data, Buffalo was not making full use of it.

To start using fixtures in your tests you need to change the way your suites in action/actions_test.go:

package actionsimport (
"testing"
"github.com/gobuffalo/packr"
"github.com/gobuffalo/suite"
)
type ActionSuite struct {
*suite.Action
}
func Test_ActionSuite(t *testing.T) {
action, err := suite.NewActionWithFixtures(App(), packr.NewBox("../fixtures"))
if err != nil {
t.Fatal(err)
}
as := &ActionSuite{
Action: action,
}
suite.Run(t, as)
}

To upgrade your model suites in models/models_test.go:

package models_testimport (
"testing"
"github.com/gobuffalo/packr"
"github.com/gobuffalo/suite"
)
type ModelSuite struct {
*suite.Model
}
func Test_ModelSuite(t *testing.T) {
model, err := suite.NewModelWithFixtures(packr.NewBox("../fixtures"))
if err != nil {
t.Fatal(err)
}
as := &ModelSuite{
Model: model,
}
suite.Run(t, as)
}

Removed Deprecations

buffalo.Context#Websocket Removed

The buffalo.Context#Websocket method didn’t offer much, and caused code clutter. Move to using http://www.gorillatoolkit.org/pkg/websocket directly.

generators.Find Removed

The github.com/gobuffalo/buffalo/generators.Find method has been removed. This is typically only used by Buffalo and other plugins for finding template files during generation. This has been removed in favor of using https://godoc.org/github.com/gobuffalo/buffalo/generators#FindByBox instead.

meta.Name Removed

Use github.com/markbates/inflect#Name instead.

Are you a company looking for Buffalo or Go training? The Gopher Guides want to help. With customizable onsite and virtual training, the Guides will bring your team up to speed quickly, and with the knowledge they need to write great code on a daily basis.

In addition to what is listed here, I recommend you read the CHANGELOG for a complete list of what has changed in v0.12.0.

--

--