text:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="generator" content="Asciidoctor 0.1.4">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ember.js 101: From Zero to Ember in PubNub Seconds</title>
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; }
audio, canvas, video { display: inline-block; }
audio:not([controls]) { display: none; height: 0; }
[hidden] { display: none; }
html { background: #fff; color: #000; font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; }
body { margin: 0; }
a:focus { outline: thin dotted; }
a:active, a:hover { outline: 0; }
h1 { font-size: 2em; margin: 0.67em 0; }
abbr[title] { border-bottom: 1px dotted; }
b, strong { font-weight: bold; }
dfn { font-style: italic; }
hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }
mark { background: #ff0; color: #000; }
code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; }
pre { white-space: pre-wrap; }
q { quotes: "\201C" "\201D" "\2018" "\2019"; }
small { font-size: 80%; }
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sup { top: -0.5em; }
sub { bottom: -0.25em; }
img { border: 0; }
svg:not(:root) { overflow: hidden; }
figure { margin: 0; }
fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
legend { border: 0; padding: 0; }
button, input, select, textarea { font-family: inherit; font-size: 100%; margin: 0; }
button, input { line-height: normal; }
button, select { text-transform: none; }
button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; }
button[disabled], html input[disabled] { cursor: default; }
input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; }
input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; }
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
textarea { overflow: auto; vertical-align: top; }
table { border-collapse: collapse; border-spacing: 0; }
*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
html, body { font-size: 100%; }
body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; }
a:hover { cursor: pointer; }
a:focus { outline: none; }
img, object, embed { max-width: 100%; height: auto; }
object, embed { height: 100%; }
img { -ms-interpolation-mode: bicubic; }
#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; }
.left { float: left !important; }
.right { float: right !important; }
.text-left { text-align: left !important; }
.text-right { text-align: right !important; }
.text-center { text-align: center !important; }
.text-justify { text-align: justify !important; }
.hide { display: none; }
.antialiased, body { -webkit-font-smoothing: antialiased; }
img { display: inline-block; vertical-align: middle; }
textarea { height: auto; min-height: 50px; }
select { width: 100%; }
p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; }
.subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { line-height: 1.4; color: #7a2518; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; }
div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; }
a { color: #005498; text-decoration: underline; line-height: inherit; }
a:hover, a:focus { color: #00467f; }
a img { border: none; }
p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; text-rendering: optimizeLegibility; }
p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; }
h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: Georgia, "URW Bookman L", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; color: #ba3925; text-rendering: optimizeLegibility; margin-top: 1em; margin-bottom: 0.5em; line-height: 1.2125em; }
h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #e99b8f; line-height: 0; }
h1 { font-size: 2.125em; }
h2 { font-size: 1.6875em; }
h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; }
h4 { font-size: 1.125em; }
h5 { font-size: 1.125em; }
h6 { font-size: 1em; }
hr { border: solid #dddddd; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; }
em, i { font-style: italic; line-height: inherit; }
strong, b { font-weight: bold; line-height: inherit; }
small { font-size: 60%; line-height: inherit; }
code { font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: normal; color: #6d180b; }
ul, ol, dl { font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; }
ul, ol { margin-left: 1.5em; }
ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; }
ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; }
ul.square { list-style-type: square; }
ul.circle { list-style-type: circle; }
ul.disc { list-style-type: disc; }
ul.no-bullet { list-style: none; }
ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; }
dl dt { margin-bottom: 0.3125em; font-weight: bold; }
dl dd { margin-bottom: 1.25em; }
abbr, acronym { text-transform: uppercase; font-size: 90%; color: #222222; border-bottom: 1px dotted #dddddd; cursor: help; }
abbr { text-transform: none; }
blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
blockquote cite { display: block; font-size: inherit; color: #555555; }
blockquote cite:before { content: "\2014 \0020"; }
blockquote cite a, blockquote cite a:visited { color: #555555; }
blockquote, blockquote p { line-height: 1.6; color: #6f6f6f; }
.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; }
.vcard li { margin: 0; display: block; }
.vcard .fn { font-weight: bold; font-size: 0.9375em; }
.vevent .summary { font-weight: bold; }
.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; }
@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; }
  h1 { font-size: 2.75em; }
  h2 { font-size: 2.3125em; }
  h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; }
  h4 { font-size: 1.4375em; } }
.print-only { display: none !important; }
@media print { * { background: transparent !important; color: #000 !important; box-shadow: none !important; text-shadow: none !important; }
  a, a:visited { text-decoration: underline; }
  a[href]:after { content: " (" attr(href) ")"; }
  abbr[title]:after { content: " (" attr(title) ")"; }
  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
  pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
  thead { display: table-header-group; }
  tr, img { page-break-inside: avoid; }
  img { max-width: 100% !important; }
  @page { margin: 0.5cm; }
  p, h2, h3, #toctitle, .sidebarblock > .content > .title { orphans: 3; widows: 3; }
  h2, h3, #toctitle, .sidebarblock > .content > .title { page-break-after: avoid; }
  .hide-on-print { display: none !important; }
  .print-only { display: block !important; }
  .hide-for-print { display: none !important; }
  .show-for-print { display: inherit !important; } }
table { background: white; margin-bottom: 1.25em; border: solid 1px #dddddd; }
table thead, table tfoot { background: whitesmoke; font-weight: bold; }
table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: #222222; text-align: left; }
table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: #222222; }
table tr.even, table tr.alt, table tr:nth-of-type(even) { background: #f9f9f9; }
table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.6; }
.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; }
.clearfix:after, .float-group:after { clear: both; }
*:not(pre) > code { font-size: 0.9375em; padding: 1px 3px 0; white-space: nowrap; background-color: #f2f2f2; border: 1px solid #cccccc; -webkit-border-radius: 4px; border-radius: 4px; text-shadow: none; }
pre, pre > code { line-height: 1.4; color: inherit; font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: normal; }
kbd.keyseq { color: #555555; }
kbd:not(.keyseq) { display: inline-block; color: #222222; font-size: 0.75em; line-height: 1.4; background-color: #F7F7F7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; margin: -0.15em 0.15em 0 0.15em; padding: 0.2em 0.6em 0.2em 0.5em; vertical-align: middle; white-space: nowrap; }
kbd kbd:first-child { margin-left: 0; }
kbd kbd:last-child { margin-right: 0; }
.menuseq, .menu { color: #090909; }
p a > code:hover { color: #561309; }
#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; }
#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; }
#header:after, #content:after, #footnotes:after, #footer:after { clear: both; }
#header { margin-bottom: 2.5em; }
#header > h1 { color: black; font-weight: normal; border-bottom: 1px solid #dddddd; margin-bottom: -28px; padding-bottom: 32px; }
#header span { color: #6f6f6f; }
#header #revnumber { text-transform: capitalize; }
#header br { display: none; }
#header br + span { padding-left: 3px; }
#header br + span:before { content: "\2013 \0020"; }
#header br + span.author { padding-left: 0; }
#header br + span.author:before { content: ", "; }
#toc { border-bottom: 3px double #ebebeb; padding-bottom: 1.25em; }
#toc > ul { margin-left: 0.25em; }
#toc ul.sectlevel0 > li > a { font-style: italic; }
#toc ul.sectlevel0 ul.sectlevel1 { margin-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
#toc ul { list-style-type: none; }
#toctitle { color: #7a2518; }
@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; }
  #toc.toc2 { position: fixed; width: 20em; left: 0; top: 0; border-right: 1px solid #ebebeb; border-bottom: 0; z-index: 1000; padding: 1em; height: 100%; overflow: auto; }
  #toc.toc2 #toctitle { margin-top: 0; }
  #toc.toc2 > ul { font-size: .95em; }
  #toc.toc2 ul ul { margin-left: 0; padding-left: 1.25em; }
  #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
  body.toc2.toc-right { padding-left: 0; padding-right: 20em; }
  body.toc2.toc-right #toc.toc2 { border-right: 0; border-left: 1px solid #ebebeb; left: auto; right: 0; } }
#content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; border-width: 0; -webkit-border-radius: 4px; border-radius: 4px; }
#content #toc > :first-child { margin-top: 0; }
#content #toc > :last-child { margin-bottom: 0; }
#content #toc a { text-decoration: none; }
#content #toctitle { font-weight: bold; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 1em; padding-left: 0.125em; }
#footer { max-width: 100%; background-color: #222222; padding: 1.25em; }
#footer-text { color: #dddddd; line-height: 1.44; }
.sect1 { padding-bottom: 1.25em; }
.sect1 + .sect1 { border-top: 3px double #ebebeb; }
#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; width: 1em; margin-left: -1em; display: block; text-decoration: none; visibility: hidden; text-align: center; font-weight: normal; }
#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: '\00A7'; font-size: .85em; vertical-align: text-top; display: block; margin-top: 0.05em; }
#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; }
#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #ba3925; text-decoration: none; }
#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #a53221; }
.imageblock, .literalblock, .listingblock, .verseblock, .videoblock { margin-bottom: 1.25em; }
.admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-align: left; font-weight: bold; }
.tableblock > caption { text-align: left; font-weight: bold; white-space: nowrap; overflow: visible; max-width: 0; }
table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; }
.admonitionblock > table { border: 0; background: none; width: 100%; }
.admonitionblock > table td.icon { text-align: center; width: 80px; }
.admonitionblock > table td.icon img { max-width: none; }
.admonitionblock > table td.icon .title { font-weight: bold; text-transform: uppercase; }
.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #dddddd; color: #6f6f6f; }
.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; }
.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 4px; border-radius: 4px; }
.exampleblock > .content > :first-child { margin-top: 0; }
.exampleblock > .content > :last-child { margin-bottom: 0; }
.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { color: #333333; }
.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { line-height: 1; margin-bottom: 0.625em; }
.exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { line-height: 1.4; }
.exampleblock.result > .content { -webkit-box-shadow: 0 1px 8px #d9d9d9; box-shadow: 0 1px 8px #d9d9d9; }
.sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 4px; border-radius: 4px; }
.sidebarblock > :first-child { margin-top: 0; }
.sidebarblock > :last-child { margin-bottom: 0; }
.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { color: #333333; }
.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { line-height: 1; margin-bottom: 0.625em; }
.sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { line-height: 1.4; }
.sidebarblock > .content > .title { color: #7a2518; margin-top: 0; line-height: 1.6; }
.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; }
.literalblock > .content pre, .listingblock > .content pre { background: none; border-width: 1px 0; border-style: dotted; border-color: #bfbfbf; -webkit-border-radius: 4px; border-radius: 4px; padding: 0.75em 0.75em 0.5em 0.75em; word-wrap: break-word; }
.literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; }
.literalblock > .content pre > code, .listingblock > .content pre > code { display: block; }
@media only screen { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.8em; } }
@media only screen and (min-width: 768px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.9em; } }
@media only screen and (min-width: 1280px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 1em; } }
.listingblock > .content { position: relative; }
.listingblock:hover code[class*=" language-"]:before { text-transform: uppercase; font-size: 0.9em; color: #999; position: absolute; top: 0.375em; right: 0.375em; }
.listingblock:hover code.asciidoc:before { content: "asciidoc"; }
.listingblock:hover code.clojure:before { content: "clojure"; }
.listingblock:hover code.css:before { content: "css"; }
.listingblock:hover code.groovy:before { content: "groovy"; }
.listingblock:hover code.html:before { content: "html"; }
.listingblock:hover code.java:before { content: "java"; }
.listingblock:hover code.javascript:before { content: "javascript"; }
.listingblock:hover code.python:before { content: "python"; }
.listingblock:hover code.ruby:before { content: "ruby"; }
.listingblock:hover code.scss:before { content: "scss"; }
.listingblock:hover code.xml:before { content: "xml"; }
.listingblock:hover code.yaml:before { content: "yaml"; }
.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; }
.listingblock.terminal pre .command:not([data-prompt]):before { content: '$'; }
table.pyhltable { border: 0; margin-bottom: 0; }
table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; }
table.pyhltable td.code { padding-left: .75em; padding-right: 0; }
.highlight.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #dddddd; }
.highlight.pygments .lineno { display: inline-block; margin-right: .25em; }
table.pyhltable .linenodiv { background-color: transparent !important; padding-right: 0 !important; }
.quoteblock { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
.quoteblock blockquote { margin: 0 0 1.25em 0; padding: 0 0 0.5625em 0; border: 0; }
.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; }
.quoteblock .attribution { margin-top: -.25em; padding-bottom: 0.5625em; font-size: inherit; color: #555555; }
.quoteblock .attribution br { display: none; }
.quoteblock .attribution cite { display: block; margin-bottom: 0.625em; }
table thead th, table tfoot th { font-weight: bold; }
table.tableblock.grid-all { border-collapse: separate; border-spacing: 1px; -webkit-border-radius: 4px; border-radius: 4px; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; }
table.tableblock.frame-topbot, table.tableblock.frame-none { border-left: 0; border-right: 0; }
table.tableblock.frame-sides, table.tableblock.frame-none { border-top: 0; border-bottom: 0; }
table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child { margin-bottom: 0; }
th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; }
th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; }
th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; }
th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; }
th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; }
th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; }
p.tableblock.header { color: #222222; font-weight: bold; }
td > div.verse { white-space: pre; }
ol { margin-left: 1.75em; }
ul li ol { margin-left: 1.5em; }
dl dd { margin-left: 1.125em; }
dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; }
ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; }
ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; }
ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; }
ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; }
ul.checklist li > p:first-child > input[type="checkbox"]:first-child { position: relative; top: 1px; }
ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; }
ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; }
ul.inline > li > * { display: block; }
.unstyled dl dt { font-weight: normal; font-style: normal; }
ol.arabic { list-style-type: decimal; }
ol.decimal { list-style-type: decimal-leading-zero; }
ol.loweralpha { list-style-type: lower-alpha; }
ol.upperalpha { list-style-type: upper-alpha; }
ol.lowerroman { list-style-type: lower-roman; }
ol.upperroman { list-style-type: upper-roman; }
ol.lowergreek { list-style-type: lower-greek; }
.hdlist > table, .colist > table { border: 0; background: none; }
.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; }
td.hdlist1 { padding-right: .8em; font-weight: bold; }
td.hdlist1, td.hdlist2 { vertical-align: top; }
.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; }
.colist > table tr > td:first-of-type { padding: 0 .8em; line-height: 1; }
.colist > table tr > td:last-of-type { padding: 0.25em 0; }
.qanda > ol > li > p > em:only-child { color: #00467f; }
.thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; }
.imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; }
.imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; }
.imageblock > .title { margin-bottom: 0; }
.imageblock.thumb, .imageblock.th { border-width: 6px; }
.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; }
.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; }
.image.left { margin-right: 0.625em; }
.image.right { margin-left: 0.625em; }
a.image { text-decoration: none; }
span.footnote, span.footnoteref { vertical-align: super; font-size: 0.875em; }
span.footnote a, span.footnoteref a { text-decoration: none; }
#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; }
#footnotes hr { width: 20%; min-width: 6.25em; margin: -.25em 0 .75em 0; border-width: 1px 0 0 0; }
#footnotes .footnote { padding: 0 0.375em; line-height: 1.3; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.2em; margin-bottom: .2em; }
#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; }
#footnotes .footnote:last-of-type { margin-bottom: 0; }
#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; }
.gist .file-data > table { border: none; background: #fff; width: 100%; margin-bottom: 0; }
.gist .file-data > table td.line-data { width: 99%; }
div.unbreakable { page-break-inside: avoid; }
.big { font-size: larger; }
.small { font-size: smaller; }
.underline { text-decoration: underline; }
.overline { text-decoration: overline; }
.line-through { text-decoration: line-through; }
.aqua { color: #00bfbf; }
.aqua-background { background-color: #00fafa; }
.black { color: black; }
.black-background { background-color: black; }
.blue { color: #0000bf; }
.blue-background { background-color: #0000fa; }
.fuchsia { color: #bf00bf; }
.fuchsia-background { background-color: #fa00fa; }
.gray { color: #606060; }
.gray-background { background-color: #7d7d7d; }
.green { color: #006000; }
.green-background { background-color: #007d00; }
.lime { color: #00bf00; }
.lime-background { background-color: #00fa00; }
.maroon { color: #600000; }
.maroon-background { background-color: #7d0000; }
.navy { color: #000060; }
.navy-background { background-color: #00007d; }
.olive { color: #606000; }
.olive-background { background-color: #7d7d00; }
.purple { color: #600060; }
.purple-background { background-color: #7d007d; }
.red { color: #bf0000; }
.red-background { background-color: #fa0000; }
.silver { color: #909090; }
.silver-background { background-color: #bcbcbc; }
.teal { color: #006060; }
.teal-background { background-color: #007d7d; }
.white { color: #bfbfbf; }
.white-background { background-color: #fafafa; }
.yellow { color: #bfbf00; }
.yellow-background { background-color: #fafa00; }
span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
.admonitionblock td.icon [class^="icon-"]:before { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; }
.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #005498; color: #003f72; }
.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; }
.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; }
.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; }
.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; }
.conum { display: inline-block; color: white !important; background-color: #222222; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; width: 20px; height: 20px; font-size: 12px; font-weight: bold; line-height: 20px; font-family: Arial, sans-serif; font-style: normal; position: relative; top: -2px; letter-spacing: -1px; }
.conum * { color: white !important; }
.conum + b { display: none; }
.conum:after { content: attr(data-value); }
.conum:not([data-value]):empty { display: none; }
.literalblock > .content > pre, .listingblock > .content > pre { -webkit-border-radius: 0; border-radius: 0; }

</style>
<style>
/* Foundation stylesheet for CodeRay (to match GitHub theme) | MIT License | http://foundation.zurb.com */
table.CodeRay { border-collapse: collapse; padding: 2px; margin-bottom: 0; border: 0; background: transparent; }
table.CodeRay td { padding: 0 .5em; vertical-align: top; }
table.CodeRay td.line-numbers { text-align: right; color: #999; border-right: 1px solid #e5e5e5; padding-left: 0; }
span.line-numbers { border-right: 1px solid #E5E5E5; color: #999; display: inline-block; margin-right: 0.5em; padding-right: 0.5em; }
.CodeRay td.line-numbers strong, .CodeRay span.line-numbers strong { font-weight: normal; }
.CodeRay .debug { color: white !important; background: blue !important; }
.CodeRay .annotation { color: #007; }
.CodeRay .attribute-name { color: #f08; }
.CodeRay .attribute-value { color: #700; }
.CodeRay .binary { color: #509; }
.CodeRay .comment  { color: #999; font-style: italic; }
.CodeRay .char { color: #04D; }
.CodeRay .char .content { color: #04D; }
.CodeRay .char .delimiter { color: #039; }
.CodeRay .class { color: #458; }
.CodeRay .complex { color: #A08; }
.CodeRay .constant { color: teal; }
.CodeRay .color { color: #0A0; }
.CodeRay .class-variable { color: #369; }
.CodeRay .decorator { color: #B0B; }
.CodeRay .definition { color: #099; }
.CodeRay .directive { color: #088; }
.CodeRay .delimiter { color: black; }
.CodeRay .doc { color: #970; }
.CodeRay .doctype { color: #34b; }
.CodeRay .doc-string { color: #D42; }
.CodeRay .escape  { color: #666; }
.CodeRay .entity { color: #800; }
.CodeRay .error { color: #808; }
.CodeRay .exception { color: #C00; }
.CodeRay .filename { color: #099; }
.CodeRay .function { color: #900; }
.CodeRay .global-variable { color: teal; }
.CodeRay .hex { color: #058; }
.CodeRay .integer  { color: #099; }
.CodeRay .include { color: #B44; }
.CodeRay .inline { color: black; }
.CodeRay .inline .inline { background: #ccc; }
.CodeRay .inline .inline .inline { background: #bbb; }
.CodeRay .inline .inline-delimiter { color: #D14; }
.CodeRay .inline-delimiter { color: #D14; }
.CodeRay .important { color: #f00; }
.CodeRay .interpreted { color: #B2B; }
.CodeRay .instance-variable { color: teal; }
.CodeRay .label { color: #970; }
.CodeRay .local-variable { color: #963; }
.CodeRay .octal { color: #40E; }
.CodeRay .predefined { color: #369; }
.CodeRay .preprocessor { color: #579; }
.CodeRay .pseudo-class { color: #00C; }
.CodeRay .predefined-type { color: #074; }
.CodeRay .reserved, .keyword  { color: #000; }
.CodeRay .key { color: #808; }
.CodeRay .key .delimiter { color: #606; }
.CodeRay .key .char { color: #80f; }
.CodeRay .value { color: #088; }
.CodeRay .regexp { background-color: #fff0ff; }
.CodeRay .regexp .content { color: #808; }
.CodeRay .regexp .delimiter { color: #404; }
.CodeRay .regexp .modifier { color: #C2C; }
.CodeRay .regexp .function  { color: #404; font-weight: bold; }
.CodeRay .string { color: #D20; }
.CodeRay .string .string { }
.CodeRay .string .string .string { background-color: #ffd0d0; }
.CodeRay .string .content { color: #D14; }
.CodeRay .string .char { color: #D14; }
.CodeRay .string .delimiter { color: #D14; }
.CodeRay .shell { color: #D14; }
.CodeRay .shell .content { }
.CodeRay .shell .delimiter { color: #D14; }
.CodeRay .symbol { color: #990073; }
.CodeRay .symbol .content { color: #A60; }
.CodeRay .symbol .delimiter { color: #630; }
.CodeRay .tag, .CodeRay .attribute-name { color: #070; }
.CodeRay .tag-special { color: #D70; }
.CodeRay .type { color: #339; }
.CodeRay .variable  { color: #036; }
.CodeRay .insert { background: #afa; }
.CodeRay .delete { background: #faa; }
.CodeRay .change { color: #aaf; background: #007; }
.CodeRay .head { color: #f8f; background: #505; }
.CodeRay .insert .insert { color: #080; }
.CodeRay .delete .delete { color: #800; }
.CodeRay .change .change { color: #66f; }
.CodeRay .head .head { color: #f4f; }

</style>
</head>
<body class="article">
<div id="header">
</div>
<div id="content">
<div class="sect1">
<h2 id="_ember_js_101_from_zero_to_ember_in_pubnub_seconds">Ember.js 101: From Zero to Ember in PubNub Seconds</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Translation: "How to get a real-time app working with PubNub
and Ember.js in 60 seconds or less".</p>
</div>
<div class="paragraph">
<p>Here at PubNub, we’ve been paying attention to the incredible ascent
of Ember.js - in the short time it’s been around, it has amassed
a huge following and changed the way we build Web Applications.</p>
</div>
<div class="paragraph">
<p>We thought it would be kind of cool to put together the smallest
possible (but still interesting) sample application showcasing
PubNub and Ember.js.</p>
</div>
<div class="paragraph">
<p>The result is a sample application that fits into 99 lines
of HTML, of which less than 60 is JavaScript. If you’re used
to coding your JavaScript in CoffeeScript, you can expect
that to easily cut in half.</p>
</div>
<div class="paragraph">
<p>Here are the goodies:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="http://pubnub.github.io/pubnub-ember/">(FIXME) Live example in CodePen</a></p>
</li>
<li>
<p><a href="http://github.com/pubnub/pubnub-ember/">Official PubNub Ember.js SDK</a></p>
</li>
<li>
<p>Source for this example in <a href="http://github.com/pubnub/pubnub-ember/blob/master/site/examples/chat/index.html">HTML</a> or <a href="http://github.com/pubnub/pubnub-ember/blob/master/site/examples/chat/index.haml">HAML</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>We’ll walk you through the HTML section by section.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_step_1_get_your_includes_on">Step 1: Get Your Includes On</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To get started, we’ll need to set up the script includes for
PubNub and Ember.js, as well as an optional stylesheet for
Bootstrap styles.</p>
</div>
<div class="paragraph">
<p>For folks who are familiar with <a href="http://bower.io/">Bower</a>, you’ll probably
start by bringing in <code>pubnub-ember</code> package using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code>bower install pubnub-ember</code></pre>
</div>
</div>
<div class="paragraph">
<p>Whether you use Bower or not, your HTML includes will end up looking something like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="doctype"><!doctype html></span>
<span class="tag"><html></span>
<span class="tag"><head></span>
<span class="tag"><script</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://code.jquery.com/jquery-1.10.1.min.js</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></script></span>
<span class="tag"><script</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://cdn.pubnub.com/pubnub.min.js</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></script></span>
<span class="tag"><script</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-1.0.0.js</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></script></span>
<span class="tag"><script</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://builds.emberjs.com/release/ember.js</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></script></span>
<span class="tag"><script</span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">components/pubnub-ember/pubnub-ember.js</span><span class="delimiter">"</span></span><span class="tag">></span><span class="tag"></script></span>
<span class="tag"><link</span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">stylesheet</span><span class="delimiter">"</span></span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"></head></span>
<span class="tag"><body></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>What does all this stuff do?</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>jquery-1.10.1.min.js</code>: bring in some JQuery (used by Ember.js)</p>
</li>
<li>
<p><code>handlebars-1.0.0.js</code>: bring in some Handlebars (used by Ember.js)</p>
</li>
<li>
<p><code>pubnub.min.js</code>: the main PubNub communication library for JavaScript</p>
</li>
<li>
<p><code>ember.js</code>: that Ember.js goodness we all know and love</p>
</li>
<li>
<p><code>pubnub-ember.js</code>: bring in the official PubNub SDK for Ember.js</p>
</li>
<li>
<p><code>bootstrap.min.css</code>: bring in the bootstrap styles</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Once these are all set, you’re good to start coding!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_step_2_set_up_your_html_layout_and_dynamic_content">Step 2: Set Up Your HTML Layout and Dynamic Content</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let’s get the HTML set up as an application template:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag"><script</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/x-handlebars</span><span class="delimiter">"</span></span> <span class="attribute-name">data-template-name</span>=<span class="string"><span class="delimiter">"</span><span class="content">application</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="inline"><div <span class="reserved">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">container</span><span class="delimiter">"</span></span>></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Ember.js needs to be able to find your app’s view. To make that happen,
we use an inline script of type <code>text/x-handlebars</code> containing the div
element we want to Ember.js-ify. When Ember.js tries to display our
application, it’ll look for the handlebars template named "application".</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag"><h4></span>Online Users<span class="tag"></h4></span>
<span class="tag"><ul></span>
  {{#each user in users}}
  <span class="tag"><li></span>{{user}}<span class="tag"></li></span>
  {{/each}}
<span class="tag"></ul></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Wow, how awesome is that? We can create a dynamic list of users
simply by using a <code>ul</code> element and an <code>li</code> element that’s
set up to iterate over all of the items in the <code>users</code> property
of the controller. For the purposes of this demo, each user object
is a simple string that comes from the channel’s presence list.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag"><h4></span>Chat History {{messages.length}}<span class="tag"></h4></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Just a header. Nothing to see here. One thing that’s kind of nifty is
that we substitute in the length attribute from the controller’s
<code>messages</code> array.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag"><form></span>
  {{input type="text" value=new_message placeholder="Enter a message"}}
  <span class="tag"><input</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">submit</span><span class="delimiter">"</span></span> <span class="error">{</span><span class="error">{</span><span class="attribute-name">action</span> <span class="attribute-name">publish</span><span class="error">}</span><span class="error">}</span> <span class="tag">/></span>
<span class="tag"></form></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This is the first interactive feature - a simple text box
that binds its content to the <code>new_message</code> property of the
controller, and a submit button for the form. The form submit
function is bound to the <code>publish</code> action in the scope. What does
it do? We’ll find out soon!</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag"><div</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">well</span><span class="delimiter">"</span></span><span class="tag">></span>
<span class="tag"><ul></span>
{{#each message in messages}}
<span class="tag"><li></span>{{message}}<span class="tag"></li></span>
{{/each}}
<span class="tag"></ul></span>
<span class="tag"></div></span>
<span class="tag"></div></span>
<span class="tag"></script></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Now that you’re already an Ember.js and PubNub expert, you
can see that this is just a dynamic collection of messages
from the controller’s <code>messages</code> object.</p>
</div>
<div class="paragraph">
<p>Not too shabby! But you may ask, how does it all work? Let’s
check out the JavaScript!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_step_3_javascript_where_the_magic_happens">Step 3: JavaScript - Where the Magic Happens</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let’s walk through the JavaScript and see how it’s all put
together.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript"><script>
window.Mini = Ember.Application.create();
<span class="keyword">var</span> user_id = <span class="string"><span class="delimiter">"</span><span class="content">User </span><span class="delimiter">"</span></span> + Math.round(Math.random() * <span class="integer">1000</span>);

Mini.PubNub = PubNubEmber.extend({
  <span class="key">cfg</span>: {
    <span class="key">subscribe_key</span>: <span class="string"><span class="delimiter">'</span><span class="content">demo</span><span class="delimiter">'</span></span>,
    <span class="key">publish_key</span>: <span class="string"><span class="delimiter">'</span><span class="content">demo</span><span class="delimiter">'</span></span>,
    <span class="key">uuid</span>: user_id
  }
});

Mini.ApplicationController = Ember.Controller.extend({
  <span class="key">needs</span>:[<span class="string"><span class="delimiter">'</span><span class="content">pubnub:main</span><span class="delimiter">'</span></span>],</code></pre>
</div>
</div>
<div class="paragraph">
<p>Let’s take this piece by piece:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>We create a normal <code>script</code> tag in the body - that’s easy enough. You may keep your JS in a separate file - that’s totally cool.</p>
</li>
<li>
<p>The next part is defining an Ember.js application where all of this lives.</p>
</li>
<li>
<p>We set up a random <code>user_id</code> for our application (your app will probably do something different).</p>
</li>
<li>
<p>We then initialize a <code>PubNubEmber</code> service that will be our bridge into the PubNub world.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Of course, for <strong>your</strong> application, you should initialize the service
with your own publish and subscribe keys from your PubNub account.</p>
</div>
<div class="paragraph">
<p>It’s important to initialize the PubNub only once during
the course of the application. The UUID is the globally
unique user ID you’d like to use for identifying the user.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>We start initializing an ApplicationController that will hold all of our application logic</p>
</li>
<li>
<p>First, we declare an Ember.js dependency on the <code>pubnub:main</code> object within the controller using Ember’s <code>needs</code> keyword</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This is the magic that lets us access the PubNub service within our
application.</p>
</div>
<div class="paragraph">
<p>The <code>controller</code> object is an Ember.js controller - that’s
the place where all of the data and functions for our application
will live. Again, notice how we’re injecting the PubNub service into
our controller - that’s how we get access to all of the Real-Time
goodness that PubNub provides.</p>
</div>
<div class="paragraph">
<p>Let’s take a look at the body of the controller object:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">  channel: <span class="string"><span class="delimiter">'</span><span class="content">The EmberJS Channel</span><span class="delimiter">'</span></span>,
  <span class="key">new_message</span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
  <span class="key">user_id</span>: user_id,
  <span class="comment">// Ember Dynamic collection for messages list (live-updates the view)</span>
  <span class="key">messages</span>: Ember.ArrayProxy.create({ <span class="key">content</span>: Ember.A([<span class="string"><span class="delimiter">'</span><span class="content">Welcome to The EmberJS Channel</span><span class="delimiter">'</span></span>]) }),
  <span class="comment">// Ember Dynamic collection for user list (live-updates the view)</span>
  <span class="key">users</span>: Ember.ArrayProxy.create({ <span class="key">content</span>: Ember.A([]) }),</code></pre>
</div>
</div>
<div class="paragraph">
<p>These are pretty self-explanatory - just settings up variables
we’ll use for the application.</p>
</div>
<div class="paragraph">
<p>One <strong>major</strong> thing I should highlight is use of the Ember.js ArrayProxy
class instead of regular JavaScript arrays. For simple/primitive properties
like Strings and Numbers, Ember.js is already set to auto-update the view.
Arrays need a little more magic. By using Ember.js ArrayProxy instances,
we can use live data binding within our view knowing that when
those arrays change in the controller, the changes will be instantly
reflected in the view.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript"><span class="function">init</span>: <span class="keyword">function</span>() {
  <span class="keyword">var</span> pn   = <span class="local-variable">this</span>.get(<span class="string"><span class="delimiter">'</span><span class="content">pubnub</span><span class="delimiter">'</span></span>);   <span class="comment">// PubNub service instance</span>
  <span class="keyword">var</span> chan = <span class="local-variable">this</span>.get(<span class="string"><span class="delimiter">'</span><span class="content">channel</span><span class="delimiter">'</span></span>);  <span class="comment">// channel name</span>
  <span class="keyword">var</span> self = <span class="local-variable">this</span>;                 <span class="comment">// reference to 'this' for callbacks</span>

  <span class="comment">// Subscribe to the Channel</span>
  pn.emSubscribe({ <span class="key">channel</span>: chan });</code></pre>
</div>
</div>
<div class="paragraph">
<p>I bet you can’t tell what that does! You’re right - it
calls the <code>emSubscribe</code> function which creates a
new channel subscription for our app. The channel name
is specified in the variables above. It’s also possible
to subscribe to multiple channels, and PubNub does all the
work to make it easy.</p>
</div>
<div class="paragraph">
<p>Ok, now that we’ve subscribed, how does our app know about
messages coming in?</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">  <span class="comment">// Register for message events</span>
  pn.on(pn.emMsgEv(chan), <span class="keyword">function</span>(payload){
    self.get(<span class="string"><span class="delimiter">'</span><span class="content">messages</span><span class="delimiter">'</span></span>).pushObject(payload.message);
  });</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here we bind an event handler to listen for message events.
The PubNub Ember.js library receives all of those events
coming from the channel and transforms them into Ember.js <code>Evented</code>
events. Here we’re saying that when a message comes in,
push it into the controller’s <code>messages</code> collection. Since
it’s not easy for Ember.js to detect an array push
call, we use the special ArrayProxy <code>pushObject</code> method
to make sure that Ember.js updates the view properly.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">  <span class="comment">// Register for presence events</span>
  pn.on(pn.emPrsEv(chan), <span class="keyword">function</span>(payload){
    self.get(<span class="string"><span class="delimiter">'</span><span class="content">users</span><span class="delimiter">'</span></span>).set(<span class="string"><span class="delimiter">'</span><span class="content">content</span><span class="delimiter">'</span></span>, pn.emListPresence(chan));
  });</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you’d like your app to display contents of the dynamic
user list, we try to keep it easy with the Ember.js
library. In the code snippet above, we register an
event listener for presence events that will update the
controller’s <code>users</code> collection with the user list that
the Ember.js library is keeping track of for us. This
applies to join and leave events. Pretty nifty!</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">  <span class="comment">// Pre-Populate the user list (optional)</span>
  pn.emHereNow({ <span class="key">channel</span>: theChannel });</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you’d like to bring in the user list, just
add the call above - it’ll fire off a presence
event, which will be handled by the presence
handler we registered above using <code>pn.on(pn.emPrsEv(theChannel) ...</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">  <span class="comment">// Populate message history (optional)</span>
  pn.emHistory({
    <span class="key">channel</span>: theChannel,
    <span class="key">count</span>: <span class="integer">500</span>
  });</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you’d like to bring in message history, just
add the call above - it’ll fire all of the message
events, which will be handled by the event handler
we registered above using <code>pn.on(pn.emMsgEv(theChannel) ...</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">});
<<span class="regexp"><span class="delimiter">/</span><span class="content">script>
<</span><span class="delimiter">/</span></span>body>
<<span class="regexp"><span class="delimiter">/</span><span class="content">html></span></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>And we’re done! We hope you found this useful,
please keep in touch and reach out if you have <a href="https://github.com/pubnub/pubnub-ember/issues">ideas</a>.
Or, if you <a href="mailto:help@pubnub.com">need a hand</a>!</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2014-08-07 11:21:53 EDT
</div>
</div>
</body>
</html>


text:
layout: post
title: What We Justify (and What We Don't)
author: Manal Ghosain
categories:
tags:
---

![Looking at the clouds](/images/clouds-bw.jpg)

Have you ever wondered why we try to justify some actions but not others?

Advertising serves one purpose only: to justify buying a product or service that satisfies a need or desire we may not even know we had.

News networks justify the need for 24 hour broadcast with an incessant stream of negativity that distorts our perception of reality.

Politicians perpetuate the same pointless rhetoric hiding behind fiscal responsibility.

These are a few examples of what we justify. Let's explore our own actions and what we try to justify.

Before looking at the actions, I'd like to highlight a few meanings of justify that are useful in this context.

## What does it mean to justify our actions?

When we need to justify our actions, it means we're trying to do one or more of the following (all can be considered synonyms).

**Rationalize:** We need the action to make sense based on reasons we believe are good enough to warrant it.

**Defend:** We think the action we're taking is the right (and the only possible) one for this situation. So we're willing to defend it with all the mental and emotional arsenal we can muster.

**Explain:** If we feel the action we're taking is opaque, we go on and on, trying to detail why it's good and why it's necessary.

All of the above and more mean one thing: _We justify things that we don't feel can stand on their own merit._

In other words, we tend to justify what doesn't _feel_ right on its own—something is off about it that needs further explanation or defending.

## Why justify?

If I look at the things I tried to justify or observed others doing so, I can fairly say that they mostly relate to the following.

**Morally/ethically questionable actions**: When we are doing something that casts even a slight shadow of impropriety, we start defending and explaining. It can be something like downloading music online, taking stationary from the office, and could escalate to betrayal or personal vendettas.

**Action opposes our truth**: We all have tendencies and ways of doing things that come to us naturally. If we're trying to do something that does not ring true to us, we will try to justify it as much as possible.

If a boss tells you to stab a coworker in the back, you'll need to justify it before you can even contemplate taking action.

In this case, we're trying to convince ourselves before convincing anyone else.

**Destructive in nature**: Any act of violence (physical or emotional) does not come naturally to us. I don't think we can feel safe and want to hurt others. Or we can feel abundance and want to fight over resources. So we fabricate fear and scarcity to justify the actions.

In order to destroy, exploit, or control, we need to come up with more than a few reasons for doing it.

On the other hand, truthful actions come to us naturally. We won't feel the need to defend or explain them. We let the actions speak for themselves.

## What's so bad about justifying?

One might ask so what? What's wrong with justifying our actions?

It's not the justifying that's the issue. But what it leads to. Consider these points.

**Ability to justify anything**: Our mind is capable of justifying anything and everything. So it's important to pay attention to what we're trying to justify. Otherwise, we can do so many harmful things and hide behind our reasons.

The need to justify or explain an action can be an indication of how we inwardly feel about it.

**Bias and judgment**: When we justify, we polarize the actions that we choose to take (or not take). We defend our action as the right one and everything else as either irrelevant or wrong.

When we see the world through the judgment of our biased actions, we close ourselves off to other possibilities and alienate others.

**Unnecessary pain and suffering**: What we justify today we may regret tomorrow. If history can teach us anything, it's that a lot of what we justified in the past did nothing more than add needless suffering of massive proportions.

One question that can be helpful in making a choice is: _How far are we willing to go and justify in order to get what we want?_

## Letting go of the need to justify

Acting from a place of love and compassion doesn't require a reason. No one asks for an explanation when we extend a loving or helping hand to someone.

We don't explain why we love our families, or do the best we can at our work, or take care of a pet.

We can then conclude that the actions that support life and are in harmony with our truth are the ones that we're naturally inclined to do.

Letting go of the need to justify and acting from a place of truth does not place judgment on the action we're taking—good vs. evil, or right vs. wrong. It's about looking within and doing what feels right intuitively without the need for further explanation.

If we let go of the need to justify, we'll probably shed a lot of emotional stress and minimize unnecessary or painful actions. And in the process, we gain freedom to be who we truly are.

##### [Photo Credit](http://www.flickr.com/photos/jonathankosread/8244072348/)

redpajama_set_name: RedPajamaGithub


text:
package cmd

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/gobuffalo/envy"
	"github.com/gobuffalo/meta"
	"github.com/sirupsen/logrus"

	"github.com/gobuffalo/pop/v5"
	"github.com/spf13/cobra"
)

var forceMigrations = false

func init() {
	decorate("test", testCmd)
	RootCmd.AddCommand(testCmd)
}

var testCmd = &cobra.Command{
	Use:                "test",
	Short:              "Run the tests for the Buffalo app. Use --force-migrations to skip schema load.",
	DisableFlagParsing: true,
	RunE: func(c *cobra.Command, args []string) error {
		os.Setenv("GO_ENV", "test")
		if _, err := os.Stat("database.yml"); err == nil {
			// there's a database
			test, err := pop.Connect("test")
			if err != nil {
				return err
			}

			// drop the test db:
			test.Dialect.DropDB()

			// create the test db:
			err = test.Dialect.CreateDB()
			if err != nil {
				return err
			}

			// Read and remove --force-migrations flag from args:
			forceMigrations = strings.Contains(strings.Join(args, ""), "--force-migrations")
			args = cutArg("--force-migrations", args)
			if forceMigrations {
				fm, err := pop.NewFileMigrator("./migrations", test)

				if err != nil {
					return err
				}

				if err := fm.Up(); err != nil {
					return err
				}

				return testRunner(args)
			}

			if schema := findSchema(); schema != nil {
				err = test.Dialect.LoadSchema(schema)
				if err != nil {
					return err
				}
			}
		}
		return testRunner(args)
	},
}

func findSchema() io.Reader {
	if f, err := os.Open(filepath.Join("migrations", "schema.sql")); err == nil {
		return f
	}
	if dev, err := pop.Connect("development"); err == nil {
		schema := &bytes.Buffer{}
		if err = dev.Dialect.DumpSchema(schema); err == nil {
			return schema
		}
	}

	if test, err := pop.Connect("test"); err == nil {
		fm, err := pop.NewFileMigrator("./migrations", test)
		if err != nil {
			return nil
		}

		if err := fm.Up(); err == nil {
			return nil
		}
	}
	return nil
}

func testRunner(args []string) error {
	var mFlag bool
	var query string

	commandArgs := []string{}
	packageArgs := []string{}

	var lastArg string
	for index, arg := range args {
		switch arg {
		case "-run", "-m":
			query = args[index+1]
			mFlag = true
		case "-v", "-timeout":
			commandArgs = append(commandArgs, arg)
		default:
			if lastArg == "-timeout" {
				commandArgs = append(commandArgs, arg)
			} else if lastArg != "-run" && lastArg != "-m" {
				packageArgs = append(packageArgs, arg)
			}
		}

		lastArg = arg
	}

	cmd := newTestCmd(commandArgs)
	if mFlag {
		return mFlagRunner{
			query: query,
			args:  commandArgs,
			pargs: packageArgs,
		}.Run()
	}

	pkgs, err := testPackages(packageArgs)
	if err != nil {
		return err
	}

	cmd.Args = append(cmd.Args, pkgs...)
	logrus.Info(strings.Join(cmd.Args, " "))
	return cmd.Run()
}

type mFlagRunner struct {
	query string
	args  []string
	pargs []string
}

func (m mFlagRunner) Run() error {
	app := meta.New(".")
	pwd, _ := os.Getwd()
	defer os.Chdir(pwd)

	pkgs, err := testPackages(m.pargs)
	if err != nil {
		return err
	}

	var errs bool
	for _, p := range pkgs {
		os.Chdir(pwd)

		if p == app.PackagePkg {
			continue
		}

		p = strings.TrimPrefix(p, app.PackagePkg+string(filepath.Separator))
		os.Chdir(p)

		cmd := newTestCmd(m.args)
		if hasTestify(p) {
			cmd.Args = append(cmd.Args, "-testify.m", m.query)
		} else {
			cmd.Args = append(cmd.Args, "-run", m.query)
		}

		logrus.Info(strings.Join(cmd.Args, " "))

		if err := cmd.Run(); err != nil {
			errs = true
		}
	}
	if errs {
		return fmt.Errorf("errors running tests")
	}
	return nil
}

func hasTestify(p string) bool {
	cmd := exec.Command("go", "test", "-thisflagdoesntexist")
	b, _ := cmd.Output()
	return bytes.Contains(b, []byte("-testify.m"))
}

func testPackages(givenArgs []string) ([]string, error) {
	// If there are args, then assume these are the packages to test.
	//
	// Instead of always returning all packages from 'go list ./...', just
	// return the given packages in this case
	if len(givenArgs) > 0 {
		return givenArgs, nil
	}

	args := []string{}
	out, err := exec.Command(envy.Get("GO_BIN", "go"), "list", "./...").Output()
	if err != nil {
		return args, err
	}
	pkgs := bytes.Split(bytes.TrimSpace(out), []byte("\n"))
	for _, p := range pkgs {
		if !strings.Contains(string(p), "/vendor/") {
			args = append(args, string(p))
		}
	}
	return args, nil
}

func newTestCmd(args []string) *exec.Cmd {
	cargs := []string{"test", "-p", "1"}
	app := meta.New(".")
	cargs = append(cargs, "-tags", app.BuildTags("development").String())
	cargs = append(cargs, args...)
	cmd := exec.Command(envy.Get("GO_BIN", "go"), cargs...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd
}

func cutArg(arg string, args []string) []string {
	for i, v := range args {
		if v == arg {
			return append(args[:i], args[i+1:]...)
		}
	}

	return args
}


text:
qx.Class.define("soap.MethodDefinition", { extend: qx.core.Object
    ,members : {
         name: null
        ,message: null
        ,ns: null
    }
});