syntax.us Let the syntax do the talking
Blog Contact Posts Questions Tags Hire Me

Question:
What is 2015_0926 Code Challenge?

The 2015_0926 Code Challenge is a learning exercise for students attending Dan's Linux JavaScript Class at 3pm on 2015-09-26 at Hacker Dojo:

http://www.meetup.com/Dans-Linux-JavaScript-Class/events/225571816/

We start with three tasks: Install Ubuntu, enhance it, and create account named ann: Next, we learn some Emacs:
  • emacs ~ann/.emacs
  • Experiment with the controls at the top of the emacs window
  • Add this syntax to file: ~ann/.emacs
    ;;
    ;; ~ann/.emacs
    ;;
    (global-set-key "\C-xs" 'shell)
    (global-set-key "\em"   'buffer-menu-other-window)
    (global-set-key "\eR"   'rename-buffer)
    ;; end of .emacs
    
  • Figure out how to save and exit
  • Verify your work:
    cat ~ann/.emacs
  • Start 'bare' emacs:
    emacs
  • Start a shell in emacs with ctrl-x then letter 's'.
  • I should see something like this:
  • Rename the shell to SHELL1 with esc-key, then letter 'R'
  • I should see something like this:
  • List all buffers in my emacs with esc-key, then letter 'm'
  • I should see something like this:

I see the buffer-menu as similar to the Mac-Dock, Ubuntu-Launcher or the Windows-Taskbar.

The buffer-menu is my favorite emacs feature; it allows my mind to QUICKLY switch focus among three types of objects:
  • different shells
  • different files
  • different folders

The buffer-menu floats 'hot' shells, files and folders to the top.

This is useful behavior.

I mostly do 4 types of tasks on a laptop:
  • Interact with file
  • Interact with folder
  • Interact with shell
  • Interact with browser

I can do the first three types inside of emacs.

When I coordinate tasks with emacs rather than a Dock, I work much faster (because of the buffer-menu).

That sequence of creating a shell, and renaming it is the most difficult task I need to know.

All other emacs tasks can be done using mouse and arrow keys.

If I am an emacs power-user, I know two ways to use emacs to interact with folders.

The GUI-way is to click on the file-cabinet at the top.

Another way, which is quicker, is to type command ctrl-x then letter 'f'.

I should see something like this:


  • After I learn some Emacs or some other editor, I Install Node.js in this folder: ~ann/node/
  • cd ~ann
    wget https://nodejs.org/dist/v5.0.0/node-v5.0.0-linux-x64.tar.gz
    tar zxf node-v5.0.0-linux-x64.tar.gz
    rm -rf node
    mv node-v5.0.0-linux-x64 node
  • Then, I add Node.js to PATH:
    export       PATH="/home/ann/node/bin:${PATH}"
    echo 'export PATH="/home/ann/node/bin:${PATH}"' >> ~ann/.bashrc
  • Run a test:
    which node
    node -e 'console.log("hello world")'
  • Install CoffeScript to test npm:
    which npm
    npm install -g coffee-script
    ls -la /home/ann/node/lib/node_modules/
    which coffee
    coffee -e 'console.log "hello coffee!"'
  • Start work on an app:
    cd ~ann
    mkdir app13
    cd    ~ann/app13
    mkdir fee haml app
    cd ~ann/app13/app
    mkdir css img js partials phones
    cd ~ann/app13
    npm init # Respond with all defaults here
    # Now I have ~ann/app13/package.json
    # I will change it later.
    echo node_modules >> .gitignore
    git init
    git add .
    git commit -am ItsAlive
  • Install HAML to enhance app13:
    cd    ~ann/app13
    echo  gems >> .gitignore
    mkdir gems
    export GEM_HOME=/home/ann/app13/gems
    export PATH=${GEM_HOME}/bin:${PATH}
    gem install haml
  • With emacs, create ~ann/app13/haml/index.haml
    !!!
    %html(lang="en" ng-app="")
      %head
        %meta(content="text/html; charset=UTF-8" http-equiv="Content-Type")/
        %meta(charset="utf-8")/
        %title Google Phone Gallery
        %link(href="/app/css/bootstrap.css" rel="stylesheet")/
        %link(href="/app/css/app.css" rel="stylesheet")/
        %script(src="/app/js/angular.js")
      %body
        %ul
          %li
            %span Nexus S
            %p
              Fast just got faster with Nexus S.
          %li
            %span Motorola XOOM™ with Wi-Fi
            %p
              The Next, Next Generation tablet.
    
  • Use haml shell command to create ~ann/app13/app/index.html
    haml -eq ~ann/app13/haml/index.haml ~ann/app13/app/index.html
  • HAML should create this:
    <!DOCTYPE html>
    <html lang="en" ng-app="">
      <head>
        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
        <meta charset="utf-8">
        <title>Google Phone Gallery</title>
        <link href="/app/css/bootstrap.css" rel="stylesheet">
        <link href="/app/css/app.css" rel="stylesheet">
        <script src="/app/js/angular.js"></script>
      </head>
      <body>
        <ul>
          <li>
            <span>Nexus S</span>
            <p>
              Fast just got faster with Nexus S.
            </p>
          </li>
          <li>
            <span>Motorola XOOM™ with Wi-Fi</span>
            <p>
              The Next, Next Generation tablet.
            </p>
          </li>
        </ul>
      </body>
    </html>
    
    Obviously HAML is easier to read than HTML.
    Note that the above code comes from AngularJS Tutorial.
    Many thanks to angularjs.org
  • I add files needed by ~ann/app13/app/index.html
    cd ~ann/app13/app/css
    wget http://angular.github.io/angular-phonecat/step-1/app/bower_components/bootstrap/dist/css/bootstrap.css
    wget http://angular.github.io/angular-phonecat/step-1/app/css/app.css
    cd ~ann/app13/app/js
    wget http://angular.github.io/angular-phonecat/step-1/app/bower_components/angular/angular.js
  • I enhnace the app so it can serve the above files:
    cd ~ann/app13
    npm install --save http-server
    node ~ann/app13/node_modules/http-server/bin/http-server
  • With broswer, I visit localhost:8080/app and should see something like this:

  • Next, I kill the http-server process to free-up port 8080 on localhost.
  • Now, I work towards deploying this software to Heroku. I use emacs to enhance ~ann/app13/package.json:
    {
      "name": "app13",
      "version": "1.0.0",
      "description": "Demo by Dan",
      "main": "node_modules/http-server/bin/http-server",
      "scripts": {
        "test": "echo 'hello world' && exit"
      },
      "author": "Dan",
      "license": "ISC",
      "dependencies": {
        "http-server": "^0.8.0"
      },
      "engines": {
        "node": "4.0.0"
      }
    }
    
  • Create a one-line Procfile:
    cd ~ann/app13/
    echo 'web: node node_modules/http-server/bin/http-server -p $PORT' > Procfile
  • Install Heroku Toolbelt (standalone):
    dan@dev06:~ $ 
    dan@dev06:~ $ 
    dan@dev06:~ $ cd ~ann
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ curl https://toolbelt.heroku.com/install.sh | grep tgz
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100   975  100   975    0     0   1465      0 --:--:-- --:--:-- --:--:--  1495
        HEROKU_CLIENT_URL="https://s3.amazonaws.com/assets.heroku.com/heroku-client/heroku-client.tgz"
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ wget https://s3.amazonaws.com/assets.heroku.com/heroku-client/heroku-client.tgz
    --2015-09-14 18:52:29--  https://s3.amazonaws.com/assets.heroku.com/heroku-client/heroku-client.tgz
    Resolving s3.amazonaws.com (s3.amazonaws.com)... 54.231.17.120
    Connecting to s3.amazonaws.com (s3.amazonaws.com)|54.231.17.120|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 1828732 (1.7M) [application/x-gtar]
    Saving to: ‘heroku-client.tgz’
    
    100%[======================================>] 1,828,732    893KB/s   in 2.0s   
    
    2015-09-14 18:52:32 (893 KB/s) - ‘heroku-client.tgz’ saved [1828732/1828732]
    
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ tar zxf heroku-client.tgz 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ ls -la heroku-client/bin/
    total 12
    drwxr-xr-x 2 dan dan 4096 Aug 31 23:56 .
    drwxr-xr-x 6 dan dan 4096 Aug 31 23:56 ..
    -rwxr-xr-x 1 dan dan  644 Aug 31 23:56 heroku
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ echo export PATH=/home/ann/heroku-client/bin:${PATH} >> ~/.bashrc
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ export      PATH=/home/ann/heroku-client/bin:${PATH}
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ heroku help
    Usage: heroku COMMAND [--app APP] [command-specific-options]
    
    Primary help topics, type "heroku help TOPIC" for more details:
    
      addons    #  manage addon resources
      apps      #  manage apps (create, destroy)
      auth      #  authentication (login, logout)
      config    #  manage app config vars
      domains   #  manage domains
      logs      #  display logs for an app
      ps        #  manage dynos (dynos, workers)
      releases  #  manage app releases
      run       #  run one-off commands (console, rake)
      sharing   #  manage collaborators on an app
    
    Additional topics:
    
      buildpacks   #  manage the buildpack for an app
      certs        #  manage ssl endpoints for an app
      drains       #  display drains for an app
      features     #  manage optional features
      fork         #  clone an existing app
      git          #  manage git for apps
      help         #  list commands and display help
      keys         #  manage authentication keys
      labs         #  manage optional features
      local        #  run heroku app locally
      maintenance  #  manage maintenance mode for an app
      members      #  manage membership in organization accounts
      orgs         #  manage organization accounts
      pg           #  manage heroku-postgresql databases
      pgbackups    #  manage backups of heroku postgresql databases
      plugins      #  manage plugins to the heroku gem
      redis        #  list redis databases for an app
      regions      #  list available regions
      stack        #  manage the stack for an app
      status       #  check status of heroku platform
      twofactor    #  manage two-factor authentication settings
      update       #  update the heroku client
      version      #  display version
    
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    dan@dev06:/home/ann $ 
    
  • Create Heroku Account (cost: $0.00!):
    https://www.heroku.com
    Memorize login and password
  • Create ssh-key for ann account (assuming ann has none yet):
    ann@dev06:~$ 
    ann@dev06:~$ ssh-keygen -t rsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/ann/.ssh/id_rsa): 
    Created directory '/home/ann/.ssh'.
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /home/ann/.ssh/id_rsa.
    Your public key has been saved in /home/ann/.ssh/id_rsa.pub.
    The key fingerprint is:
    7c:73:e0:26:8b:62:f6:a4:1d:78:22:e5:cd:3a:5c:eb ann@dev06
    The key randomart image is:
    +--[ RSA 2048]----+
    |                 |
    |                 |
    |          .      |
    |       . . .     |
    |    .   S = .    |
    |   o +.. = o     |
    |  ..B.B..        |
    |   +oX..         |
    |    ooE          |
    +-----------------+
    ann@dev06:~$ 
    ann@dev06:~$ 
    ann@dev06:~$ 
    
  • Give a copy of ann public ssh-key to heroku:
    heroku status
    heroku auth:login
    heroku auth:whoami
    heroku keys:add
  • Create blank Heroku app and then git-push my app in:
    cd ~ann/app13
    heroku create ann926
    git add .
    git commit -am ready4heroku
    git push heroku master
    I should see something like this:
    ann@dev06:~/app13$ 
    ann@dev06:~/app13$ 
    ann@dev06:~/app13$ echo 'web: node node_modules/http-server/bin/http-server -p $PORT' > Procfile
    ann@dev06:~/app13$ ls -la ~/heroku-client/bin/
    total 12
    drwxr-xr-x 2 ann ann 4096 Aug 31 23:56 .
    drwxr-xr-x 6 ann ann 4096 Aug 31 23:56 ..
    -rwxr-xr-x 1 ann ann  644 Aug 31 23:56 heroku
    ann@dev06:~/app13$ heroku auth:whoami
    pizza4.us@gmail.com
    ann@dev06:~/app13$ 
    ann@dev06:~/app13$ 
    ann@dev06:~/app13$ heroku create ann926
    Creating ann926... done, stack is cedar-14
    https://ann926.herokuapp.com/ | https://git.heroku.com/ann926.git
    Git remote heroku added
    updating Heroku CLI...done. Updated to 3.42.2
    ann@dev06:~/app13$ git add .
    ann@dev06:~/app13$ git commit -am ready4heroku
    [master 859e064] ready4heroku
     11 files changed, 34575 insertions(+), 4 deletions(-)
     create mode 100644 Procfile
     create mode 100644 app/css/app.css
     create mode 100644 app/css/bootstrap.css
     create mode 100644 app/index.html
     create mode 100644 app/js/angular.js
     create mode 100644 haml/index.haml
    ann@dev06:~/app13$ git push heroku master
    Counting objects: 22, done.
    Delta compression using up to 3 threads.
    Compressing objects: 100% (19/19), done.
    Writing objects: 100% (22/22), 272.77 KiB | 0 bytes/s, done.
    Total 22 (delta 2), reused 0 (delta 0)
    remote: Compressing source files... done.        
    remote: Building source:        
    remote: 
    remote: -----> Node.js app detected        
    remote: 
    remote: -----> Creating runtime environment        
    remote:                
    remote:        NPM_CONFIG_LOGLEVEL=error        
    remote:        NPM_CONFIG_PRODUCTION=true        
    remote:        NODE_ENV=production        
    remote:        NODE_MODULES_CACHE=true        
    remote: 
    remote: -----> Installing binaries        
    remote:        engines.node (package.json):  4.0.0        
    remote:        engines.npm (package.json):   unspecified (use default)        
    remote:                
    remote:        Downloading and installing node 4.0.0...        
    remote:        Using default npm version: 2.14.2        
    remote: 
    remote: -----> Restoring cache        
    remote:        Skipping cache (new runtime signature)        
    remote: 
    remote: -----> Building dependencies        
    remote:        Pruning any extraneous modules        
    remote:        Installing node modules (package.json)        
    remote:        http-server@0.8.0 node_modules/http-server        
    remote:        ├── opener@1.4.1        
    remote:        ├── corser@2.0.0        
    remote:        ├── colors@1.0.3        
    remote:        ├── http-proxy@1.11.2 (eventemitter3@1.1.1, requires-port@0.0.1)        
    remote:        ├── optimist@0.6.1 (wordwrap@0.0.3, minimist@0.0.10)        
    remote:        ├── portfinder@0.4.0 (async@0.9.0, mkdirp@0.5.1)        
    remote:        ├── union@0.4.4 (qs@2.3.3)        
    remote:        └── ecstatic@0.7.6 (url-join@0.0.1, mime@1.3.4, minimist@1.2.0, he@0.5.0)        
    remote: 
    remote: -----> Caching build        
    remote:        Clearing previous node cache        
    remote:        Saving 1 cacheDirectories (default):        
    remote:        - node_modules        
    remote: 
    remote: -----> Build succeeded!        
    remote:        └── http-server@0.8.0        
    remote:                
    remote: -----> Discovering process types        
    remote:        Procfile declares types -> web        
    remote: 
    remote: -----> Compressing... done, 12.2MB        
    remote: -----> Launching... done, v3        
    remote:        https://ann926.herokuapp.com/ deployed to Heroku        
    remote: 
    remote: Verifying deploy.... done.        
    To https://git.heroku.com/ann926.git
     * [new branch]      master -> master
    ann@dev06:~/app13$ 
    ann@dev06:~/app13$ 
    ann@dev06:~/app13$ 
    
    I visit:
    https://ann926.herokuapp.com/app/index.html
    It loads. Yay!


Step 2 of the AngularJS tutorial is the next stop in the code challenge:
https://docs.angularjs.org/tutorial/step_02
I follow the steps below to do this:
  • cd ~ann
    git clone app13 app14
    rsync -a  app13/node_modules app14
    rsync -a  app13/gems         app14
    cd app14
    heroku create ann9262
    git push heroku master
  • I enhance ~ann/app14/haml/index.haml
    !!!
    %html(lang="en" ng-app="phonecatApp")
      %head
        %meta(content="text/html; charset=UTF-8" http-equiv="Content-Type")/
        %meta(charset="utf-8")/
        %title Google Phone Gallery
        %link(href="/app/css/bootstrap.css" rel="stylesheet")/
        %link(href="/app/css/app.css" rel="stylesheet")/
        %script(src="/app/js/angular.js")
        %script(src="/app/js/controllers.js")
      %body(ng-controller="PhoneListCtrl")
        %ul
          %li(ng-repeat="phone in phones")
            %span {{phone.name}}
            %p {{phone.snippet}}
    
  • A good feature of AngularJS can be seen in the above HAML.
    I see three attributes which tell me that AngularJS may be affecting regions of the page.
    Many other JS libraries do not offer this feature.
    For example When I look at HTML controlled by jQuery, the HTML does not tell me this fact.
    To understand jQuery control of a page, I need to walk my mind through a multi-step procedure.
    At the end of this procedure, my mind will have three pieces of information cached:
    • Some HTML
    • Some CSS
    • Some jQuery syntax
    At that point, my mind can start writing code.
    To me it feels cumbersome and mentally taxing.
  • To see a description of the above attributes (AKA 'Directives'):
    • ng-app
    • ng-controller
    • ng-repeat
    I should read this page:
    https://docs.angularjs.org/tutorial/step_02
  • Often my concept of %html(lang="en" ng-app="phonecatApp") is simple:
    • I have some JS code named "phonecatApp"
    • That code affects this html element
  • My concept of %body(ng-controller="PhoneListCtrl") is also simple:
    • I have some JS code named "PhoneListCtrl"
    • That code affects this body element
    • The HAML declares that body element is inside html element
    • As developer, I should place PhoneListCtrl-code inside phonecatApp
  • My concept of %li(ng-repeat="phone in phones") is this:
    • I have an Angular object named 'phones'
    • The 'phones' array should be created by PhoneListCtrl because the li-element is inside the body-element
    • The ng-repeat attribute will create an li-element for each phone in the phones array
  • Now that I understand the above HAML better,
    I use haml shell command to create ~ann/app14/app/index.html
    haml -eq ~ann/app14/haml/index.haml ~ann/app14/app/index.html
  • Next, I create a file of CoffeeScript named ~ann/app14/fee/controllers.coffee
    'use strict'
    
    ### Controllers ###
    
    phonecatApp = angular.module('phonecatApp', [])
    phonecatApp.controller 'PhoneListCtrl', ($scope) ->
      $scope.phones = [
        {
          'name': 'Nexus S'
          'snippet': 'Fast just got faster with Nexus S.'
        }
        {
          'name': 'Motorola XOOM™ with Wi-Fi'
          'snippet': 'The Next, Next Generation tablet.'
        }
        {
          'name': 'MOTOROLA XOOM™'
          'snippet': 'The Next, Next Generation tablet.'
        }
      ]
      return
    
  • The above code helps me:
    • It creates object named "phonecatApp"
    • It creates object named "PhoneListCtrl" inside "phonecatApp"
    • It creates array, $scope.phones inside "PhoneListCtrl"
    • $scope.phones will be exposed as 'phones' inside of index.html
  • Now that I understand the above CoffeeScript better, I transform it into JavaScript using the coffee command:
    coffee -bpc ~ann/app14/fee/controllers.coffee > ~ann/app14/app/js/controllers.js
  • I start the local server to see how app14 behaves
    cd   ~ann/app14
    node ~ann/app14/node_modules/http-server/bin/http-server
  • With broswer, I visit localhost:8080/app and should see something like this:

  • Next, I kill the http-server process to free-up port 8080 on localhost.
  • Then, I deploy app14 to heroku:
    git add .
    git commit -am DanWasHere
    git push heroku master
  • I should see app14 here:
    https://ann9262.herokuapp.com/app/index.html
Step 5 of the AngularJS tutorial is the next stop in the code challenge:
https://docs.angularjs.org/tutorial/step_05
I follow the steps below to do this:
  • cd ~ann
    git clone app14 app15
    rsync -a  app14/node_modules app15
    rsync -a  app14/gems         app15
    cd app15
    heroku create ann9265
    git push heroku master
  • I enhance ~ann/app15/haml/index.haml
    !!!
    %html(lang="en" ng-app="phonecatApp")
      %head
        %meta(content="text/html; charset=UTF-8" http-equiv="Content-Type")/
        %meta(charset="utf-8")/
        %title Google Phone Gallery
        %link(href="/app/css/bootstrap.css" rel="stylesheet")/
        %link(href="/app/css/app.css" rel="stylesheet")/
        %script(src="/app/js/angular.js")
        %script(src="/app/js/app.js")
      %body(ng-controller="PhoneListCtrl")
        .container-fluid
          .row
            .col-md-2
              / Sidebar content
              Search:
              %input(ng-model="query")/
              Sort by:
              %select(ng-model="orderProp")
                %option(value="name") Alphabetical
                %option(value="age") Newest
            .col-md-10
              / Body content
              %ul.phones
                %li(ng-repeat="phone in phones | filter:query | orderBy:orderProp")
                  %span {{phone.name}}
                  %p {{phone.snippet}}
    
    The head-element is the same as before except that I changed the name of controllers.js to app.js.
    The new name suggests I have more than just controller syntax in that file.
  • The next Angular-related enhancement is this syntax:
    %input(ng-model="query")/
    The above syntax allows the end-user to enter a string which is then referred to as "query".
  • The next Angular-related enhancement is this syntax:
    
    %select(ng-model="orderProp")
      %option(value="name") Alphabetical
      %option(value="age") Newest
    
    The above syntax allows the end-user to enter a string (either "name" or "age").
    That string is then referred to as "orderProp".
  • The next Angular-related enhancement is this syntax:
    %li(ng-repeat="phone in phones | filter:query | orderBy:orderProp")
    The above syntax allows an end-user to display phone-names which match a string held by the query variable.
    After the match is made, the end-user can order the resulting list by name or age.
  • After I enhance ~ann/app15/haml/index.haml
    I transform it into this file: ~ann/app15/app/index.html
    haml -eq ~ann/app15/haml/index.haml ~ann/app15/app/index.html
    The HTML file should look like this:
    <!DOCTYPE html>
    <html lang="en" ng-app="phonecatApp">
      <head>
        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
        <meta charset="utf-8">
        <title>Google Phone Gallery</title>
        <link href="/app/css/bootstrap.css" rel="stylesheet">
        <link href="/app/css/app.css" rel="stylesheet">
        <script src="/app/js/angular.js"></script>
        <script src="/app/js/app.js"></script>
      </head>
      <body ng-controller="PhoneListCtrl">
        <div class="container-fluid">
          <div class="row">
            <div class="col-md-2">
              <!-- Sidebar content -->
              Search:
              <input ng-model="query">
              Sort by:
              <select ng-model="orderProp">
                <option value="name">Alphabetical</option>
                <option value="age">Newest</option>
              </select>
            </div>
            <div class="col-md-10">
              <!-- Body content -->
              <ul class="phones">
                <li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
                  <span>{{phone.name}}</span>
                  <p>{{phone.snippet}}</p>
                </li>
              </ul>
            </div>
          </div>
        </div>
      </body>
    </html>
    
    Notice that the HAML file is easier to read than the HTML file
  • Next, I create a CoffeeScript file named ~ann/app15/fee/app.coffee
    'use strict'
    
    ### app.coffee, app.js ###
    
    phonecatApp = angular.module('phonecatApp', [])
    phonecatApp.controller 'PhoneListCtrl', [
      '$scope'
      '$http'
      ($scope, $http) ->
        $http.get('phones/phones.json').success (data) ->
          $scope.phones = data
          return
        $scope.orderProp = 'age'
        return
    ]
    
    The list below describes my understanding of the above syntax:
    • I have an object named 'phonecatApp'
    • Inside of phonecatApp I have an object named 'PhoneListCtrl'
    • Inside of PhoneListCtrl I have three objects:
      • $scope
      • $http
      • Anonymous function which:
        • Copies phones/phones.json into $scope.phones
        • Sets $scope.orderProp = 'age'
  • Next, I create app.js from app.coffee so that app.js can run in your browser:
    coffee -bpc ~ann/app15/fee/app.coffee > ~ann/app15/app/js/app.js
    The file app.js should look like this:
    // Generated by CoffeeScript 1.10.0
    'use strict';
    
    /* app.coffee, app.js */
    var phonecatApp;
    
    phonecatApp = angular.module('phonecatApp', []);
    
    phonecatApp.controller('PhoneListCtrl', [
      '$scope', '$http', function($scope, $http) {
        $http.get('phones/phones.json').success(function(data) {
          $scope.phones = data;
        });
        $scope.orderProp = 'age';
      }
    ]);
    
  • Then, I get a copy of phones/phones.json so my site can serve it when PhoneListCtrl asks for it:
    cd ~ann/app15/app
    mkdir phones
    curl http://angular.github.io/angular-phonecat/step-5/app/phones/phones.json > phones/phones.json
  • I start the local server to see how app15 behaves
    cd   ~ann/app15
    node ~ann/app15/node_modules/http-server/bin/http-server
  • I should see something like this:

  • Next, I deploy it to Heroku:
    cd ~ann/app15
    git add .
    git commit -am DanWasHere
    git push heroku master
  • Then I visit this URL:
    https://ann9265.herokuapp.com/app/index.html
  • Step 8 of the AngularJS tutorial is the next stop in the code challenge:
    https://docs.angularjs.org/tutorial/step_08
    I follow the steps below to do this:
    mkdir ~ann/app16
    cd    ~ann/app16
    mkdir app fee haml
    echo node_modules > .gitignore
    echo 'web: node node_modules/http-server/bin/http-server -p $PORT' > Procfile
    cd    ~ann/app16/app
    mkdir css js partials
    cd /tmp
    git clone https://github.com/angular/angular-phonecat.git phonecat
    cd phonecat
    git checkout step-8
    cp app/css/app.css ~ann/app16/app/css/
    rsync -a app/img    ~ann/app16/app
    rsync -a app/phones ~ann/app16/app
  • Next, I attach Node.js modules to app16:
    cd ~ann/app16
    npm init # Creates node_modules
    npm install --save http-server
  • Then, I copy ~ann/app13/package.json into ~ann/app16/package.json so it has syntax below:
    {
      "name": "app13",
      "version": "1.0.0",
      "description": "Demo by Dan",
      "main": "node_modules/http-server/bin/http-server",
      "scripts": {
        "test": "echo 'hello world' && exit"
      },
      "author": "Dan",
      "license": "ISC",
      "dependencies": {
        "http-server": "^0.8.0"
      },
      "engines": {
        "node": "4.0.0"
      }
    }
    
  • Next, I wget software from twitter and angularjs.org:
    cd ~ann/app16/app/css
    wget http://angular.github.io/angular-phonecat/step-8/app/bower_components/bootstrap/dist/css/bootstrap.css
    cd ~ann/app16/app/js
    wget http://angular.github.io/angular-phonecat/step-8/app/bower_components/angular/angular.js
    wget http://angular.github.io/angular-phonecat/step-8/app/bower_components/angular-route/angular-route.js
  • Then, I create a HAML file ~ann/app16/haml/index.haml
    !!!
    %html(lang="en" ng-app="phonecatApp")
      %head
        %meta(content="text/html; charset=UTF-8" http-equiv="Content-Type")/
        %meta(charset="utf-8")/
        %title Google Phone Gallery
        %link(href="/app/css/bootstrap.css" rel="stylesheet")/
        %link(href="/app/css/app.css" rel="stylesheet")/
        %script(src="/app/js/angular.js")
        %script(src="/app/js/angular-route.js")
        %script(src="/app/js/app.js")
      %body
        %div(ng-view="")
    
  • The above HAML asks your browser to load angular-route.js which provides an API which allows me, the developer, to change page-content when the URL field changes in your browser.
  • Also the above HAML contains the ng-view attribute:
    %div(ng-view="")
    The ng-view attribute is part of an AngularJS mechanism which adds modular behavior to HTML styntax.
    The most common modular behavior which I want is 'code reuse'.
    In this app, the code I want to reuse is the syntax in the head-element of the above HAML.
    The code I want to be changeable is in the body-element.
    I will control this changeable code with the help of ng-view attribute, my code in app.js,
    and HTML files I put under ~ann/app16/app/partials
  • Then, I create a HAML file ~ann/app16/haml/phone-list.haml
    .container-fluid
      .row
        .col-md-2
          / Sidebar content
          Search:
          %input(ng-model="query")/
          Sort by:
          %select(ng-model="orderProp")
            %option(value="name") Alphabetical
            %option(value="age") Newest
        .col-md-10
          / Body content
          %ul.phones
            %li.thumbnail(ng-repeat="phone in phones | filter:query | orderBy:orderProp")
              %a.thumb(href="#/phones/{{phone.id}}")
                %img(ng-src="{{phone.imageUrl}}")/
              %a(href="#/phones/{{phone.id}}") {{phone.name}}
              %p {{phone.snippet}}
    
    The only new syntax in the above HAML is the ng-src attribute in the img-element:
    %img(ng-src="{{phone.imageUrl}}")/
    It is obvious what that attribute does. It creates a src-attribute for %img.
    Also ng-src offers a performance enhancement.
    It creates the src-attribute BEFORE the page loads into your browser.
  • Next, I create a HAML file ~ann/app16/haml/phone-detail.haml
    %img.phone(ng-src="{{phone.images[0]}}")/
    %h1 {{phone.name}}
    %p {{phone.description}}
    %ul.phone-thumbs
      %li(ng-repeat="img in phone.images")
        %img(ng-src="{{img}}")/
    %ul.specs
      %li
        %span Availability and Networks
        %dl
          %dt Availability
          %dd(ng-repeat="availability in phone.availability") {{availability}}
      %li
        %span Battery
        %dl
          %dt Type
          %dd {{phone.battery.type}}
          %dt Talk Time
          %dd {{phone.battery.talkTime}}
          %dt Standby time (max)
          %dd {{phone.battery.standbyTime}}
      %li
        %span Storage and Memory
        %dl
          %dt RAM
          %dd {{phone.storage.ram}}
          %dt Internal Storage
          %dd {{phone.storage.flash}}
      %li
        %span Connectivity
        %dl
          %dt Network Support
          %dd {{phone.connectivity.cell}}
          %dt WiFi
          %dd {{phone.connectivity.wifi}}
          %dt Bluetooth
          %dd {{phone.connectivity.bluetooth}}
          %dt Infrared
          %dd {{phone.connectivity.infrared}}
          %dt GPS
          %dd {{phone.connectivity.gps}}
      %li
        %span Android
        %dl
          %dt OS Version
          %dd {{phone.android.os}}
          %dt UI
          %dd {{phone.android.ui}}
      %li
        %span Size and Weight
        %dl
          %dt Dimensions
          %dd(ng-repeat="dim in phone.sizeAndWeight.dimensions") {{dim}}
          %dt Weight
          %dd {{phone.sizeAndWeight.weight}}
      %li
        %span Display
        %dl
          %dt Screen size
          %dd {{phone.display.screenSize}}
          %dt Screen resolution
          %dd {{phone.display.screenResolution}}
          %dt Touch screen
          %dd {{phone.display.touchScreen}}
      %li
        %span Hardware
        %dl
          %dt CPU
          %dd {{phone.hardware.cpu}}
          %dt USB
          %dd {{phone.hardware.usb}}
          %dt Audio / headphone jack
          %dd {{phone.hardware.audioJack}}
          %dt FM Radio
          %dd {{phone.hardware.fmRadio}}
          %dt Accelerometer
          %dd {{phone.hardware.accelerometer}}
      %li
        %span Camera
        %dl
          %dt Primary
          %dd {{phone.camera.primary}}
          %dt Features
          %dd {{phone.camera.features.join(', ')}}
      %li
        %span Additional Features
        %dd {{phone.additionalFeatures}}
    
    The only new syntax in the above HAML is the join() method:
    %dd {{phone.camera.features.join(', ')}}
    The above syntax creates a string from an array.
  • Then, I create HTML files from the above HAML files:
    cd ~ann/app16/haml
    haml -eq index.haml        ~ann/app16/app/index.html
    haml -eq phone-list.haml   ~ann/app16/app/partials/phone-list.html
    haml -eq phone-detail.haml ~ann/app16/app/partials/phone-detail.html
  • Then, I add CoffeeScript syntax to a file named ~ann/app16/fee/app.coffee
    'use strict'
    
    ### app.coffee, app.js ###
    
    phonecatApp = angular.module('phonecatApp', [
      'ngRoute'
      'phonecatControllers'
    ])
    phonecatApp.config [
      '$routeProvider'
      ($routeProvider) ->
        $routeProvider.when('/phones',
          templateUrl: 'partials/phone-list.html'
          controller: 'PhoneListCtrl').when('/phones/:phoneId',
          templateUrl: 'partials/phone-detail.html'
          controller: 'PhoneDetailCtrl').otherwise redirectTo: '/phones'
        return
    ]
    
    phonecatControllers = angular.module('phonecatControllers', [])
    phonecatControllers.controller 'PhoneListCtrl', [
      '$scope'
      '$http'
      ($scope, $http) ->
        $http.get('phones/phones.json').success (data) ->
          $scope.phones = data
          return
        $scope.orderProp = 'age'
        return
    ]
    phonecatControllers.controller 'PhoneDetailCtrl', [
      '$scope'
      '$routeParams'
      '$http'
      ($scope, $routeParams, $http) ->
        $http.get('phones/' + $routeParams.phoneId + '.json').success (data) ->
          $scope.phone = data
          return
        return
    ]
    
    In the above file the most interesting syntax is the call to:
    phonecatApp.config
    The above syntax uses a concept I call 'route'.
    A route has four ideas:
    • A path (which will appear in the browser URL field)
    • A templateUrl (HTML served in response to path you type into URL field)
    • A controller (JavaScript which runs also in response to path)
    Know that a path can contain a static part and a parameter.
    The value of the parameter can be fed to a controller.
  • With the above ideas in my mind, I can explain the English logic behind the call to phonecatApp.config().
    • If I set path to /phones,
      Angular serves partials/phone-list.html
      My browser runs JS in PhoneListCtrl
    • If I set path to /phones/myPhone,
      Angular serves partials/phone-detail.html
      My browser runs JS in PhoneDetailCtrl
    • If I set path to /helloWorld,
      Angular sets path to /phones
  • Next, I create app.js from app.coffee
    coffee -bpc ~ann/app16/fee/app.coffee > ~ann/app16/app/js/app.js
  • At this point I have all the syntax I need to run this app.
    Time to commit it to git:
    cd ~ann/app16/
    git init
    git add .
    git commit -am helloApp16
  • Then I deploy it to heroku
    heroku create ann9268
    git push heroku master
  • I should see my app at this URL:
    https://ann9268.herokuapp.com/app/index.html
  • Step 12 of the AngularJS tutorial is the next stop in the code challenge:
    https://docs.angularjs.org/tutorial/step_012
    I follow the steps below to do this:
    mkdir ~ann/tmp
    cd    ~ann/tmp
    git clone https://github.com/angular/angular-phonecat.git phonecat
    cd    ~ann/tmp/phonecat
    git checkout step-12
    mkdir -p ~ann/app17/app/js
    cd ~ann/tmp/phonecat/app
    rsync -a css img phones ~ann/app17/app
    cd ~ann/app17
    mkdir fee haml
    echo node_modules > .gitignore
    echo 'web: node node_modules/http-server/bin/http-server -p $PORT' > Procfile
    cp ~ann/app13/package.json .
    cd ~ann/app17/app/css
    wget http://angular.github.io/angular-phonecat/step-12/app/bower_components/bootstrap/dist/css/bootstrap.css
    cd ~ann/app17/app/js
    wget http://angular.github.io/angular-phonecat/step-12/app/bower_components/jquery/dist/jquery.js
    wget http://angular.github.io/angular-phonecat/step-12/app/bower_components/angular/angular.js
    wget http://angular.github.io/angular-phonecat/step-12/app/bower_components/angular-animate/angular-animate.js
    wget http://angular.github.io/angular-phonecat/step-12/app/bower_components/angular-route/angular-route.js
    wget http://angular.github.io/angular-phonecat/step-12/app/bower_components/angular-resource/angular-resource.js
  • Then, I create a HAML file ~ann/app17/haml/index.haml
    !!!
    %html(lang="en" ng-app="phonecatApp")
      %head
        %meta(content="text/html; charset=UTF-8" http-equiv="Content-Type")/
        %meta(charset="utf-8")/
        %title Google Phone Gallery
        %link(href="/app/css/bootstrap.css"  rel="stylesheet")/
        %link(href="/app/css/app.css"        rel="stylesheet")/
        %link(href="/app/css/animations.css" rel="stylesheet")/
        %script(src="/app/js/jquery.js")
        %script(src="/app/js/angular.js")
        %script(src="/app/js/angular-animate.js")
        %script(src="/app/js/angular-route.js")
        %script(src="/app/js/angular-resource.js")
        %script(src="/app/js/app.js")
      %body
        .view-container
          .view-frame(ng-view="")
    
    The above syntax contains no new AngularJS API calls.
    It does however ask the browser to load some new third party JS files.
  • Next, I create a HAML file ~ann/app17/haml/phone-list.haml
    .container-fluid
      .row
        .col-md-2
          / Sidebar content
          Search:
          %input(ng-model="query")/
          Sort by:
          %select(ng-model="orderProp")
            %option(value="name") Alphabetical
            %option(value="age") Newest
        .col-md-10
          / Body content
          %ul.phones
            %li.thumbnail.phone-listing(ng-repeat="phone in phones | filter:query | orderBy:orderProp")
              %a.thumb(href="#/phones/{{phone.id}}")
                %img(ng-src="{{phone.imageUrl}}")/
              %a(href="#/phones/{{phone.id}}") {{phone.name}}
              %p {{phone.snippet}}
    
  • Then, I create a HAML file ~ann/app17/haml/phone-detail.haml
    .phone-images
      %img.phone(ng-class="{active: mainImageUrl==img}" ng-repeat="img in phone.images" ng-src="{{img}}")/
    %h1 {{phone.name}}
    %p {{phone.description}}
    %ul.phone-thumbs
      %li(ng-repeat="img in phone.images")
        %img(ng-click="setImage(img)" ng-src="{{img}}")/
    %ul.specs
      %li
        %span Availability and Networks
        %dl
          %dt Availability
          %dd(ng-repeat="availability in phone.availability") {{availability}}
      %li
        %span Battery
        %dl
          %dt Type
          %dd {{phone.battery.type}}
          %dt Talk Time
          %dd {{phone.battery.talkTime}}
          %dt Standby time (max)
          %dd {{phone.battery.standbyTime}}
      %li
        %span Storage and Memory
        %dl
          %dt RAM
          %dd {{phone.storage.ram}}
          %dt Internal Storage
          %dd {{phone.storage.flash}}
      %li
        %span Connectivity
        %dl
          %dt Network Support
          %dd {{phone.connectivity.cell}}
          %dt WiFi
          %dd {{phone.connectivity.wifi}}
          %dt Bluetooth
          %dd {{phone.connectivity.bluetooth}}
          %dt Infrared
          %dd {{phone.connectivity.infrared | checkmark}}
          %dt GPS
          %dd {{phone.connectivity.gps | checkmark}}
      %li
        %span Android
        %dl
          %dt OS Version
          %dd {{phone.android.os}}
          %dt UI
          %dd {{phone.android.ui}}
      %li
        %span Size and Weight
        %dl
          %dt Dimensions
          %dd(ng-repeat="dim in phone.sizeAndWeight.dimensions") {{dim}}
          %dt Weight
          %dd {{phone.sizeAndWeight.weight}}
      %li
        %span Display
        %dl
          %dt Screen size
          %dd {{phone.display.screenSize}}
          %dt Screen resolution
          %dd {{phone.display.screenResolution}}
          %dt Touch screen
          %dd {{phone.display.touchScreen | checkmark}}
      %li
        %span Hardware
        %dl
          %dt CPU
          %dd {{phone.hardware.cpu}}
          %dt USB
          %dd {{phone.hardware.usb}}
          %dt Audio / headphone jack
          %dd {{phone.hardware.audioJack}}
          %dt FM Radio
          %dd {{phone.hardware.fmRadio | checkmark}}
          %dt Accelerometer
          %dd {{phone.hardware.accelerometer | checkmark}}
      %li
        %span Camera
        %dl
          %dt Primary
          %dd {{phone.camera.primary}}
          %dt Features
          %dd {{phone.camera.features.join(', ')}}
      %li
        %span Additional Features
        %dd {{phone.additionalFeatures}}
    
  • Next, I create HTML files from the above HAML files:
    cd ~ann/app17/haml
    haml -eq index.haml        ~ann/app17/app/index.html
    mkdir                      ~ann/app17/app/partials
    haml -eq phone-list.haml   ~ann/app17/app/partials/phone-list.html
    haml -eq phone-detail.haml ~ann/app17/app/partials/phone-detail.html
  • Then, I add CoffeeScript syntax to a file named ~ann/app17/fee/app.coffee
    'use strict'
    
    ### app.coffee, app.js ###
    
    phonecatApp = angular.module('phonecatApp', [
      'ngRoute'
      'phonecatAnimations'
      'phonecatControllers'
      'phonecatFilters'
      'phonecatServices'
    ])
    phonecatApp.config [
      '$routeProvider'
      ($routeProvider) ->
        $routeProvider.when('/phones',
          templateUrl: 'partials/phone-list.html'
          controller: 'PhoneListCtrl').when('/phones/:phoneId',
          templateUrl: 'partials/phone-detail.html'
          controller: 'PhoneDetailCtrl').otherwise redirectTo: '/phones'
        return
    ]
    
    ### Controllers ###
    
    phonecatControllers = angular.module('phonecatControllers', [])
    phonecatControllers.controller 'PhoneListCtrl', [
      '$scope'
      'Phone'
      ($scope, Phone) ->
        $scope.phones = Phone.query()
        $scope.orderProp = 'age'
        return
    ]
    phonecatControllers.controller 'PhoneDetailCtrl', [
      '$scope'
      '$routeParams'
      'Phone'
      ($scope, $routeParams, Phone) ->
        $scope.phone = Phone.get({ phoneId: $routeParams.phoneId }, (phone) ->
          $scope.mainImageUrl = phone.images[0]
          return
        )
    
        $scope.setImage = (imageUrl) ->
          $scope.mainImageUrl = imageUrl
          return
    
        return
    ]
    
    ### Filters ###
    
    angular.module('phonecatFilters', []).filter 'checkmark', ->
      (input) ->
        if input then '\u2713' else '\u2718'
    
    ### animations ###
    
    phonecatAnimations = angular.module('phonecatAnimations', [ 'ngAnimate' ])
    phonecatAnimations.animation '.phone', ->
    
      animateUp = (element, className, done) ->
        if className != 'active'
          return
        element.css
          position: 'absolute'
          top: 500
          left: 0
          display: 'block'
        jQuery(element).animate { top: 0 }, done
        (cancel) ->
          if cancel
            element.stop()
          return
    
      animateDown = (element, className, done) ->
        if className != 'active'
          return
        element.css
          position: 'absolute'
          left: 0
          top: 0
        jQuery(element).animate { top: -500 }, done
        (cancel) ->
          if cancel
            element.stop()
          return
    
      {
        addClass: animateUp
        removeClass: animateDown
      }
    
    ### Services ###
    
    phonecatServices = angular.module('phonecatServices', [ 'ngResource' ])
    phonecatServices.factory 'Phone', [
      '$resource'
      ($resource) ->
        $resource 'phones/:phoneId.json', {}, query:
          method: 'GET'
          params: phoneId: 'phones'
          isArray: true
    ]
    
  • Next, I create app.js from app.coffee
    coffee -bpc ~ann/app17/fee/app.coffee > ~ann/app17/app/js/app.js
  • At this point I have all the syntax I need to run this app.
    Time to commit it to git:
    cd ~ann/app17/
    git init
    git add .
    git commit -am helloApp17
  • Then I deploy it to heroku
    heroku create ann92612
    git push heroku master
  • I should see my app at this URL:
    https://ann92612.herokuapp.com/app/index.html

That could be considered an adequate code challenge for a two hour Meetup.

If you have questions, e-me:

bikle101@gmail.com


syntax.us Let the syntax do the talking
Blog Contact Posts Questions Tags Hire Me