diff --git a/Dockerfile b/Dockerfile
index 520102027..a17b2e77c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,9 +20,9 @@ ADD . .
RUN go get -v -t ./...
-RUN go install -v ./buffalo
+RUN go install -v -tags sqlite ./buffalo
-RUN go test -race $(go list ./... | grep -v /vendor/)
+RUN go test -tags sqlite -race $(go list ./... | grep -v /vendor/)
RUN golint -set_exit_status $(go list ./... | grep -v /vendor/)
diff --git a/README.md b/README.md
index bbb55bb9e..d2a10e61c 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ Buffalo would not be possible if not for all of the great projects it depends on
### Models/ORM (Optional)
-[github.com/markbates/pop](https://github.com/markbates/pop) - Accessing databases is nothing new in web applications. Pop, and its command line tool, Soda, were chosen because they strike a nice balance between simplifying common tasks, being idiomatic, and giving you the flexibility you need to built your app. Pop and Soda share the same core philosphies as Buffalo so they were a natural choice.
+[github.com/gobuffalo/pop](https://github.com/gobuffalo/pop) - Accessing databases is nothing new in web applications. Pop, and its command line tool, Soda, were chosen because they strike a nice balance between simplifying common tasks, being idiomatic, and giving you the flexibility you need to built your app. Pop and Soda share the same core philosphies as Buffalo so they were a natural choice.
### Sessions, Cookies, Websockets, and more...
diff --git a/SHOULDERS.md b/SHOULDERS.md
index 90b74c797..759f34d98 100644
--- a/SHOULDERS.md
+++ b/SHOULDERS.md
@@ -126,21 +126,21 @@ Thank you to the following **GIANTS**:
* [github.com/markbates/inflect](https://github.com/markbates/inflect)
-* [github.com/markbates/pop](https://github.com/markbates/pop)
+* [github.com/gobuffalo/pop](https://github.com/gobuffalo/pop)
-* [github.com/markbates/pop/columns](https://github.com/markbates/pop/columns)
+* [github.com/gobuffalo/pop/columns](https://github.com/gobuffalo/pop/columns)
-* [github.com/markbates/pop/fizz](https://github.com/markbates/pop/fizz)
+* [github.com/gobuffalo/pop/fizz](https://github.com/gobuffalo/pop/fizz)
-* [github.com/markbates/pop/fizz/translators](https://github.com/markbates/pop/fizz/translators)
+* [github.com/gobuffalo/pop/fizz/translators](https://github.com/gobuffalo/pop/fizz/translators)
-* [github.com/markbates/pop/nulls](https://github.com/markbates/pop/nulls)
+* [github.com/gobuffalo/pop/nulls](https://github.com/gobuffalo/pop/nulls)
-* [github.com/markbates/pop/soda/cmd](https://github.com/markbates/pop/soda/cmd)
+* [github.com/gobuffalo/pop/soda/cmd](https://github.com/gobuffalo/pop/soda/cmd)
-* [github.com/markbates/pop/soda/cmd/generate](https://github.com/markbates/pop/soda/cmd/generate)
+* [github.com/gobuffalo/pop/soda/cmd/generate](https://github.com/gobuffalo/pop/soda/cmd/generate)
-* [github.com/markbates/pop/soda/cmd/schema](https://github.com/markbates/pop/soda/cmd/schema)
+* [github.com/gobuffalo/pop/soda/cmd/schema](https://github.com/gobuffalo/pop/soda/cmd/schema)
* [github.com/markbates/refresh/refresh](https://github.com/markbates/refresh/refresh)
diff --git a/binding/binding.go b/binding/binding.go
index 8bea8183c..0074411ff 100644
--- a/binding/binding.go
+++ b/binding/binding.go
@@ -8,8 +8,8 @@ import (
"sync"
"time"
+ "github.com/gobuffalo/pop/nulls"
"github.com/gobuffalo/x/httpx"
- "github.com/markbates/pop/nulls"
"github.com/monoculum/formam"
"github.com/pkg/errors"
)
diff --git a/buffalo/cmd/build/templates/a.go.tmpl b/buffalo/cmd/build/templates/a.go.tmpl
index 6fb8b4958..04247da38 100644
--- a/buffalo/cmd/build/templates/a.go.tmpl
+++ b/buffalo/cmd/build/templates/a.go.tmpl
@@ -3,7 +3,7 @@ package a
import (
"log"
"strings"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
"github.com/markbates/inflect"
"github.com/gobuffalo/packr"
<%= if (opts.Environment != "development") { %>
diff --git a/buffalo/cmd/build/templates/main.go.tmpl b/buffalo/cmd/build/templates/main.go.tmpl
index 9df351853..56af3c807 100644
--- a/buffalo/cmd/build/templates/main.go.tmpl
+++ b/buffalo/cmd/build/templates/main.go.tmpl
@@ -10,7 +10,7 @@ import (
_ "<%= opts.ActionsPkg %>"
<%= if (opts.WithPop) { %>
"github.com/gobuffalo/packr"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
"<%= opts.ModelsPkg %>"
<% } %>
<%= if (opts.WithGrifts) { %>
diff --git a/buffalo/cmd/db.go b/buffalo/cmd/db.go
index ffce819e5..a71f2a8ba 100644
--- a/buffalo/cmd/db.go
+++ b/buffalo/cmd/db.go
@@ -2,7 +2,7 @@ package cmd
import (
"github.com/gobuffalo/buffalo/buffalo/cmd/destroy"
- "github.com/markbates/pop/soda/cmd"
+ "github.com/gobuffalo/pop/soda/cmd"
"github.com/spf13/cobra"
)
diff --git a/buffalo/cmd/test.go b/buffalo/cmd/test.go
index 1d73c8196..4097e7438 100644
--- a/buffalo/cmd/test.go
+++ b/buffalo/cmd/test.go
@@ -14,7 +14,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
"github.com/spf13/cobra"
)
diff --git a/buffalo/cmd/update.go b/buffalo/cmd/update.go
new file mode 100644
index 000000000..09f43ca1d
--- /dev/null
+++ b/buffalo/cmd/update.go
@@ -0,0 +1,23 @@
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/gobuffalo/buffalo/buffalo/cmd/updater"
+ "github.com/spf13/cobra"
+)
+
+// updateCmd represents the info command
+var updateCmd = &cobra.Command{
+ Use: "update",
+ Short: fmt.Sprintf("will attempt to upgrade a Buffalo application to version %s", Version),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ updater.Version = Version
+ return updater.Run()
+ },
+}
+
+func init() {
+ decorate("update", RootCmd)
+ RootCmd.AddCommand(updateCmd)
+}
diff --git a/buffalo/cmd/updater/dep.go b/buffalo/cmd/updater/dep.go
new file mode 100644
index 000000000..d0bc5d80c
--- /dev/null
+++ b/buffalo/cmd/updater/dep.go
@@ -0,0 +1,86 @@
+package updater
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/gobuffalo/envy"
+ "github.com/markbates/deplist"
+ "github.com/pkg/errors"
+ "golang.org/x/sync/errgroup"
+)
+
+type lockToml struct {
+ Name string `toml:"name"`
+ Branch string `toml:"branch"`
+ Packages []string `toml:"packages"`
+ Revision string `toml:"revision"`
+ Version string `toml:"version"`
+}
+
+func goGetUpdate(r *Runner) error {
+ fmt.Println("~~~ Running go get ~~~")
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ wg, _ := errgroup.WithContext(ctx)
+ deps, err := deplist.List()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ for dep := range deps {
+ args := []string{"get", "-u"}
+ args = append(args, dep)
+ cc := exec.Command(envy.Get("GO_BIN", "go"), args...)
+ f := func() error {
+ cc.Stdin = os.Stdin
+ cc.Stderr = os.Stderr
+ cc.Stdout = os.Stdout
+ return cc.Run()
+ }
+ wg.Go(f)
+ }
+ err = wg.Wait()
+ if err != nil {
+ return errors.Errorf("We encountered the following error trying to install and update the dependencies for this application:\n%s", err)
+ }
+ return nil
+}
+
+// DepEnsure runs `dep ensure -v` to make sure that any newly changed
+// imports are added to dep.
+func DepEnsure(r *Runner) error {
+ if !r.App.WithDep {
+ return goGetUpdate(r)
+ }
+ fmt.Println("~~~ Running dep ensure ~~~")
+ cc := exec.Command("dep", "ensure", "-v")
+ cc.Stdin = os.Stdin
+ cc.Stderr = os.Stderr
+ cc.Stdout = os.Stdout
+ if err := cc.Run(); err != nil {
+ return errors.WithStack(err)
+ }
+
+ for _, p := range []string{"github.com/gobuffalo/tags@v2.0.0", "github.com/gobuffalo/suite@v2.0.0"} {
+ cc = exec.Command("dep", "ensure", "-v", "-add", p)
+ cc.Stdin = os.Stdin
+ cc.Stderr = os.Stderr
+ cc.Stdout = os.Stdout
+ if err := cc.Run(); err != nil {
+ return errors.WithStack(err)
+ }
+ }
+
+ for _, p := range []string{"github.com/markbates/inflect"} {
+ cc = exec.Command("dep", "ensure", "-v", "-update", p)
+ cc.Stdin = os.Stdin
+ cc.Stderr = os.Stderr
+ cc.Stdout = os.Stdout
+ if err := cc.Run(); err != nil {
+ return errors.WithStack(err)
+ }
+ }
+ return nil
+}
diff --git a/buffalo/cmd/updater/deprecations.go b/buffalo/cmd/updater/deprecations.go
new file mode 100644
index 000000000..94c411f19
--- /dev/null
+++ b/buffalo/cmd/updater/deprecations.go
@@ -0,0 +1,43 @@
+package updater
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+)
+
+// DeprecrationsCheck will either log, or fix, deprecated items in the application
+func DeprecrationsCheck(r *Runner) error {
+ fmt.Println("~~~ Checking for deprecations ~~~")
+ b, err := ioutil.ReadFile("main.go")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ if bytes.Contains(b, []byte("app.Start")) {
+ r.Warnings = append(r.Warnings, "app.Start has been removed in v0.11.0. Use app.Serve Instead. [main.go]")
+ }
+
+ return filepath.Walk(filepath.Join(r.App.Root, "actions"), func(path string, info os.FileInfo, err error) error {
+ if info.IsDir() {
+ return nil
+ }
+
+ if filepath.Ext(path) != ".go" {
+ return nil
+ }
+
+ b, err := ioutil.ReadFile(path)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ if bytes.Contains(b, []byte("Websocket()")) {
+ r.Warnings = append(r.Warnings, fmt.Sprintf("buffalo.Context#Websocket has been deprecated in v0.11.0. Use github.com/gorilla/websocket directly. [%s]", path))
+ }
+
+ return nil
+ })
+}
diff --git a/buffalo/cmd/updater/imports.go b/buffalo/cmd/updater/imports.go
new file mode 100644
index 000000000..1213a6ad5
--- /dev/null
+++ b/buffalo/cmd/updater/imports.go
@@ -0,0 +1,170 @@
+package updater
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/printer"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+// ImportConverter will changes imports from a -> b
+type ImportConverter struct {
+ Data map[string]string
+}
+
+// Process will walk all the .go files in an application, excluding ./vendor.
+// It will then attempt to convert any old import paths to any new import paths
+// used by this version Buffalo.
+func (c ImportConverter) Process(r *Runner) error {
+ fmt.Println("~~~ Rewriting Imports ~~~")
+ err := filepath.Walk(".", func(p string, info os.FileInfo, err error) error {
+ for _, n := range []string{"vendor", "node_modules", ".git"} {
+ if strings.HasPrefix(p, n+string(filepath.Separator)) {
+ return nil
+ }
+ }
+ if info.IsDir() {
+ return nil
+ }
+ ext := filepath.Ext(p)
+ if ext == ".go" {
+ if err := c.rewriteFile(p); err != nil {
+ return errors.WithStack(err)
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if r.App.WithDep {
+ b, err := ioutil.ReadFile("Gopkg.toml")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ for k := range c.Data {
+ if bytes.Contains(b, []byte(k)) {
+ r.Warnings = append(r.Warnings, fmt.Sprintf("Your Gopkg.toml contains the following import that need to be changed MANUALLY: %s", k))
+ }
+ }
+ }
+ return nil
+}
+
+// TAKEN FROM https://gist.github.com/jackspirou/61ce33574e9f411b8b4a
+// rewriteFile rewrites import statments in the named file
+// according to the rules supplied by the map of strings.
+func (c ImportConverter) rewriteFile(name string) error {
+
+ // create an empty fileset.
+ fset := token.NewFileSet()
+
+ // parse the .go file.
+ // we are parsing the entire file with comments, so we don't lose anything
+ // if we need to write it back out.
+ f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
+ if err != nil {
+ e := err.Error()
+ msg := "expected 'package', found 'EOF'"
+ if e[len(e)-len(msg):] == msg {
+ return nil
+ }
+ return err
+ }
+
+ // iterate through the import paths. if a change occurs update bool.
+ change := false
+ for _, i := range f.Imports {
+
+ // unquote the import path value.
+ path, err := strconv.Unquote(i.Path.Value)
+ if err != nil {
+ return err
+ }
+
+ // match import path with the given replacement map
+ if rpath, ok := c.match(path); ok {
+ fmt.Printf("[IMPORT] %s: %s -> %s\n", name, path, rpath)
+ i.Path.Value = strconv.Quote(rpath)
+ change = true
+ }
+ }
+
+ for _, cg := range f.Comments {
+ for _, cl := range cg.List {
+ if strings.HasPrefix(cl.Text, "// import \"") {
+
+ // trim off extra comment stuff
+ ctext := cl.Text
+ ctext = strings.TrimPrefix(ctext, "// import")
+ ctext = strings.TrimSpace(ctext)
+
+ // unquote the comment import path value
+ ctext, err := strconv.Unquote(ctext)
+ if err != nil {
+ return err
+ }
+
+ // match the comment import path with the given replacement map
+ if ctext, ok := c.match(ctext); ok {
+ cl.Text = "// import " + strconv.Quote(ctext)
+ change = true
+ }
+ }
+ }
+ }
+
+ // if no change occured, then we don't need to write to disk, just return.
+ if !change {
+ return nil
+ }
+
+ // since the imports changed, resort them.
+ ast.SortImports(fset, f)
+
+ // create a temporary file, this easily avoids conflicts.
+ temp := name + ".temp"
+ w, err := os.Create(temp)
+ if err != nil {
+ return err
+ }
+
+ // write changes to .temp file, and include proper formatting.
+ err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(w, fset, f)
+ if err != nil {
+ return err
+ }
+
+ // close the writer
+ err = w.Close()
+ if err != nil {
+ return err
+ }
+
+ // rename the .temp to .go
+ return os.Rename(temp, name)
+}
+
+// match takes an import path and replacement map.
+func (c ImportConverter) match(importpath string) (string, bool) {
+ for key, value := range c.Data {
+ if len(importpath) >= len(key) {
+ if importpath[:len(key)] == key {
+ result := path.Join(value, importpath[len(key):])
+ return result, true
+ }
+ }
+ }
+ return importpath, false
+}
diff --git a/buffalo/cmd/updater/npm.go b/buffalo/cmd/updater/npm.go
new file mode 100644
index 000000000..6384cf52b
--- /dev/null
+++ b/buffalo/cmd/updater/npm.go
@@ -0,0 +1,91 @@
+package updater
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+
+ "html/template"
+
+ "github.com/gobuffalo/buffalo/generators/assets/webpack"
+ "github.com/gobuffalo/buffalo/generators/newapp"
+ "github.com/pkg/errors"
+)
+
+// PackageJSONCheck will compare the current default Buffalo
+// package.json against the applications package.json. If they are
+// different you have the option to overwrite the existing package.json
+// file with the new one.
+func PackageJSONCheck(r *Runner) error {
+ fmt.Println("~~~ Checking package.json ~~~")
+
+ if !r.App.WithWebpack {
+ return nil
+ }
+
+ g := newapp.Generator{
+ App: r.App,
+ Bootstrap: 3,
+ }
+
+ box := webpack.TemplateBox
+
+ f, err := box.MustString("package.json.tmpl")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ tmpl, err := template.New("package.json").Parse(f)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ bb := &bytes.Buffer{}
+ err = tmpl.Execute(bb, map[string]interface{}{
+ "opts": g,
+ })
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ b, err := ioutil.ReadFile("package.json")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if string(b) == bb.String() {
+ return nil
+ }
+
+ if !ask("Your package.json file is different from the latest Buffalo template.\nWould you like to REPLACE yours with the latest template?") {
+ fmt.Println("\tskipping package.json")
+ return nil
+ }
+
+ pf, err := os.Create("package.json")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ _, err = pf.Write(bb.Bytes())
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ err = pf.Close()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ var cmd *exec.Cmd
+ if r.App.WithYarn {
+ cmd = exec.Command("yarn", "install")
+ } else {
+ cmd = exec.Command("npm", "install")
+ }
+
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+}
diff --git a/buffalo/cmd/updater/runner.go b/buffalo/cmd/updater/runner.go
new file mode 100644
index 000000000..b80edcaa2
--- /dev/null
+++ b/buffalo/cmd/updater/runner.go
@@ -0,0 +1,50 @@
+package updater
+
+import (
+ "fmt"
+
+ "github.com/gobuffalo/buffalo/meta"
+ "github.com/pkg/errors"
+)
+
+// Check interface for runnable checker functions
+type Check func(*Runner) error
+
+// Runner will run all compatible checks
+type Runner struct {
+ App meta.App
+ Warnings []string
+}
+
+// Run all compatible checks
+func Run() error {
+ fmt.Printf("! This updater will attempt to update your application to Buffalo version: %s\n", Version)
+ if !ask("Do you wish to continue?") {
+ fmt.Println("~~~ cancelling update ~~~")
+ return nil
+ }
+
+ r := &Runner{
+ App: meta.New("."),
+ Warnings: []string{},
+ }
+
+ defer func() {
+ if len(r.Warnings) == 0 {
+ return
+ }
+
+ fmt.Println("\n\n----------------------------")
+ fmt.Printf("!!! (%d) Warnings Were Found !!!\n\n", len(r.Warnings))
+ for _, w := range r.Warnings {
+ fmt.Printf("[WARNING]: %s\n", w)
+ }
+ }()
+
+ for _, c := range checks {
+ if err := c(r); err != nil {
+ return errors.WithStack(err)
+ }
+ }
+ return nil
+}
diff --git a/buffalo/cmd/updater/updater.go b/buffalo/cmd/updater/updater.go
new file mode 100644
index 000000000..2d8b62b87
--- /dev/null
+++ b/buffalo/cmd/updater/updater.go
@@ -0,0 +1,40 @@
+package updater
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+)
+
+// Version is the current Buffalo version. It is set here by the cmd package.
+// This is due to circular dependencies
+var Version string
+
+var replace = map[string]string{
+ "github.com/markbates/pop": "github.com/gobuffalo/pop",
+ "github.com/markbates/validate": "github.com/gobuffalo/validate",
+ "github.com/satori/go.uuid": "github.com/gobuffalo/uuid",
+}
+
+var ic = ImportConverter{
+ Data: replace,
+}
+
+var checks = []Check{
+ ic.Process,
+ WebpackCheck,
+ PackageJSONCheck,
+ DepEnsure,
+ DeprecrationsCheck,
+}
+
+func ask(q string) bool {
+ fmt.Printf("? %s [y/n]\n", q)
+
+ reader := bufio.NewReader(os.Stdin)
+ text, _ := reader.ReadString('\n')
+
+ text = strings.ToLower(strings.TrimSpace(text))
+ return text == "y" || text == "yes"
+}
diff --git a/buffalo/cmd/updater/webpack.go b/buffalo/cmd/updater/webpack.go
new file mode 100644
index 000000000..fd1a30d96
--- /dev/null
+++ b/buffalo/cmd/updater/webpack.go
@@ -0,0 +1,75 @@
+package updater
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "html/template"
+
+ "github.com/gobuffalo/buffalo/generators/assets/webpack"
+ "github.com/gobuffalo/buffalo/generators/newapp"
+ "github.com/pkg/errors"
+)
+
+// WebpackCheck will compare the current default Buffalo
+// webpack.config.js against the applications webpack.config.js. If they are
+// different you have the option to overwrite the existing webpack.config.js
+// file with the new one.
+func WebpackCheck(r *Runner) error {
+ fmt.Println("~~~ Checking webpack.config.js ~~~")
+
+ if !r.App.WithWebpack {
+ return nil
+ }
+
+ g := newapp.Generator{
+ App: r.App,
+ Bootstrap: 3,
+ }
+
+ box := webpack.TemplateBox
+
+ f, err := box.MustString("webpack.config.js.tmpl")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ tmpl, err := template.New("webpack").Parse(f)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ bb := &bytes.Buffer{}
+ err = tmpl.Execute(bb, map[string]interface{}{
+ "opts": g,
+ })
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ b, err := ioutil.ReadFile("webpack.config.js")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if string(b) == bb.String() {
+ return nil
+ }
+
+ if !ask("Your webpack.config.js file is different from the latest Buffalo template.\nWould you like to replace yours with the latest template?") {
+ fmt.Println("\tSkipping webpack.config.js")
+ return nil
+ }
+
+ wf, err := os.Create("webpack.config.js")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ _, err = wf.Write(bb.Bytes())
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ return wf.Close()
+}
diff --git a/default_context.go b/default_context.go
index 1528cbf90..b1b69076f 100644
--- a/default_context.go
+++ b/default_context.go
@@ -14,8 +14,8 @@ import (
"github.com/gobuffalo/buffalo/binding"
"github.com/gobuffalo/buffalo/render"
+ "github.com/gobuffalo/pop"
"github.com/gorilla/websocket"
- "github.com/markbates/pop"
"github.com/pkg/errors"
)
diff --git a/generators/assets/webpack/webpack.go b/generators/assets/webpack/webpack.go
index dae6a24d2..ce4681712 100644
--- a/generators/assets/webpack/webpack.go
+++ b/generators/assets/webpack/webpack.go
@@ -13,6 +13,9 @@ import (
"github.com/sirupsen/logrus"
)
+// TemplateBox contains all templates needed for the webpack generator
+var TemplateBox = packr.NewBox("../webpack/templates")
+
var logo = &makr.RemoteFile{
File: makr.NewFile("assets/images/logo.svg", ""),
RemotePath: assets.LogoURL,
@@ -45,7 +48,7 @@ func (w Generator) Run(root string, data makr.Data) error {
g.Add(logo)
- files, err := generators.FindByBox(packr.NewBox("../webpack/templates"))
+ files, err := generators.FindByBox(TemplateBox)
if err != nil {
return errors.WithStack(err)
}
diff --git a/generators/resource/templates/actions/resource-json-xml.go.tmpl b/generators/resource/templates/actions/resource-json-xml.go.tmpl
new file mode 100644
index 000000000..964d43b04
--- /dev/null
+++ b/generators/resource/templates/actions/resource-json-xml.go.tmpl
@@ -0,0 +1,171 @@
+package actions
+
+import (
+
+ "github.com/pkg/errors"
+ "github.com/gobuffalo/buffalo"
+ "github.com/gobuffalo/pop"
+ "{{.opts.App.ModelsPkg}}"
+)
+
+// This file is generated by Buffalo. It offers a basic structure for
+// adding, editing and deleting a page. If your model is more
+// complex or you need more than the basic implementation you need to
+// edit this file.
+
+// Following naming logic is implemented in Buffalo:
+// Model: Singular ({{.opts.Model.Model}})
+// DB Table: Plural ({{.opts.Model.Table}})
+// Resource: Plural ({{.opts.Name.Resource}})
+// Path: Plural (/{{.opts.Name.URL}})
+// View Template Folder: Plural (/templates/{{.opts.FilesPath}}/)
+
+// {{.opts.Name.Resource}}Resource is the resource for the {{.opts.Model.Model}} model
+type {{.opts.Name.Resource}}Resource struct{
+ buffalo.Resource
+}
+
+// List gets all {{.opts.Model.ModelPlural}}. This function is mapped to the path
+// GET /{{.opts.Name.URL}}
+func (v {{.opts.Name.Resource}}Resource) List(c buffalo.Context) error {
+ // Get the DB connection from the context
+ tx, ok := c.Value("tx").(*pop.Connection)
+ if !ok {
+ return errors.WithStack(errors.New("no transaction found"))
+ }
+
+ {{.opts.Model.VarCasePlural}} := &models.{{.opts.Model.ModelPlural}}{}
+
+ // Paginate results. Params "page" and "per_page" control pagination.
+ // Default values are "page=1" and "per_page=20".
+ q := tx.PaginateFromParams(c.Params())
+
+ // Retrieve all {{.opts.Model.ModelPlural}} from the DB
+ if err := q.All({{.opts.Model.VarCasePlural}}); err != nil {
+ return errors.WithStack(err)
+ }
+
+ // Add the paginator to the headers so clients know how to paginate.
+ c.Response().Header().Set("X-Pagination", q.Paginator.String())
+
+ return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCasePlural}}))
+}
+
+// Show gets the data for one {{.opts.Model.Model}}. This function is mapped to
+// the path GET /{{.opts.Name.URL}}/{{"{"}}{{.opts.Name.ParamID}}}
+func (v {{.opts.Name.Resource}}Resource) Show(c buffalo.Context) error {
+ // Get the DB connection from the context
+ tx, ok := c.Value("tx").(*pop.Connection)
+ if !ok {
+ return errors.WithStack(errors.New("no transaction found"))
+ }
+
+ // Allocate an empty {{.opts.Model.Model}}
+ {{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
+
+ // To find the {{.opts.Model.Model}} the parameter {{.opts.Name.ParamID}} is used.
+ if err := tx.Find({{.opts.Model.VarCaseSingular}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
+ return c.Error(404, err)
+ }
+
+ return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
+}
+
+// New default implementation. Returns a 404
+func (v {{.opts.Name.Resource}}Resource) New(c buffalo.Context) error {
+ return c.Error(404, errors.New("not available"))
+}
+
+// Create adds a {{.opts.Model.Model}} to the DB. This function is mapped to the
+// path POST /{{.opts.Name.URL}}
+func (v {{.opts.Name.Resource}}Resource) Create(c buffalo.Context) error {
+ // Allocate an empty {{.opts.Model.Model}}
+ {{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
+
+ // Bind {{.opts.Model.VarCaseSingular}} to the html form elements
+ if err := c.Bind({{.opts.Model.VarCaseSingular}}); err != nil {
+ return errors.WithStack(err)
+ }
+
+ // Get the DB connection from the context
+ tx, ok := c.Value("tx").(*pop.Connection)
+ if !ok {
+ return errors.WithStack(errors.New("no transaction found"))
+ }
+
+ // Validate the data from the html form
+ verrs, err := tx.ValidateAndCreate({{.opts.Model.VarCaseSingular}})
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if verrs.HasAny() {
+ // Render errors as {{.opts.MimeType}}
+ return c.Render(400, r.{{.opts.MimeType}}(verrs))
+ }
+
+ return c.Render(201, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
+}
+
+// Edit default implementation. Returns a 404
+func (v {{.opts.Name.Resource}}Resource) Edit(c buffalo.Context) error {
+ return c.Error(404, errors.New("not available"))
+}
+
+// Update changes a {{.opts.Model.Model}} in the DB. This function is mapped to
+// the path PUT /{{.opts.Name.URL}}/{{"{"}}{{.opts.Name.ParamID}}}
+func (v {{.opts.Name.Resource}}Resource) Update(c buffalo.Context) error {
+ // Get the DB connection from the context
+ tx, ok := c.Value("tx").(*pop.Connection)
+ if !ok {
+ return errors.WithStack(errors.New("no transaction found"))
+ }
+
+ // Allocate an empty {{.opts.Model.Model}}
+ {{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
+
+ if err := tx.Find({{.opts.Model.VarCaseSingular}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
+ return c.Error(404, err)
+ }
+
+ // Bind {{.opts.Model.Model}} to the html form elements
+ if err := c.Bind({{.opts.Model.VarCaseSingular}}); err != nil {
+ return errors.WithStack(err)
+ }
+
+ verrs, err := tx.ValidateAndUpdate({{.opts.Model.VarCaseSingular}})
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if verrs.HasAny() {
+ // Render errors as {{.opts.MimeType}}
+ return c.Render(400, r.{{.opts.MimeType}}(verrs))
+ }
+
+ return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
+}
+
+// Destroy deletes a {{.opts.Model.Model}} from the DB. This function is mapped
+// to the path DELETE /{{.opts.Name.URL}}/{{"{"}}{{.opts.Name.ParamID}}}
+func (v {{.opts.Name.Resource}}Resource) Destroy(c buffalo.Context) error {
+ // Get the DB connection from the context
+ tx, ok := c.Value("tx").(*pop.Connection)
+ if !ok {
+ return errors.WithStack(errors.New("no transaction found"))
+ }
+
+ // Allocate an empty {{.opts.Model.Model}}
+ {{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
+
+ // To find the {{.opts.Model.Model}} the parameter {{.opts.Name.ParamID}} is used.
+ if err := tx.Find({{.opts.Model.VarCaseSingular}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
+ return c.Error(404, err)
+ }
+
+ if err := tx.Destroy({{.opts.Model.VarCaseSingular}}); err != nil {
+ return errors.WithStack(err)
+ }
+
+ return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
+}
diff --git a/generators/resource/templates/actions/resource-use_model.go.tmpl b/generators/resource/templates/actions/resource-use_model.go.tmpl
index b443e8b7a..095384f68 100644
--- a/generators/resource/templates/actions/resource-use_model.go.tmpl
+++ b/generators/resource/templates/actions/resource-use_model.go.tmpl
@@ -4,7 +4,7 @@ import (
"github.com/pkg/errors"
"github.com/gobuffalo/buffalo"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
"{{.opts.App.ModelsPkg}}"
)
diff --git a/generators/soda/soda.go b/generators/soda/soda.go
index a5e7314a6..2a0df4d39 100644
--- a/generators/soda/soda.go
+++ b/generators/soda/soda.go
@@ -2,7 +2,7 @@ package soda
import (
"github.com/gobuffalo/makr"
- sg "github.com/markbates/pop/soda/cmd/generate"
+ sg "github.com/gobuffalo/pop/soda/cmd/generate"
)
// Run the soda generator
@@ -26,7 +26,7 @@ func (sd Generator) Run(root string, data makr.Data) error {
f.Should = should
g.Add(f)
- c := makr.NewCommand(makr.GoGet("github.com/markbates/pop/..."))
+ c := makr.NewCommand(makr.GoGet("github.com/gobuffalo/pop/..."))
c.Should = should
g.Add(c)
@@ -47,7 +47,7 @@ import (
"log"
"github.com/gobuffalo/envy"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
)
// DB is a connection to your database to be used
diff --git a/middleware/pop_transaction.go b/middleware/pop_transaction.go
index 6a3b51663..2313b5eed 100644
--- a/middleware/pop_transaction.go
+++ b/middleware/pop_transaction.go
@@ -6,7 +6,7 @@ import (
"github.com/pkg/errors"
"github.com/gobuffalo/buffalo"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
)
// PopTransaction is a piece of Buffalo middleware that wraps each
diff --git a/middleware/pop_transaction_test.go b/middleware/pop_transaction_test.go
index b67da2e1a..62611ad70 100644
--- a/middleware/pop_transaction_test.go
+++ b/middleware/pop_transaction_test.go
@@ -8,10 +8,10 @@ import (
"time"
"github.com/gobuffalo/buffalo"
- "github.com/markbates/pop"
+ "github.com/gobuffalo/pop"
+ "github.com/gobuffalo/uuid"
"github.com/markbates/willie"
"github.com/pkg/errors"
- "github.com/satori/go.uuid"
"github.com/stretchr/testify/require"
)
diff --git a/options.go b/options.go
index d9bae0cd8..eedff35c1 100644
--- a/options.go
+++ b/options.go
@@ -11,9 +11,9 @@ import (
"github.com/fatih/color"
"github.com/gobuffalo/buffalo/worker"
"github.com/gobuffalo/envy"
+ "github.com/gobuffalo/pop"
"github.com/gorilla/sessions"
"github.com/markbates/going/defaults"
- "github.com/markbates/pop"
)
// Options are used to configure and define how your application should run.
diff --git a/render/auto.go b/render/auto.go
index f9ba46fa3..2c026967f 100644
--- a/render/auto.go
+++ b/render/auto.go
@@ -4,8 +4,8 @@ import (
"context"
"fmt"
"io"
- "path"
"reflect"
+ "regexp"
"strings"
"github.com/markbates/inflect"
@@ -91,7 +91,8 @@ func (htmlAutoRenderer) ContentType() string {
}
func (ir htmlAutoRenderer) Render(w io.Writer, data Data) error {
- name := inflect.Name(ir.typeName().Singular())
+ name := inflect.Name(ir.typeName())
+ name = inflect.Name(name.Singular())
pname := inflect.Name(name.Plural())
if ir.isPlural() {
@@ -100,68 +101,45 @@ func (ir htmlAutoRenderer) Render(w io.Writer, data Data) error {
data[name.VarCaseSingular()] = ir.model
}
- cp, ok := data["current_path"].(string)
switch data["method"] {
- case "PUT":
- code := ir.status(data)
- // if successful redirect to the GET version of the URL
- // PUT /users/1 -> redirect -> GET /users/1
- if code < 400 {
- return ErrRedirect{
- Status: code,
- URL: cp,
- }
- }
- if ok {
- // PUT /users/1 -> /users
- cp = path.Dir(cp)
- } else {
- cp = pname.File()
- }
- return ir.HTML(fmt.Sprintf("%s/edit.html", cp)).Render(w, data)
- case "POST":
- if err := ir.redirect(cp, w, data); err != nil {
+ case "PUT", "POST":
+ if err := ir.redirect(pname, w, data); err != nil {
if er, ok := err.(ErrRedirect); ok && er.Status >= 300 && er.Status < 400 {
return err
}
+ if data["method"] == "PUT" {
+ return ir.HTML(fmt.Sprintf("%s/edit.html", pname.File())).Render(w, data)
+ }
+ return ir.HTML(fmt.Sprintf("%s/new.html", pname.File())).Render(w, data)
}
- return ir.HTML(fmt.Sprintf("%s/new.html", cp)).Render(w, data)
+ return nil
case "DELETE":
- if ok {
- // DELETE /users/{id} -> /users
- cp = path.Dir(cp)
- } else {
- cp = "/" + pname.URL()
- }
return ErrRedirect{
Status: 302,
- URL: cp,
+ URL: "/" + pname.URL(),
}
}
- if ok {
+ if cp, ok := data["current_path"].(string); ok {
if strings.HasSuffix(cp, "/edit") {
- // GET /users/{id}/edit -> /users
- cp = path.Dir(path.Dir(cp))
- return ir.HTML(fmt.Sprintf("%s/edit.html", cp)).Render(w, data)
+ return ir.HTML(fmt.Sprintf("%s/edit.html", pname.File())).Render(w, data)
}
if strings.HasSuffix(cp, "/new") {
- // GET /users/new -> /users
- cp = path.Dir(cp)
- return ir.HTML(fmt.Sprintf("%s/new.html", cp)).Render(w, data)
+ return ir.HTML(fmt.Sprintf("%s/new.html", pname.File())).Render(w, data)
}
- if ir.isPlural() {
- // GET /users - if it's a slice/array render the index page
- return ir.HTML(fmt.Sprintf("%s/%s.html", cp, "index")).Render(w, data)
+ x, err := regexp.Compile(fmt.Sprintf("%s/.+", pname.URL()))
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ if x.MatchString(cp) {
+ return ir.HTML(fmt.Sprintf("%s/show.html", pname.File())).Render(w, data)
}
- // GET /users/{id}
- return ir.HTML(fmt.Sprintf("%s/show.html", path.Dir(cp))).Render(w, data)
}
- return errors.New("could not auto render this model, please render it manually")
+ return ir.HTML(fmt.Sprintf("%s/%s.html", pname.File(), "index")).Render(w, data)
}
-func (ir htmlAutoRenderer) redirect(path string, w io.Writer, data Data) error {
+func (ir htmlAutoRenderer) redirect(name inflect.Name, w io.Writer, data Data) error {
rv := reflect.Indirect(reflect.ValueOf(ir.model))
f := rv.FieldByName("ID")
if !f.IsValid() {
@@ -172,34 +150,32 @@ func (ir htmlAutoRenderer) redirect(path string, w io.Writer, data Data) error {
rt := reflect.TypeOf(fi)
zero := reflect.Zero(rt)
if fi != zero.Interface() {
- url := fmt.Sprintf("%s/%v", path, f.Interface())
+ url := fmt.Sprintf("/%s/%v", name.URL(), f.Interface())
+ code := 302
+ if i, ok := data["status"].(int); ok {
+ if i >= 300 {
+ code = i
+ }
+ }
return ErrRedirect{
- Status: ir.status(data),
+ Status: code,
URL: url,
}
}
return errNoID
}
-func (ir htmlAutoRenderer) status(data Data) int {
- if i, ok := data["status"].(int); ok {
- if i >= 300 {
- return i
- }
- }
- return 302
-}
-
-func (ir htmlAutoRenderer) typeName() inflect.Name {
+func (ir htmlAutoRenderer) typeName() string {
rv := reflect.Indirect(reflect.ValueOf(ir.model))
rt := rv.Type()
switch rt.Kind() {
case reflect.Slice, reflect.Array:
el := rt.Elem()
- return inflect.Name(el.Name())
+ return el.Name()
+ default:
+ return rt.Name()
}
- return inflect.Name(rt.Name())
}
func (ir htmlAutoRenderer) isPlural() bool {
diff --git a/render/auto.go~HEAD b/render/auto.go~HEAD
new file mode 100644
index 000000000..f9ba46fa3
--- /dev/null
+++ b/render/auto.go~HEAD
@@ -0,0 +1,213 @@
+package render
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "path"
+ "reflect"
+ "strings"
+
+ "github.com/markbates/inflect"
+ "github.com/pkg/errors"
+)
+
+var errNoID = errors.New("no ID on model")
+
+// ErrRedirect indicates to Context#Render that this is a
+// redirect and a template shouldn't be rendered.
+type ErrRedirect struct {
+ Status int
+ URL string
+}
+
+func (ErrRedirect) Error() string {
+ return ""
+}
+
+// Auto figures out how to render the model based information
+// about the request and the name of the model. Auto supports
+// automatic rendering of HTML, JSON, and XML. Any status code
+// give to Context#Render between 300 - 400 will be respected
+// by Auto. Other status codes are not.
+/*
+# Rules for HTML template lookup:
+GET /users - users/index.html
+GET /users/id - users/show.html
+GET /users/new - users/new.html
+GET /users/id/edit - users/edit.html
+POST /users - (redirect to /users/id or render user/new.html)
+PUT /users/edit - (redirect to /users/id or render user/edit.html)
+DELETE /users/id - redirect to /users
+*/
+func Auto(ctx context.Context, i interface{}) Renderer {
+ e := New(Options{})
+ return e.Auto(ctx, i)
+}
+
+// Auto figures out how to render the model based information
+// about the request and the name of the model. Auto supports
+// automatic rendering of HTML, JSON, and XML. Any status code
+// give to Context#Render between 300 - 400 will be respected
+// by Auto. Other status codes are not.
+/*
+# Rules for HTML template lookup:
+GET /users - users/index.html
+GET /users/id - users/show.html
+GET /users/new - users/new.html
+GET /users/id/edit - users/edit.html
+POST /users - (redirect to /users/id or render user/new.html)
+PUT /users/edit - (redirect to /users/id or render user/edit.html)
+DELETE /users/id - redirect to /users
+*/
+func (e *Engine) Auto(ctx context.Context, i interface{}) Renderer {
+ ct, ok := ctx.Value("contentType").(string)
+ if !ok {
+ ct = "text/html"
+ }
+ ct = strings.ToLower(ct)
+
+ if strings.Contains(ct, "json") {
+ return e.JSON(i)
+ }
+
+ if strings.Contains(ct, "xml") {
+ return e.XML(i)
+ }
+
+ return htmlAutoRenderer{
+ Engine: e,
+ model: i,
+ }
+}
+
+type htmlAutoRenderer struct {
+ *Engine
+ model interface{}
+}
+
+func (htmlAutoRenderer) ContentType() string {
+ return "text/html"
+}
+
+func (ir htmlAutoRenderer) Render(w io.Writer, data Data) error {
+ name := inflect.Name(ir.typeName().Singular())
+ pname := inflect.Name(name.Plural())
+
+ if ir.isPlural() {
+ data[pname.VarCasePlural()] = ir.model
+ } else {
+ data[name.VarCaseSingular()] = ir.model
+ }
+
+ cp, ok := data["current_path"].(string)
+ switch data["method"] {
+ case "PUT":
+ code := ir.status(data)
+ // if successful redirect to the GET version of the URL
+ // PUT /users/1 -> redirect -> GET /users/1
+ if code < 400 {
+ return ErrRedirect{
+ Status: code,
+ URL: cp,
+ }
+ }
+ if ok {
+ // PUT /users/1 -> /users
+ cp = path.Dir(cp)
+ } else {
+ cp = pname.File()
+ }
+ return ir.HTML(fmt.Sprintf("%s/edit.html", cp)).Render(w, data)
+ case "POST":
+ if err := ir.redirect(cp, w, data); err != nil {
+ if er, ok := err.(ErrRedirect); ok && er.Status >= 300 && er.Status < 400 {
+ return err
+ }
+ }
+ return ir.HTML(fmt.Sprintf("%s/new.html", cp)).Render(w, data)
+ case "DELETE":
+ if ok {
+ // DELETE /users/{id} -> /users
+ cp = path.Dir(cp)
+ } else {
+ cp = "/" + pname.URL()
+ }
+ return ErrRedirect{
+ Status: 302,
+ URL: cp,
+ }
+ }
+ if ok {
+ if strings.HasSuffix(cp, "/edit") {
+ // GET /users/{id}/edit -> /users
+ cp = path.Dir(path.Dir(cp))
+ return ir.HTML(fmt.Sprintf("%s/edit.html", cp)).Render(w, data)
+ }
+ if strings.HasSuffix(cp, "/new") {
+ // GET /users/new -> /users
+ cp = path.Dir(cp)
+ return ir.HTML(fmt.Sprintf("%s/new.html", cp)).Render(w, data)
+ }
+
+ if ir.isPlural() {
+ // GET /users - if it's a slice/array render the index page
+ return ir.HTML(fmt.Sprintf("%s/%s.html", cp, "index")).Render(w, data)
+ }
+ // GET /users/{id}
+ return ir.HTML(fmt.Sprintf("%s/show.html", path.Dir(cp))).Render(w, data)
+ }
+
+ return errors.New("could not auto render this model, please render it manually")
+}
+
+func (ir htmlAutoRenderer) redirect(path string, w io.Writer, data Data) error {
+ rv := reflect.Indirect(reflect.ValueOf(ir.model))
+ f := rv.FieldByName("ID")
+ if !f.IsValid() {
+ return errNoID
+ }
+
+ fi := f.Interface()
+ rt := reflect.TypeOf(fi)
+ zero := reflect.Zero(rt)
+ if fi != zero.Interface() {
+ url := fmt.Sprintf("%s/%v", path, f.Interface())
+
+ return ErrRedirect{
+ Status: ir.status(data),
+ URL: url,
+ }
+ }
+ return errNoID
+}
+
+func (ir htmlAutoRenderer) status(data Data) int {
+ if i, ok := data["status"].(int); ok {
+ if i >= 300 {
+ return i
+ }
+ }
+ return 302
+}
+
+func (ir htmlAutoRenderer) typeName() inflect.Name {
+ rv := reflect.Indirect(reflect.ValueOf(ir.model))
+ rt := rv.Type()
+ switch rt.Kind() {
+ case reflect.Slice, reflect.Array:
+ el := rt.Elem()
+ return inflect.Name(el.Name())
+ }
+ return inflect.Name(rt.Name())
+}
+
+func (ir htmlAutoRenderer) isPlural() bool {
+ rv := reflect.Indirect(reflect.ValueOf(ir.model))
+ rt := rv.Type()
+ switch rt.Kind() {
+ case reflect.Slice, reflect.Array, reflect.Map:
+ return true
+ }
+ return false
+}
diff --git a/render/auto_test.go b/render/auto_test.go
index 052970810..f71319702 100644
--- a/render/auto_test.go
+++ b/render/auto_test.go
@@ -1,7 +1,6 @@
package render_test
import (
- "fmt"
"os"
"path/filepath"
"strings"
@@ -52,218 +51,196 @@ func Test_Auto_XML(t *testing.T) {
func Test_Auto_HTML_List(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/index.html", p), "INDEX: <%= len(cars) %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.GET(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
- return c.Render(200, e.Auto(c, Cars{
- {Name: "Ford"},
- {Name: "Chevy"},
- }))
- })
+ err := withHTMLFile("cars/index.html", "INDEX: <%= len(cars) %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET("/cars", func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Cars{
+ {Name: "Ford"},
+ {Name: "Chevy"},
+ }))
+ })
- w := willie.New(app)
- res := w.HTML("/%s", p).Get()
+ w := willie.New(app)
+ res := w.HTML("/cars").Get()
- r.Contains(res.Body.String(), "INDEX: 2")
- })
- r.NoError(err)
- }
+ r.Contains(res.Body.String(), "INDEX: 2")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_Show(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/show.html", p), "Show: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.GET(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
- return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
- })
-
- w := willie.New(app)
- res := w.HTML("/%s/1", p).Get()
- r.Contains(res.Body.String(), "Show: Honda")
+ err := withHTMLFile("cars/show.html", "Show: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET("/cars/{id}", func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
})
- r.NoError(err)
- }
+
+ w := willie.New(app)
+ res := w.HTML("/cars/1").Get()
+ r.Contains(res.Body.String(), "Show: Honda")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_New(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/new.html", p), "New: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.GET(fmt.Sprintf("/%s/new", p), func(c buffalo.Context) error {
- return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
- })
-
- w := willie.New(app)
- res := w.HTML("/%s/new", p).Get()
- r.Contains(res.Body.String(), "New: Honda")
+ err := withHTMLFile("cars/new.html", "New: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET("/cars/new", func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
})
- r.NoError(err)
- }
+
+ w := willie.New(app)
+ res := w.HTML("/cars/new").Get()
+ r.Contains(res.Body.String(), "New: Honda")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_Create(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/new.html", p), "New: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.POST(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
- return c.Render(201, e.Auto(c, Car{Name: "Honda"}))
- })
-
- w := willie.New(app)
- res := w.HTML("/%s", p).Post(nil)
- r.Contains(res.Body.String(), "New: Honda")
+ err := withHTMLFile("cars/new.html", "New: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.POST("/cars", func(c buffalo.Context) error {
+ return c.Render(201, e.Auto(c, Car{Name: "Honda"}))
})
- r.NoError(err)
- }
+
+ w := willie.New(app)
+ res := w.HTML("/cars").Post(nil)
+ r.Contains(res.Body.String(), "New: Honda")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_Create_Redirect(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- app := buffalo.New(buffalo.Options{})
- app.POST(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
- return c.Render(201, render.Auto(c, Car{
- ID: 1,
- Name: "Honda",
- }))
- })
+ app := buffalo.New(buffalo.Options{})
+ app.POST("/cars", func(c buffalo.Context) error {
+ return c.Render(201, render.Auto(c, Car{
+ ID: 1,
+ Name: "Honda",
+ }))
+ })
- w := willie.New(app)
- res := w.HTML("/%s", p).Post(nil)
- r.Equal(fmt.Sprintf("/%s/1", p), res.Location())
- r.Equal(302, res.Code)
- }
+ w := willie.New(app)
+ res := w.HTML("/cars").Post(nil)
+ r.Equal("/cars/1", res.Location())
+ r.Equal(302, res.Code)
}
func Test_Auto_HTML_Create_Redirect_Error(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/new.html", p), "Create: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.POST(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
- b := Car{
- Name: "Honda",
- }
- return c.Render(422, e.Auto(c, b))
- })
-
- w := willie.New(app)
- res := w.HTML("/%s", p).Post(nil)
- r.Equal(422, res.Code)
- r.Contains(res.Body.String(), "Create: Honda")
+ err := withHTMLFile("cars/new.html", "Create: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.POST("/cars", func(c buffalo.Context) error {
+ b := Car{
+ Name: "Honda",
+ }
+ return c.Render(422, e.Auto(c, b))
})
- r.NoError(err)
- }
+
+ w := willie.New(app)
+ res := w.HTML("/cars").Post(nil)
+ r.Equal(422, res.Code)
+ r.Contains(res.Body.String(), "Create: Honda")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_Edit(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/edit.html", p), "Edit: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.GET(fmt.Sprintf("/%s/{id}/edit", p), func(c buffalo.Context) error {
- return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
- })
-
- w := willie.New(app)
- res := w.HTML("/%s/1/edit", p).Get()
- r.Contains(res.Body.String(), "Edit: Honda")
+ err := withHTMLFile("cars/edit.html", "Edit: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET("/cars/{id}/edit", func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
})
- r.NoError(err)
- }
+
+ w := willie.New(app)
+ res := w.HTML("/cars/1/edit").Get()
+ r.Contains(res.Body.String(), "Edit: Honda")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_Update(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/edit.html", p), "Update: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.PUT(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
- return c.Render(422, e.Auto(c, Car{Name: "Honda"}))
- })
+ err := withHTMLFile("cars/edit.html", "Update: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.PUT("/cars/{id}", func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
+ })
- w := willie.New(app)
- res := w.HTML("/%s/1", p).Put(nil)
+ w := willie.New(app)
+ res := w.HTML("/cars/1").Put(nil)
- r.Contains(res.Body.String(), "Update: Honda")
- })
- r.NoError(err)
- }
+ r.Contains(res.Body.String(), "Update: Honda")
+ })
+ r.NoError(err)
}
func Test_Auto_HTML_Update_Redirect(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- app := buffalo.New(buffalo.Options{})
- app.PUT(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
- b := Car{
- ID: 1,
- Name: "Honda",
- }
- return c.Render(200, render.Auto(c, b))
- })
+ app := buffalo.New(buffalo.Options{})
+ app.PUT("/cars/{id}", func(c buffalo.Context) error {
+ b := Car{
+ ID: 1,
+ Name: "Honda",
+ }
+ return c.Render(200, render.Auto(c, b))
+ })
- w := willie.New(app)
- res := w.HTML("/%s/1", p).Put(nil)
- r.Equal(fmt.Sprintf("/%s/1", p), res.Location())
- r.Equal(302, res.Code)
- }
+ w := willie.New(app)
+ res := w.HTML("/cars/1").Put(nil)
+ r.Equal("/cars/1", res.Location())
+ r.Equal(302, res.Code)
}
func Test_Auto_HTML_Update_Redirect_Error(t *testing.T) {
r := require.New(t)
- for _, p := range []string{"cars", "admin/cars"} {
- err := withHTMLFile(fmt.Sprintf("%s/edit.html", p), "Update: <%= car.Name %>", func(e *render.Engine) {
- app := buffalo.New(buffalo.Options{})
- app.PUT(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
- b := Car{
- ID: 1,
- Name: "Honda",
- }
- return c.Render(422, e.Auto(c, b))
- })
-
- w := willie.New(app)
- res := w.HTML("/%s/1", p).Put(nil)
- r.Equal(422, res.Code)
- r.Contains(res.Body.String(), "Update: Honda")
- })
- r.NoError(err)
- }
-}
-
-func Test_Auto_HTML_Destroy_Redirect(t *testing.T) {
- r := require.New(t)
-
- for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile("cars/edit.html", "Update: <%= car.Name %>", func(e *render.Engine) {
app := buffalo.New(buffalo.Options{})
- app.DELETE(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
+ app.PUT("/cars/{id}", func(c buffalo.Context) error {
b := Car{
ID: 1,
Name: "Honda",
}
- return c.Render(200, render.Auto(c, b))
+ return c.Render(422, e.Auto(c, b))
})
w := willie.New(app)
- res := w.HTML("/%s/1", p).Delete()
- r.Equal(fmt.Sprintf("/%s", p), res.Location())
- r.Equal(302, res.Code)
- }
+ res := w.HTML("/cars/1").Put(nil)
+ r.Equal(422, res.Code)
+ r.Contains(res.Body.String(), "Update: Honda")
+ })
+ r.NoError(err)
+}
+
+func Test_Auto_HTML_Destroy_Redirect(t *testing.T) {
+ r := require.New(t)
+
+ app := buffalo.New(buffalo.Options{})
+ app.DELETE("/cars/{id}", func(c buffalo.Context) error {
+ b := Car{
+ ID: 1,
+ Name: "Honda",
+ }
+ return c.Render(200, render.Auto(c, b))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/cars/1").Delete()
+ r.Equal("/cars", res.Location())
+ r.Equal(302, res.Code)
}
func withHTMLFile(name string, contents string, fn func(*render.Engine)) error {
diff --git a/render/auto_test.go~HEAD b/render/auto_test.go~HEAD
new file mode 100644
index 000000000..052970810
--- /dev/null
+++ b/render/auto_test.go~HEAD
@@ -0,0 +1,294 @@
+package render_test
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/gobuffalo/buffalo"
+ "github.com/gobuffalo/buffalo/render"
+ "github.com/gobuffalo/packr"
+ "github.com/markbates/willie"
+ "github.com/stretchr/testify/require"
+)
+
+type Car struct {
+ ID int
+ Name string
+}
+
+type Cars []Car
+
+func Test_Auto_JSON(t *testing.T) {
+ r := require.New(t)
+
+ app := buffalo.New(buffalo.Options{})
+ app.GET("/cars", func(c buffalo.Context) error {
+ return c.Render(200, render.Auto(c, []string{"Honda", "Toyota", "Ford", "Chevy"}))
+ })
+
+ w := willie.New(app)
+
+ res := w.JSON("/cars").Get()
+ r.Equal(`["Honda","Toyota","Ford","Chevy"]`, strings.TrimSpace(res.Body.String()))
+}
+
+func Test_Auto_XML(t *testing.T) {
+ r := require.New(t)
+
+ app := buffalo.New(buffalo.Options{})
+ app.GET("/cars", func(c buffalo.Context) error {
+ return c.Render(200, render.Auto(c, []string{"Honda", "Toyota", "Ford", "Chevy"}))
+ })
+
+ w := willie.New(app)
+
+ res := w.XML("/cars").Get()
+ r.Equal("Honda\nToyota\nFord\nChevy", strings.TrimSpace(res.Body.String()))
+}
+
+func Test_Auto_HTML_List(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/index.html", p), "INDEX: <%= len(cars) %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Cars{
+ {Name: "Ford"},
+ {Name: "Chevy"},
+ }))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s", p).Get()
+
+ r.Contains(res.Body.String(), "INDEX: 2")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Show(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/show.html", p), "Show: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/1", p).Get()
+ r.Contains(res.Body.String(), "Show: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_New(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/new.html", p), "New: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET(fmt.Sprintf("/%s/new", p), func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/new", p).Get()
+ r.Contains(res.Body.String(), "New: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Create(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/new.html", p), "New: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.POST(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
+ return c.Render(201, e.Auto(c, Car{Name: "Honda"}))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s", p).Post(nil)
+ r.Contains(res.Body.String(), "New: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Create_Redirect(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ app := buffalo.New(buffalo.Options{})
+ app.POST(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
+ return c.Render(201, render.Auto(c, Car{
+ ID: 1,
+ Name: "Honda",
+ }))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s", p).Post(nil)
+ r.Equal(fmt.Sprintf("/%s/1", p), res.Location())
+ r.Equal(302, res.Code)
+ }
+}
+
+func Test_Auto_HTML_Create_Redirect_Error(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/new.html", p), "Create: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.POST(fmt.Sprintf("/%s", p), func(c buffalo.Context) error {
+ b := Car{
+ Name: "Honda",
+ }
+ return c.Render(422, e.Auto(c, b))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s", p).Post(nil)
+ r.Equal(422, res.Code)
+ r.Contains(res.Body.String(), "Create: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Edit(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/edit.html", p), "Edit: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.GET(fmt.Sprintf("/%s/{id}/edit", p), func(c buffalo.Context) error {
+ return c.Render(200, e.Auto(c, Car{Name: "Honda"}))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/1/edit", p).Get()
+ r.Contains(res.Body.String(), "Edit: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Update(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/edit.html", p), "Update: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.PUT(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
+ return c.Render(422, e.Auto(c, Car{Name: "Honda"}))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/1", p).Put(nil)
+
+ r.Contains(res.Body.String(), "Update: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Update_Redirect(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ app := buffalo.New(buffalo.Options{})
+ app.PUT(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
+ b := Car{
+ ID: 1,
+ Name: "Honda",
+ }
+ return c.Render(200, render.Auto(c, b))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/1", p).Put(nil)
+ r.Equal(fmt.Sprintf("/%s/1", p), res.Location())
+ r.Equal(302, res.Code)
+ }
+}
+
+func Test_Auto_HTML_Update_Redirect_Error(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ err := withHTMLFile(fmt.Sprintf("%s/edit.html", p), "Update: <%= car.Name %>", func(e *render.Engine) {
+ app := buffalo.New(buffalo.Options{})
+ app.PUT(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
+ b := Car{
+ ID: 1,
+ Name: "Honda",
+ }
+ return c.Render(422, e.Auto(c, b))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/1", p).Put(nil)
+ r.Equal(422, res.Code)
+ r.Contains(res.Body.String(), "Update: Honda")
+ })
+ r.NoError(err)
+ }
+}
+
+func Test_Auto_HTML_Destroy_Redirect(t *testing.T) {
+ r := require.New(t)
+
+ for _, p := range []string{"cars", "admin/cars"} {
+ app := buffalo.New(buffalo.Options{})
+ app.DELETE(fmt.Sprintf("/%s/{id}", p), func(c buffalo.Context) error {
+ b := Car{
+ ID: 1,
+ Name: "Honda",
+ }
+ return c.Render(200, render.Auto(c, b))
+ })
+
+ w := willie.New(app)
+ res := w.HTML("/%s/1", p).Delete()
+ r.Equal(fmt.Sprintf("/%s", p), res.Location())
+ r.Equal(302, res.Code)
+ }
+}
+
+func withHTMLFile(name string, contents string, fn func(*render.Engine)) error {
+ tmpDir := filepath.Join(os.TempDir(), filepath.Dir(name))
+ err := os.MkdirAll(tmpDir, 0766)
+ if err != nil {
+ return err
+ }
+ defer os.Remove(tmpDir)
+
+ tmpFile, err := os.Create(filepath.Join(tmpDir, filepath.Base(name)))
+ if err != nil {
+ return err
+ }
+ defer os.Remove(tmpFile.Name())
+
+ _, err = tmpFile.Write([]byte(contents))
+ if err != nil {
+ return err
+ }
+
+ e := render.New(render.Options{
+ TemplatesBox: packr.NewBox(os.TempDir()),
+ })
+
+ fn(e)
+ return nil
+}
diff --git a/render/helpers.go b/render/helpers.go
index 64fc2680f..95f6c2648 100644
--- a/render/helpers.go
+++ b/render/helpers.go
@@ -5,8 +5,8 @@ import (
"net/http"
"github.com/gobuffalo/plush"
+ "github.com/gobuffalo/pop"
"github.com/gobuffalo/tags"
- "github.com/markbates/pop"
"github.com/pkg/errors"
)