Blue Jay | Go Toolkit for the Web
https://blue-jay.github.io/
Recent content on Blue Jay | Go Toolkit for the WebHugo -- gohugo.ioen-usBlue Jay Overview
https://blue-jay.github.io/
Mon, 01 Jan 0001 00:00:00 +0000https://blue-jay.github.io/
<p>Blue Jay is a web toolkit for <a href="https://golang.org/">Go</a>. It’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’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’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’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’s not the only niche.
The designers of Go wanted to build a language that solved problems between the Google development teams.
It’s a modern language that allows you to easily multi-thread your applications. It’s a “get stuff done”
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 +0000https://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 +0000https://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’t be afraid to change code. You don’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’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’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">{
"Asset":{
"Folder":"asset"
},
"Email":{
"Username":"",
"Password":"",
"Hostname":"",
"Port":25,
"From":""
},
"Form":{
"FileStorageFolder":"filestorage"
},
"Generation":{
"TemplateFolder":"generate"
},
"MySQL":{
"Username":"root",
"Password":"",
"Database":"blueprint",
"Charset":"utf8mb4",
"Collation":"utf8mb4_unicode_ci",
"Hostname":"127.0.0.1",
"Port":3306,
"Parameter":"parseTime=true",
"Migration":{
"Folder":"migration/mysql",
"Table":"migration_blueprint",
"Extension":"sql"
}
},
"Server":{
"Hostname":"",
"UseHTTP":true,
"UseHTTPS":false,
"RedirectToHTTPS":false,
"HTTPPort":80,
"HTTPSPort":443,
"CertFile":"tls/server.crt",
"KeyFile":"tls/server.key"
},
"Session":{
"AuthKey":"PzCh6FNAB7/jhmlUQ0+25sjJ+WgcJeKR2bAOtnh9UnfVN+WJSBvY/YC80Rs+rbMtwfmSP4FUSxKPtpYKzKFqFA==",
"EncryptKey":"3oTKCcKjDHMUlV+qur2Ve664SPpSuviyGQ/UqnroUD8=",
"CSRFKey":"xULAGF5FcWvqHsXaovNFJYfgCt6pedRPROqNvsZjU18=",
"Name":"sess",
"Options":{
"Path":"/",
"Domain":"",
"MaxAge":28800,
"Secure":false,
"HttpOnly":true
}
},
"Template":{
"Root":"base",
"Children":[
"partial/favicon",
"partial/menu",
"partial/footer"
]
},
"View":{
"BaseURI":"/",
"Extension":"tmpl",
"Folder":"view",
"Caching":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:"Asset"`
Email email.Info `json:"Email"`
Form form.Info `json:"Form"`
Generation generate.Info `json:"Generation"`
MySQL mysql.Info `json:"MySQL"`
Server server.Info `json:"Server"`
Session session.Info `json:"Session"`
Template view.Template `json:"Template"`
View view.Info `json:"View"`
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’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("note/index")
v.Vars["items"] = 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 …string)</li>
<li>repopulating a form: c.Repopulate(v map[string]interface{}, fields …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["email"]</code></li>
<li>current user ID: <code>c.UserID</code></li>
<li>view package: <code>c.View.New("home/index")</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 . "lib/email"</code>,
then delete the imports and referencing code</li>
</ol>
Assets
https://blue-jay.github.io/assets/
Thu, 30 Jun 2016 21:07:13 +0100https://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’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"><!-- Favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="/static/favicon/apple-touch-icon-57x57.png?v1.0=3eepn6WlLO">
<link rel="apple-touch-icon" sizes="60x60" href="/static/favicon/apple-touch-icon-60x60.png?v1.0=3eepn6WlLO">
<link rel="apple-touch-icon" sizes="72x72" href="/static/favicon/apple-touch-icon-72x72.png?v1.0=3eepn6WlLO">
<!-- CSS and Fonts -->
<link media="all" rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css?1466973904" />
<link media="all" rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans:300,400,bold,italic" />
<link media="all" rel="stylesheet" type="text/css" href="/static/css/all.css?1466973904" />
</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’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’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 +0000https://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 (
"net/http"
"os"
"path"
"github.com/blue-jay/blueprint/controller/status"
"github.com/blue-jay/blueprint/lib/flight"
"github.com/blue-jay/core/router"
)
// Load the routes.
func Load() {
// Serve static files
router.Get("/static/*filepath", 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 && !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 (
"net/http"
"github.com/blue-jay/blueprint/lib/flight"
"github.com/blue-jay/core/router"
)
// 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("status/index")
v.Vars["title"] = "404 Not Found"
v.Vars["message"] = "Page could not be found."
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("status/index")
v.Vars["title"] = "405 Method Not Allowed"
v.Vars["message"] = "Method is not allowed."
v.Render(w, r)
}
}
...
</code></pre>
Middleware
https://blue-jay.github.io/middleware/
Mon, 01 Jan 0001 00:00:00 +0000https://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 (
"fmt"
"net/http"
"time"
)
// 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("2006-01-02 03:04:05 PM"), 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 “prettier”. If you look at the
<a href="https://github.com/blue-jay/blueprint/blob/master/lib/boot/middleware.go">lib/boot</a>
package, you’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("/notepad", Index, acl.DisallowAnon, logrequest.Handler)
router.Get("/notepad/create", Create, acl.DisallowAnon, logrequest.Handler)
// Use Chain() to apply middleware
c := router.Chain(acl.DisallowAnon, logrequest.Handler)
router.Get("/notepad", Index, c...)
router.Get("/notepad/create", 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("/notepad", Index, c...)
router.Get("/notepad/create", Create, c...)
router.Post("/notepad", Store, c...)
router.Get("/notepad/view/:id", Show, c...)
router.Get("/notepad/edit/:id", Edit, c...)
router.Patch("/notepad/edit/:id", Update, c...)
router.Delete("/notepad/:id", Destroy, c...)
}
</code></pre>
Controllers
https://blue-jay.github.io/controllers/
Mon, 01 Jan 0001 00:00:00 +0000https://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’s a good idea to follow a naming convention for the different pieces.
Laravel developers will notice it’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’ll be working with many different
models in your controllers.</p>
<pre><code class="language-go">func Load() {
...
// "Get" is the Method
// "/notepad" is the Path
router.Get("/notepad", 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("note/index")
v.Vars["items"] = 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{"Welcome to Blueprint!", 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, "email", "password"); !valid {
sess.AddFlash(flash.Info{"Field missing: " + missingField, flash.Error})
sess.Save(r, w)
LoginGET(w, r)
return
}
// With flight
c := flight.Context(w, r)
if !c.FormValid("name", "email", "password") {
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’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("note/create")
form.Repopulate(r.Form, v.Vars, "name")
v.Render(w, r)
// With flight
c := flight.Context(w, r)
v := c.View.New("note/create")
c.Repopulate(v.Vars, "name")
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("about/index").Render(w, r)
// Render with variables
c := flight.Context(w, r)
v := c.View.New("home/index")
v.Vars["first_name"] = c.Sess.Values["first_name"]
v.Render(w, r)
// Render with different base template (base.tmpl is used by default)
c := flight.Context(w, r)
v := c.View.New("home/index").Base("single")
v.Render(w, r)
</code></pre>
<h2 id="return-flash-over-ajax">Return Flash over Ajax</h2>
<p>If you’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’s called:
<strong>ShowFlash()</strong>. You’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("/flashes", 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{"An error occurred on the server. Please try again later.", 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("/flashes", 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("This is the subject", "This is the body!")
if err != nil {
c.FlashError(err)
return
}
</code></pre>
Models
https://blue-jay.github.io/models/
Mon, 01 Jan 0001 00:00:00 +0000https://blue-jay.github.io/models/
<h2 id="basic-usage">Basic Usage</h2>
<p>It’s a good idea to abstract the database layer out so if you need to make
changes, you don’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("name") {
Create(w, r)
return
}
_, err := note.Create(c.DB, r.FormValue("name"), c.UserID)
if err != nil {
c.FlashError(err)
Create(w, r)
return
}
c.FlashSuccess("Item added.")
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("id"), c.UserID)
if err != nil {
c.FlashError(err)
c.Redirect(uri)
return
}
v := c.View.New("note/show")
v.Vars["item"] = 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(&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("note/index")
v.Vars["items"] = 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(&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("name") {
Edit(w, r)
return
}
_, err := note.Update(c.DB, r.FormValue("name"), c.Param("id"), c.UserID)
if err != nil {
c.FlashError(err)
Edit(w, r)
return
}
c.FlashSuccess("Item updated.")
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("id"), c.UserID)
if err != nil {
c.FlashError(err)
} else {
c.FlashNotice("Item deleted.")
}
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("id"), c.UserID)
if err != nil {
c.FlashError(err)
} else {
c.FlashNotice("Item deleted.")
}
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’ll have to run
them manually.</p>
<p>Add the PostgreSQL structure to the env.json.example file:</p>
<pre><code class="language-json">"PostgreSQL":{
"Username":"root",
"Password":"",
"Database":"blueprint",
"Hostname":"127.0.0.1",
"Port":5432,
"Parameter":"",
"MigrationFolder":"migration/postgresql",
"Extension":"sql"
},
</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:"PostgreSQL"`
</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 +0100https://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 "title"}}About Blueprint{{end}}
{{define "head"}}{{end}}
{{define "content"}}
<div class="page-header">
<h1>{{template "title" .}}</h1>
</div>
<p>Blueprint lays the foundation for your web application using the Go language.</p>
{{template "footer" .}}
{{end}}
{{define "foot"}}{{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 "github.com/blue-jay/core/view"
c := flight.Context(w, r)
v := c.View.New("about/index")
// Variables would go here like this: v.Vars["first_name"] = session.Values["first_name"]
v.Render(w, r)
</code></pre>
<p>If you don’t have to pass any variables to the template, you could shorten it like this:</p>
<pre><code class="language-go">// import "github.com/blue-jay/core/view"
c := flight.Context(w, r)
c.View.New("about/index").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("about/index").Base("alternate")
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("about/about").Base("alternate").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 (
"fmt"
"html/template"
)
// Map returns a template.FuncMap for LINK that returns a hyperlink tag.
func Map(baseURI string) template.FuncMap {
f := make(template.FuncMap)
f["LINK"] = func(path, name string) template.HTML {
return template.HTML(fmt.Sprintf(`<a href="%v%v">%v</a>`, 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 "register" "Create a new account."}}
</code></pre>
<p>And the code would render like this:</p>
<pre><code class="language-html"><a href="/register">Create a new account.</a>
</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"><!-- CSS file with media attribute -->
{{CSS "static/css/normalize3.0.0.min.css" "all"}}
<!-- parses with timestamp to -->
<link media="all" rel="stylesheet" type="text/css" href="/static/css/normalize3.0.0.min.css?1435528339" />
<!-- JS file -->
{{JS "static/js/jquery1.11.0.min.js"}}
<!-- parses with timestamp to -->
<script type="text/javascript" src="/static/js/jquery1.11.0.min.js?1435528404"></script>
<!-- Hyperlinks -->
{{LINK "register" "Create a new account."}}
<!-- parses to -->
<a href="/register">Create a new account.</a>
<!-- Output an unescaped variable (not a safe idea, but it is useful when troubleshooting) -->
{{.SomeVariable | NOESCAPE}}
<!-- Time format for mysql.NullTime -->
{{NULLTIME .SomeTime}}
<!-- parses to format -->
3:04 PM 01/02/2006
<!-- Time format helper for mysql.NullTime that shows the latest of the two variables -->
{{PRETTYTIME .CreatedAt .UpdatedAt}}
<!-- parses to format -->
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’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 (
"net/http"
"github.com/blue-jay/blueprint/lib/flight"
"github.com/blue-jay/core/view"
)
// 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["id"] != nil {
v.Vars["AuthLevel"] = "auth"
} else {
v.Vars["AuthLevel"] = "anon"
}
}
</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 "auth"}}
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"><!-- Use AuthLevel=auth to determine if a user is logged in (if session.Values["id"] != nil) -->
{{if eq .AuthLevel "auth"}}
You are logged in.
{{else}}
You are not logged in.
{{end}}
<!-- Use BaseURI to print the base URL specified in the env.json file, ends in slash -->
<li><a href="{{.BaseURI}}about">About</a></li>
<!-- Use CurrentURI to print the current URL, does not end in slash -->
<li><a href="{{.CurrentURI}}">Current Page</a></li>
<!-- Use ParentURI to print the URL up one level, does not end in slash -->
<li><a href="{{.ParentURI}}">Parent Page</a></li>
<!-- Use GrandparentURI to print the URL up two levels, does not end in slash -->
<li><a href="{{.GrandparentURI}}">Grandparent Page</a></li>
<!-- Use token to output the CSRF token in a form -->
<input type="hidden" name="token" value="{{.token}}">
</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’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 “thirty”, 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
“thirty” 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’t have to use these names, but it’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 “default value” field in these
functions so you can set them to an empty string (“”) 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 “default value”
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’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"><!-- TEXT accepts the element name, a default value, and then a period -->
<input {{TEXT "email" .item.Email .}} type="email" class="form-control" id="email" />
<!-- then parses to a name attribute when no repopulation value is passed -->
<input name="email" type="email" class="form-control" id="email" />
<!-- and parses to a name and a value when a repopulation value is passed -->
<input name="email" value="[email protected]" type="email" class="form-control" id="email" />
<!-- TEXTAREA accepts the element name, a default value, and then a period -->
<textarea rows="5" class="form-control" id="name" name="name" />{{TEXTAREA "name" .item.Name .}}</textarea>
<!-- then parses to nothing when no repopulation value is passed -->
<textarea rows="5" class="form-control" id="name" name="name" /></textarea>
<!-- and parses to a value when a repopulation value is passed -->
<textarea rows="5" class="form-control" id="name" name="name" />Sample text</textarea>
<!-- CHECKBOX accepts the element name, value, default value, and then a period -->
<label><input {{CHECKBOX "rememberme" "r1" .item.RememberMe .}}> Remember me</label>
<!-- then parses to a type, name, and value attribute when no repopulation value is passed -->
<label><input type="checkbox" name="rememberme" value="r1"> Remember me</label>
<!-- and parses to a type, name, value, and the word 'checked' when a repopulation value is passed -->
<label><input type="checkbox" name="rememberme" value="r1" checked> Remember me</label>
<!-- RADIO accepts the element name, value, default value, and then a period -->
<label><input {{RADIO "options" "burger" .item.Option .}}> Burger</label>
<!-- then parses to a type, name, and value attribute when no repopulation value is passed -->
<label><input type="radio" name="options" value="burger"> Burger</label>
<!-- and parses to a type, name, value attribute, and the word 'checked' when a repopulation value is passed -->
<label><input type="radio" name="options" value="burger" checked> Burger</label>
<!-- OPTION accepts the element name, value, default value, and then a period -->
<select name="select"><option {{OPTION "select" "Apple" .item.Select .}}>Apple</option></select>
<!-- then parses to a value attribute when no repopulation value is passed -->
<select name="select"><option value="Apple">Apple</option></select>
<!-- and parses to a value attribute and the word 'selected' when a repopulation value is passed -->
<select name="select"><option value="Apple" selected>Apple</option></select>
</code></pre>
<p>Here are examples of all the fields with the Bootrap structure and classes:</p>
<pre><code class="language-html"><div class="form-group">
<label for="email">Email Address</label>
<div><input {{TEXT "email" .item.Email .}} type="email" class="form-control" id="email" maxlength="48" placeholder="Email" /></div>
</div>
<div class="form-group">
<label for="name">Item</label>
<div><textarea rows="5" class="form-control" id="name" name="name" placeholder="Type your text here..." />{{TEXTAREA "name" .}}</textarea></div>
</div>
<div class="checkbox">
<label>
<input {{CHECKBOX "rememberme" "r1" .item.RememberMe .}}> Remember me
</label>
</div>
<div class="checkbox">
<label>
<input {{CHECKBOX "rememberme" "r2" .item.RememberMe .}}> Remember me
</label>
</div>
<div class="radio">
<label>
<input {{RADIO "options" "burger" .item.Option .}}> Burger
</label>
</div>
<div class="radio">
<label>
<input {{RADIO "options" "taco" .item.Option .}}> Taco
</label>
</div>
<select class="form-control" name="select">
<option {{OPTION "select" "Apple" .item.Select .}}>Apple</option>
<option {{OPTION "select" "Banana" .item.Select .}}>Banana</option>
<option {{OPTION "select" "cherry" .item.Select .}}>Cherry</option>
</select>
<select multiple class="form-control" name="mselect">
<option {{OPTION "mselect" "red" .item.Select .}}>Red</option>
<option {{OPTION "mselect" "green" .item.Select .}}>Green</option>
<option {{OPTION "mselect" "blue" .item.Select .}}>Blue</option>
</select>
</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"><!-- Example of a PATCH request -->
<form method="post" action="{{$.CurrentURI}}?_method=patch">
<!-- Example of a DELETE request -->
<form class="button-form" method="post" action="{{$.GrandparentURI}}/{{.item.ID}}?_method=delete">
</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"><form method="post" action="{{$.CurrentURI}}?_method=patch">
<div class="form-group">
<label for="email">Email Address</label>
<div><input {{TEXT "email" .item.Email .}} type="email" class="form-control" id="email" /></div>
</div>
<div class="form-group">
<label for="password">Password</label>
<div><input {{TEXT "password" .item.Password .}} type="password" class="form-control" id="password" /></div>
</div>
<input type="submit" class="btn btn-primary" value="Change" class="button" />
<input type="hidden" name="_token" value="{{$.token}}">
</form>
</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("/user/edit/:id", Edit, c...)
// Handle the Update Page Form Submissions - PATCH HTTP method
router.Patch("user/edit/:id", Update, c...)
</code></pre>
<h2 id="header-and-footer">Header and Footer</h2>
<p>It’s also easy to add template-specific code before the closing </head> and </body> tags:</p>
<pre><code class="language-html"><!-- Code is added before the closing </head> tag -->
{{define "head"}}<meta name="robots" content="noindex">{{end}}
...
<!-- Code is added before the closing </body> tag -->
{{define "foot"}}{{JS "//www.google.com/recaptcha/api.js"}}{{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("An error occurred on the server.");
flashSuccess("Item added!");
flashNotice("Item deleted.");
flashWarning("Field missing: email");
</code></pre>
Library
https://blue-jay.github.io/library/
Mon, 01 Jan 0001 00:00:00 +0000https://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’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’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’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’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 (
"encoding/base64"
"fmt"
"net/smtp"
)
</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’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("", c.Username, c.Password, c.Hostname)
// Create the header
header := make(map[string]string)
header["From"] = c.From
header["To"] = to
header["Subject"] = subject
header["MIME-Version"] = "1.0"
header["Content-Type"] = `text/plain; charset="utf-8"`
header["Content-Transfer-Encoding"] = "base64"
// Set the message
message := ""
for k, v := range header {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
// Send the email
err := smtp.SendMail(
fmt.Sprintf("%s:%d", 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("This is the subject", "This is the body!")
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 +0000https://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’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 ‘up’ file and ‘down’ file</li>
<li><code>jay migrate:mysql all</code> for applying all ‘up’ migrations</li>
<li><code>jay migrate:mysql reset</code> for applying all ‘down’ migrations</li>
<li><code>jay migrate:mysql refresh</code> for applying all ‘down’ then ‘up’ 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 ‘up’ migration</li>
<li><code>jay migrate:mysql down</code> for applying one one ‘down’ 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 <description></code></li>
<li>Required arguments: <code><required></code></li>
<li>Optional arguments: <code>[<optional>]</code></li>
</ul>
Find and Replace
https://blue-jay.github.io/find-replace/
Mon, 01 Jan 0001 00:00:00 +0000https://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 <folder> <text> [<extension>] [<recursive>] [<filename>]
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:
<folder> Folder to search
<text> Case-sensitive text to find.
[<extension>] File name or extension to search in. Use * as a wildcard.
Directory names are not valid.
[<recursive>] True to search in subfolders. Default: true
[<filename>] 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 <folder> <find> [<replace>] [<extension>] [<recursive>] [<filename>] [<commit>]
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:
<folder> Folder to search
<find> Case-sensitive text to replace.
[<replace>] Text to replace with.
[<extension>] File name or extension to search in. Use * as a wildcard.
Directory names are not valid.
[<recursive>] True to search in subfolders. Default: true
[<filename>] True to include file path in results if matched. Default: false
[<commit>] 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 "red" in all *.go files in the current folder and in subfolders.
jay find . red
# Find the word "red" in all files in the current folder only.
jay find . red "*.*" false
# Find the word "red" in *.go files in current folder and in subfolders and
# include file paths that match also.
jay find . red "*.go" true true
# Replace the word "red" with the word "blue" in all *.go files in the current
# folder and in subfolders.
jay replace . red blue
# Replace the word "red" with the word "blue" in all *.go files in current
# folder only.
jay replace . red blue "*.go" false
# Change the name of the project in current folder and in subfolders and all
# imports to another repository.
jay replace . "blue-jay/blueprint" "user/project"
</code></pre>
Database Migration
https://blue-jay.github.io/database-migration/
Mon, 01 Jan 0001 00:00:00 +0000https://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 ‘up’ file and a
‘down’ file. The ‘up’ file contains code (like SQL) which is applied to the
database you add features or fix bugs in your database. The ‘down’ 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 –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’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 "test" # 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’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 "feature-store-books"</code>. He opens the ‘up’ file
and adds the SQL query to create a new table with the following columns: title,
author, and publication_date. In the ‘down’ 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 "feature-add-publisher"</code>.
Steve adds the SQL <code>ADD COLUMN</code> code to the new ‘up’ file and the SQL
<code>DROP COLUMN</code> code to the new ‘down’ 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 ‘up’ and ‘down’
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 ‘up’ 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 ‘down’ 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 ‘up’ 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 ‘up’ 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 ‘up’ 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 ‘down’ file.</p>
<h2 id="other-database-changes">Other Database Changes</h2>
<p>If the ‘up’ and ‘down’ 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 +0000https://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’t have to
retype or copy and paste the same code over again. It’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 –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 ‘single’ if creating one file or ‘collection’ 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’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’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 (
"net/http"
)
// 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’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’s purely to aid with organization of the template pairs.</p>
<pre><code class="language-json">{
"config.type": "single",
"config.output": "middleware/{{.package}}/{{.package}}.go",
"package": ""
}
</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 (
"net/http"
)
// 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 (
"net/http"
"github.com/blue-jay/blueprint/controller/status"
"github.com/blue-jay/core/router"
)
var (
uri = "/{{.url}}"
)
// Load the routes.
func Load() {
c := router.Chain()
router.Get(uri, Index, c...)
router.Get(uri+"/create", Create, c...)
router.Post(uri+"/create", Store, c...)
router.Get(uri+"/view/:id", Show, c...)
router.Get(uri+"/edit/:id", Edit, c...)
router.Patch(uri+"/edit/:id", Update, c...)
router.Delete(uri+"/:id", 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">{
"config.type": "single",
"config.output": "controller/{{.package}}/{{.package}}.go",
"package": "",
"url": ""
}
</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 (
"net/http"
"github.com/blue-jay/blueprint/controller/status"
"github.com/blue-jay/core/router"
)
var (
uri = "/banana"
)
// Load the routes.
func Load() {
c := router.Chain()
router.Get(uri, Index, c...)
router.Get(uri+"/create", Create, c...)
router.Post(uri+"/create", Store, c...)
router.Get(uri+"/view/:id", Show, c...)
router.Get(uri+"/edit/:id", Edit, c...)
router.Patch(uri+"/edit/:id", Update, c...)
router.Delete(uri+"/:id", 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’t actually
want to parse this code because it’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 "title"}}{{end}}
{{define "head"}}{{end}}
{{define "content"}}
<div class="page-header">
<h1>{{template "title" .}}</h1>
</div>
<p>Not Implemented</p>
{{template "footer" .}}
{{end}}
{{define "foot"}}{{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">{
"config.type": "single",
"config.output": "view/{{.model}}/{{.name}}.tmpl",
"config.parse": false,
"model": "",
"name": ""
}
</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 "title"}}{{end}}
{{define "head"}}{{end}}
{{define "content"}}
<div class="page-header">
<h1>{{template "title" .}}</h1>
</div>
<p>Not Implemented</p>
{{template "footer" .}}
{{end}}
{{define "foot"}}{{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’s how
you know it doesn’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">{
"config.type": "collection",
"config.collection": [
{
"view/create": {
"model": "{{.model}}"
}
},
{
"view/edit": {
"model": "{{.model}}"
}
},
{
"view/index": {
"model": "{{.model}}"
}
},
{
"view/show": {
"model": "{{.model}}"
}
}
],
"model": ""
}
</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’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">{
"config.type": "single",
"config.output": "view/{{.model}}/create.tmpl",
"config.parse": false,
"model": ""
}
</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">{
"config.type": "collection",
"config.collection": [
{
"model/default": {
"package": "{{.model}}",
"table": "{{.model}}"
}
},
{
"controller/default": {
"package": "{{.controller}}",
"url": "{{.controller}}",
"model": "{{.model}}",
"view": "{{.view}}"
}
},
{
"view/default": {
"model": "{{.view}}"
}
}
],
"model": "",
"controller": "",
"view": ""
}
</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>