Buffalo v0.12.0 Released!
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
.