Blue Jay | Go Toolkit for the Web https://blue-jay.github.io/ Recent content on Blue Jay | Go Toolkit for the Web Hugo -- gohugo.io en-us Blue Jay Overview https://blue-jay.github.io/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/ <p>Blue Jay is a web toolkit for <a href="https://golang.org/">Go</a>. It&rsquo;s a collection of command-line tools and a web blueprint that allows you to easily structure your web application. There is no rigid framework to which you have to conform and the tools are easy to start using.</p> <p>There are a few components:</p> <ul> <li><a href="https://github.com/blue-jay/blueprint"><strong>Blueprint</strong></a> is a model-view-controller (MVC) style web skeleton.</li> <li><a href="https://github.com/blue-jay/jay"><strong>Jay</strong></a> is a command line tool with find/replace, database migrations, and code generation.</li> <li><a href="https://github.com/blue-jay/core"><strong>Core</strong></a> is a collection of packages used by Blueprint and Jay that can also be used by other projects.</li> </ul> <h2 id="high-level">High Level</h2> <p>Blueprint is a web application with a built-in web server and MySQL integration. The application has a public home page, authenticated home page, login page, register page, about page, and a simple notepad to demonstrate GET, POST, PATCH, and DELETE operations.</p> <p>The entrypoint for the web app is <strong>blueprint.go</strong> which uses the <strong>boot</strong> package to load the application settings, create the session store, connect to the database, set up the views, load the routes, attach the middleware, and then start the web server.</p> <p>The front end is built using <a href="http://getbootstrap.com/">Bootstrap</a> with a few small changes to fonts and spacing. The flash messages are customized so they show up at the bottom right corner of the screen. All of the error and warning messages should display to the user or in the console. Informational messages are displayed to the user via flash messages that disappear after 4 seconds.</p> <p>Blueprint also works well with <a href="https://www.npmjs.com/">npm</a> and <a href="http://gulpjs.com/">Gulp</a>. A Gulp script is included that automates the compiling of SASS, concatenation of JavaScript, generation of favicons, and copying of static assets like Bootstrap and jQuery (which are managed by npm) to the <strong>asset/static</strong> folder.</p> <p>Jay is a command-line tool that pairs nicely with Blueprint. It has find/replace functionality to make code refactoring is a little easier. It performs database migration to easily update your database when sharing code between teams. Jay provides template-based code generation that allows you to build files like controllers, models, middleware, or even multiple views. All templates (*.gen files) are parsed using the <strong>text/template</strong> package from the Go standard library and all generation instructions (*.json files) allow you to specify which variables to pass via <strong>Jay</strong> as well as in which folder to create the templates. You can also build collections of templates to generate more than one file set which is great when you want to scaffold out a component with CRUD (create, read, update, and delete).</p> <h2 id="quick-start-website-with-jay">Quick Start Website with Jay</h2> <ol> <li>To download Blueprint, run the following command: <code>go get github.com/blue-jay/blueprint</code></li> <li>To download Jay, run the following command: <code>go get github.com/blue-jay/jay</code></li> <li>In your terminal, CD to the <strong>blueprint</strong> folder.</li> <li>Run this command to create the env.json file from env.json.example: <code>jay env make</code></li> <li>Set the environment variable, JAYCONFIG, to the env.json file path. For example: <ul> <li>On Windows: <code>SET JAYCONFIG=C:\bluejay\workspace\src\github.com\blue-jay\blueprint\env.json</code></li> <li>On Linux/OS X: <code>export JAYCONFIG=$HOME/workspace/src/github.com/blue-jay/blueprint/env.json</code></li> </ul></li> <li>Start a MySQL instance.</li> <li>Edit the <strong>MySQL</strong> section of env.json to match your database login information.</li> <li>Create the database and tables using the command: <code>jay migrate:mysql all</code></li> <li>Run the application: <code>go run blueprint.go</code></li> <li>Open your web browser to <a href="http://localhost">http://localhost</a> and you should see the welcome page.</li> <li>Navigate to the register page at <a href="http://localhost/register">http://localhost/register</a> and create a new user.</li> <li>You can now login at <a href="http://localhost/login">http://localhost/login</a>.</li> </ol> <h3 id="os-specific-instructions">OS Specific Instructions</h3> <p>There are also more detailed guides available by operating system:</p> <ul> <li><a href="https://github.com/blue-jay/blueprint/wiki/Blueprint-Setup-for-Amazon-AMI">Setup for Amazon AMI</a></li> <li><a href="https://github.com/blue-jay/blueprint/wiki/Blueprint-Setup-for-Ubuntu-AMI">Setup for Ubuntu AMI</a></li> <li><a href="https://github.com/blue-jay/blueprint/wiki/Blueprint-Setup-for-OS-X">Setup for OS X</a></li> <li><a href="https://github.com/blue-jay/blueprint/wiki/Blueprint-Setup-for-Windows">Setup for Windows</a></li> </ul> <h2 id="quick-start-website-without-jay">Quick Start Website without Jay</h2> <ol> <li>To download Blueprint, run the following command: <code>go get github.com/blue-jay/blueprint</code></li> <li>Start a MySQL instance.</li> <li>Make a copy of env.json.example and name it: <strong>env.json</strong></li> <li>Edit the <strong>MySQL</strong> section in <strong>env.json</strong> so the connection information matches your MySQL instance.</li> <li>In the <strong>Session</strong> section, you should generate new passwords for the following keys: <ul> <li>AuthKey should be a 64 byte password and then base64 encoded</li> <li>EncryptKey should be a 32 byte password and then base64 encoded</li> <li>CSRFKey should be a 32 byte password and then base64 encoded</li> </ul></li> <li>Create a database called <strong>blueprint</strong> in MySQL.</li> <li>Import <strong>migration/mysql/20160630_020000.000000_init.up.sql</strong> to create the tables.</li> <li>In your terminal, CD to the <strong>blueprint</strong> folder.</li> <li>Run the application using the command: <code>go run blueprint.go</code></li> <li>Open your web browser to <a href="http://localhost">http://localhost</a> and you should see the welcome page.</li> <li>Navigate to the register page at <a href="http://localhost/register">http://localhost/register</a> and create a new user.</li> <li>You can now login at <a href="http://localhost/login">http://localhost/login</a>.</li> </ol> <h2 id="why-blue-jay">Why Blue Jay?</h2> <p>After 300 stars on GitHub, I realized people really liked the boilerplate Model-View-Controller (MVC) web application in Go called <a href="https://github.com/josephspurrier/gowebapp">gowebapp</a> so I rewrote it with better documentation.</p> <p>Go is a blast to code in and it&rsquo;s great being part of a helpful community. Blue Jay provides a quickstart for developers with a lean web skeleton called <strong>Blueprint</strong> that demonstrates how to structure a web application with sample code.</p> <p>One of the things you&rsquo;ll notice while using Blueprint is how to abstract out external packages to make it easy to swap out components. Ultimately, you should be able to write code once and use it in all of your other projects. The <strong>lib</strong> folder is a great place for all these packages with very few dependencies.</p> <p>You&rsquo;ll also notice certain packages need to be thread-safe when building web applications. An example is the <strong>github.com/core/view</strong> package which provides thread-safe template caching.</p> <p>The other reason for Blue Jay is the command-line tool, <strong>jay</strong>, which provides an easy way to find/replace in a project when refactoring, migrate your database forwards or backwards, and generate a file or sets or files using the Go <a href="https://golang.org/pkg/html/template/">html/template</a> package. Code generation can help you build faster and more efficiently with less mistakes.</p> <h2 id="why-go">Why Go?</h2> <p>One of the big draws to Go is the rich standard library. The standard library includes a web server, web-safe templating, and many other tools necessary to build a web application. Any features missing from the standard library are written by other Go developers who are happy to contribute to the thriving community.</p> <p>Go allows you to write code that compiles to the majority of the architectures we use today so all your code is pretty much portable. Go excels when you want to write command line apps instead of just scripts, but that&rsquo;s not the only niche. The designers of Go wanted to build a language that solved problems between the Google development teams. It&rsquo;s a modern language that allows you to easily multi-thread your applications. It&rsquo;s a &ldquo;get stuff done&rdquo; language.</p> <h2 id="screenshots">Screenshots</h2> <h3 id="public-home">Public Home</h3> <p><img src="https://blue-jay.github.io/images/home_anon.png" alt="Image of Public Home" /></p> <h3 id="about">About</h3> <p><img src="https://blue-jay.github.io/images/about.png" alt="Image of About" /></p> <h3 id="register">Register</h3> <p><img src="https://blue-jay.github.io/images/register.png" alt="Image of Register" /></p> <h3 id="login">Login</h3> <p><img src="https://blue-jay.github.io/images/login.png" alt="Image of Login" /></p> <h3 id="authenticated-home">Authenticated Home</h3> <p><img src="https://blue-jay.github.io/images/home_auth.png" alt="Image of Auth Home" /></p> <h3 id="view-all-notes">View All Notes</h3> <p><img src="https://blue-jay.github.io/images/notepad_index.png" alt="Image of Notepad View" /></p> <h3 id="add-note">Add Note</h3> <p><img src="https://blue-jay.github.io/images/notepad_create.png" alt="Image of Notepad Add" /></p> <h3 id="view-one-note">View One Note</h3> <p><img src="https://blue-jay.github.io/images/notepad_view.png" alt="Image of Notepad Edit" /></p> <h3 id="edit-note">Edit Note</h3> <p><img src="https://blue-jay.github.io/images/notepad_edit.png" alt="Image of Notepad Edit" /></p> <h2 id="feedback">Feedback</h2> <p>All feedback is welcome. Let me know if you have any suggestions, questions, or criticisms. If something is not idiomatic to Go, please let me know know so we can make it better.</p> Structure https://blue-jay.github.io/structure/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/structure/ <p>It helps to understand the folder structure so you know where each of the components lives.</p> <h2 id="blueprint-structure">Blueprint Structure</h2> <p>The project is organized into the following folders:</p> <pre><code class="language-text">asset/ |----dynamic/ - private assets like SASS files, single JavaScript files, and logo.png for favicon generation |----static/ - public assets like CSS, JavaScript, and favicon.ico for Android, Apple, etc controller/ - packages with routes and application logic filestorage/ - files uploaded from an HTML form generate/ - template pairs (.gen and .json) for generating code using jay lib/ - packages you'll build that are used by the application (recommended to build with minimum dependencies) |----boot/ - package for registering services and setting up all the components |----env/ - package that holds the structure for the application settings |----flight/ - package with a context that can be used by controllers to access application settings middleware/ - packages that return a http.Handler to wrap around routes for ACL, request logging, etc migration/ - migration database files |----mysql/ - MySQL files for migrating database up and down model/ - packages with database queries and structs matching tables view/ - HTML templates parsed using the Go html/template package viewfunc/ - packages that return a template.FuncMap for use in views viewmodify/ - packages that modify view prior to rendering to add varibles like CSRF token and auth level </code></pre> <p>The following files exist at the project root:</p> <pre><code class="language-text">blueprint.go - entrypoint for the application env.json.example - application config template for variables gulpfile.js - Gulp configuration that compiles SASS, concatenates JavaScript, etc package.json - npm configuration that loads Gulp, Boostrap, Underscore.js, etc </code></pre> <h2 id="blueprint-external-go-packages">Blueprint External Go Packages</h2> <p>There are a few external packages used in Blueprint:</p> <pre><code class="language-text">github.com/gorilla/context - registry for global request variables github.com/gorilla/csrf - CSRF protection for gorilla sessions github.com/gorilla/sessions - cookie and filesystem sessions github.com/go-sql-driver/mysql - MySQL driver github.com/husobee/vestigo - HTTP router with wildcards github.com/jmoiron/sqlx - MySQL general purpose extensions github.com/justinas/alice - middleware chaining golang.org/x/crypto/bcrypt - password hashing algorithm </code></pre> <h2 id="jay-structure">Jay Structure</h2> <p>The project is simply a command-line interface for packages in the Core library <a href="https://github.com/blue-jay/core">https://github.com/blue-jay/core</a>. The packages that Jay uses from the Core library are:</p> <pre><code class="language-text">env/ - package that creates and updates the env.json config file find/ - package that finds case-sensitive matched text in files generate/ - package that generates code from template pairs migrate/ - package that handles the database migrations replace/ - package that replaces case-sensitive matched text in files </code></pre> <p>The following file exists at the project root of Jay:</p> <pre><code class="language-text">jay.go - entrypoint for the application </code></pre> <h2 id="jay-external-go-packages">Jay External Go Packages</h2> <p>There is only one external packages used in Jay (not including the Core library):</p> <pre><code class="language-text">gopkg.in/alecthomas/kingpin.v2 - command-line and flag parser </code></pre> <h2 id="core-structure">Core Structure</h2> <p>The Core project contains many packages that are all divided into individual folders. Each is well documented so the <a href="https://godoc.org/github.com/blue-jay/core">GoDoc</a> page should provide enough information on each one.</p> Configuration https://blue-jay.github.io/configuration/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/configuration/ <h2 id="basic-usage">Basic Usage</h2> <p>Throughout this documentation, keep in mind everything in Blueprint is configurable. You are not using a framework so don&rsquo;t be afraid to change code. You don&rsquo;t need to use any of the components included with Blueprint, but it does give you a nice foundation to start from. If you want to use YAML instead of JSON, it&rsquo;s recommended to create a wrapper library in the <strong>lib</strong> folder and then load your env.yaml file via the <strong>blueprint.go</strong> file.</p> <h2 id="jay-command-env">Jay Command: env</h2> <p>One of the first steps before using Blueprint is to create <strong>env.json</strong>. You can make a copy of <strong>env.json.example</strong> and then name it <strong>env.json</strong>, just be sure to generate a new <strong>AuthKey</strong>, <strong>EncryptKey</strong>, and <strong>CSRFKey</strong> in the <strong>Session</strong> section.</p> <p>You can also use <strong>jay</strong> to create the env.json file with new session keys. Just CD to the <strong>blueprint</strong> folder and then run: <code>jay env make</code></p> <p>Here are the commands you can use with <code>jay env</code>:</p> <pre><code class="language-bash"># Create a new env.json file from env.json.example with newly generated session keys jay env make # Show a new set of session keys that can be copied and pasted into env.json jay env keyshow # Generate a new set of session keys and automatically apply them to env.json env env keyupdate </code></pre> <p>The <strong>env.json</strong> file is a good place to set variables for the application so you don&rsquo;t have to hardcode them. If you want to add any of your own settings, you can add them to <strong>env.json</strong> and update the <strong>Info</strong> struct in the <strong>lib/env</strong> package. Here is an example <strong>env.json</strong>:</p> <pre><code class="language-json">{ &quot;Asset&quot;:{ &quot;Folder&quot;:&quot;asset&quot; }, &quot;Email&quot;:{ &quot;Username&quot;:&quot;&quot;, &quot;Password&quot;:&quot;&quot;, &quot;Hostname&quot;:&quot;&quot;, &quot;Port&quot;:25, &quot;From&quot;:&quot;&quot; }, &quot;Form&quot;:{ &quot;FileStorageFolder&quot;:&quot;filestorage&quot; }, &quot;Generation&quot;:{ &quot;TemplateFolder&quot;:&quot;generate&quot; }, &quot;MySQL&quot;:{ &quot;Username&quot;:&quot;root&quot;, &quot;Password&quot;:&quot;&quot;, &quot;Database&quot;:&quot;blueprint&quot;, &quot;Charset&quot;:&quot;utf8mb4&quot;, &quot;Collation&quot;:&quot;utf8mb4_unicode_ci&quot;, &quot;Hostname&quot;:&quot;127.0.0.1&quot;, &quot;Port&quot;:3306, &quot;Parameter&quot;:&quot;parseTime=true&quot;, &quot;Migration&quot;:{ &quot;Folder&quot;:&quot;migration/mysql&quot;, &quot;Table&quot;:&quot;migration_blueprint&quot;, &quot;Extension&quot;:&quot;sql&quot; } }, &quot;Server&quot;:{ &quot;Hostname&quot;:&quot;&quot;, &quot;UseHTTP&quot;:true, &quot;UseHTTPS&quot;:false, &quot;RedirectToHTTPS&quot;:false, &quot;HTTPPort&quot;:80, &quot;HTTPSPort&quot;:443, &quot;CertFile&quot;:&quot;tls/server.crt&quot;, &quot;KeyFile&quot;:&quot;tls/server.key&quot; }, &quot;Session&quot;:{ &quot;AuthKey&quot;:&quot;PzCh6FNAB7/jhmlUQ0+25sjJ+WgcJeKR2bAOtnh9UnfVN+WJSBvY/YC80Rs+rbMtwfmSP4FUSxKPtpYKzKFqFA==&quot;, &quot;EncryptKey&quot;:&quot;3oTKCcKjDHMUlV+qur2Ve664SPpSuviyGQ/UqnroUD8=&quot;, &quot;CSRFKey&quot;:&quot;xULAGF5FcWvqHsXaovNFJYfgCt6pedRPROqNvsZjU18=&quot;, &quot;Name&quot;:&quot;sess&quot;, &quot;Options&quot;:{ &quot;Path&quot;:&quot;/&quot;, &quot;Domain&quot;:&quot;&quot;, &quot;MaxAge&quot;:28800, &quot;Secure&quot;:false, &quot;HttpOnly&quot;:true } }, &quot;Template&quot;:{ &quot;Root&quot;:&quot;base&quot;, &quot;Children&quot;:[ &quot;partial/favicon&quot;, &quot;partial/menu&quot;, &quot;partial/footer&quot; ] }, &quot;View&quot;:{ &quot;BaseURI&quot;:&quot;/&quot;, &quot;Extension&quot;:&quot;tmpl&quot;, &quot;Folder&quot;:&quot;view&quot;, &quot;Caching&quot;:true } } </code></pre> <h2 id="production">Production</h2> <p>When you move your application to production, you should make the following changes:</p> <ul> <li>Set <strong>Server</strong>.<strong>Hostname</strong> to the server</li> <li>Set <strong>Server</strong>.<strong>UseHTTPS</strong> to true</li> <li>Generate a certificate and key for HTTPS and place in the <strong>tls</strong> folder</li> <li>Set <strong>Session</strong>.<strong>Secure</strong> to true</li> </ul> <h2 id="configuration-structure">Configuration Structure</h2> <p>The <strong>env.json</strong> file contains the configuration for Blueprint and Jay. It removes the need to hardcode any of these values and makes it easy to move Blueprint to another system with a different setup. The <strong>env.json</strong> file is parsed to the <strong>Info</strong> struct from the <strong>lib/env</strong> package:</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/lib/env/env.go">Source</a></p> <pre><code class="language-go">// Info contains the application settings. type Info struct { Asset asset.Info `json:&quot;Asset&quot;` Email email.Info `json:&quot;Email&quot;` Form form.Info `json:&quot;Form&quot;` Generation generate.Info `json:&quot;Generation&quot;` MySQL mysql.Info `json:&quot;MySQL&quot;` Server server.Info `json:&quot;Server&quot;` Session session.Info `json:&quot;Session&quot;` Template view.Template `json:&quot;Template&quot;` View view.Info `json:&quot;View&quot;` path string } </code></pre> <p>The <strong>Info</strong> struct is a container that nests structs from packages in the <strong>Core</strong> library that need variables configured. The <strong>Path</strong> variable is the location of the env.json file. Here is a list mapping the JSON keys to structs:</p> <pre><code class="language-text">Asset - Info struct in core/asset Email - Info struct in core/email Form - Info struct in core/form Generate - Info struct in core/generate MySQL - Info struct in core/mysql Server - Info struct in core/server Session - Info struct in core/session Template - Template struct in core/view View - Info struct in core/view </code></pre> <h2 id="using-flight">Using Flight</h2> <p>The <strong>flight</strong> package provides access to the session variables, env.json settings, database connections, views, and general shortcuts. It&rsquo;s mostly designed for controllers, but it can be used by other packages as well.</p> <p>To use package, you would call the <strong>flight.Context()</strong> function like this:</p> <pre><code class="language-go">// Index displays the items. func Index(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) items, _, err := note.ByUserID(c.DB, c.UserID) if err != nil { c.FlashError(err) items = []note.Item{} } v := c.View.New(&quot;note/index&quot;) v.Vars[&quot;items&quot;] = items v.Render(w, r) } </code></pre> <p>Flight needs a <strong>http.ResponseWriter</strong> and a <strong>http.Request</strong> so it can access the sessions variables and provide shortcuts for the following tasks:</p> <ul> <li>reading URL parameters: c.Param(name string)</li> <li>redirecting to a page: c.Redirect(urlStr string)</li> <li>validation a form and sets a flash message: c.FormValid(fields &hellip;string)</li> <li>repopulating a form: c.Repopulate(v map[string]interface{}, fields &hellip;string)</li> <li>setting flash messages and then saving the session: c.FlashSuccess(message string)</li> </ul> <p>Flight also provides access to the following:</p> <ul> <li>configuration settings from env.json: <code>c.Config.Asset.Folder</code></li> <li>session: <code>c.Sess.Values[&quot;email&quot;]</code></li> <li>current user ID: <code>c.UserID</code></li> <li>view package: <code>c.View.New(&quot;home/index&quot;)</code></li> <li>database connection: <code>items, _, err := note.ByUserID(c.DB, c.UserID)</code></li> </ul> <p>It is not a requirement to use the <strong>flight</strong> package, but it makes working with the different web components much easier. Feel free to modify the package to fit your needs. Just make sure it is thread-safe.</p> <h2 id="enable-https">Enable HTTPS</h2> <p>To enable HTTPS:</p> <ol> <li>Set <strong>UseHTTPS</strong> to <strong>true</strong> in the <strong>env.json</strong> file</li> <li>Create a folder called <strong>tls</strong> in the project root folder</li> <li>Place your own certificate and key files in the <strong>tls</strong> folder</li> </ol> <p><strong>Note:</strong> If you want to redirect HTTP to HTTPS, you can set <strong>RedirectToHTTPS</strong> to <strong>true</strong> in the <strong>env.json</strong> file as well.</p> <h2 id="tip-add-a-section">Tip: Add a Section</h2> <p>To add a new key called <strong>Captcha</strong>, your workflow would consist of the following:</p> <ol> <li>Create a new package in the <strong>lib</strong> folder called <strong>captcha</strong></li> <li>Create a struct called <strong>Info</strong> in the <strong>lib/captcha</strong> package</li> <li>Add the struct to the <strong>Info</strong> struct in the <strong>lib/env</strong> package</li> <li>Add the <strong>Captcha</strong> key and any values to the <strong>env.json</strong> file</li> <li>Add code to the <strong>RegisterServices()</strong> function in the <strong>lib/boot</strong> package to pass the any additional settings to the <strong>lib/flight</strong> package at start up</li> <li>Add code to your controllers that references uses <strong>flight.Context()</strong> to retrieve your <strong>lib/captcha</strong> package settings</li> </ol> <h2 id="tip-remove-a-section">Tip: Remove a Section</h2> <p>To remove the <strong>Email</strong> key, your workflow would consist of the following:</p> <ol> <li>Remove the <strong>Email</strong> key and value from the <strong>env.json</strong> file</li> <li>Remove the <strong>Email</strong> nested struct from the <strong>Info</strong> struct in the <strong>lib/env</strong> package</li> <li>Remove any code setting up the package from the <strong>RegisterServices()</strong> function in the <strong>boot</strong> package</li> <li>Remove the <strong>lib/email</strong> package from the filesystem</li> <li>Find any references to the <strong>lib/email</strong> package in your code using the jay command line, <code>jay find . &quot;lib/email&quot;</code>, then delete the imports and referencing code</li> </ol> Assets https://blue-jay.github.io/assets/ Thu, 30 Jun 2016 21:07:13 +0100 https://blue-jay.github.io/assets/ <h2 id="basic-usage">Basic Usage</h2> <p>Out of the box, all of the static assets like CSS and JavaScript are ready to demo. If you want to make changes to the file, it&rsquo;s best to use the tools provided. The <strong>asset</strong> folder contains a <strong>dynamic</strong> folder and a <strong>static</strong> folder.</p> <p>The <strong>dynamic</strong> folder contains the Syntactically Awesome Style Sheets (SASS), individual JavaScript files, and a large PNG image which is used to generate favicons for different platforms like Android, iPhone, etc. <em>This is the folder in which you want to make your changes.</em> The dynamic folder holds some of the assets required to generate the assets in the <strong>static</strong> folder.</p> <p>The <strong>static</strong> folder contains the minified CSS and JavaScript as well as the generated favicons. The <strong>static</strong> folder is designed to be served up so the files can be accessed like this:</p> <pre><code class="language-html">&lt;!-- Favicons --&gt; &lt;link rel=&quot;apple-touch-icon&quot; sizes=&quot;57x57&quot; href=&quot;/static/favicon/apple-touch-icon-57x57.png?v1.0=3eepn6WlLO&quot;&gt; &lt;link rel=&quot;apple-touch-icon&quot; sizes=&quot;60x60&quot; href=&quot;/static/favicon/apple-touch-icon-60x60.png?v1.0=3eepn6WlLO&quot;&gt; &lt;link rel=&quot;apple-touch-icon&quot; sizes=&quot;72x72&quot; href=&quot;/static/favicon/apple-touch-icon-72x72.png?v1.0=3eepn6WlLO&quot;&gt; &lt;!-- CSS and Fonts --&gt; &lt;link media=&quot;all&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/css/bootstrap.min.css?1466973904&quot; /&gt; &lt;link media=&quot;all&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;//fonts.googleapis.com/css?family=Open+Sans:300,400,bold,italic&quot; /&gt; &lt;link media=&quot;all&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/css/all.css?1466973904&quot; /&gt; </code></pre> <p>Check out the <a href="https://blue-jay.github.io/controllers">Controllers</a> and <a href="https://blue-jay.github.io/views/#included-functions">Views</a> pages for how the files are served and how timestamps are append to them for cach management.</p> <p>If you make changes to any of the files in the <strong>dynamic</strong> folder, you still need a way to compile/minify them and then move them to the <strong>static</strong> folder so we&rsquo;ll use Gulp to do that.</p> <h2 id="install-npm">Install npm</h2> <p>The Node Package Manager (npm) helps install packages that work with NodeJS. If you don&rsquo;t have NodeJS and npm installed, you can install the latest version from <a href="https://nodejs.org">https://nodejs.org</a>.</p> <h2 id="install-gulp-and-dependencies">Install Gulp and Dependencies</h2> <p>Once npm is installed, you can open your terminal and CD to the root of the project folder. You can then run these commands:</p> <pre><code class="language-bash"># Install Gulp globally npm install -g gulp-cli # Install Gulp locally and dependencies from package.json npm install </code></pre> <h2 id="gulp">Gulp</h2> <p>Once the environment is set up, you should have your terminal open to the root of the project folder. There are a couple commands you can use with Gulp that are in the <a href="https://github.com/blue-jay/blueprint/blob/master/gulpfile.js">gulpfile.js</a>.</p> <pre><code class="language-bash"># Compile the SASS from asset/dynamic/sass and store CSS in asset/static/css/all.css gulp sass # Concat the JavaScript from asset/dynamic/js and store JS in asset/static/js/all.js gulp javascript # Copy the jQuery files from node_modules/jquery to asset/static/js gulp jquery # Copy the Bootstrap files from node_modules/bootstrap to asset/static gulp bootstrap # Copy the Underscore files from note_modules/underscore to asset/static/js gulp underscore # Run tasks favicon-generate and favicon-inject gulp favicon # Generate favicons from asset/dynamic/logo.png and copy to /asset/static/favicon gulp favicon-generate # Generate view/partial/favicon.tmpl with favicon tags gulp favicon-inject # Update the asset/dynamic/favicon/data.json file with the latest version from the RealFaviconGenerator website gulp favicon-update # Run the sass and javascript tasks when any of the files change gulp watch # Run all the tasks once gulp init # Run just the sass and javascript tasks once gulp default </code></pre> <p>It is best to run <code>gulp watch</code> so when you are working with the SASS and JavaScript files so they will automatically generate in the <strong>static</strong> folder for you.</p> Routing https://blue-jay.github.io/routing/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/routing/ <h2 id="basic-usage">Basic Usage</h2> <p>When a user requests a page from your application, the routes determine which page is shown. The route is a URL that is mapped to a controller function. To simplify the organization, the routes are stored in the controller files. The controller files are all organized under the <strong>controller</strong> folder.</p> <h2 id="routing">Routing</h2> <p>In the <strong>boot</strong> package, the <strong>RegisterServices()</strong> function calls the <strong>controller.LoadRoutes()</strong> function. The <strong>LoadRoutes()</strong> function in the <strong>controller</strong> package loads the routes for each of the individual controllers:</p> <pre><code class="language-go">// LoadRoutes loads the routes for each of the controllers. func LoadRoutes() { about.Load() debug.Load() register.Load() login.Load() home.Load() static.Load() status.Load() notepad.Load() } </code></pre> <p>Here is the <strong>Load()</strong> function from the <strong>controller/notepad</strong> package:</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/controller/static/static.go">Source</a></p> <pre><code class="language-go">// Package static serves static files like CSS, JavaScript, and images. package static import ( &quot;net/http&quot; &quot;os&quot; &quot;path&quot; &quot;github.com/blue-jay/blueprint/controller/status&quot; &quot;github.com/blue-jay/blueprint/lib/flight&quot; &quot;github.com/blue-jay/core/router&quot; ) // Load the routes. func Load() { // Serve static files router.Get(&quot;/static/*filepath&quot;, Index) } // Index maps static files. func Index(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) // File path path := path.Join(c.Config.Asset.Folder, r.URL.Path[1:]) // Only serve files if fi, err := os.Stat(path); err == nil &amp;&amp; !fi.IsDir() { http.ServeFile(w, r, path) return } status.Error404(w, r) } </code></pre> <h2 id="error-pages">Error Pages</h2> <p>A few errors pages are already defined for you like the <strong>404</strong> (Page Not Found) and <strong>405</strong> (Method Not Allowed) pages.</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/controller/status/status.go">Source</a></p> <pre><code class="language-go">// Package status provides all the error pages like 404, 405, 500, 501, // and the page when a CSRF token is invalid. package status import ( &quot;net/http&quot; &quot;github.com/blue-jay/blueprint/lib/flight&quot; &quot;github.com/blue-jay/core/router&quot; ) // Load the routes. func Load() { router.MethodNotAllowed(Error405) router.NotFound(Error404) } // Error404 - Page Not Found. func Error404(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) w.WriteHeader(http.StatusNotFound) v := c.View.New(&quot;status/index&quot;) v.Vars[&quot;title&quot;] = &quot;404 Not Found&quot; v.Vars[&quot;message&quot;] = &quot;Page could not be found.&quot; v.Render(w, r) } // Error405 - Method Not Allowed. func Error405(allowedMethods string) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) w.WriteHeader(http.StatusMethodNotAllowed) v := c.View.New(&quot;status/index&quot;) v.Vars[&quot;title&quot;] = &quot;405 Method Not Allowed&quot; v.Vars[&quot;message&quot;] = &quot;Method is not allowed.&quot; v.Render(w, r) } } ... </code></pre> Middleware https://blue-jay.github.io/middleware/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/middleware/ <h2 id="basic-usage">Basic Usage</h2> <p>Middleware, in the context of Go, is applied during routing to provide features like request/response logging, access controls lists (ACLs), and header modification. Middleware is either applied to every request (like for request logging) or specified routes (like for ACLs).</p> <p>There are a few pieces of middleware included. The package called <strong>csrf</strong> protects against Cross-Site Request Forgery attacks. The <strong>logrequest</strong> package will log every request made against the website to the console. The <strong>rest</strong> package allows the HTTP method to be changed during a form submission to DELETE or PATCH instead of POST.</p> <h2 id="creating-middleware">Creating Middleware</h2> <p>An example of a piece of middleware that is applied to every request is <strong>middleware/logrequest</strong>. When a page is requested, the middleware will print to the console: the time of the request, remote IP address, HTTP method, and the URL requested.</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/middleware/logrequest/logrequest.go">Source</a></p> <pre><code class="language-go">// Package logrequest provides an http.Handler that logs when a request is // made to the application and lists the remote address, the HTTP method, // and the URL. package logrequest import ( &quot;fmt&quot; &quot;net/http&quot; &quot;time&quot; ) // Handler will log the HTTP requests. func Handler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Println(time.Now().Format(&quot;2006-01-02 03:04:05 PM&quot;), r.RemoteAddr, r.Method, r.URL) next.ServeHTTP(w, r) }) } </code></pre> <p>This is an example of the minimum code required for middleware:</p> <pre><code class="language-go">// Handler func Handler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Logic BEFORE the other handlers and function goes here next.ServeHTTP(w, r) // Logic AFTER the other handlers and function goes here }) } </code></pre> <h2 id="chaining">Chaining</h2> <p>The more middleware you use, the more it stacks up like this and makes it hard to read:</p> <pre><code class="language-go">return context.ClearHandler(rest.Handler(logrequest.Handler(setUpCSRF))) </code></pre> <p>Before <a href="https://github.com/justinas/alice">justinas/alice</a>, a workaround was to use a variable and reassign it multiple times like this:</p> <pre><code class="language-go">h = setUpCSRF(h) h = logrequest.Handler(h) h = rest.Handler(h) return context.ClearHandler(h) </code></pre> <p>You can see chaining in action in <a href="https://github.com/blue-jay/blueprint/blob/master/controller/notepad/notepad.go">controller/notepad</a> where the controller uses the <strong>router.ChainHandler()</strong> function. The function is a wrapper for the <a href="https://github.com/justinas/alice">justinas/alice</a> package which makes using middleware more scalable and a little &ldquo;prettier&rdquo;. If you look at the <a href="https://github.com/blue-jay/blueprint/blob/master/lib/boot/middleware.go">lib/boot</a> package, you&rsquo;ll see the <strong>ChainHandler()</strong> function. There is also a <strong>Chain()</strong> function that can be used to chain middleware for routes or to pass to <strong>ChainHandler()</strong>.</p> <pre><code class="language-go">// Apply middleware to routes individually router.Get(&quot;/notepad&quot;, Index, acl.DisallowAnon, logrequest.Handler) router.Get(&quot;/notepad/create&quot;, Create, acl.DisallowAnon, logrequest.Handler) // Use Chain() to apply middleware c := router.Chain(acl.DisallowAnon, logrequest.Handler) router.Get(&quot;/notepad&quot;, Index, c...) router.Get(&quot;/notepad/create&quot;, Create, c...) // Pass Chain() to ChainHandler() c := router.Chain( // Chain middleware, bottom runs first h, // Handler to wrap setUpCSRF, // Prevent CSRF rest.Handler, // Support changing HTTP method sent via query string logrequest.Handler, // Log every request context.ClearHandler, // Prevent memory leak with gorilla.sessions ) return router.ChainHandler(c...) </code></pre> <p><strong>ChainHandler()</strong> accepts one or more of the http.Handler type and returns a http.Handler.</p> <p><strong>Chain()</strong> accepts one or more of the http.Handler type and returns an array of the alice.Constructor type.</p> <h2 id="apply-to-every-request">Apply to Every Request</h2> <p>In <a href="https://github.com/blue-jay/blueprint/blob/master/blueprint.go">blueprint.go</a>, the application calls <strong>boot.SetUpMiddleware(router.Instance())</strong> which applies the middleware to the router. The middleware is called on every request.</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/lib/boot/middleware.go">Source</a></p> <pre><code class="language-go">// SetUpMiddleware contains the middleware that applies to every request. func SetUpMiddleware(h http.Handler) http.Handler { return router.ChainHandler( // Chain middleware, top middlware runs first h, // Handler to wrap setUpCSRF, // Prevent CSRF rest.Handler, // Support changing HTTP method sent via query string logrequest.Handler, // Log every request context.ClearHandler, // Prevent memory leak with gorilla.sessions ) </code></pre> <h2 id="apply-to-specific-routes">Apply to Specific Routes</h2> <p>In <a href="https://github.com/blue-jay/blueprint/blob/master/controller/notepad/notepad.go">controller/notepad</a>, the application creates a chain of middleware and then applies it to only certain routes. In this scenario, the pages are only accessible if the user is authenticated.</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/controller/notepad/notepad.go">Source</a></p> <pre><code class="language-go">func Load() { // Add middleware that disallows anonymous access c := router.Chain(acl.DisallowAnon) // Map HTTP methods and URLs to functions wrapped in the middleware chain router.Get(&quot;/notepad&quot;, Index, c...) router.Get(&quot;/notepad/create&quot;, Create, c...) router.Post(&quot;/notepad&quot;, Store, c...) router.Get(&quot;/notepad/view/:id&quot;, Show, c...) router.Get(&quot;/notepad/edit/:id&quot;, Edit, c...) router.Patch(&quot;/notepad/edit/:id&quot;, Update, c...) router.Delete(&quot;/notepad/:id&quot;, Destroy, c...) } </code></pre> Controllers https://blue-jay.github.io/controllers/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/controllers/ <h2 id="basic-usage">Basic Usage</h2> <p>The controller files are all organized under the <strong>controller</strong> folder. The controllers handle the interactions between the models and the views as well as specify which routes to map to which functions.</p> <p>It&rsquo;s a good idea to follow a naming convention for the different pieces. Laravel developers will notice it&rsquo;s very similar, but with a few changes.</p> <table> <thead> <tr> <th align="center">Method</th> <th align="center">Path</th> <th align="center">Function</th> <th align="center">View</th> </tr> </thead> <tbody> <tr> <td align="center">GET</td> <td align="center">/notepad</td> <td align="center">Index</td> <td align="center">index.tmpl</td> </tr> <tr> <td align="center">GET</td> <td align="center">/notepad/create</td> <td align="center">Create</td> <td align="center">create.tmpl</td> </tr> <tr> <td align="center">POST</td> <td align="center">/notepad/create</td> <td align="center">Store</td> <td align="center"></td> </tr> <tr> <td align="center">GET</td> <td align="center">/notepad/view/:id</td> <td align="center">Show</td> <td align="center">show.tmpl</td> </tr> <tr> <td align="center">GET</td> <td align="center">/notepad/edit/:id</td> <td align="center">Edit</td> <td align="center">edit.tmpl</td> </tr> <tr> <td align="center">PATCH</td> <td align="center">/notepad/edit/:id</td> <td align="center">Update</td> <td align="center"></td> </tr> <tr> <td align="center">DELETE</td> <td align="center">/notepad/:id</td> <td align="center">Destroy</td> <td align="center"></td> </tr> </tbody> </table> <p>Here is a controller that follows the naming convention. Notice the model name (<strong>note</strong>) matches the view folder (<strong>note/index</strong>). The model does not need to match the controller because you&rsquo;ll be working with many different models in your controllers.</p> <pre><code class="language-go">func Load() { ... // &quot;Get&quot; is the Method // &quot;/notepad&quot; is the Path router.Get(&quot;/notepad&quot;, Index, acl.DisallowAnon) ... } // Index displays the items. func Index(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) items, _, err := note.ByUserID(c.DB, c.UserID) if err != nil { c.FlashError(err) items = []note.Item{} } v := c.View.New(&quot;note/index&quot;) v.Vars[&quot;items&quot;] = items v.Render(w, r) } </code></pre> <h2 id="access-a-session">Access a Session</h2> <p>Sessions provide access to flash messages as well as variables that are set at login. You <strong>must</strong> remember to save the sessions once you make a change to them.</p> <pre><code class="language-go">// Get the current session sess, _ := session.Instance(r) ... // Save the session after you are finished making changes sess.Save(r, w) </code></pre> <h2 id="trigger-flash-message">Trigger Flash Message</h2> <p>Flash messages will appear to the user on next page load. They only display once. The built-in messages are tied to Bootstrap classes:</p> <ul> <li>flash.Success is green (alert-success)</li> <li>flash.Warning is yellow (alert-warning)</li> <li>flash.Notice is blue (alert-info)</li> <li>flash.Error is red (alert-danger)</li> </ul> <pre><code class="language-go">sess.AddFlash(flash.Info{&quot;Welcome to Blueprint!&quot;, flash.Success}) sess.Save(r, w) // Ensure you save the session after making a change to it </code></pre> <h2 id="validate-a-form">Validate a Form</h2> <p>The <strong>form</strong> package makes it easy to validate required fields. It works on the inputs: text, textarea, checkbox, radio, and select. The function, <strong>form.Required()</strong>, requires the request and then any number of fields as it is a variadic function. You can use the <code>form</code> package by itself it you want more control over the error message or you can use the <code>flight</code> package which handles the error message for you:</p> <pre><code class="language-go">// Without flight if valid, missingField := form.Required(r, &quot;email&quot;, &quot;password&quot;); !valid { sess.AddFlash(flash.Info{&quot;Field missing: &quot; + missingField, flash.Error}) sess.Save(r, w) LoginGET(w, r) return } // With flight c := flight.Context(w, r) if !c.FormValid(&quot;name&quot;, &quot;email&quot;, &quot;password&quot;) { Create(w, r) return } </code></pre> <h2 id="repopulate-form-fields">Repopulate Form Fields</h2> <p>The <strong>form</strong> package can also repopulate the form fields after a submission that is missing information. It is also a variadic function so it can accepts more than one field. You&rsquo;ll need to use blocks from the <strong>form</strong> package in your view as well. Check out the <a href="https://blue-jay.github.io/views/#repopulate-form-fields">Views</a> page to see how to use them.</p> <pre><code class="language-go">// Without flight c := flight.Context(w, r) v := c.View.New(&quot;note/create&quot;) form.Repopulate(r.Form, v.Vars, &quot;name&quot;) v.Render(w, r) // With flight c := flight.Context(w, r) v := c.View.New(&quot;note/create&quot;) c.Repopulate(v.Vars, &quot;name&quot;) v.Render(w, r) </code></pre> <h2 id="render-a-template">Render a Template</h2> <p>You can render a template a few ways (check out the <a href="https://blue-jay.github.io/views">Views</a> page for more clarification):</p> <pre><code class="language-go">// Render without adding any variables c := flight.Context(w, r) c.View.New(&quot;about/index&quot;).Render(w, r) // Render with variables c := flight.Context(w, r) v := c.View.New(&quot;home/index&quot;) v.Vars[&quot;first_name&quot;] = c.Sess.Values[&quot;first_name&quot;] v.Render(w, r) // Render with different base template (base.tmpl is used by default) c := flight.Context(w, r) v := c.View.New(&quot;home/index&quot;).Base(&quot;single&quot;) v.Render(w, r) </code></pre> <h2 id="return-flash-over-ajax">Return Flash over Ajax</h2> <p>If you&rsquo;re using Ajax to retrieve content, you can also retrieve the flash messages this way so they can be displayed without refreshing the page. There is JavaScript that is already designed to show a Flash message and it&rsquo;s called: <strong>ShowFlash()</strong>. You&rsquo;ll just need to make a call to the page and then pass the output to <strong>ShowFlash()</strong>. The code is below.</p> <p>Code for the controller:</p> <pre><code class="language-go">// Load the routes. func Load() { router.Get(&quot;/flashes&quot;, Index) } // Index displays the flash messages in JSON. func Index(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) // Set the flash message c.Sess.AddFlash(flash.Info{&quot;An error occurred on the server. Please try again later.&quot;, flash.Error}) c.Sess.Save(r, w) // Display the flash messages as JSON flash.SendFlashes(w, r) } </code></pre> <p>Code in JavaScript:</p> <pre><code class="language-javascript">$.get(&quot;/flashes&quot;, function(data) { showFlash(data); }); </code></pre> <h2 id="interact-with-a-model">Interact with a Model</h2> <p>The models contain all the SQL code so the controllers just call the model functions to interact with the data.</p> <pre><code class="language-go">// Get database result result, norows, err := user.ByEmail(email) if nowrows { // User does not exist } else if err != nil { // Display error message } else if passhash.MatchString(result.Password, password) { // Password matches! } else { // Password does not match } </code></pre> <h2 id="send-an-email">Send an Email</h2> <p>There is also a simple package that sends emails once the SMTP settings in env.json point to your SMTP server.</p> <pre><code class="language-go">// Email a user c := flight.Context(w, r) err := c.Config.Email.Send(&quot;This is the subject&quot;, &quot;This is the body!&quot;) if err != nil { c.FlashError(err) return } </code></pre> Models https://blue-jay.github.io/models/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/models/ <h2 id="basic-usage">Basic Usage</h2> <p>It&rsquo;s a good idea to abstract the database layer out so if you need to make changes, you don&rsquo;t have to look through the controllers to find the queries. All the queries are stored in the <strong>model</strong> folder.</p> <p>Blue Jay supports MySQL by default, but can easily be expanded to use other database systems. There is also a PostgreSQL package you can use <a href="https://github.com/blue-jay/core/blob/master/storage/driver/postgresql/postgresql.go">here</a>. The instructions are at the bottom of this page.</p> <h2 id="connect-to-the-database">Connect to the database</h2> <p>You only need to connect to the database once. The connection pool is handled by the <a href="https://github.com/go-sql-driver/mysql">go-sql-driver/mysql</a> package. The connection is started by the <strong>lib/boot</strong> package:</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/lib/boot/boot.go">Source</a></p> <pre><code class="language-go">// Connect to the MySQL database mysqlDB, _ := config.MySQL.Connect(true) </code></pre> <h2 id="model-layout">Model Layout</h2> <p>Every model should have a table name, a struct to represent the columns, and a Connection interface. The interface makes the model much more testable so it can be easily mocked and interchanged.</p> <p>A good interface for each model looks like this:</p> <pre><code>// Connection is an interface for making queries. type Connection interface { Exec(query string, args ...interface{}) (sql.Result, error) Get(dest interface{}, query string, args ...interface{}) error Select(dest interface{}, query string, args ...interface{}) error } </code></pre> <p>And a good create function looks like this:</p> <pre><code>// Create adds an item. func Create(db Connection, name string, userID string) (sql.Result, error) { result, err := db.Exec(fmt.Sprintf(` INSERT INTO %v (name, user_id) VALUES (?,?) `, table), name, userID) return result, err } </code></pre> <h1 id="crud-operations">CRUD Operations</h1> <p>Below are common operations and how controllers can interact with models. All the controller functions are from <a href="https://github.com/blue-jay/blueprint/blob/master/controller/notepad/notepad.go">notepad.go</a> and the model functions are from <a href="https://github.com/blue-jay/blueprint/blob/master/model/note/note.go">note.go</a>.</p> <h2 id="create-an-item">Create an Item</h2> <p>Use <strong>db.Exec()</strong> to create an item or a table.</p> <h3 id="controller">Controller</h3> <pre><code class="language-go">// Store handles the create form submission. func Store(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) if !c.FormValid(&quot;name&quot;) { Create(w, r) return } _, err := note.Create(c.DB, r.FormValue(&quot;name&quot;), c.UserID) if err != nil { c.FlashError(err) Create(w, r) return } c.FlashSuccess(&quot;Item added.&quot;) c.Redirect(uri) } </code></pre> <h3 id="model">Model</h3> <pre><code class="language-go">// Create adds an item. func Create(db Connection, name string, userID string) (sql.Result, error) { result, err := db.Exec(fmt.Sprintf(` INSERT INTO %v (name, user_id) VALUES (?,?) `, table), name, userID) return result, err } </code></pre> <h2 id="get-an-item-by-item-id">Get an Item by Item ID</h2> <p>Use <strong>db.Get()</strong> to get a single item.</p> <h3 id="controller-1">Controller</h3> <pre><code class="language-go">// Show displays a single item. func Show(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) item, _, err := note.ByID(c.DB, c.Param(&quot;id&quot;), c.UserID) if err != nil { c.FlashError(err) c.Redirect(uri) return } v := c.View.New(&quot;note/show&quot;) v.Vars[&quot;item&quot;] = item v.Render(w, r) } </code></pre> <h3 id="model-1">Model</h3> <pre><code class="language-go">// ByID gets item by ID. func ByID(db Connection, ID string, userID string) (Item, bool, error) { result := Item{} err := db.Get(&amp;result, fmt.Sprintf(` SELECT id, name, user_id, created_at, updated_at, deleted_at FROM %v WHERE id = ? AND user_id = ? AND deleted_at IS NULL LIMIT 1 `, table), ID, userID) return result, err == sql.ErrNoRows, err } </code></pre> <h2 id="get-items-by-user-id">Get Items by User ID</h2> <p>Use <strong>db.Select()</strong> to get multiple items.</p> <h3 id="controller-2">Controller</h3> <pre><code class="language-go">// Index displays the items. func Index(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) items, _, err := note.ByUserID(c.DB, c.UserID) if err != nil { c.FlashError(err) items = []note.Item{} } v := c.View.New(&quot;note/index&quot;) v.Vars[&quot;items&quot;] = items v.Render(w, r) } </code></pre> <h3 id="model-2">Model</h3> <pre><code class="language-go">// ByUserID gets all entities for a user. func ByUserID(db Connection, userID string) ([]Item, bool, error) { var result []Item err := db.Select(&amp;result, fmt.Sprintf(` SELECT id, name, user_id, created_at, updated_at, deleted_at FROM %v WHERE user_id = ? AND deleted_at IS NULL `, table), userID) return result, err == sql.ErrNoRows, err } </code></pre> <h2 id="update-an-item">Update an Item</h2> <p>Use <strong>db.Exec()</strong> to update one or more items.</p> <h3 id="controller-3">Controller</h3> <pre><code class="language-go">// Update handles the edit form submission. func Update(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) if !c.FormValid(&quot;name&quot;) { Edit(w, r) return } _, err := note.Update(c.DB, r.FormValue(&quot;name&quot;), c.Param(&quot;id&quot;), c.UserID) if err != nil { c.FlashError(err) Edit(w, r) return } c.FlashSuccess(&quot;Item updated.&quot;) c.Redirect(uri) } </code></pre> <h3 id="model-3">Model</h3> <pre><code class="language-go">// Update makes changes to an existing item. func Update(db Connection, name string, ID string, userID string) (sql.Result, error) { result, err := db.Exec(fmt.Sprintf(` UPDATE %v SET name = ? WHERE id = ? AND user_id = ? AND deleted_at IS NULL LIMIT 1 `, table), name, ID, userID) return result, err } </code></pre> <h2 id="soft-delete-an-item">Soft Delete an Item</h2> <p>A soft delete leaves the item in the database, but marks it as deleted with a timestamp.</p> <h3 id="controller-4">Controller</h3> <pre><code class="language-go">// Destroy handles the delete form submission. func Destroy(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) _, err := note.DeleteSoft(c.DB, c.Param(&quot;id&quot;), c.UserID) if err != nil { c.FlashError(err) } else { c.FlashNotice(&quot;Item deleted.&quot;) } c.Redirect(uri) } </code></pre> <h3 id="model-4">Model</h3> <pre><code class="language-go">// Delete marks an item as removed. func DeleteSoft(db Connection, ID string, userID string) (sql.Result, error) { result, err := db.Exec(fmt.Sprintf(` UPDATE %v SET deleted_at = NOW() WHERE id = ? AND user_id = ? AND deleted_at IS NULL LIMIT 1 `, table), ID, userID) return result, err } </code></pre> <h2 id="hard-delete-an-item">Hard Delete an Item</h2> <p>A hard delete removes the item from the database.</p> <h3 id="controller-5">Controller</h3> <pre><code class="language-go">// Destroy handles the delete form submission. func Destroy(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) _, err := note.DeleteHard(c.DB, c.Param(&quot;id&quot;), c.UserID) if err != nil { c.FlashError(err) } else { c.FlashNotice(&quot;Item deleted.&quot;) } c.Redirect(uri) } </code></pre> <h3 id="model-5">Model</h3> <pre><code class="language-go">// DeleteHard removes an item. func DeleteHard(db Connection, ID string, userID string) (sql.Result, error) { result, err := db.Exec(fmt.Sprintf(` DELETE FROM %v WHERE id = ? AND user_id = ? AND deleted_at IS NULL `, table), ID, userID) return result, err } </code></pre> <h2 id="postgresql-support">PostgreSQL Support</h2> <p>To use PostgreSQL, you need to a few lines of code. The migration support is not built into <a href="https://github.com/blue-jay/jay">Jay</a> yet so you&rsquo;ll have to run them manually.</p> <p>Add the PostgreSQL structure to the env.json.example file:</p> <pre><code class="language-json">&quot;PostgreSQL&quot;:{ &quot;Username&quot;:&quot;root&quot;, &quot;Password&quot;:&quot;&quot;, &quot;Database&quot;:&quot;blueprint&quot;, &quot;Hostname&quot;:&quot;127.0.0.1&quot;, &quot;Port&quot;:5432, &quot;Parameter&quot;:&quot;&quot;, &quot;MigrationFolder&quot;:&quot;migration/postgresql&quot;, &quot;Extension&quot;:&quot;sql&quot; }, </code></pre> <p>Ensure the JSON is readable by to the <strong>Info</strong> struct in <strong>lib/env</strong> so add this line:</p> <pre><code class="language-go">PostgreSQL postgresql.Info `json:&quot;PostgreSQL&quot;` </code></pre> <p>Add these lines to the <strong>RegisterServices()</strong> function in <strong>lib/boot</strong>:</p> <pre><code class="language-go">// Connect to the PostgreSQL database postgresqldb, _ := config.PostgreSQL.Connect(true) </code></pre> <p>If you want to replace MySQL, you can change the line at the bottom of the <strong>RegisterServices()</strong> function in <strong>lib/boot</strong> to pass PostgreSQL instead of MySQL:</p> <pre><code class="language-go">// Store the database connection in flight flight.StoreDB(postgresqldb) </code></pre> <p>If you want to add instead of replacing MySQL, you need to: - Add a new function to the <strong>lib/flight</strong> package similar to <strong>StoreDB()</strong> - And a new line to the <strong>Info</strong> struct in the <strong>lib/flight</strong> - Add the new variable to the <strong>Context()</strong> function in <strong>lib/flight</strong></p> Views https://blue-jay.github.io/views/ Tue, 08 Mar 2016 21:07:13 +0100 https://blue-jay.github.io/views/ <h2 id="basic-usage">Basic Usage</h2> <p>Views contain the HTML served by your application and separate your controller/application logic from your presentation logic. The views are parsed by the <a href="https://golang.org/pkg/html/template/">html/template</a> package.</p> <p>A view should include the four <strong>define</strong> blocks (<strong>title</strong>, <strong>head</strong>, <strong>content</strong>, and <strong>foot</strong>) and may look like this:</p> <pre><code class="language-html">{{define &quot;title&quot;}}About Blueprint{{end}} {{define &quot;head&quot;}}{{end}} {{define &quot;content&quot;}} &lt;div class=&quot;page-header&quot;&gt; &lt;h1&gt;{{template &quot;title&quot; .}}&lt;/h1&gt; &lt;/div&gt; &lt;p&gt;Blueprint lays the foundation for your web application using the Go language.&lt;/p&gt; {{template &quot;footer&quot; .}} {{end}} {{define &quot;foot&quot;}}{{end}} </code></pre> <p>Since this view is stored at <strong>view/about/index.tmpl</strong>, we render it using the <strong>view</strong> helper package like so:</p> <pre><code class="language-go">// import &quot;github.com/blue-jay/core/view&quot; c := flight.Context(w, r) v := c.View.New(&quot;about/index&quot;) // Variables would go here like this: v.Vars[&quot;first_name&quot;] = session.Values[&quot;first_name&quot;] v.Render(w, r) </code></pre> <p>If you don&rsquo;t have to pass any variables to the template, you could shorten it like this:</p> <pre><code class="language-go">// import &quot;github.com/blue-jay/core/view&quot; c := flight.Context(w, r) c.View.New(&quot;about/index&quot;).Render(w, r) </code></pre> <h2 id="base-template">Base Template</h2> <p>By default, the <strong>view/base.tmpl</strong> template is used as the base template (as specified in env.json). If you want to change the base template for a template, you can try this:</p> <pre><code class="language-go">c := flight.Context(w, r) v := c.View.New(&quot;about/index&quot;).Base(&quot;alternate&quot;) v.Render(w, r) </code></pre> <p>A shorter way to specify the view with a different base template and then render is like this:</p> <pre><code class="language-go">c := flight.Context(w, r) c.View.New(&quot;about/about&quot;).Base(&quot;alternate&quot;).Render(w, r) </code></pre> <h2 id="view-package">View Package</h2> <p>The <strong>core/view</strong> package is a wrapper for the Go <a href="https://golang.org/pkg/html/template/">html/template</a> package and provides the following:</p> <ul> <li>thread-safe template caching</li> <li>ability to extend the list of functions available in templates</li> <li>ability to modify the variables available in templates</li> </ul> <p>The set up of the <strong>view</strong> package is handled by the <strong>lib/boot</strong> package. The config is then passed to <code>flight</code> so it can be accessed by controllers.</p> <pre><code class="language-go">// Set up the views config.View.SetTemplates(config.Template.Root, config.Template.Children) // Set up the functions for the views config.View.SetFuncMaps( config.Asset.Map(config.View.BaseURI), link.Map(config.View.BaseURI), noescape.Map(), prettytime.Map(), form.Map(), ) // Set up the variables and modifiers for the views config.View.SetModifiers( authlevel.Modify, uri.Modify, xsrf.Token, flash.Modify, ) // Store the variables in flight flight.StoreConfig(*config) </code></pre> <h2 id="organization">Organization</h2> <p>The HTML templates are organized in folders under the <strong>view</strong> folder:</p> <pre><code class="language-text">about/index.tmpl - quick blurb about the app home/index.tmpl - public and authenticated home page login/index.tmpl - login page note/create.tmpl - create a note note/edit.tmpl - edit a note note/index.tmpl - view all notes note/show.tmpl - view a note partial/favicon.tmpl - favicon metadata generated by gulpfile.js partial/footer.tmpl - footer at the bottom of all pages partial/menu.tmpl - menu at the top of all pages register/index.tmpl - register page base.tmpl - base template for all pages </code></pre> <h2 id="view-functions">View Functions</h2> <p>The Go template packages supports passing a FuncMap which maps names to a function. This means you can add functions so they are available to the views. These functions are stored in the <strong>viewfunc</strong> folder. Here is an example of a <strong>LINK</strong> function that can be used to create hyperlinks and the code is stored in <strong>viewfunc/link/link.go</strong>:</p> <pre><code class="language-go">// Package link provides a funcmap for html/template to generate a hyperlink. package link import ( &quot;fmt&quot; &quot;html/template&quot; ) // Map returns a template.FuncMap for LINK that returns a hyperlink tag. func Map(baseURI string) template.FuncMap { f := make(template.FuncMap) f[&quot;LINK&quot;] = func(path, name string) template.HTML { return template.HTML(fmt.Sprintf(`&lt;a href=&quot;%v%v&quot;&gt;%v&lt;/a&gt;`, baseURI, path, name)) } return f } </code></pre> <p>To use this function in a template, you would write it like this:</p> <pre><code class="language-html">{{LINK &quot;register&quot; &quot;Create a new account.&quot;}} </code></pre> <p>And the code would render like this:</p> <pre><code class="language-html">&lt;a href=&quot;/register&quot;&gt;Create a new account.&lt;/a&gt; </code></pre> <p>Once you create a new funcmap, you make it available to the views by adding it to the <strong>view.SetFuncMaps()</strong> function in the <strong>lib/boot/boot.go</strong> file:</p> <pre><code class="language-go">// Set up the functions for the views config.View.SetFuncMaps( config.Asset.Map(config.View.BaseURI), link.Map(config.View.BaseURI), noescape.Map(), prettytime.Map(), form.Map(), ) </code></pre> <h2 id="included-functions">Included Functions</h2> <p>There are a few functions that are included to make working with the templates and static files easier:</p> <pre><code class="language-html">&lt;!-- CSS file with media attribute --&gt; {{CSS &quot;static/css/normalize3.0.0.min.css&quot; &quot;all&quot;}} &lt;!-- parses with timestamp to --&gt; &lt;link media=&quot;all&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/css/normalize3.0.0.min.css?1435528339&quot; /&gt; &lt;!-- JS file --&gt; {{JS &quot;static/js/jquery1.11.0.min.js&quot;}} &lt;!-- parses with timestamp to --&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;/static/js/jquery1.11.0.min.js?1435528404&quot;&gt;&lt;/script&gt; &lt;!-- Hyperlinks --&gt; {{LINK &quot;register&quot; &quot;Create a new account.&quot;}} &lt;!-- parses to --&gt; &lt;a href=&quot;/register&quot;&gt;Create a new account.&lt;/a&gt; &lt;!-- Output an unescaped variable (not a safe idea, but it is useful when troubleshooting) --&gt; {{.SomeVariable | NOESCAPE}} &lt;!-- Time format for mysql.NullTime --&gt; {{NULLTIME .SomeTime}} &lt;!-- parses to format --&gt; 3:04 PM 01/02/2006 &lt;!-- Time format helper for mysql.NullTime that shows the latest of the two variables --&gt; {{PRETTYTIME .CreatedAt .UpdatedAt}} &lt;!-- parses to format --&gt; 3:04 PM 01/02/2006 </code></pre> <h2 id="view-variables">View Variables</h2> <p>There is an easy way to add variables so they are available in the views. The <strong>viewmodify</strong> folder contains packages that define variables in the view.Vars map. Since you are editing the map right before it renders, it will overwrite any other variables that were set in the controllers so it&rsquo;s best to choose names or pick a naming convention for your variables.</p> <p>You can also modify the view.Info struct before it renders if you need to display a different view or use a different base template.</p> <p>In the <strong>viewmodify/authlevel/authlevel.go</strong> file, the <strong>AuthLevel</strong> variable is made available so the views can determine if the user is authenticated or not:</p> <p><a href="https://github.com/blue-jay/blueprint/blob/master/viewmodify/authlevel/authlevel.go">Source</a></p> <pre><code class="language-go">// Package authlevel adds an AuthLevel variable to the view template. package authlevel import ( &quot;net/http&quot; &quot;github.com/blue-jay/blueprint/lib/flight&quot; &quot;github.com/blue-jay/core/view&quot; ) // Modify sets AuthLevel in the template to auth if the user is authenticated. // Sets AuthLevel to anon if not authenticated. func Modify(w http.ResponseWriter, r *http.Request, v *view.Info) { c := flight.Context(w, r) // Set the AuthLevel to auth if the user is logged in if c.Sess.Values[&quot;id&quot;] != nil { v.Vars[&quot;AuthLevel&quot;] = &quot;auth&quot; } else { v.Vars[&quot;AuthLevel&quot;] = &quot;anon&quot; } } </code></pre> <p>To use the variable, you could write this type of logic into your view:</p> <pre><code class="language-html">{{if eq .AuthLevel &quot;auth&quot;}} You are logged in. {{else}} You are not logged in. {{end}} </code></pre> <p>Once you create a new package, you make it available to the views by adding it to the <strong>view.SetModifiers()</strong> function in the <strong>lib/boot/boot.go</strong> file:</p> <pre><code class="language-go">// Set up the variables and modifiers for the views config.View.SetModifiers( authlevel.Modify, uri.Modify, xsrf.Token, flash.Modify, ) </code></pre> <h2 id="included-variables">Included Variables</h2> <p>There are a few included variables you can use in templates:</p> <pre><code class="language-html">&lt;!-- Use AuthLevel=auth to determine if a user is logged in (if session.Values[&quot;id&quot;] != nil) --&gt; {{if eq .AuthLevel &quot;auth&quot;}} You are logged in. {{else}} You are not logged in. {{end}} &lt;!-- Use BaseURI to print the base URL specified in the env.json file, ends in slash --&gt; &lt;li&gt;&lt;a href=&quot;{{.BaseURI}}about&quot;&gt;About&lt;/a&gt;&lt;/li&gt; &lt;!-- Use CurrentURI to print the current URL, does not end in slash --&gt; &lt;li&gt;&lt;a href=&quot;{{.CurrentURI}}&quot;&gt;Current Page&lt;/a&gt;&lt;/li&gt; &lt;!-- Use ParentURI to print the URL up one level, does not end in slash --&gt; &lt;li&gt;&lt;a href=&quot;{{.ParentURI}}&quot;&gt;Parent Page&lt;/a&gt;&lt;/li&gt; &lt;!-- Use GrandparentURI to print the URL up two levels, does not end in slash --&gt; &lt;li&gt;&lt;a href=&quot;{{.GrandparentURI}}&quot;&gt;Grandparent Page&lt;/a&gt;&lt;/li&gt; &lt;!-- Use token to output the CSRF token in a form --&gt; &lt;input type=&quot;hidden&quot; name=&quot;token&quot; value=&quot;{{.token}}&quot;&gt; </code></pre> <h2 id="repopulate-form-fields">Repopulate Form Fields</h2> <p>When a form is submitted and there are errors like when a required field is missing, the same web page should reload. Unfortunately, forms are not refilled so there are a few helpers from the <strong>form</strong> package that will help you refill, select, and check.</p> <p>These functions actually do both form population (filling in a form from a database record) and form repopulation (filling in a form from a form submission where something went wrong like a required field was left blank). These functions will fill the form with values from the form submission over the values from the database record. If you refresh the page, the form submission values will be forgotten and the values from the database record will return.</p> <p>For instance, say your form has two fields: name and age. Both are required fields. You type in your name, John Doe, forget to type in your age, and click the submit button. The page will reload, show an error message, and the functions will automatically refill the name field with your name. The record has not been saved in the database, but the name field is remembered from the form submission (form repopulation). You enter in your age as 30, click the submit button, and the record is successfully added to the database.</p> <p>Let&rsquo;s say you want to edit the database record and you want to enter in a new age. You load the edit page and the functions will automatically refill the name field and the age field with the values from the database (form population). You try to be sneaky and change your age to the word &ldquo;thirty&rdquo;, instead of the number 30. You click the submit button, the page reloads, shows an error message, and refills the name field and the age. The age field will be filled with the word &ldquo;thirty&rdquo; because the form submission values take priority over the values from the database. If you clicked the refresh button in your browser, the age field would then load the number 30 from the database instead.</p> <p>There are two views that benefit from these functions: create.tmpl and edit.tmpl. You don&rsquo;t have to use these names, but it&rsquo;s just an example to show you these functions will primarily be used in the the template to create a new record in the database (create.tmpl) and the template to edit an existing record in the database (edit.tmpl).</p> <p>In the create.tmpl view, there may be required fields. If the form requires typing in a long block of text for a couple answers and the user forgets one of the required fields, when the page reloads, all the text will not be there. These functions will refill, select, and check all the form inputs specified in the controller. The create.tmpl does not use the &ldquo;default value&rdquo; field in these functions so you can set them to an empty string (&ldquo;&rdquo;) or set them to a view variable so you can copy and paste the same code between create.tmpl and edit.tmpl. The edit.tmpl view requires the same logic, but also needs the &ldquo;default value&rdquo; to prefill, select, and check the form values from view variables (which will probably be pulled from a model struct).</p> <p>Check out the <a href="https://blue-jay.github.io/controllers/#repopulate-form-fields">Repopulate Form Fields</a> section on the Controllers page. It will show you the single line of code needed in your controller so these fields repopulate on form submission.</p> <p>These are the functions/blocks to use in your templates. Notice that some of the HTML attributes are missing like <strong>name</strong>, <strong>value</strong>, and <strong>type</strong> from the elements. The blocks will automatically fill these in for you so you don&rsquo;t have the write the name of the element multiple times. By the way, it took very little code to add this functionality so check out the <a href="https://github.com/blue-jay/core/blob/master/form/form.go">form</a> package to see how it was accomplished.</p> <pre><code class="language-html">&lt;!-- TEXT accepts the element name, a default value, and then a period --&gt; &lt;input {{TEXT &quot;email&quot; .item.Email .}} type=&quot;email&quot; class=&quot;form-control&quot; id=&quot;email&quot; /&gt; &lt;!-- then parses to a name attribute when no repopulation value is passed --&gt; &lt;input name=&quot;email&quot; type=&quot;email&quot; class=&quot;form-control&quot; id=&quot;email&quot; /&gt; &lt;!-- and parses to a name and a value when a repopulation value is passed --&gt; &lt;input name=&quot;email&quot; value=&quot;[email protected]&quot; type=&quot;email&quot; class=&quot;form-control&quot; id=&quot;email&quot; /&gt; &lt;!-- TEXTAREA accepts the element name, a default value, and then a period --&gt; &lt;textarea rows=&quot;5&quot; class=&quot;form-control&quot; id=&quot;name&quot; name=&quot;name&quot; /&gt;{{TEXTAREA &quot;name&quot; .item.Name .}}&lt;/textarea&gt; &lt;!-- then parses to nothing when no repopulation value is passed --&gt; &lt;textarea rows=&quot;5&quot; class=&quot;form-control&quot; id=&quot;name&quot; name=&quot;name&quot; /&gt;&lt;/textarea&gt; &lt;!-- and parses to a value when a repopulation value is passed --&gt; &lt;textarea rows=&quot;5&quot; class=&quot;form-control&quot; id=&quot;name&quot; name=&quot;name&quot; /&gt;Sample text&lt;/textarea&gt; &lt;!-- CHECKBOX accepts the element name, value, default value, and then a period --&gt; &lt;label&gt;&lt;input {{CHECKBOX &quot;rememberme&quot; &quot;r1&quot; .item.RememberMe .}}&gt; Remember me&lt;/label&gt; &lt;!-- then parses to a type, name, and value attribute when no repopulation value is passed --&gt; &lt;label&gt;&lt;input type=&quot;checkbox&quot; name=&quot;rememberme&quot; value=&quot;r1&quot;&gt; Remember me&lt;/label&gt; &lt;!-- and parses to a type, name, value, and the word 'checked' when a repopulation value is passed --&gt; &lt;label&gt;&lt;input type=&quot;checkbox&quot; name=&quot;rememberme&quot; value=&quot;r1&quot; checked&gt; Remember me&lt;/label&gt; &lt;!-- RADIO accepts the element name, value, default value, and then a period --&gt; &lt;label&gt;&lt;input {{RADIO &quot;options&quot; &quot;burger&quot; .item.Option .}}&gt; Burger&lt;/label&gt; &lt;!-- then parses to a type, name, and value attribute when no repopulation value is passed --&gt; &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;options&quot; value=&quot;burger&quot;&gt; Burger&lt;/label&gt; &lt;!-- and parses to a type, name, value attribute, and the word 'checked' when a repopulation value is passed --&gt; &lt;label&gt;&lt;input type=&quot;radio&quot; name=&quot;options&quot; value=&quot;burger&quot; checked&gt; Burger&lt;/label&gt; &lt;!-- OPTION accepts the element name, value, default value, and then a period --&gt; &lt;select name=&quot;select&quot;&gt;&lt;option {{OPTION &quot;select&quot; &quot;Apple&quot; .item.Select .}}&gt;Apple&lt;/option&gt;&lt;/select&gt; &lt;!-- then parses to a value attribute when no repopulation value is passed --&gt; &lt;select name=&quot;select&quot;&gt;&lt;option value=&quot;Apple&quot;&gt;Apple&lt;/option&gt;&lt;/select&gt; &lt;!-- and parses to a value attribute and the word 'selected' when a repopulation value is passed --&gt; &lt;select name=&quot;select&quot;&gt;&lt;option value=&quot;Apple&quot; selected&gt;Apple&lt;/option&gt;&lt;/select&gt; </code></pre> <p>Here are examples of all the fields with the Bootrap structure and classes:</p> <pre><code class="language-html">&lt;div class=&quot;form-group&quot;&gt; &lt;label for=&quot;email&quot;&gt;Email Address&lt;/label&gt; &lt;div&gt;&lt;input {{TEXT &quot;email&quot; .item.Email .}} type=&quot;email&quot; class=&quot;form-control&quot; id=&quot;email&quot; maxlength=&quot;48&quot; placeholder=&quot;Email&quot; /&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;label for=&quot;name&quot;&gt;Item&lt;/label&gt; &lt;div&gt;&lt;textarea rows=&quot;5&quot; class=&quot;form-control&quot; id=&quot;name&quot; name=&quot;name&quot; placeholder=&quot;Type your text here...&quot; /&gt;{{TEXTAREA &quot;name&quot; .}}&lt;/textarea&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;checkbox&quot;&gt; &lt;label&gt; &lt;input {{CHECKBOX &quot;rememberme&quot; &quot;r1&quot; .item.RememberMe .}}&gt; Remember me &lt;/label&gt; &lt;/div&gt; &lt;div class=&quot;checkbox&quot;&gt; &lt;label&gt; &lt;input {{CHECKBOX &quot;rememberme&quot; &quot;r2&quot; .item.RememberMe .}}&gt; Remember me &lt;/label&gt; &lt;/div&gt; &lt;div class=&quot;radio&quot;&gt; &lt;label&gt; &lt;input {{RADIO &quot;options&quot; &quot;burger&quot; .item.Option .}}&gt; Burger &lt;/label&gt; &lt;/div&gt; &lt;div class=&quot;radio&quot;&gt; &lt;label&gt; &lt;input {{RADIO &quot;options&quot; &quot;taco&quot; .item.Option .}}&gt; Taco &lt;/label&gt; &lt;/div&gt; &lt;select class=&quot;form-control&quot; name=&quot;select&quot;&gt; &lt;option {{OPTION &quot;select&quot; &quot;Apple&quot; .item.Select .}}&gt;Apple&lt;/option&gt; &lt;option {{OPTION &quot;select&quot; &quot;Banana&quot; .item.Select .}}&gt;Banana&lt;/option&gt; &lt;option {{OPTION &quot;select&quot; &quot;cherry&quot; .item.Select .}}&gt;Cherry&lt;/option&gt; &lt;/select&gt; &lt;select multiple class=&quot;form-control&quot; name=&quot;mselect&quot;&gt; &lt;option {{OPTION &quot;mselect&quot; &quot;red&quot; .item.Select .}}&gt;Red&lt;/option&gt; &lt;option {{OPTION &quot;mselect&quot; &quot;green&quot; .item.Select .}}&gt;Green&lt;/option&gt; &lt;option {{OPTION &quot;mselect&quot; &quot;blue&quot; .item.Select .}}&gt;Blue&lt;/option&gt; &lt;/select&gt; </code></pre> <h2 id="change-http-methods">Change HTTP Methods</h2> <p>When you submit a form a a website, the site most likely sends a POST request to the server. In order for us to make our application more RESTful, we can use utilize the simple <a href="https://github.com/blue-jay/blueprint/blob/master/middleware/rest/rest.go"><strong>rest</strong></a> package to change the HTTP method from a URL query string. The <strong>rest</strong> middleware is already applied to every request which is configured in the <strong>boot</strong> package.</p> <p>To change the form method, add this line to your form action and change the value to match a method like <strong>DELETE</strong> or <strong>PATCH</strong>. It will automatically be converted to uppercase.</p> <p>The query string key should be: <strong>_method</strong>.</p> <pre><code class="language-html">&lt;!-- Example of a PATCH request --&gt; &lt;form method=&quot;post&quot; action=&quot;{{$.CurrentURI}}?_method=patch&quot;&gt; &lt;!-- Example of a DELETE request --&gt; &lt;form class=&quot;button-form&quot; method=&quot;post&quot; action=&quot;{{$.GrandparentURI}}/{{.item.ID}}?_method=delete&quot;&gt; </code></pre> <p>This is an example of a form that is updating and email and a password using the PATCH HTTP method:</p> <pre><code class="language-html">&lt;form method=&quot;post&quot; action=&quot;{{$.CurrentURI}}?_method=patch&quot;&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;label for=&quot;email&quot;&gt;Email Address&lt;/label&gt; &lt;div&gt;&lt;input {{TEXT &quot;email&quot; .item.Email .}} type=&quot;email&quot; class=&quot;form-control&quot; id=&quot;email&quot; /&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt; &lt;div&gt;&lt;input {{TEXT &quot;password&quot; .item.Password .}} type=&quot;password&quot; class=&quot;form-control&quot; id=&quot;password&quot; /&gt;&lt;/div&gt; &lt;/div&gt; &lt;input type=&quot;submit&quot; class=&quot;btn btn-primary&quot; value=&quot;Change&quot; class=&quot;button&quot; /&gt; &lt;input type=&quot;hidden&quot; name=&quot;_token&quot; value=&quot;{{$.token}}&quot;&gt; &lt;/form&gt; </code></pre> <p>The routes for this page would look something like this:</p> <pre><code class="language-go">// Display the Update Page - typical GET HTTP method router.Get(&quot;/user/edit/:id&quot;, Edit, c...) // Handle the Update Page Form Submissions - PATCH HTTP method router.Patch(&quot;user/edit/:id&quot;, Update, c...) </code></pre> <h2 id="header-and-footer">Header and Footer</h2> <p>It&rsquo;s also easy to add template-specific code before the closing </head> and </body> tags:</p> <pre><code class="language-html">&lt;!-- Code is added before the closing &lt;/head&gt; tag --&gt; {{define &quot;head&quot;}}&lt;meta name=&quot;robots&quot; content=&quot;noindex&quot;&gt;{{end}} ... &lt;!-- Code is added before the closing &lt;/body&gt; tag --&gt; {{define &quot;foot&quot;}}{{JS &quot;//www.google.com/recaptcha/api.js&quot;}}{{end}} </code></pre> <h2 id="javascript">JavaScript</h2> <p>There are a few built-in functions that you can use to trigger a flash notification using JavaScript.</p> <pre><code class="language-javascript">flashError(&quot;An error occurred on the server.&quot;); flashSuccess(&quot;Item added!&quot;); flashNotice(&quot;Item deleted.&quot;); flashWarning(&quot;Field missing: email&quot;); </code></pre> Library https://blue-jay.github.io/library/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/library/ <h2 id="basic-usage">Basic Usage</h2> <p>If you look through the <a href="https://github.com/blue-jay/core"><strong>Core</strong></a> library, you&rsquo;ll see packages that provide different functionality. These packages are designed to be shared and used through the application. Since Blueprint is a web application, some of the packages must be thread-safe to ensure they won&rsquo;t throw any errors if they are accessed at the same time by two separate threads.</p> <p>As you create your own packages, you can store them in the <strong>blueprint/lib</strong> folder to keep them organized and separate.</p> <p>It&rsquo;s a good idea to keep them light on dependencies so you can reuse them again in later projects. Packages that are too tightly-coupled require time to rework so you might as well do it right from the start.</p> <p>Let&rsquo;s take a look at the <strong>email</strong> package in sections. The first of the three sections is the package declaration and import section. Notice how the only packages the <strong>email</strong> package uses are part of the standard library. This is a good indicator that the package could be moved to a different project without rewriting.</p> <p><a href="https://github.com/blue-jay/core/blob/master/email/email.go">Source</a></p> <pre><code class="language-go">// Package email provides email sending via SMTP. package email import ( &quot;encoding/base64&quot; &quot;fmt&quot; &quot;net/smtp&quot; ) </code></pre> <p>The Info struct holds the details for the SMTP server and it should be added to the <strong>Info</strong> struct of the <strong>lib/env</strong> package and then parsed from the env.json file. See the <a href="https://blue-jay.github.io/configuration/">Configuration</a> section for more information about adding to env.json.</p> <pre><code class="language-go">// Info holds the details for the SMTP server. type Info struct { Username string Password string Hostname string Port int From string } </code></pre> <p>The final section of the <strong>email</strong> package sends an email using the configuration settings. You&rsquo;ll notice the function is a struct method which makes it really easy to use from the <strong>flight</strong> package.</p> <pre><code class="language-go">// Send an email. func (c Info) Send(to, subject, body string) error { auth := smtp.PlainAuth(&quot;&quot;, c.Username, c.Password, c.Hostname) // Create the header header := make(map[string]string) header[&quot;From&quot;] = c.From header[&quot;To&quot;] = to header[&quot;Subject&quot;] = subject header[&quot;MIME-Version&quot;] = &quot;1.0&quot; header[&quot;Content-Type&quot;] = `text/plain; charset=&quot;utf-8&quot;` header[&quot;Content-Transfer-Encoding&quot;] = &quot;base64&quot; // Set the message message := &quot;&quot; for k, v := range header { message += fmt.Sprintf(&quot;%s: %s\r\n&quot;, k, v) } message += &quot;\r\n&quot; + base64.StdEncoding.EncodeToString([]byte(body)) // Send the email err := smtp.SendMail( fmt.Sprintf(&quot;%s:%d&quot;, c.Hostname, c.Port), auth, c.From, []string{to}, []byte(message), ) return err } </code></pre> <p>To use in a controller, you would get the context from the <strong>flight</strong> package and then from the <strong>Config</strong> variable, you could access the <strong>email</strong> struct and then the <code>Send()</code> function.</p> <pre><code class="language-go">// Index sends an email. func Index(w http.ResponseWriter, r *http.Request) { c := flight.Context(w, r) err := c.Config.Email.Send(&quot;This is the subject&quot;, &quot;This is the body!&quot;) if err != nil { c.FlashError(err) return } } </code></pre> <h2 id="code-generation">Code Generation</h2> <p>A new <strong>lib</strong> package can be generated using this command: <code>jay generate lib/default package:value</code></p> Jay Overview https://blue-jay.github.io/jay/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/jay/ <h2 id="basic-usage">Basic Usage</h2> <p>The command-line tool, <strong>Jay</strong>, has been mentioned throughout this documentation quite a few times now so it&rsquo;s a good time to talk a little about the tool.</p> <p><strong>Jay</strong> uses the brilliant <a href="github.com/alecthomas/kingpin">github.com/alecthomas/kingpin</a> package to manage the help documents, commands, subcommands, and arguments. It takes care of groundwork so it can focus on the actual tasks.</p> <p>If you ever want to see the help documents for a command or subcommand, add <code>-h</code> or <code>--help</code> to the end and the help documentation should be able to assist. If you are still having problems, check out the <a href="https://godoc.org/github.com/blue-jay/jay">Jay GoDoc</a>.</p> <p>When using <strong>Jay</strong>, there are flags, commands, and subcommands.</p> <p>The available flags throughout are:</p> <ul> <li><code>-h</code> or <code>--help</code> for help documentation</li> <li><code>-v</code> or <code>--version</code> for version information</li> <li><code>-c</code> or <code>--config</code> to specify the env.json config file (otherwise JAYCONFIG environment variable will be used)</li> </ul> <p>The available commands are:</p> <ul> <li><code>jay env</code> for managing the env.json file</li> <li><code>jay find</code> for locating text inside files in subfolders</li> <li><code>jay replace</code> for replacing text inside files in subfolder</li> <li><code>jay generate</code> for creating code based on templates using the <strong>text/template</strong> package</li> <li><code>jay migrate:mysql</code> for managing the MySQL database state</li> </ul> <p>The available subcommands are:</p> <ul> <li><code>jay env make</code> for creating env.json from env.json.example</li> <li><code>jay env keyshow</code> for showing newly generated session keys</li> <li><code>jay env updateshow</code> for updating env.json with newly generation session keys</li> <li><code>jay migrate:mysql make</code> for creating a new migration &lsquo;up&rsquo; file and &lsquo;down&rsquo; file</li> <li><code>jay migrate:mysql all</code> for applying all &lsquo;up&rsquo; migrations</li> <li><code>jay migrate:mysql reset</code> for applying all &lsquo;down&rsquo; migrations</li> <li><code>jay migrate:mysql refresh</code> for applying all &lsquo;down&rsquo; then &lsquo;up&rsquo; migrations</li> <li><code>jay migrate:mysql status</code> for displaying the current database state</li> <li><code>jay migrate:mysql up</code> for applying only one &lsquo;up&rsquo; migration</li> <li><code>jay migrate:mysql down</code> for applying one one &lsquo;down&rsquo; migration</li> </ul> <p>There is also a common syntax used by each of the commands, subcommands, and arguments that make the help documents easy to follow.</p> <ul> <li>Flags have one or two dashes in the front: <code>-h, --help</code></li> <li>Commands follow the application name (jay): <code>jay migrate:mysql</code></li> <li>Subcommands follow the command: <code>jay migrate:mysql make</code></li> <li>Arguments follow the command or subcommand: <code>jay migrate:mysql make &lt;description&gt;</code></li> <li>Required arguments: <code>&lt;required&gt;</code></li> <li>Optional arguments: <code>[&lt;optional&gt;]</code></li> </ul> Find and Replace https://blue-jay.github.io/find-replace/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/find-replace/ <h2 id="basic-usage">Basic Usage</h2> <p><strong>Jay</strong> includes the ability to do a case-sensitive find and replace. This is great when you have many files to look through and need simple way to find and replace quickly.</p> <h2 id="find-usage">Find Usage</h2> <pre><code class="language-bash">usage: jay find &lt;folder&gt; &lt;text&gt; [&lt;extension&gt;] [&lt;recursive&gt;] [&lt;filename&gt;] Search for files containing matching text. Flags: -h, --help Show context-sensitive help (also try --help-long and --help-man). -v, --version Show application version. Args: &lt;folder&gt; Folder to search &lt;text&gt; Case-sensitive text to find. [&lt;extension&gt;] File name or extension to search in. Use * as a wildcard. Directory names are not valid. [&lt;recursive&gt;] True to search in subfolders. Default: true [&lt;filename&gt;] True to include file path in results if matched. Default: false </code></pre> <h2 id="replace-usage">Replace Usage</h2> <pre><code class="language-bash">usage: jay replace &lt;folder&gt; &lt;find&gt; [&lt;replace&gt;] [&lt;extension&gt;] [&lt;recursive&gt;] [&lt;filename&gt;] [&lt;commit&gt;] Search for files containing matching text and then replace it with new text. Flags: -h, --help Show context-sensitive help (also try --help-long and --help-man). -v, --version Show application version. Args: &lt;folder&gt; Folder to search &lt;find&gt; Case-sensitive text to replace. [&lt;replace&gt;] Text to replace with. [&lt;extension&gt;] File name or extension to search in. Use * as a wildcard. Directory names are not valid. [&lt;recursive&gt;] True to search in subfolders. Default: true [&lt;filename&gt;] True to include file path in results if matched. Default: false [&lt;commit&gt;] True to makes the changes instead of just displaying them. Default: true </code></pre> <h2 id="find-and-replace-examples">Find and Replace Examples</h2> <p>Here are examples are how to use find and replace:</p> <pre><code class="language-bash"># Find the word &quot;red&quot; in all *.go files in the current folder and in subfolders. jay find . red # Find the word &quot;red&quot; in all files in the current folder only. jay find . red &quot;*.*&quot; false # Find the word &quot;red&quot; in *.go files in current folder and in subfolders and # include file paths that match also. jay find . red &quot;*.go&quot; true true # Replace the word &quot;red&quot; with the word &quot;blue&quot; in all *.go files in the current # folder and in subfolders. jay replace . red blue # Replace the word &quot;red&quot; with the word &quot;blue&quot; in all *.go files in current # folder only. jay replace . red blue &quot;*.go&quot; false # Change the name of the project in current folder and in subfolders and all # imports to another repository. jay replace . &quot;blue-jay/blueprint&quot; &quot;user/project&quot; </code></pre> Database Migration https://blue-jay.github.io/database-migration/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/database-migration/ <h2 id="basic-usage">Basic Usage</h2> <p>Database migrations are a great way to manage incremental database changes. The migration state is stored in the same database (the one specified in env.json) and recorded in the <strong>migration</strong> table. This is how the <code>jay migrate:mysql status</code>command knows which migration was performed last.</p> <p>Each incremental change should have a set of two files: an &lsquo;up&rsquo; file and a &lsquo;down&rsquo; file. The &lsquo;up&rsquo; file contains code (like SQL) which is applied to the database you add features or fix bugs in your database. The &lsquo;down&rsquo; file contains the code to remove the change or undo it.</p> <p>More than one migration can now be run on a database. You just have to specify a different table value in the env.json file under the MySQL.Migration.Table key.</p> <p><strong>Note 1</strong>: The <code>jay migrate</code> commands requires the environment variable, <strong>JAYCONFIG</strong>, to point to the env.json file path. The migration folder is specified in the env.json file under <strong>MySQL.Migration.Folder</strong>. If the environment variable is not set, you can specify the &ndash;config or -c flag and pass the path to env.json.</p> <p><strong>Note 2</strong>: Make sure you do not create any migrations with a date earlier than any applied migrations. Jay will not go back and apply them. They must be created sequentially.</p> <p>When you start using <strong>Blueprint</strong> you&rsquo;ll see two migration files already exist in the <strong>migration/mysql</strong> folder:</p> <ul> <li>20160630_020000.000000_init.up.sql - adds the initial tables and data</li> <li>20160630_020000.000000_init.down.sql - removes the tables and data</li> </ul> <p><strong>Jay</strong> provides a few commands to make the migrations easier. Here is a list of the commands:</p> <pre><code class="language-bash">jay migrate:mysql make &quot;test&quot; # Create new migration jay migrate:mysql all # Advance all migrations jay migrate:mysql reset # Rollback all migrations jay migrate:mysql refresh # Rollback all migrations then advance all migrations jay migrate:mysql status # See last 'up' migration jay migrate:mysql up # Apply only the next 'up' migration jay migrate:mysql down # Apply only the current 'down' migration </code></pre> <h2 id="workflow">Workflow</h2> <p>Let&rsquo;s walk through an typical workflow. Assume the <strong>blueprint</strong> folder is the root of a git repository. Developer Joe needs to add a feature to the application to store books. Joe creates a new branch called feature-store-books. He needs to create a new table called <strong>book</strong> so he creates a new database migration: <code>jay migrate:mysql make &quot;feature-store-books&quot;</code>. He opens the &lsquo;up&rsquo; file and adds the SQL query to create a new table with the following columns: title, author, and publication_date. In the &lsquo;down&rsquo; file, he writes the code to drop the table. He also writes the code to create, read, update, and delete the books in his Go code and then performs a <code>git merge</code> and then a <code>git push</code>. Developer Steve needs to add an additional column to the <strong>book</strong> table called: publisher. Steve performs a <code>git pull</code> to download the latest code from the repository. In order to update his local database to the latest version of code, runs: <code>jay migrate:mysql all</code>. Then, Steve creates a new migration for the new requirement: <code>jay migrate:mysql make &quot;feature-add-publisher&quot;</code>. Steve adds the SQL <code>ADD COLUMN</code> code to the new &lsquo;up&rsquo; file and the SQL <code>DROP COLUMN</code> code to the new &lsquo;down&rsquo; file and then performs a <code>git merge</code> and a <code>git push</code> again.</p> <h2 id="migrate-make">migrate make</h2> <p>The <code>jay migrate:mysql make [description]</code> command create a new &lsquo;up&rsquo; and &lsquo;down&rsquo; migration in the <strong>database/migration</strong> folder.</p> <h2 id="migrate-all">migrate all</h2> <p>The <code>jay migrate:mysql all</code> command finds the current status of the database and then applies all the code from each of the &lsquo;up&rsquo; files in chronological order based on filename.</p> <h2 id="migrate-reset">migrate reset</h2> <p>The <code>jay migrate:mysql reset</code> command finds the current status of the database and then applies all the code from each of the &lsquo;down&rsquo; files in reverse chronological order based on filename which should remove all tables and data with the exception of an an empty <strong>migration</strong> table.</p> <p><strong>Note:</strong> If any tables were created outside of the migrations, they should still be in the database.</p> <h2 id="migrate-refresh">migrate refresh</h2> <p>The <code>jay migrate:mysql refresh</code> command runs a <code>jay migrate reset</code> and then applies all the code from each of the &lsquo;up&rsquo; files in chronological order based on filename.</p> <h2 id="migrate-status">migrate status</h2> <p>The <code>jay migrate:mysql status</code> command reads the latest record in the <strong>migration</strong> table which is the last &lsquo;up&rsquo; file that was applied to the database.</p> <h2 id="migrate-up">migrate up</h2> <p>The <code>jay migrate:mysql up</code> command applies only the code in the next &lsquo;up&rsquo; file in chronological order based on filename.</p> <h2 id="migrate-down">migrate down</h2> <p>The <code>jay migrate:mysql down</code> command applies only the code in the current &lsquo;down&rsquo; file.</p> <h2 id="other-database-changes">Other Database Changes</h2> <p>If the &lsquo;up&rsquo; and &lsquo;down&rsquo; files are written properly, tables created manually outside the migrations should be left untouched by the <code>jay migrate</code> commands. The only changes <code>jay migrate</code> makes to the database outside of the migrations are the creation of the database itself based on the settings in env.json and the modification of the <strong>migration</strong> table.</p> Code Generation https://blue-jay.github.io/generation/ Mon, 01 Jan 0001 00:00:00 +0000 https://blue-jay.github.io/generation/ <h2 id="basic-usage">Basic Usage</h2> <p>Code generation makes it easy to build out new features so you don&rsquo;t have to retype or copy and paste the same code over again. It&rsquo;s especially useful when prototyping and need to see how something will look or work.</p> <p><strong>Note</strong>: The <code>jay generate</code> commands requires the environment variable, <strong>JAYCONFIG</strong>, to point to the env.json file path. The generation folder containing the templates is specified in the env.json file under <strong>Generation.TemplateFolder</strong>. If the environment variable is not set, you can specify the &ndash;config or -c flag and pass the path to env.json.</p> <p><strong>Jay</strong> tries to make it easy to generate code by using tools that you already know: the <strong>text/template</strong> package from the standard Go library and JSON.</p> <p>A template pair consists of a .json file and a .gen file. The .gen file contains the <strong>text/template</strong> parsable code. The .json file contains other information needed for the generation process like:</p> <ul> <li>config.type - either &lsquo;single&rsquo; if creating one file or &lsquo;collection&rsquo; if referencing one or more other .json files</li> <li>config.output - relative path at which to create the file</li> <li>config.parse - either true or false</li> </ul> <p>In the .json file, any other keys outside these three will be applied directly to the .gen file. If you leave a key blank, that tells <code>jay generate</code> that it needs to be passed via command-line.</p> <p>Don&rsquo;t worry, there are already a boatload of templates ready to use. They are organized by type in the <strong>generate</strong> folder of Blueprint. If .json file does not have a .gen pair in the same folder, then it has a <strong>config.parse</strong> value of <strong>collection</strong> which means it references other .json files with a <strong>config.parse</strong> value of <strong>single</strong>.</p> <p>The <strong>lib/flight</strong> package is a helper package that reduces a lot of code from the controller. It contains functions to simplify the use of flash messages, form validation, form repopulation, and URL parameters.</p> <ul> <li>controller <ul> <li>bare.gen | bare.json - creates a bare controller with only placeholder functions</li> <li>default.gen | default.json - creates a controller with CRUD ready code with the <strong>flight</strong> package</li> <li>nameonly.gen | nameonly.json - creates a controller with CRUD ready code with the <strong>flight</strong> package and only a <strong>name</strong> column</li> <li>noflight.gen | noflight.json - creates a controller with CRUD ready code without the <strong>flight</strong> package</li> </ul></li> <li>crud <ul> <li>bare.json - creates a bare controller, model, and four views</li> <li>default.json - creates a controller, model, and four views CRUD ready with the <strong>flight</strong> package</li> <li>nameonly.json - creates a controller, model, and four views CRUD ready with the <strong>flight</strong> package and only a <strong>name</strong> column</li> <li>noflight.json - creates a controller, model, and four views CRUD ready without the <strong>flight</strong> package</li> <li>table.json - creates a controller, model, and four views CRUD ready with the <strong>flight</strong> package and only a <strong>name</strong> column that displays in a table view</li> </ul></li> <li>lib <ul> <li>default.gen | default.json - creates a thread-safe wrapper package</li> </ul></li> <li>middleware <ul> <li>default.gen | default.json - creates a middleware package</li> </ul></li> <li>model <ul> <li>bare.gen | bare.json - creates a bare model with only placeholder functions</li> <li>default.gen | default.json - creates a model with CRUD ready code</li> <li>nameonly.gen | nameonly.json - creates a model with CRUD ready code and only a <strong>name</strong> column</li> </ul></li> <li>view <ul> <li>bare.json - creates four bare views with only a basic structure</li> <li>create.gen | create.json - creates a CRUD ready creation form view</li> <li>create_bare.gen | create_bare.json - creates a bare view with only a basic structure</li> <li>default.json - creates four views with CRUD ready code</li> <li>edit.gen | edit.json - creates a CRUD ready edit form view</li> <li>edit_bare.gen | edit_bare.json - creates a bare view with only a basic structure</li> <li>index.gen | index.json - creates a CRUD ready display view for multiple items</li> <li>index_bare.gen | index_bare.json - creates a bare view with only a basic structure</li> <li>index_table.gen | index_table.json - creates a CRUD ready table view for multiple items</li> <li>show.gen | show.json - creates a CRUD ready display view for a single item</li> <li>show_bare.gen | show_bare.json - creates a bare view with only a basic structure</li> <li>show_table.gen | show_table.json - creates a CRUD ready table view for a single item</li> <li>single.gen | single.json - creates a bare view with only a basic structure</li> <li>table.json - creates four views with CRUD ready code in a table view</li> </ul></li> <li>viewfunc <ul> <li>default.gen | default.json - creates a FuncMap package for use with the <strong>html/template</strong> package</li> </ul></li> <li>viewmodify <ul> <li>default.gen | default.json - creates a package to modify the <strong>lib/view</strong> package before rendering</li> </ul></li> </ul> <p>Let&rsquo;s view a couple examples.</p> <h2 id="middleware-example">Middleware Example</h2> <p>The <strong>generate/middleware/default.gen</strong> file contains this template. The {{.package}} variable is the only required variable for this template so it must be added to the .json file as a key with blank value. This forces <code>jay generate</code> to require the user to pass a <strong>package</strong> variable before it will generate the template.</p> <pre><code class="language-go">// Package {{.package}} package {{.package}} import ( &quot;net/http&quot; ) // Handler func Handler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Logic BEFORE the other handlers and function goes here next.ServeHTTP(w, r) // Logic AFTER the other handlers and function goes here }) } </code></pre> <p>The <strong>generate/middleware/default.json</strong> file contains this JSON. The <strong>config.type</strong> is set to <strong>single</strong> because it&rsquo;s only generating one file. The <strong>config.output</strong> is the relative path where the file will be generated. Notice that the <strong>config.output</strong> also uses the <strong>package</strong> variable. This is the beauty of the <code>jay generate</code> tool: the .json files are actually parsed multiple times (limit is set to 100) to ensure all variables are set.</p> <p>In the first iteration of parsing, the <strong>package</strong> key is set to value passed via the command-line. In the second iteration of parsing, the <strong>{{.package}}</strong> variables are set to the same value because the top level <strong>package</strong> key becomes a variable itself.</p> <p>All first level keys (config.type, config.output, package) become variables after the first iteration of parsing. If a variable is misspelled and is never filled, a helpful error will display to the command-line.</p> <p>The folder structure of the templates (model, controller, etc) has no effect on the generation, it&rsquo;s purely to aid with organization of the template pairs.</p> <pre><code class="language-json">{ &quot;config.type&quot;: &quot;single&quot;, &quot;config.output&quot;: &quot;middleware/{{.package}}/{{.package}}.go&quot;, &quot;package&quot;: &quot;&quot; } </code></pre> <p>To generate this template, the command would look like this. The <strong>package</strong> key is separated from the value by a colon (:). An argument of <code>package:</code> without a value is also acceptable, but would end up creating a file called <strong>.go</strong> in the <strong>middleware</strong> folder.</p> <pre><code class="language-bash">jay generate middleware/default package:test </code></pre> <p>The final output would be to the file, <strong>middleware/test/test.go</strong>, and would look like this:</p> <pre><code class="language-go">// Package test package test import ( &quot;net/http&quot; ) // Handler func Handler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Logic BEFORE the other handlers and function goes here next.ServeHTTP(w, r) // Logic AFTER the other handlers and function goes here }) } </code></pre> <h2 id="controller-example">Controller Example</h2> <p>This is a snippet of the template in <strong>generate/controller/bare.gen</strong>. In this template, there are two variables: <strong>package</strong> and <strong>url</strong>.</p> <pre><code class="language-go">// Package {{.package}} package {{.package}} import ( &quot;net/http&quot; &quot;github.com/blue-jay/blueprint/controller/status&quot; &quot;github.com/blue-jay/core/router&quot; ) var ( uri = &quot;/{{.url}}&quot; ) // Load the routes. func Load() { c := router.Chain() router.Get(uri, Index, c...) router.Get(uri+&quot;/create&quot;, Create, c...) router.Post(uri+&quot;/create&quot;, Store, c...) router.Get(uri+&quot;/view/:id&quot;, Show, c...) router.Get(uri+&quot;/edit/:id&quot;, Edit, c...) router.Patch(uri+&quot;/edit/:id&quot;, Update, c...) router.Delete(uri+&quot;/:id&quot;, Destroy, c...) } // Index displays the items. func Index(w http.ResponseWriter, r *http.Request) { status.Error501(w, r) } // Create displays the create form. func Create(w http.ResponseWriter, r *http.Request) { status.Error501(w, r) } ... </code></pre> <p>The <strong>generate/controller/bare.json</strong> file contains this JSON.</p> <pre><code class="language-json">{ &quot;config.type&quot;: &quot;single&quot;, &quot;config.output&quot;: &quot;controller/{{.package}}/{{.package}}.go&quot;, &quot;package&quot;: &quot;&quot;, &quot;url&quot;: &quot;&quot; } </code></pre> <p>To generate this template, the command would look like this. Notice the second variable, <strong>url</strong>.</p> <pre><code class="language-bash">jay generate controller/bare package:monkey url:banana </code></pre> <p>The final output would be to the file, <strong>controller/monkey/monkey.go</strong>, and would look like this:</p> <pre><code class="language-go">// Package monkey package monkey import ( &quot;net/http&quot; &quot;github.com/blue-jay/blueprint/controller/status&quot; &quot;github.com/blue-jay/core/router&quot; ) var ( uri = &quot;/banana&quot; ) // Load the routes. func Load() { c := router.Chain() router.Get(uri, Index, c...) router.Get(uri+&quot;/create&quot;, Create, c...) router.Post(uri+&quot;/create&quot;, Store, c...) router.Get(uri+&quot;/view/:id&quot;, Show, c...) router.Get(uri+&quot;/edit/:id&quot;, Edit, c...) router.Patch(uri+&quot;/edit/:id&quot;, Update, c...) router.Delete(uri+&quot;/:id&quot;, Destroy, c...) } // Index displays the items. func Index(w http.ResponseWriter, r *http.Request) { status.Error501(w, r) } // Create displays the create form. func Create(w http.ResponseWriter, r *http.Request) { status.Error501(w, r) } ... </code></pre> <h2 id="single-view-example">Single View Example</h2> <p>The <strong>generate/view/single.gen</strong> file contains this template. We don&rsquo;t actually want to parse this code because it&rsquo;s a view itself. We can tell the parser not to parse in the single.json file by setting the value of <strong>config.parse</strong> to <strong>false</strong>.</p> <pre><code class="language-html">{{define &quot;title&quot;}}{{end}} {{define &quot;head&quot;}}{{end}} {{define &quot;content&quot;}} &lt;div class=&quot;page-header&quot;&gt; &lt;h1&gt;{{template &quot;title&quot; .}}&lt;/h1&gt; &lt;/div&gt; &lt;p&gt;Not Implemented&lt;/p&gt; {{template &quot;footer&quot; .}} {{end}} {{define &quot;foot&quot;}}{{end}} </code></pre> <p>The <strong>generate/view/single.json</strong> file contains this JSON. Again, we set <strong>config.parse</strong> to false and only use the variables for specifying the <strong>config.output</strong> path. The variables are not used inside the single.json template at all.</p> <pre><code class="language-json">{ &quot;config.type&quot;: &quot;single&quot;, &quot;config.output&quot;: &quot;view/{{.model}}/{{.name}}.tmpl&quot;, &quot;config.parse&quot;: false, &quot;model&quot;: &quot;&quot;, &quot;name&quot;: &quot;&quot; } </code></pre> <p>To generate this template, the command would look like this. Notice the second variable, <strong>url</strong>.</p> <pre><code class="language-bash">jay generate view/single model:test name:create </code></pre> <p>The final output would be to the file, <strong>view/test/create.tmpl</strong>, and would look like this - no difference from the template itself since it was not parsed, just copied.</p> <pre><code class="language-html">{{define &quot;title&quot;}}{{end}} {{define &quot;head&quot;}}{{end}} {{define &quot;content&quot;}} &lt;div class=&quot;page-header&quot;&gt; &lt;h1&gt;{{template &quot;title&quot; .}}&lt;/h1&gt; &lt;/div&gt; &lt;p&gt;Not Implemented&lt;/p&gt; {{template &quot;footer&quot; .}} {{end}} {{define &quot;foot&quot;}}{{end}} </code></pre> <h1 id="multiple-view-example">Multiple View Example</h1> <p>The <strong>generate/view/default.json</strong> file contains this JSON, but does not have a matching default.gen file. This is because the .json file simply passes variables to other .json files which have matching .gen files that generate files. First, the <strong>config.type</strong> key has a value of <strong>collection</strong>, that&rsquo;s how you know it doesn&rsquo;t have a matching .gen file. Next, the <strong>config.collection</strong> key contains an array of items that specify which template pairs to pass variables to.</p> <p>The <strong>model</strong> key under each template receives the <strong>model</strong> variable from the root of the package which is required from the command-line.</p> <pre><code class="language-json">{ &quot;config.type&quot;: &quot;collection&quot;, &quot;config.collection&quot;: [ { &quot;view/create&quot;: { &quot;model&quot;: &quot;{{.model}}&quot; } }, { &quot;view/edit&quot;: { &quot;model&quot;: &quot;{{.model}}&quot; } }, { &quot;view/index&quot;: { &quot;model&quot;: &quot;{{.model}}&quot; } }, { &quot;view/show&quot;: { &quot;model&quot;: &quot;{{.model}}&quot; } } ], &quot;model&quot;: &quot;&quot; } </code></pre> <p>In this example, the default.json file will pass variables to the following files:</p> <ul> <li>generate/view/create.json</li> <li>generate/view/edit.json</li> <li>generate/view/index.json</li> <li>generate/view/show.json</li> </ul> <p>The files are all still relative to the root <strong>generate</strong> folder. Also, if you look at each of the four files, you&rsquo;ll see that they require only one variable: <strong>model</strong>. Here is the contents of <strong>generate/view/create.json</strong>:</p> <pre><code class="language-json">{ &quot;config.type&quot;: &quot;single&quot;, &quot;config.output&quot;: &quot;view/{{.model}}/create.tmpl&quot;, &quot;config.parse&quot;: false, &quot;model&quot;: &quot;&quot; } </code></pre> <p>To generate all four of these templates, the command would look like this.</p> <pre><code class="language-bash">jay generate view/default model:test </code></pre> <p>The final output would be to the following files:</p> <ul> <li>view/test/create.json</li> <li>view/test/edit.json</li> <li>view/test/index.json</li> <li>view/test/show.json</li> </ul> <h2 id="crud-example">CRUD Example</h2> <p>If you put all those piece above together, you can generate the controller, model, and all four views necessary for a working CRUD component in a single command.</p> <p>The <strong>generate/crud/default.json</strong> file contains this JSON:</p> <pre><code class="language-json">{ &quot;config.type&quot;: &quot;collection&quot;, &quot;config.collection&quot;: [ { &quot;model/default&quot;: { &quot;package&quot;: &quot;{{.model}}&quot;, &quot;table&quot;: &quot;{{.model}}&quot; } }, { &quot;controller/default&quot;: { &quot;package&quot;: &quot;{{.controller}}&quot;, &quot;url&quot;: &quot;{{.controller}}&quot;, &quot;model&quot;: &quot;{{.model}}&quot;, &quot;view&quot;: &quot;{{.view}}&quot; } }, { &quot;view/default&quot;: { &quot;model&quot;: &quot;{{.view}}&quot; } } ], &quot;model&quot;: &quot;&quot;, &quot;controller&quot;: &quot;&quot;, &quot;view&quot;: &quot;&quot; } </code></pre> <p>To generate all the components, the command would look like this.</p> <pre><code class="language-bash">jay generate crud/default model:note controller:notepad view:note </code></pre>