From 2463b9603e9e5212999ace104c6cdb2ae4d9cefe Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 13:28:06 -0500 Subject: [PATCH 1/8] add web pack to the "new" generator closes #18 --- Dockerfile | 4 + buffalo/cmd/app_generators.go | 48 +++++----- buffalo/cmd/generate.go | 5 +- buffalo/cmd/generate/bootstrap.go | 75 --------------- buffalo/cmd/generate/bootswatch.go | 88 ----------------- buffalo/cmd/generate/jquery.go | 66 ------------- buffalo/cmd/generate/railjs.go | 51 ---------- buffalo/cmd/generate/webpack.go | 145 +++++++++++++++++++++++++++++ buffalo/cmd/new.go | 25 +++-- buffalo/cmd/test.go | 2 +- 10 files changed, 187 insertions(+), 322 deletions(-) delete mode 100644 buffalo/cmd/generate/bootstrap.go delete mode 100644 buffalo/cmd/generate/bootswatch.go delete mode 100644 buffalo/cmd/generate/jquery.go delete mode 100644 buffalo/cmd/generate/railjs.go create mode 100644 buffalo/cmd/generate/webpack.go diff --git a/Dockerfile b/Dockerfile index b7b8b9134..377639b33 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,9 @@ FROM golang +RUN apt-get update +RUN curl -sL https://deb.nodesource.com/setup_7.x | bash +RUN apt-get install -y build-essential nodejs + ENV BP=$GOPATH/src/github.com/markbates/buffalo RUN mkdir -p $BP diff --git a/buffalo/cmd/app_generators.go b/buffalo/cmd/app_generators.go index b52ed4f31..16c7db937 100644 --- a/buffalo/cmd/app_generators.go +++ b/buffalo/cmd/app_generators.go @@ -7,7 +7,7 @@ import ( "github.com/markbates/gentronics" ) -func newAppGenerator() *gentronics.Generator { +func newAppGenerator(data gentronics.Data) *gentronics.Generator { g := gentronics.New() g.Add(gentronics.NewFile("main.go", nMain)) g.Add(gentronics.NewFile("Procfile", nProcfile)) @@ -20,8 +20,10 @@ func newAppGenerator() *gentronics.Generator { g.Add(gentronics.NewFile("grifts/routes.go", nGriftRoutes)) g.Add(gentronics.NewFile("templates/index.html", nIndexHTML)) g.Add(gentronics.NewFile("templates/application.html", nApplicationHTML)) - g.Add(gentronics.NewFile("assets/js/application.js", "")) - g.Add(gentronics.NewFile("assets/css/application.css", nApplicationCSS)) + if skipWebpack { + g.Add(gentronics.NewFile("assets/js/application.js", "")) + g.Add(gentronics.NewFile("assets/css/application.css", "")) + } g.Add(gentronics.NewFile(".gitignore", nGitignore)) g.Add(gentronics.NewCommand(goGet("github.com/markbates/refresh/..."))) g.Add(gentronics.NewCommand(goInstall("github.com/markbates/refresh"))) @@ -29,8 +31,7 @@ func newAppGenerator() *gentronics.Generator { g.Add(gentronics.NewCommand(goInstall("github.com/markbates/grift"))) g.Add(gentronics.NewCommand(goGet("github.com/motemen/gore"))) g.Add(gentronics.NewCommand(goInstall("github.com/motemen/gore"))) - g.Add(generate.NewJQueryGenerator()) - g.Add(generate.NewBootstrapGenerator()) + g.Add(generate.NewWebpackGenerator(data)) g.Add(newSodaGenerator()) g.Add(gentronics.NewCommand(appGoGet())) g.Add(generate.Fmt) @@ -171,36 +172,31 @@ const nApplicationHTML = ` Buffalo - {{ titleName }} - {{#if withBootstrap }} - - {{/if}} - + {{#if withWebpack}} + + {{else}} + + {{/if}} \{{ yield }} - - {{#if withJQuery }} - - {{/if}} - {{#if withBootstrap }} - - {{/if}} - + {{#if withWebpack}} + + {{else}} + + {{/if}} ` -const nApplicationCSS = `body { - font-family: helvetica; -} -` - const nGitignore = `vendor/ **/*.log **/*.sqlite bin/ node_modules/ +.sass-cache/ +assets/dist/ {{ name }} ` @@ -237,6 +233,8 @@ ignored_folders: - assets - grifts - tmp +- node_modules +- .sass-cache included_extensions: - .go - .html @@ -250,4 +248,8 @@ log_name: buffalo ` const nProcfile = `web: {{name}}` -const nProcfileDev = `web: buffalo dev` +const nProcfileDev = `web: buffalo dev +{{#if withWebpack}} +assets: webpack --watch +{{/if}} +` diff --git a/buffalo/cmd/generate.go b/buffalo/cmd/generate.go index 97ee2aeff..c58be4d34 100644 --- a/buffalo/cmd/generate.go +++ b/buffalo/cmd/generate.go @@ -32,11 +32,8 @@ var generateCmd = &cobra.Command{ } func init() { - generateCmd.AddCommand(generate.JQueryCmd) - generateCmd.AddCommand(generate.RailsJSCmd) - generateCmd.AddCommand(generate.BootstrapCmd) - generateCmd.AddCommand(generate.BootswatchCmd) generateCmd.AddCommand(generate.ResourceCmd) generateCmd.AddCommand(generate.GothCmd) + generateCmd.AddCommand(generate.WebpackCmd) RootCmd.AddCommand(generateCmd) } diff --git a/buffalo/cmd/generate/bootstrap.go b/buffalo/cmd/generate/bootstrap.go deleted file mode 100644 index 61718571c..000000000 --- a/buffalo/cmd/generate/bootstrap.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © 2016 Mark Bates -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package generate - -import ( - "path/filepath" - - "github.com/markbates/gentronics" - "github.com/spf13/cobra" -) - -// BootstrapCmd will generate new Bootstrap files. Regardless of whatever -// other settings you might, this will generate jQuery files as that is a -// pre-requisite of Bootstrap. -var BootstrapCmd = &cobra.Command{ - Use: "bootstrap", - Short: "Generates Bootstrap 3 files", - RunE: func(cmd *cobra.Command, args []string) error { - return NewBootstrapGenerator().Run(".", gentronics.Data{ - "withBootstrap": true, - }) - }, -} - -// NewBootstrapGenerator will generate new Bootstrap files. Regardless of whatever -// other settings you might, this will generate jQuery files as that is a -// pre-requisite of Bootstrap. -func NewBootstrapGenerator() *gentronics.Generator { - should := func(data gentronics.Data) bool { - if p, ok := data["withBootstrap"]; ok { - return p.(bool) - } - return false - } - g := gentronics.New() - jf := &gentronics.RemoteFile{ - File: gentronics.NewFile(filepath.Join("assets", "css", "bootstrap.css"), ""), - } - jf.Should = should - jf.RemotePath = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" - g.Add(jf) - - jf = &gentronics.RemoteFile{ - File: gentronics.NewFile(filepath.Join("assets", "js", "bootstrap.js"), ""), - } - jf.Should = should - jf.RemotePath = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" - g.Add(jf) - g.Add(&gentronics.Func{ - Should: should, - Runner: func(rootPath string, data gentronics.Data) error { - data["withJQuery"] = true - return NewJQueryGenerator().Run(rootPath, data) - }, - }) - return g -} diff --git a/buffalo/cmd/generate/bootswatch.go b/buffalo/cmd/generate/bootswatch.go deleted file mode 100644 index 5c5ffb3df..000000000 --- a/buffalo/cmd/generate/bootswatch.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © 2016 Mark Bates -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package generate - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/markbates/gentronics" - "github.com/spf13/cobra" -) - -// BootswatchCmd will generate new Bootswatch themes. Regardless of whatever -// other settings you have, this will generate jQuery and Bootstrap files as -// they are pre-requisites for Bootswatch -var BootswatchCmd = &cobra.Command{ - Use: "bootswatch [theme]", - Short: "Generates Bootswatch 3 files", - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("You must choose a theme! [%s]", strings.Join(bootswatchThemes, ", ")) - } - g, err := NewBootswatchGenerator(args[0]) - if err != nil { - return err - } - return g.Run(".", gentronics.Data{ - "withBootswatch": true, - }) - }, -} - -// NewBootswatchGenerator will generate new Bootswatch themes. Regardless of whatever -// other settings you have, this will generate jQuery and Bootstrap files as -// they are pre-requisites for Bootswatch -func NewBootswatchGenerator(theme string) (*gentronics.Generator, error) { - themeFound := false - for _, t := range bootswatchThemes { - if t == theme { - themeFound = true - break - } - } - if !themeFound { - return nil, fmt.Errorf("Could not find a Bootswatch theme for %s!", theme) - } - g := gentronics.New() - g.Add(&gentronics.Func{ - Should: func(data gentronics.Data) bool { return true }, - Runner: func(rootPath string, data gentronics.Data) error { - data["withJQuery"] = true - data["withBootstrap"] = true - err := NewJQueryGenerator().Run(rootPath, data) - if err != nil { - return err - } - return NewBootstrapGenerator().Run(rootPath, data) - }, - }) - jf := &gentronics.RemoteFile{ - File: gentronics.NewFile(filepath.Join("assets", "css", "bootstrap.css"), ""), - } - jf.RemotePath = fmt.Sprintf("https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/%s/bootstrap.min.css", theme) - g.Add(jf) - - return g, nil -} - -var bootswatchThemes = []string{"cerulean", "cosmo", "cyborg", "darkly", "flatly", "journal", "lumen", "paper", "readable", "sandstone", "simplex", "slate", "spacelab", "superhero", "united", "yeti"} diff --git a/buffalo/cmd/generate/jquery.go b/buffalo/cmd/generate/jquery.go deleted file mode 100644 index d4250070a..000000000 --- a/buffalo/cmd/generate/jquery.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © 2016 Mark Bates -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package generate - -import ( - "path/filepath" - - "github.com/markbates/gentronics" - "github.com/spf13/cobra" -) - -// JQueryCmd will generate jQuery files. -var JQueryCmd = &cobra.Command{ - Use: "jquery", - Short: "Generates an assets/jquery.js file", - RunE: func(cmd *cobra.Command, args []string) error { - data := gentronics.Data{ - "withJQuery": true, - } - return NewJQueryGenerator().Run(".", data) - }, -} - -// NewJQueryGenerator will generate jQuery files. -func NewJQueryGenerator() *gentronics.Generator { - should := func(data gentronics.Data) bool { - if p, ok := data["withJQuery"]; ok { - return p.(bool) - } - return false - } - - g := gentronics.New() - jf := &gentronics.RemoteFile{ - File: gentronics.NewFile(filepath.Join("assets", "js", "jquery.js"), ""), - } - jf.Should = should - jf.RemotePath = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js" - g.Add(jf) - - jm := &gentronics.RemoteFile{ - File: gentronics.NewFile(filepath.Join("assets", "js", "jquery.map"), ""), - } - jm.Should = should - jm.RemotePath = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.map" - g.Add(jm) - return g -} diff --git a/buffalo/cmd/generate/railjs.go b/buffalo/cmd/generate/railjs.go deleted file mode 100644 index 0c3c6aacd..000000000 --- a/buffalo/cmd/generate/railjs.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2016 Mark Bates -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package generate - -import ( - "path/filepath" - - "github.com/markbates/gentronics" - "github.com/spf13/cobra" -) - -// RailsJSCmd generates the jQuery UJS file from the Rails project. -var RailsJSCmd = &cobra.Command{ - Use: "railsjs", - Short: "Generates an assets/rails.js file", - Long: `Generates the jQuery UJS file from the Rails project. -More information can be found at: -https://github.com/rails/jquery-ujs`, - RunE: func(cmd *cobra.Command, args []string) error { - return NewRailsJSGenerator().Run(".", gentronics.Data{}) - }, -} - -// NewRailsJSGenerator generates the jQuery UJS file from the Rails project. -func NewRailsJSGenerator() *gentronics.Generator { - g := gentronics.New() - jf := &gentronics.RemoteFile{ - File: gentronics.NewFile(filepath.Join("assets", "js", "rails.js"), ""), - } - jf.RemotePath = "https://raw.githubusercontent.com/rails/jquery-ujs/master/src/rails.js" - g.Add(jf) - return g -} diff --git a/buffalo/cmd/generate/webpack.go b/buffalo/cmd/generate/webpack.go new file mode 100644 index 000000000..a40f2da55 --- /dev/null +++ b/buffalo/cmd/generate/webpack.go @@ -0,0 +1,145 @@ +// Copyright © 2016 Mark Bates +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package generate + +import ( + "fmt" + "os/exec" + + "github.com/markbates/gentronics" + "github.com/spf13/cobra" +) + +// WebpackCmd generates a new actions/resource file and a stub test. +var WebpackCmd = &cobra.Command{ + Use: "webpack", + Short: "Generates a webpack asset pipeline.", + RunE: func(cmd *cobra.Command, args []string) error { + data := gentronics.Data{ + "withWebpack": true, + } + return NewWebpackGenerator(data).Run(".", data) + }, +} + +// NewWebpackGenerator generates a new actions/resource file and a stub test. +func NewWebpackGenerator(data gentronics.Data) *gentronics.Generator { + g := gentronics.New() + _, err := exec.LookPath("npm") + if err != nil { + fmt.Println("Could not find npm/node. Skipping webpack generation.") + g.Add(gentronics.NewFile("assets/.git-keep", "")) + return g + } + + should := func(data gentronics.Data) bool { + if b, ok := data["withWebpack"]; ok { + return b.(bool) + } + return false + } + g.Should = should + f := gentronics.NewFile("webpack.config.js", nWebpack) + f.Should = should + g.Add(f) + f = gentronics.NewFile("assets/src/js/application.js", wApplicationJS) + f.Should = should + g.Add(f) + f = gentronics.NewFile("assets/src/css/application.scss", wApplicationCSS) + f.Should = should + g.Add(f) + c := gentronics.NewCommand(exec.Command("npm", "install", "webpack", "-g")) + c.Should = should + g.Add(c) + c = gentronics.NewCommand(exec.Command("npm", "init", "-y")) + c.Should = should + g.Add(c) + + modules := []string{"webpack", "sass-loader", "css-loader", "style-loader", "node-sass", + "babel-loader", "extract-text-webpack-plugin", "babel", "babel-core", "url-loader", "file-loader", + "jquery", "bootstrap", "path", "font-awesome", "npm-install-webpack-plugin", "jquery-ujs", + } + args := []string{"install", "--save"} + args = append(args, modules...) + g.Add(gentronics.NewCommand(exec.Command("npm", args...))) + return g +} + +var nWebpack = `var webpack = require("webpack"); + +var ExtractTextPlugin = require("extract-text-webpack-plugin"); + +module.exports = { + entry: [ + "./assets/src/js/application.js", + "./assets/src/css/application.scss", + "./node_modules/jquery-ujs/src/rails.js" + ], + output: { + filename: "application.js", + path: "./assets/dist" + }, + plugins: [ + new webpack.ProvidePlugin({ + $: "jquery", + jQuery: "jquery" + }), + new ExtractTextPlugin("application.css") + ], + module: { + loaders: [{ + test: /\.jsx?$/, + loader: "babel", + exclude: /node_modules/ + }, { + test: /\.scss$/, + loader: ExtractTextPlugin.extract( + "style", + "css?sourceMap!sass?sourceMap" + ) + }, { + test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, + loader: "url?limit=10000&mimetype=application/font-woff" + }, { + test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, + loader: "url?limit=10000&mimetype=application/font-woff" + }, { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + loader: "url?limit=10000&mimetype=application/octet-stream" + }, { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + loader: "file" + }, { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + loader: "url?limit=10000&mimetype=image/svg+xml" + }] + } +}; +` + +const wApplicationJS = `require("bootstrap/dist/js/bootstrap.js"); + +$(() => { + +});` +const wApplicationCSS = `@import "~bootstrap/dist/css/bootstrap.css"; +@import "~font-awesome/css/font-awesome.css"; +` diff --git a/buffalo/cmd/new.go b/buffalo/cmd/new.go index 88f127453..a2b095549 100644 --- a/buffalo/cmd/new.go +++ b/buffalo/cmd/new.go @@ -35,8 +35,7 @@ import ( var force bool var verbose bool var skipPop bool -var skipJQuery bool -var skipBootstrap bool +var skipWebpack bool var dbType = "postgres" var newCmd = &cobra.Command{ @@ -100,18 +99,17 @@ func genNewFiles(name, rootPath string) error { packagePath := packagePath(rootPath) data := map[string]interface{}{ - "name": name, - "titleName": inflect.Titleize(name), - "packagePath": packagePath, - "actionsPath": filepath.Join(packagePath, "actions"), - "modelsPath": filepath.Join(packagePath, "models"), - "withPop": !skipPop, - "withJQuery": !skipJQuery, - "withBootstrap": !skipBootstrap, - "dbType": dbType, + "name": name, + "titleName": inflect.Titleize(name), + "packagePath": packagePath, + "actionsPath": filepath.Join(packagePath, "actions"), + "modelsPath": filepath.Join(packagePath, "models"), + "withPop": !skipPop, + "withWebpack": !skipWebpack, + "dbType": dbType, } - g := newAppGenerator() + g := newAppGenerator(data) return g.Run(rootPath, data) } @@ -120,7 +118,6 @@ func init() { newCmd.Flags().BoolVarP(&force, "force", "f", false, "delete and remake if the app already exists") newCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "verbosely print out the go get/install commands") newCmd.Flags().BoolVar(&skipPop, "skip-pop", false, "skips adding pop/soda to your app") - newCmd.Flags().BoolVar(&skipJQuery, "skip-jquery", false, "skips adding jQuery to your app") - newCmd.Flags().BoolVar(&skipBootstrap, "skip-bootstrap", false, "skips adding Bootstrap to your app") + newCmd.Flags().BoolVar(&skipWebpack, "skip-webpack", false, "skips adding Webpack to your app") newCmd.Flags().StringVar(&dbType, "db-type", "postgres", "specify the type of database you want to use [postgres, mysql, sqlite3]") } diff --git a/buffalo/cmd/test.go b/buffalo/cmd/test.go index 309c9da05..2a9ed3378 100644 --- a/buffalo/cmd/test.go +++ b/buffalo/cmd/test.go @@ -33,7 +33,7 @@ var testCmd = &cobra.Command{ DisableFlagParsing: true, Run: func(c *cobra.Command, args []string) { os.Setenv("GO_ENV", "test") - tt.RootCmd.Run(c, args) + tt.Run(tt.GoBuilder(args)) }, } From 21301554aec61a6e4065e412ddf785b5ccdc1be8 Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 16:45:04 -0500 Subject: [PATCH 2/8] fixed up the examples to bring them more in line --- .gitignore | 1 + examples/hello-world/actions/app.go | 26 +++++++--- examples/hello-world/actions/render.go | 22 ++++----- examples/hello-world/grifts/routes.go | 7 ++- examples/hello-world/main.go | 8 ++- examples/html-crud/actions/actions_test.go | 19 +------ examples/html-crud/actions/app.go | 49 ++++++++++++------- examples/html-crud/actions/render.go | 22 ++++----- examples/html-crud/grifts/routes.go | 7 ++- examples/html-crud/main.go | 8 ++- .../html-resource/actions/actions_test.go | 19 +------ examples/html-resource/actions/app.go | 39 ++++++++++----- examples/html-resource/actions/render.go | 22 ++++----- examples/html-resource/grifts/routes.go | 3 +- examples/html-resource/main.go | 7 ++- examples/json-crud/actions/actions_test.go | 19 +------ examples/json-crud/actions/app.go | 46 ++++++++++------- examples/json-crud/grifts/routes.go | 3 +- examples/json-crud/main.go | 7 ++- .../json-resource/actions/actions_test.go | 19 +------ examples/json-resource/actions/app.go | 37 ++++++++------ examples/json-resource/actions/users_test.go | 33 +++++++------ examples/json-resource/grifts/routes.go | 3 +- examples/json-resource/main.go | 7 ++- examples/websockets/actions/app.go | 27 +++++++--- examples/websockets/actions/render.go | 22 ++++----- examples/websockets/grifts/routes.go | 3 +- examples/websockets/main.go | 8 ++- middleware.go | 13 +++++ middleware_test.go | 36 ++++++++++++++ 30 files changed, 306 insertions(+), 236 deletions(-) diff --git a/.gitignore b/.gitignore index ab4783b4e..c1d8f110f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ generated/ .vendor/ bin/* gin-bin +*.sqlite* diff --git a/examples/hello-world/actions/app.go b/examples/hello-world/actions/app.go index 21d6fc48e..16cf2cf44 100644 --- a/examples/hello-world/actions/app.go +++ b/examples/hello-world/actions/app.go @@ -1,17 +1,29 @@ package actions import ( - "net/http" + "os" "github.com/markbates/buffalo" + "github.com/markbates/going/defaults" ) -func App() http.Handler { - a := buffalo.Automatic(buffalo.Options{}) - a.Env = "development" +// ENV is used to help switch settings based on where the +// application is being run. Default is "development". +var ENV = defaults.String(os.Getenv("GO_ENV"), "development") +var app *buffalo.App - a.ServeFiles("/assets", assetsPath()) - a.GET("/", HomeHandler) +// App is where all routes and middleware for buffalo +// should be defined. This is the nerve center of your +// application. +func App() *buffalo.App { + if app == nil { + app = buffalo.Automatic(buffalo.Options{ + Env: ENV, + }) - return a + app.ServeFiles("/assets", assetsPath()) + app.GET("/", HomeHandler) + } + + return app } diff --git a/examples/hello-world/actions/render.go b/examples/hello-world/actions/render.go index 66086a057..ae36c36d8 100644 --- a/examples/hello-world/actions/render.go +++ b/examples/hello-world/actions/render.go @@ -2,26 +2,26 @@ package actions import ( "net/http" - "path" - "runtime" + rice "github.com/GeertJohan/go.rice" "github.com/markbates/buffalo/render" + "github.com/markbates/buffalo/render/resolvers" ) var r *render.Engine +var resolver = &resolvers.RiceBox{ + Box: rice.MustFindBox("../templates"), +} func init() { r = render.New(render.Options{ - TemplatesPath: fromHere("../templates"), - HTMLLayout: "application.html", + HTMLLayout: "application.html", + CacheTemplates: ENV == "production", + FileResolver: resolver, }) } -func assetsPath() http.Dir { - return http.Dir(fromHere("../assets")) -} - -func fromHere(p string) string { - _, filename, _, _ := runtime.Caller(1) - return path.Join(path.Dir(filename), p) +func assetsPath() http.FileSystem { + box := rice.MustFindBox("../assets") + return box.HTTPBox() } diff --git a/examples/hello-world/grifts/routes.go b/examples/hello-world/grifts/routes.go index 322f9cf30..f40a73f97 100644 --- a/examples/hello-world/grifts/routes.go +++ b/examples/hello-world/grifts/routes.go @@ -3,14 +3,13 @@ package grifts import ( "os" - "github.com/markbates/buffalo" - . "github.com/markbates/grift/grift" "github.com/markbates/buffalo/examples/hello-world/actions" + . "github.com/markbates/grift/grift" "github.com/olekukonko/tablewriter" ) var _ = Add("routes", func(c *Context) error { - a := actions.App().(*buffalo.App) + a := actions.App() routes := a.Routes() table := tablewriter.NewWriter(os.Stdout) @@ -22,4 +21,4 @@ var _ = Add("routes", func(c *Context) error { table.SetAlignment(tablewriter.ALIGN_LEFT) table.Render() return nil -}) \ No newline at end of file +}) diff --git a/examples/hello-world/main.go b/examples/hello-world/main.go index 04bb57422..485a28b92 100644 --- a/examples/hello-world/main.go +++ b/examples/hello-world/main.go @@ -1,13 +1,17 @@ package main import ( + "fmt" "log" "net/http" + "os" "github.com/markbates/buffalo/examples/hello-world/actions" + "github.com/markbates/going/defaults" ) func main() { - log.Fatal(http.ListenAndServe(":3000", actions.App())) + port := defaults.String(os.Getenv("PORT"), "3000") + log.Printf("Starting hello-world on port %s\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) } - diff --git a/examples/html-crud/actions/actions_test.go b/examples/html-crud/actions/actions_test.go index 1e4d2c25a..be91fe153 100644 --- a/examples/html-crud/actions/actions_test.go +++ b/examples/html-crud/actions/actions_test.go @@ -1,28 +1,11 @@ package actions_test import ( - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/html-crud/models" - "github.com/markbates/buffalo/middleware" "github.com/markbates/pop" ) func tx(fn func(tx *pop.Connection)) { - tmw := middleware.PopTransaction - defer func() { - middleware.PopTransaction = tmw - }() - models.DB.MigrateReset("../migrations") - models.DB.Rollback(func(tx *pop.Connection) { - middleware.PopTransaction = func(db *pop.Connection) buffalo.MiddlewareFunc { - return func(h buffalo.Handler) buffalo.Handler { - return func(c buffalo.Context) error { - c.Set("tx", tx) - return h(c) - } - } - } - fn(tx) - }) + fn(models.DB) } diff --git a/examples/html-crud/actions/app.go b/examples/html-crud/actions/app.go index d3745cd0a..a47f9960e 100644 --- a/examples/html-crud/actions/app.go +++ b/examples/html-crud/actions/app.go @@ -2,31 +2,44 @@ package actions import ( "net/http" + "os" "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/html-crud/models" "github.com/markbates/buffalo/middleware" + "github.com/markbates/going/defaults" ) -func App() http.Handler { - a := buffalo.Automatic(buffalo.Options{}) - a.Env = "development" +// ENV is used to help switch settings based on where the +// application is being run. Default is "development". +var ENV = defaults.String(os.Getenv("GO_ENV"), "development") +var app *buffalo.App - a.ServeFiles("/assets", assetsPath()) - a.Use(middleware.PopTransaction(models.DB)) - a.GET("/", func(c buffalo.Context) error { - return c.Redirect(http.StatusPermanentRedirect, "/users") - }) +// App is where all routes and middleware for buffalo +// should be defined. This is the nerve center of your +// application. +func App() *buffalo.App { + if app == nil { + app = buffalo.Automatic(buffalo.Options{ + Env: ENV, + }) - g := a.Group("/users") - g.Use(findUserMW) - g.GET("/", UsersList) - g.GET("/new", UsersNew) - g.GET("/{user_id}", UsersShow) - g.GET("/{user_id}/edit", UsersEdit) - g.POST("/", UsersCreate) - g.PUT("/{user_id}", UsersUpdate) - g.DELETE("/{user_id}", UsersDelete) + app.ServeFiles("/assets", assetsPath()) + app.Use(middleware.PopTransaction(models.DB)) + app.GET("/", func(c buffalo.Context) error { + return c.Redirect(http.StatusPermanentRedirect, "/users") + }) - return a + g := app.Group("/users") + g.Use(findUserMW) + g.GET("/", UsersList) + g.GET("/new", UsersNew) + g.GET("/{user_id}", UsersShow) + g.GET("/{user_id}/edit", UsersEdit) + g.POST("/", UsersCreate) + g.PUT("/{user_id}", UsersUpdate) + g.DELETE("/{user_id}", UsersDelete) + } + + return app } diff --git a/examples/html-crud/actions/render.go b/examples/html-crud/actions/render.go index 66086a057..ae36c36d8 100644 --- a/examples/html-crud/actions/render.go +++ b/examples/html-crud/actions/render.go @@ -2,26 +2,26 @@ package actions import ( "net/http" - "path" - "runtime" + rice "github.com/GeertJohan/go.rice" "github.com/markbates/buffalo/render" + "github.com/markbates/buffalo/render/resolvers" ) var r *render.Engine +var resolver = &resolvers.RiceBox{ + Box: rice.MustFindBox("../templates"), +} func init() { r = render.New(render.Options{ - TemplatesPath: fromHere("../templates"), - HTMLLayout: "application.html", + HTMLLayout: "application.html", + CacheTemplates: ENV == "production", + FileResolver: resolver, }) } -func assetsPath() http.Dir { - return http.Dir(fromHere("../assets")) -} - -func fromHere(p string) string { - _, filename, _, _ := runtime.Caller(1) - return path.Join(path.Dir(filename), p) +func assetsPath() http.FileSystem { + box := rice.MustFindBox("../assets") + return box.HTTPBox() } diff --git a/examples/html-crud/grifts/routes.go b/examples/html-crud/grifts/routes.go index 214a7f009..fac038042 100644 --- a/examples/html-crud/grifts/routes.go +++ b/examples/html-crud/grifts/routes.go @@ -3,14 +3,13 @@ package grifts import ( "os" - "github.com/markbates/buffalo" - . "github.com/markbates/grift/grift" "github.com/markbates/buffalo/examples/html-crud/actions" + . "github.com/markbates/grift/grift" "github.com/olekukonko/tablewriter" ) var _ = Add("routes", func(c *Context) error { - a := actions.App().(*buffalo.App) + a := actions.App() routes := a.Routes() table := tablewriter.NewWriter(os.Stdout) @@ -22,4 +21,4 @@ var _ = Add("routes", func(c *Context) error { table.SetAlignment(tablewriter.ALIGN_LEFT) table.Render() return nil -}) \ No newline at end of file +}) diff --git a/examples/html-crud/main.go b/examples/html-crud/main.go index 57e424dbe..440f9dcf5 100644 --- a/examples/html-crud/main.go +++ b/examples/html-crud/main.go @@ -1,13 +1,17 @@ package main import ( + "fmt" "log" "net/http" + "os" "github.com/markbates/buffalo/examples/html-crud/actions" + "github.com/markbates/going/defaults" ) func main() { - log.Fatal(http.ListenAndServe(":3000", actions.App())) + port := defaults.String(os.Getenv("PORT"), "3000") + log.Printf("Starting html-crud on port %s\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) } - diff --git a/examples/html-resource/actions/actions_test.go b/examples/html-resource/actions/actions_test.go index a71288e19..c3e9a25f8 100644 --- a/examples/html-resource/actions/actions_test.go +++ b/examples/html-resource/actions/actions_test.go @@ -1,28 +1,11 @@ package actions_test import ( - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/html-resource/models" - "github.com/markbates/buffalo/middleware" "github.com/markbates/pop" ) func tx(fn func(tx *pop.Connection)) { - tmw := middleware.PopTransaction - defer func() { - middleware.PopTransaction = tmw - }() - models.DB.MigrateReset("../migrations") - models.DB.Rollback(func(tx *pop.Connection) { - middleware.PopTransaction = func(db *pop.Connection) buffalo.MiddlewareFunc { - return func(h buffalo.Handler) buffalo.Handler { - return func(c buffalo.Context) error { - c.Set("tx", tx) - return h(c) - } - } - } - fn(tx) - }) + fn(models.DB) } diff --git a/examples/html-resource/actions/app.go b/examples/html-resource/actions/app.go index b1ab32b56..280793dd4 100644 --- a/examples/html-resource/actions/app.go +++ b/examples/html-resource/actions/app.go @@ -2,27 +2,40 @@ package actions import ( "net/http" + "os" "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/html-resource/models" "github.com/markbates/buffalo/middleware" + "github.com/markbates/going/defaults" ) -func App() http.Handler { - a := buffalo.Automatic(buffalo.Options{}) - a.Env = "development" +// ENV is used to help switch settings based on where the +// application is being run. Default is "development". +var ENV = defaults.String(os.Getenv("GO_ENV"), "development") +var app *buffalo.App - a.ServeFiles("/assets", assetsPath()) - a.Use(middleware.PopTransaction(models.DB)) - a.GET("/", func(c buffalo.Context) error { - return c.Redirect(http.StatusPermanentRedirect, "/users") - }) +// App is where all routes and middleware for buffalo +// should be defined. This is the nerve center of your +// application. +func App() *buffalo.App { + if app == nil { + app = buffalo.Automatic(buffalo.Options{ + Env: ENV, + }) - g := a.Resource("/users", &UsersResource{}) - g.Use(findUserMW("user_id")) + app.ServeFiles("/assets", assetsPath()) + app.Use(middleware.PopTransaction(models.DB)) + app.GET("/", func(c buffalo.Context) error { + return c.Redirect(http.StatusPermanentRedirect, "/users") + }) - g = a.Resource("/people", &UsersResource{}) - g.Use(findUserMW("person_id")) + g := app.Resource("/users", &UsersResource{}) + g.Use(findUserMW("user_id")) - return a + g = app.Resource("/people", &UsersResource{}) + g.Use(findUserMW("person_id")) + } + + return app } diff --git a/examples/html-resource/actions/render.go b/examples/html-resource/actions/render.go index 66086a057..ae36c36d8 100644 --- a/examples/html-resource/actions/render.go +++ b/examples/html-resource/actions/render.go @@ -2,26 +2,26 @@ package actions import ( "net/http" - "path" - "runtime" + rice "github.com/GeertJohan/go.rice" "github.com/markbates/buffalo/render" + "github.com/markbates/buffalo/render/resolvers" ) var r *render.Engine +var resolver = &resolvers.RiceBox{ + Box: rice.MustFindBox("../templates"), +} func init() { r = render.New(render.Options{ - TemplatesPath: fromHere("../templates"), - HTMLLayout: "application.html", + HTMLLayout: "application.html", + CacheTemplates: ENV == "production", + FileResolver: resolver, }) } -func assetsPath() http.Dir { - return http.Dir(fromHere("../assets")) -} - -func fromHere(p string) string { - _, filename, _, _ := runtime.Caller(1) - return path.Join(path.Dir(filename), p) +func assetsPath() http.FileSystem { + box := rice.MustFindBox("../assets") + return box.HTTPBox() } diff --git a/examples/html-resource/grifts/routes.go b/examples/html-resource/grifts/routes.go index 4be5c057e..55601cb9d 100644 --- a/examples/html-resource/grifts/routes.go +++ b/examples/html-resource/grifts/routes.go @@ -3,14 +3,13 @@ package grifts import ( "os" - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/html-resource/actions" . "github.com/markbates/grift/grift" "github.com/olekukonko/tablewriter" ) var _ = Add("routes", func(c *Context) error { - a := actions.App().(*buffalo.App) + a := actions.App() routes := a.Routes() table := tablewriter.NewWriter(os.Stdout) diff --git a/examples/html-resource/main.go b/examples/html-resource/main.go index b92800ab6..d482b465a 100644 --- a/examples/html-resource/main.go +++ b/examples/html-resource/main.go @@ -1,12 +1,17 @@ package main import ( + "fmt" "log" "net/http" + "os" "github.com/markbates/buffalo/examples/html-resource/actions" + "github.com/markbates/going/defaults" ) func main() { - log.Fatal(http.ListenAndServe(":3000", actions.App())) + port := defaults.String(os.Getenv("PORT"), "3000") + log.Printf("Starting html-resource on port %s\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) } diff --git a/examples/json-crud/actions/actions_test.go b/examples/json-crud/actions/actions_test.go index 0bbf9c902..bb7a6dd64 100644 --- a/examples/json-crud/actions/actions_test.go +++ b/examples/json-crud/actions/actions_test.go @@ -1,28 +1,11 @@ package actions_test import ( - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/json-crud/models" - "github.com/markbates/buffalo/middleware" "github.com/markbates/pop" ) func tx(fn func(tx *pop.Connection)) { - tmw := middleware.PopTransaction - defer func() { - middleware.PopTransaction = tmw - }() - models.DB.MigrateReset("../migrations") - models.DB.Rollback(func(tx *pop.Connection) { - middleware.PopTransaction = func(db *pop.Connection) buffalo.MiddlewareFunc { - return func(h buffalo.Handler) buffalo.Handler { - return func(c buffalo.Context) error { - c.Set("tx", tx) - return h(c) - } - } - } - fn(tx) - }) + fn(models.DB) } diff --git a/examples/json-crud/actions/app.go b/examples/json-crud/actions/app.go index acda2dbf9..f156e8346 100644 --- a/examples/json-crud/actions/app.go +++ b/examples/json-crud/actions/app.go @@ -1,30 +1,38 @@ package actions import ( - "net/http" + "os" "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/json-crud/models" "github.com/markbates/buffalo/middleware" + "github.com/markbates/going/defaults" ) -func App() http.Handler { - a := buffalo.Automatic(buffalo.Options{}) - a.Use(func(h buffalo.Handler) buffalo.Handler { - return func(c buffalo.Context) error { - // since this is a JSON-only app, let's make sure the - // content-type is always json - c.Request().Header.Set("Content-Type", "application/json") - return h(c) - } - }) - a.Use(middleware.PopTransaction(models.DB)) +// ENV is used to help switch settings based on where the +// application is being run. Default is "development". +var ENV = defaults.String(os.Getenv("GO_ENV"), "development") +var app *buffalo.App - a.Use(findUserMW) - a.GET("/users", UsersList) - a.GET("/users/{user_id}", UsersShow) - a.POST("/users", UsersCreate) - a.PUT("/users/{user_id}", UsersUpdate) - a.DELETE("/users/{user_id}", UsersDelete) - return a +// App is where all routes and middleware for buffalo +// should be defined. This is the nerve center of your +// application. +func App() *buffalo.App { + if app == nil { + app = buffalo.Automatic(buffalo.Options{ + Env: ENV, + }) + + app.Use(middleware.SetContentType("application/json")) + app.Use(middleware.PopTransaction(models.DB)) + + app.Use(findUserMW) + app.GET("/users", UsersList) + app.GET("/users/{user_id}", UsersShow) + app.POST("/users", UsersCreate) + app.PUT("/users/{user_id}", UsersUpdate) + app.DELETE("/users/{user_id}", UsersDelete) + } + + return app } diff --git a/examples/json-crud/grifts/routes.go b/examples/json-crud/grifts/routes.go index 410abb99d..f6838b204 100644 --- a/examples/json-crud/grifts/routes.go +++ b/examples/json-crud/grifts/routes.go @@ -3,14 +3,13 @@ package grifts import ( "os" - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/json-crud/actions" . "github.com/markbates/grift/grift" "github.com/olekukonko/tablewriter" ) var _ = Add("routes", func(c *Context) error { - a := actions.App().(*buffalo.App) + a := actions.App() routes := a.Routes() table := tablewriter.NewWriter(os.Stdout) diff --git a/examples/json-crud/main.go b/examples/json-crud/main.go index 91bc387ec..92ba6fca1 100644 --- a/examples/json-crud/main.go +++ b/examples/json-crud/main.go @@ -1,12 +1,17 @@ package main import ( + "fmt" "log" "net/http" + "os" "github.com/markbates/buffalo/examples/json-crud/actions" + "github.com/markbates/going/defaults" ) func main() { - log.Fatal(http.ListenAndServe(":3000", actions.App())) + port := defaults.String(os.Getenv("PORT"), "3000") + log.Printf("Starting json-crud on port %s\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) } diff --git a/examples/json-resource/actions/actions_test.go b/examples/json-resource/actions/actions_test.go index b276a8dbf..1cd0c0955 100644 --- a/examples/json-resource/actions/actions_test.go +++ b/examples/json-resource/actions/actions_test.go @@ -1,28 +1,11 @@ package actions_test import ( - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/json-resource/models" - "github.com/markbates/buffalo/middleware" "github.com/markbates/pop" ) func tx(fn func(tx *pop.Connection)) { - tmw := middleware.PopTransaction - defer func() { - middleware.PopTransaction = tmw - }() - models.DB.MigrateReset("../migrations") - models.DB.Rollback(func(tx *pop.Connection) { - middleware.PopTransaction = func(db *pop.Connection) buffalo.MiddlewareFunc { - return func(h buffalo.Handler) buffalo.Handler { - return func(c buffalo.Context) error { - c.Set("tx", tx) - return h(c) - } - } - } - fn(tx) - }) + fn(models.DB) } diff --git a/examples/json-resource/actions/app.go b/examples/json-resource/actions/app.go index e0ce23c2c..e633fbd7b 100644 --- a/examples/json-resource/actions/app.go +++ b/examples/json-resource/actions/app.go @@ -1,26 +1,33 @@ package actions import ( - "net/http" + "os" "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/json-resource/models" "github.com/markbates/buffalo/middleware" + "github.com/markbates/going/defaults" ) -func App() http.Handler { - a := buffalo.Automatic(buffalo.Options{}) - a.Use(func(h buffalo.Handler) buffalo.Handler { - return func(c buffalo.Context) error { - // since this is a JSON-only app, let's make sure the - // content-type is always json - c.Request().Header.Set("Content-Type", "application/json") - return h(c) - } - }) - a.Use(middleware.PopTransaction(models.DB)) +// ENV is used to help switch settings based on where the +// application is being run. Default is "development". +var ENV = defaults.String(os.Getenv("GO_ENV"), "development") +var app *buffalo.App - g := a.Resource("/users", &UsersResource{}) - g.Use(findUserMW) - return a +// App is where all routes and middleware for buffalo +// should be defined. This is the nerve center of your +// application. +func App() *buffalo.App { + if app == nil { + app = buffalo.Automatic(buffalo.Options{ + Env: ENV, + }) + app.Use(middleware.SetContentType("application/json")) + app.Use(middleware.PopTransaction(models.DB)) + + g := app.Resource("/users", &UsersResource{}) + g.Use(findUserMW) + } + + return app } diff --git a/examples/json-resource/actions/users_test.go b/examples/json-resource/actions/users_test.go index ab5c1db08..9cd824c49 100644 --- a/examples/json-resource/actions/users_test.go +++ b/examples/json-resource/actions/users_test.go @@ -14,22 +14,23 @@ import ( func Test_UsersList(t *testing.T) { r := require.New(t) - tx(func(tx *pop.Connection) { - w := willie.New(actions.App()) - - r.NoError(tx.Create(&models.User{ - FirstName: "Mark", - LastName: "Bates", - Email: "mark@example.com", - })) - - res := w.JSON("/users").Get() - r.Equal(200, res.Code) - - users := models.Users{} - res.Bind(&users) - r.Len(users, 1) - }) + tx := models.DB + // tx(func(tx *pop.Connection) { + w := willie.New(actions.App()) + + r.NoError(tx.Create(&models.User{ + FirstName: "Mark", + LastName: "Bates", + Email: "mark@example.com", + })) + + res := w.JSON("/users").Get() + r.Equal(200, res.Code) + + users := models.Users{} + res.Bind(&users) + r.Len(users, 1) + // }) } func Test_UsersShow(t *testing.T) { diff --git a/examples/json-resource/grifts/routes.go b/examples/json-resource/grifts/routes.go index 2daa8ec4e..1cd452055 100644 --- a/examples/json-resource/grifts/routes.go +++ b/examples/json-resource/grifts/routes.go @@ -3,14 +3,13 @@ package grifts import ( "os" - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/json-resource/actions" . "github.com/markbates/grift/grift" "github.com/olekukonko/tablewriter" ) var _ = Add("routes", func(c *Context) error { - a := actions.App().(*buffalo.App) + a := actions.App() routes := a.Routes() table := tablewriter.NewWriter(os.Stdout) diff --git a/examples/json-resource/main.go b/examples/json-resource/main.go index 34dace7da..c7bb57cbe 100644 --- a/examples/json-resource/main.go +++ b/examples/json-resource/main.go @@ -1,12 +1,17 @@ package main import ( + "fmt" "log" "net/http" + "os" "github.com/markbates/buffalo/examples/json-resource/actions" + "github.com/markbates/going/defaults" ) func main() { - log.Fatal(http.ListenAndServe(":3000", actions.App())) + port := defaults.String(os.Getenv("PORT"), "3000") + log.Printf("Starting json-resource on port %s\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) } diff --git a/examples/websockets/actions/app.go b/examples/websockets/actions/app.go index f267283d2..5a40a4684 100644 --- a/examples/websockets/actions/app.go +++ b/examples/websockets/actions/app.go @@ -1,17 +1,30 @@ package actions import ( - "net/http" + "os" "github.com/markbates/buffalo" + "github.com/markbates/going/defaults" ) -func App() http.Handler { - a := buffalo.Automatic(buffalo.Options{Env: "development"}) +// ENV is used to help switch settings based on where the +// application is being run. Default is "development". +var ENV = defaults.String(os.Getenv("GO_ENV"), "development") +var app *buffalo.App - a.ServeFiles("/assets", assetsPath()) - a.GET("/", HomeHandler) - a.GET("/socket", SocketHandler) +// App is where all routes and middleware for buffalo +// should be defined. This is the nerve center of your +// application. +func App() *buffalo.App { + if app == nil { + app = buffalo.Automatic(buffalo.Options{ + Env: ENV, + }) - return a + app.ServeFiles("/assets", assetsPath()) + app.GET("/", HomeHandler) + app.GET("/socket", SocketHandler) + } + + return app } diff --git a/examples/websockets/actions/render.go b/examples/websockets/actions/render.go index 66086a057..ae36c36d8 100644 --- a/examples/websockets/actions/render.go +++ b/examples/websockets/actions/render.go @@ -2,26 +2,26 @@ package actions import ( "net/http" - "path" - "runtime" + rice "github.com/GeertJohan/go.rice" "github.com/markbates/buffalo/render" + "github.com/markbates/buffalo/render/resolvers" ) var r *render.Engine +var resolver = &resolvers.RiceBox{ + Box: rice.MustFindBox("../templates"), +} func init() { r = render.New(render.Options{ - TemplatesPath: fromHere("../templates"), - HTMLLayout: "application.html", + HTMLLayout: "application.html", + CacheTemplates: ENV == "production", + FileResolver: resolver, }) } -func assetsPath() http.Dir { - return http.Dir(fromHere("../assets")) -} - -func fromHere(p string) string { - _, filename, _, _ := runtime.Caller(1) - return path.Join(path.Dir(filename), p) +func assetsPath() http.FileSystem { + box := rice.MustFindBox("../assets") + return box.HTTPBox() } diff --git a/examples/websockets/grifts/routes.go b/examples/websockets/grifts/routes.go index 23ea8ac8e..5d6798696 100644 --- a/examples/websockets/grifts/routes.go +++ b/examples/websockets/grifts/routes.go @@ -3,14 +3,13 @@ package grifts import ( "os" - "github.com/markbates/buffalo" "github.com/markbates/buffalo/examples/websockets/actions" . "github.com/markbates/grift/grift" "github.com/olekukonko/tablewriter" ) var _ = Add("routes", func(c *Context) error { - a := actions.App().(*buffalo.App) + a := actions.App() routes := a.Routes() table := tablewriter.NewWriter(os.Stdout) diff --git a/examples/websockets/main.go b/examples/websockets/main.go index bf2890193..7087b2aa0 100644 --- a/examples/websockets/main.go +++ b/examples/websockets/main.go @@ -1,13 +1,17 @@ package main import ( + "fmt" "log" "net/http" + "os" "github.com/markbates/buffalo/examples/websockets/actions" + "github.com/markbates/going/defaults" ) func main() { - log.Fatal(http.ListenAndServe(":3000", actions.App())) + port := defaults.String(os.Getenv("PORT"), "3000") + log.Printf("Starting websockets on port %s\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) } - diff --git a/middleware.go b/middleware.go index ef73db962..2af15190c 100644 --- a/middleware.go +++ b/middleware.go @@ -63,6 +63,19 @@ func (ms *MiddlewareStack) Skip(mw MiddlewareFunc, handlers ...Handler) { } } +func (ms *MiddlewareStack) Replace(mw1 MiddlewareFunc, mw2 MiddlewareFunc) { + m1k := funcKey(mw1) + stack := []MiddlewareFunc{} + for _, mw := range ms.stack { + if funcKey(mw) == m1k { + stack = append(stack, mw2) + } else { + stack = append(stack, mw) + } + } + ms.stack = stack +} + func (ms *MiddlewareStack) handler(h Handler) Handler { if len(ms.stack) > 0 { mh := func(_ Handler) Handler { diff --git a/middleware_test.go b/middleware_test.go index 48fbea10f..9cfc8edb0 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -33,6 +33,42 @@ func Test_App_Use(t *testing.T) { r.Equal([]string{"start", "handler", "end"}, log) } +// Test_Middleware_Replace tests that middleware gets added +func Test_Middleware_Replace(t *testing.T) { + r := require.New(t) + + log := []string{} + a := New(Options{}) + mw1 := func(h Handler) Handler { + return func(c Context) error { + log = append(log, "m1 start") + err := h(c) + log = append(log, "m1 end") + return err + } + } + mw2 := func(h Handler) Handler { + return func(c Context) error { + log = append(log, "m2 start") + err := h(c) + log = append(log, "m2 end") + return err + } + } + a.Use(mw1) + a.Middleware.Replace(mw1, mw2) + + a.GET("/", func(c Context) error { + log = append(log, "handler") + return nil + }) + + w := willie.New(a) + w.Request("/").Get() + r.Len(log, 3) + r.Equal([]string{"m2 start", "handler", "m2 end"}, log) +} + // Test_Middleware_Skip tests that middleware gets skipped func Test_Middleware_Skip(t *testing.T) { r := require.New(t) From 0ac1b13a7d056f1e44811ef1f33f9e5af8919301 Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 17:05:19 -0500 Subject: [PATCH 3/8] move the asset packing to the public dir --- buffalo/cmd/generate/webpack.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/buffalo/cmd/generate/webpack.go b/buffalo/cmd/generate/webpack.go index a40f2da55..9aac77019 100644 --- a/buffalo/cmd/generate/webpack.go +++ b/buffalo/cmd/generate/webpack.go @@ -60,10 +60,10 @@ func NewWebpackGenerator(data gentronics.Data) *gentronics.Generator { f := gentronics.NewFile("webpack.config.js", nWebpack) f.Should = should g.Add(f) - f = gentronics.NewFile("assets/src/js/application.js", wApplicationJS) + f = gentronics.NewFile("assets/js/application.js", wApplicationJS) f.Should = should g.Add(f) - f = gentronics.NewFile("assets/src/css/application.scss", wApplicationCSS) + f = gentronics.NewFile("assets/css/application.scss", wApplicationCSS) f.Should = should g.Add(f) c := gentronics.NewCommand(exec.Command("npm", "install", "webpack", "-g")) @@ -89,13 +89,13 @@ var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { entry: [ - "./assets/src/js/application.js", - "./assets/src/css/application.scss", + "./assets/js/application.js", + "./assets/css/application.scss", "./node_modules/jquery-ujs/src/rails.js" ], output: { filename: "application.js", - path: "./assets/dist" + path: "./public/assets" }, plugins: [ new webpack.ProvidePlugin({ From 4f17d57937ff6e5416217eac9baf7de9fbeb9b45 Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 17:12:27 -0500 Subject: [PATCH 4/8] added Replace to the middleware stack --- middleware.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/middleware.go b/middleware.go index 2af15190c..e36cb3602 100644 --- a/middleware.go +++ b/middleware.go @@ -63,6 +63,8 @@ func (ms *MiddlewareStack) Skip(mw MiddlewareFunc, handlers ...Handler) { } } +// Replace a piece of middleware with another piece of middleware. Great for +// testing. func (ms *MiddlewareStack) Replace(mw1 MiddlewareFunc, mw2 MiddlewareFunc) { m1k := funcKey(mw1) stack := []MiddlewareFunc{} From c8cf9bc810a046fb996292dd1856c7d489f6a8be Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 17:12:42 -0500 Subject: [PATCH 5/8] add the logo into new apps --- buffalo/cmd/app_generators.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/buffalo/cmd/app_generators.go b/buffalo/cmd/app_generators.go index 16c7db937..83e8db62f 100644 --- a/buffalo/cmd/app_generators.go +++ b/buffalo/cmd/app_generators.go @@ -20,6 +20,10 @@ func newAppGenerator(data gentronics.Data) *gentronics.Generator { g.Add(gentronics.NewFile("grifts/routes.go", nGriftRoutes)) g.Add(gentronics.NewFile("templates/index.html", nIndexHTML)) g.Add(gentronics.NewFile("templates/application.html", nApplicationHTML)) + g.Add(&gentronics.RemoteFile{ + File: gentronics.NewFile("public/assets/logo.svg", ""), + RemotePath: "https://raw.githubusercontent.com/markbates/buffalo/master/logo.svg", + }) if skipWebpack { g.Add(gentronics.NewFile("assets/js/application.js", "")) g.Add(gentronics.NewFile("assets/css/application.css", "")) @@ -231,6 +235,7 @@ ignored_folders: - log - logs - assets +- public - grifts - tmp - node_modules @@ -239,6 +244,8 @@ included_extensions: - .go - .html - .md +- .js +- .tmpl build_path: /tmp build_delay: 200ns binary_name: {{name}}-build From a20d75b08e4c3d8712edc034a57f704e79bc2f8a Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 17:24:45 -0500 Subject: [PATCH 6/8] put the logo into assets/images/logo.svg --- buffalo/cmd/app_generators.go | 28 ++++++++--------- examples/json-resource/actions/users_test.go | 33 ++++++++++---------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/buffalo/cmd/app_generators.go b/buffalo/cmd/app_generators.go index 83e8db62f..fe7731171 100644 --- a/buffalo/cmd/app_generators.go +++ b/buffalo/cmd/app_generators.go @@ -20,8 +20,9 @@ func newAppGenerator(data gentronics.Data) *gentronics.Generator { g.Add(gentronics.NewFile("grifts/routes.go", nGriftRoutes)) g.Add(gentronics.NewFile("templates/index.html", nIndexHTML)) g.Add(gentronics.NewFile("templates/application.html", nApplicationHTML)) + g.Add(gentronics.NewFile("public/assets/.gitignore", "")) g.Add(&gentronics.RemoteFile{ - File: gentronics.NewFile("public/assets/logo.svg", ""), + File: gentronics.NewFile("public/images/logo.svg", ""), RemotePath: "https://raw.githubusercontent.com/markbates/buffalo/master/logo.svg", }) if skipWebpack { @@ -101,8 +102,10 @@ func App() *buffalo.App { app.Use(middleware.PopTransaction(models.DB)) {{/if}} - app.ServeFiles("/assets", assetsPath()) app.GET("/", HomeHandler) + + app.ServeFiles("/assets", assetsPath()) + app.ServeFiles("/", publicPath()) } return app @@ -133,7 +136,12 @@ func init() { } func assetsPath() http.FileSystem { - box := rice.MustFindBox("../assets") + box := rice.MustFindBox("../public/assets") + return box.HTTPBox() +} + +func publicPath() http.FileSystem { + box := rice.MustFindBox("../public") return box.HTTPBox() } ` @@ -176,20 +184,12 @@ const nApplicationHTML = ` Buffalo - {{ titleName }} - {{#if withWebpack}} - - {{else}} - - {{/if}} + - \{{ yield }} - {{#if withWebpack}} - - {{else}} - - {{/if}} + \{{ yield }} + ` diff --git a/examples/json-resource/actions/users_test.go b/examples/json-resource/actions/users_test.go index 9cd824c49..ab5c1db08 100644 --- a/examples/json-resource/actions/users_test.go +++ b/examples/json-resource/actions/users_test.go @@ -14,23 +14,22 @@ import ( func Test_UsersList(t *testing.T) { r := require.New(t) - tx := models.DB - // tx(func(tx *pop.Connection) { - w := willie.New(actions.App()) - - r.NoError(tx.Create(&models.User{ - FirstName: "Mark", - LastName: "Bates", - Email: "mark@example.com", - })) - - res := w.JSON("/users").Get() - r.Equal(200, res.Code) - - users := models.Users{} - res.Bind(&users) - r.Len(users, 1) - // }) + tx(func(tx *pop.Connection) { + w := willie.New(actions.App()) + + r.NoError(tx.Create(&models.User{ + FirstName: "Mark", + LastName: "Bates", + Email: "mark@example.com", + })) + + res := w.JSON("/users").Get() + r.Equal(200, res.Code) + + users := models.Users{} + res.Bind(&users) + r.Len(users, 1) + }) } func Test_UsersShow(t *testing.T) { From fef3fbb06799c6ed53d754dd6fb4d085eb5cc6fa Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Tue, 20 Dec 2016 17:48:20 -0500 Subject: [PATCH 7/8] improved the landing page for new applications --- buffalo/cmd/app_generators.go | 43 +++++++++++++++++++++++++++++++++-- buffalo/cmd/new.go | 1 + buffalo/cmd/version.go | 2 +- handler.go | 4 +++- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/buffalo/cmd/app_generators.go b/buffalo/cmd/app_generators.go index fe7731171..ba6ac18fb 100644 --- a/buffalo/cmd/app_generators.go +++ b/buffalo/cmd/app_generators.go @@ -178,7 +178,43 @@ func Test_HomeHandler(t *testing.T) { } ` -const nIndexHTML = `

Welcome to Buffalo!

` +const nIndexHTML = `
+
+ +
+
+

Welcome to Buffalo! [v{{version}}]

+

+ https://github.com/markbates/buffalo +

+

+ Documentation +

+ +
+

Defined Routes

+ + + + + + + + + + \{{#each routes as |r|}} + + + + + + \{{/each}} + +
METHODPATHHANDLER
\{{r.Method}}\{{r.Path}}\{{r.HandlerName}}
+
+
+ +` const nApplicationHTML = ` @@ -188,7 +224,10 @@ const nApplicationHTML = ` - \{{ yield }} +
+ \{{ yield }} +
+ diff --git a/buffalo/cmd/new.go b/buffalo/cmd/new.go index a2b095549..f4b4b3191 100644 --- a/buffalo/cmd/new.go +++ b/buffalo/cmd/new.go @@ -107,6 +107,7 @@ func genNewFiles(name, rootPath string) error { "withPop": !skipPop, "withWebpack": !skipWebpack, "dbType": dbType, + "version": Version, } g := newAppGenerator(data) diff --git a/buffalo/cmd/version.go b/buffalo/cmd/version.go index 3b56ce872..09421fb2b 100644 --- a/buffalo/cmd/version.go +++ b/buffalo/cmd/version.go @@ -1,4 +1,4 @@ package cmd // Version is the current version of the buffalo binary -var Version = "0.4.7" +var Version = "0.5.0.pre1" diff --git a/handler.go b/handler.go index 1b56e7990..3cce443fc 100644 --- a/handler.go +++ b/handler.go @@ -42,7 +42,9 @@ func (a *App) handlerToHandler(h Handler) http.Handler { params: params, logger: a.Logger, session: a.getSession(req, ws), - data: map[string]interface{}{}, + data: map[string]interface{}{ + "routes": a.Routes(), + }, } err := a.Middleware.handler(h)(c) From 570c80ef6aa2cdff96396f511812f92fa18fa6d2 Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Wed, 21 Dec 2016 15:06:23 -0500 Subject: [PATCH 8/8] we don't need to generate the assetsPath helper any more --- buffalo/cmd/app_generators.go | 12 ------------ buffalo/cmd/generate/webpack.go | 3 ++- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/buffalo/cmd/app_generators.go b/buffalo/cmd/app_generators.go index ba6ac18fb..c88c52ddc 100644 --- a/buffalo/cmd/app_generators.go +++ b/buffalo/cmd/app_generators.go @@ -20,15 +20,10 @@ func newAppGenerator(data gentronics.Data) *gentronics.Generator { g.Add(gentronics.NewFile("grifts/routes.go", nGriftRoutes)) g.Add(gentronics.NewFile("templates/index.html", nIndexHTML)) g.Add(gentronics.NewFile("templates/application.html", nApplicationHTML)) - g.Add(gentronics.NewFile("public/assets/.gitignore", "")) g.Add(&gentronics.RemoteFile{ File: gentronics.NewFile("public/images/logo.svg", ""), RemotePath: "https://raw.githubusercontent.com/markbates/buffalo/master/logo.svg", }) - if skipWebpack { - g.Add(gentronics.NewFile("assets/js/application.js", "")) - g.Add(gentronics.NewFile("assets/css/application.css", "")) - } g.Add(gentronics.NewFile(".gitignore", nGitignore)) g.Add(gentronics.NewCommand(goGet("github.com/markbates/refresh/..."))) g.Add(gentronics.NewCommand(goInstall("github.com/markbates/refresh"))) @@ -104,7 +99,6 @@ func App() *buffalo.App { app.GET("/", HomeHandler) - app.ServeFiles("/assets", assetsPath()) app.ServeFiles("/", publicPath()) } @@ -135,11 +129,6 @@ func init() { }) } -func assetsPath() http.FileSystem { - box := rice.MustFindBox("../public/assets") - return box.HTTPBox() -} - func publicPath() http.FileSystem { box := rice.MustFindBox("../public") return box.HTTPBox() @@ -239,7 +228,6 @@ const nGitignore = `vendor/ bin/ node_modules/ .sass-cache/ -assets/dist/ {{ name }} ` diff --git a/buffalo/cmd/generate/webpack.go b/buffalo/cmd/generate/webpack.go index 9aac77019..43f366095 100644 --- a/buffalo/cmd/generate/webpack.go +++ b/buffalo/cmd/generate/webpack.go @@ -43,10 +43,11 @@ var WebpackCmd = &cobra.Command{ // NewWebpackGenerator generates a new actions/resource file and a stub test. func NewWebpackGenerator(data gentronics.Data) *gentronics.Generator { g := gentronics.New() + g.Add(gentronics.NewFile("public/assets/application.js", "// generated")) + g.Add(gentronics.NewFile("public/assets/application.css", "// generated")) _, err := exec.LookPath("npm") if err != nil { fmt.Println("Could not find npm/node. Skipping webpack generation.") - g.Add(gentronics.NewFile("assets/.git-keep", "")) return g }