Modern workflows

FOR MODERN WEBAPPS

Why?

  • Scaffold new projects using templates

  • Build process.

    • Minifize, concatenate scripts and stylesheets.
    • If using AMD pass those modules to r.js.
  • Compile CoffeeScript & Sass.

  • Refresh the browser whenever a change is made.

  • Lint scripts for language best-practices.

  • Image Optimization using OptiPNG and JPEGTran.

  • Package management.

  • Unit Testing in a headless WebKit.

  • Built-in preview server.

Instalation

Installing nodejs

# Install nvm - Node Version Manager
curl https://raw.github.com/creationix/nvm/master/install.sh | sh

# Load nvm 
source ~/.nvm/nvm.sh

# Install nodejs vr 0.10
nvm install 0.10

# Load nodejs vr 0.10
nvm use 0.10

# Set version 0.10 as default
nvm alias default 0.10

# Install packages as global
npm -g install yo bower grunt-cli generator-webapp generator-angular

Installing Ruby

# Install rvm - Ruby Version Manager
curl -L https://get.rvm.io | bash -s stable

# Load rvm
source ~/.rvm/scripts/rvm

# List available ruby versions
rvm list

# Install a ruby version and set it as default
rvm install 2.0.0-p353

# Load ruby version 2.0.0-p353 and set as default
rvm use 2.0.0-p353 --default

# Install packages
gem install --pre sass compass

~/.bashrc

Check rvm and nvm are correctly installed in bashrc.

[[ -s ~/.nvm/nvm.sh ]] && . ~/.nvm/nvm.sh

[[ -s ~/.rvm/scripts/rvm ]] && . ~/.rvm/scripts/rvm

Installing Yeoman, Grunt, Bower

npm -g install yo bower grunt-cli

# Install a yeoman generator
npm -g install generator-webapp

NodeJS

Platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications.

http://s3.amazonaws.com/four.livejournal/20091117/jsconf.pdf

var result = db.query("select * from T");
  • What is the software doing while it queries the database?

  • either blocks the entire process or implies multiple execution stacks.

  • Other threads of execution can run while waiting.

  • Context switching is not free

  • Execution stacks take up memory

  • For massive concurrency, cannot use an OS thread for each connection

  • why isn’t everyone using event loops, callbacks, and non-blocking I/O?

  • For reasons both cultural and infrastructural.

We are taught to:

puts("Enter your name: ");
var name = gets();
puts("Name: " + name);

instead of:

puts("Enter your name: ");
gets(function (name) {
  puts("Name: " + name);
});
db.query("select..", function (result) {
    // callback
});
  • Allows the program to return to the event loop immediately.

  • This is how I/O should be done.

  • every I/O call must take a callback

  • JavaScript lacked an existing I/O API

design principles

  • Non-blocking I/O

    • every I/O call must take a callback.
  • Built-in support for the most important protocols

    • HTTP, DNS, TLS
  • Low-level.

    • Do not remove functionality present at the POSIX layer.
  • Stream everything; never force the buffering of data.

Why JavaScript?

http://blogs.gnome.org/alexl/2008/09/09/embeddable-languages-an-implementation/

Lets look why Gnome decided to use it

Its a modern dynamic language

  • Its dynamically typed
  • it has precise gargabe collection
  • lambda functions
  • generators
  • array comprehension
  • etc

Its object oriented

  • The JavaScript prototype-based model is a bit weird, but its simple and it works.

It has no “platform” of its own

The Gnome platform (Gtk+, Gio, etc) doesn’t have to fight with any “native” versions of the same functionallity.

Lots of people know it

Chances of finding someone who knows JS is far higher than finding someone who knows e.g. lua or scheme

Lots of activity

  • The language continually evolves.

  • Competetive implementations, each trying to be best.

    • TraceMoney vs Google v8.
    • Such performance work and focus is unlikely to happen in smaller and less used languages.

var object;
object = {
    "a": 1,
    "b": true,
    "c": [1,2,3]
};

object.__proto__ = {
    "a": 1,
    "b": true,
    "c": [1,2,3]
};

object.__proto__.__proto__ === {}.__proto__;

object.__proto__.__proto__.__proto__ === null;
var b = {
    "a": 1,
    "b": true,
    "c": [1,2,3]
};

var object = Object.create(b);

object["a"] = 1;
object["b"] = true;
object["c"] = [1, 2, 3];
// Object.defineProperties
var a = {
  "a": { value: "hello" },
  "b": { value: true },
  "c": {
    get: function() { return true },
    set: function(value) { console.log("Setting `b` to", value) }
  }
};

var b = {
    "a": 1,
    "b": true,
    "c": [1,2,3]
};

var object = Object.create(b, a);
function A() {
    this["a"] = 1;
    this["b"] = true;
    this["c"] = [1,2,3];
}

function B() {
    this["a"] = 1;
    this["b"] = true;
    this["c"] = [1,2,3];
}

A.prototype = new B();

var object = new A();

AngularJS scope prototypal inheritance

Closures

Functions which take a small amount of metadata that describes the environment in which they were defined.


var counter = (function () {
    var count = 0;
    return function () {
        console.log(count++);
    }
})();

counter(); // 0
counter(); // 1

Java 7 doesn't support closures

Apply Functional Programming Principles

97 Things Every Programmer Should Know

Mastery of the functional programming paradigm can greatly improve the quality of the code you write in other contexts.

Monads

composable computation descriptions

class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  return :: a -> m a
  fail :: String -> m a

Material del curso JavaScript para Profesionales by redradix

https://github.com/redradix/material-projs.git

Curious about functional programming?

http://slides.com/gsklee/functional-programming-in-5-minutes

http://learnyouahaskell.com/chapters

Yeoman

The web's scaffolding tool for modern webapps

  • yo scaffolds out a new application

    • writing your Grunt configuration and pulling in relevant Grunt tasks.
  • Grunt is used to build, preview and test your project

    • thanks to help from tasks curated by the Yeoman team and grunt-contrib.
  • Bower is used for dependency management

    • so that you no longer have to manually download and manage your scripts.

Features

  • CSS Autoprefixing (new)
  • Built-in preview server with LiveReload
  • Automagically compile CoffeeScript & Sass
  • Automagically lint your scripts
  • Automagically wire up your Bower components with bower-install.
  • Awesome Image Optimization (via OptiPNG, pngquant, jpegtran and gifsicle)
  • Mocha Unit Testing with PhantomJS
  • Optional - Bootstrap for Sass
  • Optional - Leaner Modernizr builds (new)

Select a generator

yo

Generator options

yo webapp # to use webapp generator directly

Generators

webapp hola ke hase

The reference generator

https://github.com/yeoman/generator-webapp

  • CSS Autoprefixing (new)
  • Built-in preview server with LiveReload
  • Automagically compile CoffeeScript & Sass
  • Automagically lint your scripts
  • Automagically wire up your Bower components with bower-install.
  • Awesome Image Optimization (via OptiPNG, pngquant, jpegtran and gifsicle)
  • Mocha Unit Testing with PhantomJS
  • Optional - Bootstrap for Sass
  • Optional - Leaner Modernizr builds (new)

webapp generator

The reference generator

https://github.com/yeoman/generator-webapp

# Install reveal generator
npm install -g generator-webapp

# Create a dir for the project
mkdir project; cd project;

# Scaffold
yo webapp

# Start server
grunt serve

# Build project
grunt build

reveal generator

https://github.com/slara/generator-reveal

Create revealjs slides.

# Install reveal generator
npm install -g generator-reveal

# Create a dir for the slides
mkdir slides; cd slides;

# Scaffold
yo reveal

# Create a new slide
yo reveal:slide "Slide Title" --markdown

# Start grunt server
grunt server

# Build
grunt build

Slide list

slides/list.json

[
    "index.md",
    [
        "vertical-html.html",
        "vertical-markdown.md"
    ],
    [
        "vertical-html-2.html",
        "vertical-markdown-2.md"
    ],
    "the-end.md"
]

markdown slide example

slides/slide.md

<!-- .slide: data-background="#ff0000" -->

* item1 <!-- .element: class="fragment" data-fragment-index="2" -->

* item2 <!-- .element: class="fragment" data-fragment-index="1" -->

Angular Full Stack Generator

https://github.com/DaftMonk/generator-angular-fullstack

Yeoman generator for creating MEAN stack applications, using MongoDB, Express, AngularJS, and Node.

  • Express server integrated with grunt tasks
  • Livereload of client and server files
  • Support for Jade and CoffeeScript
  • Easy deployment workflow.
  • Optional MongoDB integration
  • Optional Passport integration for adding user accounts
npm install -g generator-angular-fullstack

# Launch your express server in development mode.
grunt serve

# Launch your express server in debug-brk mode with a node-inspector tab.
grunt serve:debug

# Launch your express server in production mode, uses the minified/optimized production folder.
grunt serve:dist

Bower

A package manager for the web

Install bower

npm install -g bower

bower.json

Create a bower.json file

bower init
{
  "name": "webapp",
  "private": true,
  "dependencies": {
    "bootstrap-sass-official": "~3.1.0",
    "modernizr": "~2.6.2",
    "jquery": "~1.11.0"
  },
  "devDependencies": {}
}

Install dependencies

Install the dependencies listed in the current directory's bower.json

bower install
bower search [<name>]

Install a package

bower install <package> --save

# Using a different name and a specific version of a package
bower install <name>=<package>#<version> --save

Conflicts

Grunt bowerInstall

Grunt bowerInstall task will install automatically js and css files in the app/index.html file if the dependency defines a main.js entry in its bower.json file.

grunt bowerInstall

Note: gruntfile.js created with latest yeoman webapp generator will install dependencies automatically.

The primary endpoints of the installed packages.

app/index.html

<!-- build:js scripts/vendor.js -->
<!-- bower:js -->
<script src="../bower_components/jquery/dist/jquery.js"></script>
<script src="../bower_components/bootstrap-sass-.../affix.js"></script>
<script src="../bower_components/bootstrap-sass-.../alert.js"></script>
...
<!-- endbower -->
<!-- endbuild -->

app/styles/main.scss

$icon-font-path: "/bower_components/bootstrap-sass-.../fonts/bootstrap/";

// bower:scss
@import '../../bower_components/bootstrap-sass-.../bootstrap.scss';
// endbower

Custom install dir

Configurable through .bowerrc file.

{
  "directory": "public/bower_components"
}

GRUNT

The JavaScript Task Runner

Gruntfile.js

'use strict';
// wrapper function
module.exports = function (grunt) {
  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);
  // Define the configuration for all the tasks
  grunt.initConfig({
  });
  // Load manually a plugin.
  grunt.loadNpmTasks('grunt-contrib-uglify');
  // Define custom tasks and aliases
  grunt.registerTask('wait', function () {});

  grunt.registerTask('default', [
    'newer:jshint',
    'build'
  ]);
};

Tasks

grunt.initConfig({
  concat: {
    // concat task configuration goes here.
  },
  uglify: {
    // uglify task configuration goes here.
  },
  // Arbitrary non-task-specific properties.
  my_property: 'whatever',
  my_src_files: ['foo/*.js', 'bar/*.js'],
});

Task Configuration and Targets

grunt.initConfig({
  concat: {
    foo: {
      // concat task "foo" target options and files go here.
    },
    bar: {
      // concat task "bar" target options and files go here.
    },
  },
  uglify: {
    bar: {
      // uglify task "bar" target options and files go here.
    },
  },
});
grunt concat:foo

Grunt custom tasks

grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);
grunt.registerTask('foo', 'My "foo" task.', function() {
  grunt.log.writeln('Currently running the "default" task.');
  // Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
  grunt.task.run('bar', 'baz');
  // Or:
  grunt.task.run(['bar', 'baz']);
});

Example: wait untill the server is up

grunt.registerTask('wait', function () {
    var interval, socket, done = this.async();
    grunt.log.ok('Waiting for server reload...');

    socket = new require('net').Socket();
    socket.on('error', function () {});
    socket.connect(grunt.config.data.yeoman.port, function () {
        socket.destroy();
        clearInterval(interval);
        grunt.log.writeln('Done waiting!');
        done();
    });

    interval = setInterval(function () {
        socket.connect(grunt.config.data.yeoman.port);
    }, 100);
});

Files

// Compact Format
concat: {
  src: ['src/bb.js', 'src/bbb.js'],
  dest: 'dest/b.js',
},
// Files Object Format
concat: {
  files: {
    'dest/a.js': ['src/aa.js', 'src/aaa.js'],
    'dest/a1.js': ['src/aa1.js', 'src/aaa1.js'],
  },
},
// Files Array Format
concat: {
  files: [
    {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'},
    {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'},
  ],
},

Building the files object dynamically

files: [
  {
    expand: true,     // Enable dynamic expansion.
    cwd: 'lib/',      // Src matches are relative to this path.
    src: ['**/*.js'], // Actual pattern(s) to match.
    dest: 'build/',   // Destination path prefix.
    ext: '.min.js',   // Dest filepaths will have this extension.
    extDot: 'first',  // Extensions in filenames begin after the first dot
    flatten: false    // Remove all path parts from generated dest paths.
  },
],

Globbing patterns

  • * matches any number of characters, but not /
  • ? matches a single character, but not /
  • ** matches any number of characters, including /, as long as it's the only thing in a path part
  • {} allows for a comma-separated list of "or" expressions
  • ! at the beginning of a pattern will negate the match


Tricky examples

// Here, bar.js is first, followed by the remaining files, in alpha order:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}

// All files except for bar.js, in alpha order:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}

Grunt plugins

Lets take a look into the plugins used by generator-webapp

Grunt contrib copy

https://github.com/gruntjs/grunt-contrib-copy

Copy files and folders.

copy: {
  dist: {
    files: [{
      expand: true,
      dot: true,
      cwd: '<%= yeoman.app %>',
      dest: '<%= yeoman.dist %>/public',
      src: [
        '*.{ico,png,txt}',
        '.htaccess',
        'bower_components/**/*',
        'images/{,*/}*.{webp}',
        'fonts/**/*'
      ]
    }]
  },
},

grunt-bower-install

https://github.com/stephenplusplus/grunt-bower-install

Inject your Bower dependencies right into your HTML from Grunt.

Gruntfile.js

bowerInstall: {
    app: {
        src: [
            // html support
            '<%= config.app %>/index.html'
            '<%= config.app %>/**/*.html',
            // jade support
            '<%= config.app %>/views/**/*.jade',
            // .scss & .sass support
            '<%= config.app %>/styles/main.scss',
        ],
        exclude: ['bower_components/bootstrap/dist/js/bootstrap.js']
    }
},

app/index.html

<!-- bower:js -->
<script src="../bower_components/jquery/dist/jquery.js"></script>
<!-- endbower -->

app/styles/main.scss

$icon-font-path: "/bower_components/bootstrap-sass-official/...";

// bower:scss
@import '../../bower_components/.../bootstrap.scss';
// endbower
# Run the task from command line
grunt bowerInstall

# The latest webapp watches bower.json and runs bowerInstall
grunt watch

Grunt-usemin

https://github.com/yeoman/grunt-usemin

Replaces references to non-optimized scripts or stylesheets.

usemin exports 2 different tasks:

  • useminPrepare.

    • prepares the configuration to transform specific construction (blocks) into a single line.
    • transformation flow: concat -> uglifyjs
  • usemin

    • replaces the blocks by the file they reference
    • references to assets by their revisioned version (grunt-rev)

useminPrepare

app/index.html

<!-- build:js js/main.js -->
<script src="js/app.js"></script>
<script src="js/controllers/thing-controller.js"></script>
<script src="js/models/thing-model.js"></script>
<script src="js/views/thing-view.js"></script>
<!-- endbuild -->

Gruntfile.js

useminPrepare: {
    options: {
        dest: '<%= config.dist %>'
    },
    html: '<%= config.app %>/index.html'
},

Generated configuration for concat and uglify tasks

{
  concat: {
    '.tmp/concat/js/main.js': [
      'app/js/app.js',
      'app/js/controllers/thing-controller.js',
      'app/js/models/thing-model.js',
      'app/js/views/thing-view.js'
    ]
  },
  uglifyjs: {
    'dist/js/main.js': ['.tmp/concat/js/main.js']
  }
}

Performs rewrites based on rev and the useminPrepare configuration

usemin: {
  options: {
      assetsDirs: ['<%= config.dist %>', '<%= config.dist %>/images']
  },
  html: ['<%= config.dist %>/{,*/}*.html'],
  css: ['<%= config.dist %>/styles/{,*/}*.css']
},
<!-- build:js js/main.js -->
<script src="js/app.js"></script>
<script src="js/controllers/thing-controller.js"></script>
<script src="js/models/thing-model.js"></script>
<script src="js/views/thing-view.js"></script>
<!-- endbuild -->

After usemin

<script src="js/main.js"></script>

After usemin + grunt-rev

<script src="js/b6c3df09.main.js"></script>

autoprefixer

https://github.com/nDmitry/grunt-autoprefixer

  • Forget about vendor prefixes and write normal CSS according to the latest W3C specs.

  • Avoid any special language (like Sass) or special mixins.

  • Uses data from Can I Use, understands which browsers are actual and popular and adds only the necessary vendor prefixes.

Lets take some CSS3

a {
  background: linear-gradient(to top, black, white);
  display: flex
}

::placeholder {
  color: #ccc
}

Supporting the last version of each browser and IE7

autoprefixer: {
    options: {
            browsers: ["last 1 version", "> 1%", "Explorer 7"]
    }
]

We get

a {
  background: -webkit-gradient(linear, left bottom, left top, 
    from(black), to(white));
  background: -webkit-linear-gradient(bottom, black, white);
  background: linear-gradient(to top, black, white);
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex; }

::-webkit-input-placeholder {
  color: #cccccc; }
::-moz-placeholder {
  color: #cccccc; }
:-ms-input-placeholder {
  color: #cccccc; }
::placeholder {
  color: #cccccc; }

Supporting the latest version of each browser

autoprefixer: {
    options: {
            browsers: ['last 1 version']
    }
}

We get:

a {
  background: linear-gradient(to top, black, white);
  display: flex; }

::placeholder {
  color: #cccccc; }

grunt-contrib-watch

https://github.com/gruntjs/grunt-contrib-watch

Watches files for changes and runs tasks based on the changed files.

Gruntfile.js

    watch: {
        sass: {
            files: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
            tasks: ['sass:server', 'autoprefixer']
        },

        js: {
            files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
            tasks: ['newer:jshint:all'],
            options: {
                livereload: true
            }
        },
    }

Grunt sass

https://github.com/sindresorhus/grunt-sass

Compile SCSS to CSS using node-sass (the experimental and superfast Node.js based Sass compiler)

sass: {
    options: {
        sourceMap: true,
        loadPath: [
            'bower_components'
        ]
    },
    server: {
        files: [{
            expand: true,
            cwd: '<%= config.app %>/styles',
            src: ['*.scss'],
            dest: '.tmp/styles',
            ext: '.css'
        }]
    }
},

grunt-contrib-jshint

A tool that helps to detect errors and potential problems in your JavaScript code.

http://www.jshint.com/ https://github.com/gruntjs/grunt-contrib-jshint

Gruntfile.js

  // Make sure code styles are up to par and there are no obvious mistakes
  jshint: {
    options: {
      jshintrc: '.jshintrc',
      reporter: require('jshint-stylish')
    },
    // Check the node backend files
    server: {
      options: {
        jshintrc: 'lib/.jshintrc'
      },
      src: [ 'lib/{,*/}*.js']
    },
  },

grunt-contrib-connect

https://github.com/gruntjs/grunt-contrib-connect

Start a connect web server.

connect: {
    options: {
        port: 9000,
        open: true,
        livereload: 35729,
        // Change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
    },
    livereload: {
        options: {
            middleware: function(connect) {
                return [
                    // Add created files
                    connect.static('.tmp'),
                    // Add files under /bower_components prefix
                    connect().use('/bower_components', 
                        connect.static('./bower_components')),
                    // Add content under app/
                    connect.static(config.app),
                ];
            }
        }
    },
}

Grunt connect proxy

https://github.com/drewzboto/grunt-connect-proxy

Provides a http proxy as middleware for the grunt-contrib-connect plugin.

Add a proxy to avoid CORS issues when developing against a backend

connect: { ...
    livereload: {
        options: {
            middleware: function(connect) {
                return [
                    ...
                    connect.static(config.app),
                    require('grunt-connect-proxy/lib/utils').proxyRequest,
                ];
            }
        }
    },
    proxies: [{
            context: '/',
            host: 'localhost'
    }]
}

Grunt concurrent

https://github.com/sindresorhus/grunt-concurrent

Run some tasks in parallel

  • to speed up the build process, for example :-)

Gruntfile.js

    concurrent: {
      server: [
        'compass:server',
        'jade:compile',
        'copy:styles'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin',
        'htmlmin'
      ]
    },

Grunt ngmin

https://github.com/btford/grunt-ngmin

Grunt plugin for pre-minifying Angular apps

Turns this

angular.module('whatever').controller('MyCtrl', function ($scope, $http) { ... });

Into

angular.module('whatever').controller('MyCtrl', ['$scope', '$http', function ($scope, $http) { ... }]);
// Allow the use of non-minsafe AngularJS files. Automatically makes it
// minsafe compatible so Uglify does not destroy the ng references
ngmin: {
  dist: {
    files: [{
      expand: true,
      cwd: '.tmp/concat/scripts',
      src: '*.js',
      dest: '.tmp/concat/scripts'
    }]
  }
},

Assemble.io

https://github.com/assemble/assemble

A static site generator for Node.js, Grunt.js, and Yeoman.

Why?

  • Static generation.

  • Partials to create reusable fragments.

  • Layouts to wrap pages with commonly used elements.

  • Pages defined as HTML/templates, JSON or YAML.

  • Template data in JSON, YAML, YAML front matter, or passed directly as an object.

# Install assemble generator
npm install -g generator-assemble

# Create a dir for the slides
mkdir assemble; cd assemble;

# Scaffold
yo assemble

# Start grunt server
grunt serve

# Build
grunt build

Yo assemble generator

src/
├── content
│   ├── about.md
│   └── index.md
├── data
│   └── site.yml
└── templates
    ├── layouts
    │   ├── minisite.hbs
    │   └── default.hbs
    ├── pages
    │   ├── about.hbs
    │   ├── blog.hbs
    │   └── index.hbs
    └── partials
        ├── carousel.hbs
        └── navbar-fixed-top.hbs

Gruntfile.js

assemble: {
    options: {
        flatten: true,
        assets: '<%= config.dist %>/assets',
        layout: 'default.hbs',
        layoutdir: '<%= config.src %>/templates/layouts/',
        data: '<%= config.src %>/data/*.{json,yml}',
        partials: '<%= config.src %>/templates/partials/*.hbs',
        plugins: ['assemble-contrib-permalinks'],
    },
    // Targets
    server: {
        files: {
            '.tmp/': ['<%= config.src %>/templates/pages/**/*.hbs']
        }
    },
    dist: {
        files: {
            '<%= config.dist %>/': ['src/templates/pages/**/*.hbs']
        }
    }
},

The context

Data object passed to the templates

Use the data filename to access properties


src/data/myTemplate.json

{
  "title ": "Heads up!" 
}

src/templates/pages/myTemplate.html

<h1 > {{alert.title }} </h1 >

Root context

  • Is where the data object starts

  • data.{json,yaml} gets loaded into the root of the context


src/data/data.json

{
  "title ": "My Title" 
}

src/templates/pages/myTemplate.hbs

{{title }}

"this" expression

This expression in any context to refer to the current context.

src/data/people.json

[
  "Jon Schlinkert",
  "Brian Woodward" 
]

src/templates/pages/people.html

<ul> 
  {{#each people }} 
  <li> {{this }} </li > 
  {{/each }} 
</ul>
<ul>
  <li>Jon Schlinkert</li>
  <li>Brian Woodward</li>
</ul>

YAML front matter

An optional section of valid YAML that is placed at the top of a page and is used for maintaining metadata for the page and its contents.

---
title: YAML Front Matter
description: A very simple way to add structured data to a page.
---
<h1> {{ title }} </h1>
<p> {{ description }} </p>
Page content here...

Layouts

Layouts are used for "wrapping" the content of individual pages with common elements

src/
└── templates
    └── layouts
        ├── sidebar.hbs
        └── default.hbs

Gruntfile.js

assemble: {
  options: {
    layout: 'default.hbs',
    layoutdir: '<%= config.src %>/templates/layouts/',
  },
  // Targets with parametrized options
  docs: {
    options: {layout: 'docs-layout.hbs' },
    files: {'docs/': ['src/docs/*.hbs' ]},
  },
  site: {
    options: {layout: 'site-layout.hbs' },
    files: {'site/': ['src/site/*.hbs' ]},
  }
}

src/template/layouts/default.hbs

<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset ="UTF-8">
    <title> {{title }} </title> 
  </head> 
  <body> 
    {{> body  }} 
  </body> 
</html>

Nested layouts

src/template/layouts/sidebar.hbs

---
layout: default.hbs
---
<div class="row">
  <div class="col-lg-3">
    <div class="sidebar">
        ...
    </div>
  </div>
  <div class="col-lg-9">
    {{> body }}
  </div>
</div>

Pages

src/
└── templates
    └── pages
        ├── about.hbs
        ├── blog.hbs
        ├── showcase.hbs
        └── index.hbs

Pages collection

Pages are automatically available through the pages object.

<ul>
  {{#each pages}}
  <li{{#if this.isCurrentPage}} class="active"{{/if}}>
     <a href="{{relative dest this.dest}}">{{ data.title }}</a>
  </li>
  {{/each}}
</ul>

Markdown page

src/templates/pages/markdown.hbs

---
title: Introducción
---

{{md 'src/content/wiki/introduccion.md'}}

{{md 'src/content/wiki/sass.md'}}

Advanced example

src/templates/pages/example.hbs

---
title: Examples
description: "One Partial, Many Possibilities"

datos: 
    class: panel
    content: Hola que tal!!?
---

<div class="page-header">
  <h1>{{title}}</h1>
  {{> module datos}}
</div>

src/templates/partials/module.hbs

<div class="{{class}}">
  {{#if content}}<h2>{{{content}}}</h2>{{/if}}  
</div>

handlebars

http://assemble.io/helpers/

{{#each}}, {{#if}} and {{#unless}}

{{embed 'src/code-examples/*.*'}}

{{#eachProperty object}}
    {{key}} - {{value}}<br/>
{{/eachProperty }}

<a href="{{_relative "src" "dist"}}/assets/css/styles.css"></a>
<a href="../../dist/assets/css/styles.css"></a>

Partials

Reusable fragments

src/
└── templates
    └── partials
        ├── carousel.hbs
        ├── modal-window.hbs
        └── navbar-fixed-top.hbs

Para incrustarlo en algún lugar simplemente escribimos \{{> navbar-fixed-top }}

Yo assemble generator

src/
├── content
│   ├── about.md
│   └── index.md
├── data
│   └── site.yml
└── templates
    ├── layouts
    │   ├── minisite.hbs
    │   └── default.hbs
    ├── pages
    │   ├── about.hbs
    │   ├── blog.hbs
    │   └── index.hbs
    └── partials
        ├── carousel.hbs
        └── navbar-fixed-top.hbs

Content

  • Assemble generator creates a dir for placing the content.
src/
└── content
    │   └── graficas/evolucion.html
    ├── about.html
    └── index.md

Site global data

src/data/site.yaml

title: Generator_tests
description: Irontec: Internet y Sistemas sobre GNU / LinuX
ogimage: images/Irontec.png
keywords: 
    - irontec
    - linux
    - GNU/Linux

src/templates/layouts/default.hbs

<title>{{title}} | {{site.title}}</title>
<meta name="description" content="{{site.description}}">
<meta name="keywords" content="{{site.keywords}}" />
<meta property="og:image" content="{{site.ogimage}}"/>